Bug 1195930 - Part 5: Core changes, support for storage directory versioning, new metadata v2 files, origin suffix added to API methods, origin attributes used across the implementation, more upgrade tests; r=asuth
authorJan Varga <jan.varga@gmail.com>
Sun, 05 Jun 2016 21:42:25 +0200
changeset 341577 e345136f5a86c04232e3bc0548cafd7976f8bd42
parent 341576 1e03e4b3efaf2b80b4912dd338aaee90a546dfbf
child 341578 bc2fd77579c9ca71bd5d4e155ca32597656b3298
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1195930
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1195930 - Part 5: Core changes, support for storage directory versioning, new metadata v2 files, origin suffix added to API methods, origin attributes used across the implementation, more upgrade tests; r=asuth
dom/asmjscache/AsmJSCache.cpp
dom/base/nsDOMWindowUtils.cpp
dom/cache/Context.cpp
dom/cache/ManagerId.cpp
dom/cache/Types.h
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/test/unit/idbSubdirUpgrade1_profile.zip
dom/indexedDB/test/unit/idbSubdirUpgrade2_profile.zip
dom/indexedDB/test/unit/storagePersistentUpgrade_profile.zip
dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
dom/indexedDB/test/unit/xpcshell-parent-process.ini
dom/quota/ActorsParent.cpp
dom/quota/QuotaManager.h
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -584,16 +584,17 @@ private:
 
   nsCOMPtr<nsIEventTarget> mOwningThread;
   const PrincipalInfo mPrincipalInfo;
   const OpenMode mOpenMode;
   const WriteParams mWriteParams;
 
   // State initialized during eInitial:
   quota::PersistenceType mPersistence;
+  nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
   RefPtr<DirectoryLock> mDirectoryLock;
 
   // State initialized during eReadyToReadMetadata
   nsCOMPtr<nsIFile> mDirectory;
   nsCOMPtr<nsIFile> mMetadataFile;
   Metadata mMetadata;
@@ -680,18 +681,18 @@ ParentRunnable::InitOnMainThread()
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &mOrigin,
-                                          &mIsApp);
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
+                                          &mOrigin, &mIsApp);
   NS_ENSURE_SUCCESS(rv, rv);
 
   InitPersistenceType();
 
   mEnforcingQuota =
     QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mIsApp);
 
   return NS_OK;
@@ -722,18 +723,18 @@ ParentRunnable::ReadMetadata()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == eReadyToReadMetadata);
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
 
   nsresult rv =
-    qm->EnsureOriginIsInitialized(mPersistence, mGroup, mOrigin, mIsApp,
-                                  getter_AddRefs(mDirectory));
+    qm->EnsureOriginIsInitialized(mPersistence, mSuffix, mGroup, mOrigin,
+                                  mIsApp, getter_AddRefs(mDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mResult = JS::AsmJSCache_StorageInitFailure;
     return rv;
   }
 
   rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2906,17 +2906,18 @@ nsDOMWindowUtils::GetFileReferences(cons
                                     int32_t* aSliceRefCnt, JSContext* aCx,
                                     bool* aResult)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCString origin;
   nsresult rv =
-    quota::QuotaManager::GetInfoFromWindow(window, nullptr, &origin, nullptr);
+    quota::QuotaManager::GetInfoFromWindow(window, nullptr, nullptr, &origin,
+                                           nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   IDBOpenDBOptions options;
   JS::Rooted<JS::Value> optionsVal(aCx, aOptions);
   if (!options.Init(aCx, optionsVal)) {
     return NS_ERROR_TYPE_ERR;
   }
 
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -372,16 +372,17 @@ Context::QuotaInitRunnable::Run()
       if (mCanceled) {
         resolver->Resolve(NS_ERROR_ABORT);
         break;
       }
 
       RefPtr<ManagerId> managerId = mManager->GetManagerId();
       nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
       nsresult rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                                       &mQuotaInfo.mSuffix,
                                                        &mQuotaInfo.mGroup,
                                                        &mQuotaInfo.mOrigin,
                                                        &mQuotaInfo.mIsApp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         resolver->Resolve(rv);
         break;
       }
 
@@ -430,16 +431,17 @@ Context::QuotaInitRunnable::Run()
       if (mCanceled) {
         resolver->Resolve(NS_ERROR_ABORT);
         break;
       }
 
       QuotaManager* qm = QuotaManager::Get();
       MOZ_ASSERT(qm);
       nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
+                                                  mQuotaInfo.mSuffix,
                                                   mQuotaInfo.mGroup,
                                                   mQuotaInfo.mOrigin,
                                                   mQuotaInfo.mIsApp,
                                                   getter_AddRefs(mQuotaInfo.mDir));
       if (NS_FAILED(rv)) {
         resolver->Resolve(rv);
         break;
       }
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -23,17 +23,18 @@ ManagerId::Create(nsIPrincipal* aPrincip
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // The QuotaManager::GetInfoFromPrincipal() has special logic for system
   // and about: principals.  We need to use the same modified origin in
   // order to interpret calls from QM correctly.
   nsCString quotaOrigin;
   nsresult rv = QuotaManager::GetInfoFromPrincipal(aPrincipal,
-                                                   nullptr,   //group
+                                                   nullptr,   // suffix
+                                                   nullptr,   // group
                                                    &quotaOrigin,
                                                    nullptr);  // is app
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   RefPtr<ManagerId> ref = new ManagerId(aPrincipal, quotaOrigin);
   ref.forget(aManagerIdOut);
 
   return NS_OK;
--- a/dom/cache/Types.h
+++ b/dom/cache/Types.h
@@ -26,16 +26,17 @@ static const Namespace INVALID_NAMESPACE
 
 typedef int64_t CacheId;
 static const CacheId INVALID_CACHE_ID = -1;
 
 struct QuotaInfo
 {
   QuotaInfo() : mIsApp(false) { }
   nsCOMPtr<nsIFile> mDir;
+  nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
   bool mIsApp;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -7186,16 +7186,17 @@ protected:
 
   // Must be released on the main thread!
   RefPtr<DirectoryLock> mDirectoryLock;
 
   RefPtr<FactoryOp> mDelayedOp;
   nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
 
   const CommonFactoryRequestParams mCommonParams;
+  nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
   nsCString mDatabaseId;
   nsString mDatabaseFilePath;
   State mState;
   bool mIsApp;
   bool mEnforcingQuota;
   const bool mDeleting;
@@ -17629,16 +17630,20 @@ Maintenance::DirectoryWork()
 
   if (IsAborted()) {
     return NS_ERROR_ABORT;
   }
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
+  if (NS_WARN_IF(NS_FAILED(quotaManager->EnsureStorageIsInitialized()))) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsCOMPtr<nsIFile> storageDir = GetFileForPath(quotaManager->GetStoragePath());
   MOZ_ASSERT(storageDir);
 
   bool exists;
   MOZ_ALWAYS_SUCCEEDS(storageDir->Exists(&exists));
   if (!exists) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -17794,23 +17799,25 @@ Maintenance::DirectoryWork()
         }
 
         // Found a database.
         if (databasePaths.IsEmpty()) {
           MOZ_ASSERT(group.IsEmpty());
           MOZ_ASSERT(origin.IsEmpty());
 
           int64_t dummyTimeStamp;
+          nsCString dummySuffix;
           bool dummyIsApp;
           if (NS_WARN_IF(NS_FAILED(
-                QuotaManager::GetDirectoryMetadata(originDir,
-                                                   &dummyTimeStamp,
-                                                   group,
-                                                   origin,
-                                                   &dummyIsApp)))) {
+                quotaManager->GetDirectoryMetadata2(originDir,
+                                                    &dummyTimeStamp,
+                                                    dummySuffix,
+                                                    group,
+                                                    origin,
+                                                    &dummyIsApp)))) {
             // Not much we can do here...
             continue;
           }
         }
 
         MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
 
         databasePaths.AppendElement(idbFilePath);
@@ -20149,17 +20156,17 @@ FactoryOp::CheckPermission(ContentParent
       }
 
       mChromeWriteAccessAllowed = canWrite;
     } else {
       mChromeWriteAccessAllowed = true;
     }
 
     if (State::Initial == mState) {
-      QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, &mIsApp);
+      QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin, &mIsApp);
 
       MOZ_ASSERT(!QuotaManager::IsFirstPromptRequired(persistenceType, mOrigin,
                                                       mIsApp));
 
       mEnforcingQuota =
         QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
     }
 
@@ -20171,20 +20178,25 @@ FactoryOp::CheckPermission(ContentParent
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  nsCString suffix;
   nsCString group;
   nsCString origin;
   bool isApp;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, &group, &origin, &isApp);
+  rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                          &suffix,
+                                          &group,
+                                          &origin,
+                                          &isApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
 #ifdef IDB_MOBILE
   if (persistenceType == PERSISTENCE_TYPE_PERSISTENT &&
       !QuotaManager::IsOriginWhitelistedForPersistentStorage(origin) &&
       !isApp) {
@@ -20216,16 +20228,17 @@ FactoryOp::CheckPermission(ContentParent
       }
     }
   } else {
     permission = PermissionRequestBase::kPermissionAllowed;
   }
 
   if (permission != PermissionRequestBase::kPermissionDenied &&
       State::Initial == mState) {
+    mSuffix = suffix;
     mGroup = group;
     mOrigin = origin;
     mIsApp = isApp;
 
     mEnforcingQuota =
       QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
   }
 
@@ -20690,16 +20703,17 @@ OpenDatabaseOp::DoDatabaseWork()
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   nsresult rv =
     quotaManager->EnsureOriginIsInitialized(persistenceType,
+                                            mSuffix,
                                             mGroup,
                                             mOrigin,
                                             mIsApp,
                                             getter_AddRefs(dbDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -1066,29 +1066,30 @@ IDBDatabase::GetQuotaInfo(nsACString& aO
   PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo();
   MOZ_ASSERT(principalInfo);
 
   switch (principalInfo->type()) {
     case PrincipalInfo::TNullPrincipalInfo:
       MOZ_CRASH("Is this needed?!");
 
     case PrincipalInfo::TSystemPrincipalInfo:
-      QuotaManager::GetInfoForChrome(nullptr, &aOrigin, nullptr);
+      QuotaManager::GetInfoForChrome(nullptr, nullptr, &aOrigin, nullptr);
       return NS_OK;
 
     case PrincipalInfo::TContentPrincipalInfo: {
       nsresult rv;
       nsCOMPtr<nsIPrincipal> principal =
         PrincipalInfoToPrincipal(*principalInfo, &rv);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       rv = QuotaManager::GetInfoFromPrincipal(principal,
                                               nullptr,
+                                              nullptr,
                                               &aOrigin,
                                               nullptr);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       return NS_OK;
     }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1f8066a4a032a181e308dc5c74203287c0e01115
GIT binary patch
literal 8066
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bbebcc12c7dce08d41fc6d0a84dffe7714d179af
GIT binary patch
literal 8398
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ecdb0e5ee0022e174560074b36e97dd6c22f13c7
GIT binary patch
literal 9480
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
BBccER
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
@@ -0,0 +1,72 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const openParams = [
+    // This one lives in storage/default/http+++www.mozilla.org
+    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
+
+    // This one lives in storage/default/1007+f+app+++system.gaiamobile.org
+    { appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
+      dbName: "dbM", dbVersion: 1 },
+
+    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
+    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
+      dbName: "dbN", dbVersion: 1 },
+  ];
+
+  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                         .getService(SpecialPowers.Ci.nsIIOService);
+
+  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
+                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+
+  function openDatabase(params) {
+    let uri = ios.newURI(params.url, null, null);
+    let principal =
+      ssm.createCodebasePrincipal(uri,
+                                  {appId: params.appId || ssm.NO_APPID,
+                                   inIsolatedMozBrowser: params.inIsolatedMozBrowser});
+    let request = indexedDB.openForPrincipal(principal, params.dbName,
+                                             params.dbVersion);
+    return request;
+  }
+
+  for (let i = 1; i <= 2; i++) {
+    clearAllDatabases(continueToNextStepSync);
+    yield undefined;
+
+    installPackagedProfile("idbSubdirUpgrade" + i + "_profile");
+
+    for (let params of openParams) {
+      let request = openDatabase(params);
+      request.onerror = errorHandler;
+      request.onupgradeneeded = unexpectedSuccessHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "success", "Correct event type");
+    }
+
+    resetAllDatabases(continueToNextStepSync);
+    yield undefined;
+
+    for (let params of openParams) {
+      let request = openDatabase(params);
+      request.onerror = errorHandler;
+      request.onupgradeneeded = unexpectedSuccessHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "success", "Correct event type");
+    }
+  }
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
@@ -0,0 +1,70 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const openParams = [
+    // This one lives in storage/default/http+++www.mozilla.org
+    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
+
+    // This one lives in storage/default/1007+f+app+++system.gaiamobile.org
+    { appId: 1007, inIsolatedMozBrowser: false, url: "app://system.gaiamobile.org",
+      dbName: "dbM", dbVersion: 1 },
+
+    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
+    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
+      dbName: "dbN", dbVersion: 1 },
+  ];
+
+  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+                         .getService(SpecialPowers.Ci.nsIIOService);
+
+  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
+                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
+
+  function openDatabase(params) {
+    let uri = ios.newURI(params.url, null, null);
+    let principal =
+      ssm.createCodebasePrincipal(uri,
+                                  {appId: params.appId || ssm.NO_APPID,
+                                   inIsolatedMozBrowser: params.inIsolatedMozBrowser});
+    let request = indexedDB.openForPrincipal(principal, params.dbName,
+                                             params.dbVersion);
+    return request;
+  }
+
+  clearAllDatabases(continueToNextStepSync);
+  yield undefined;
+
+  installPackagedProfile("storagePersistentUpgrade_profile");
+
+  for (let params of openParams) {
+    let request = openDatabase(params);
+    request.onerror = errorHandler;
+    request.onupgradeneeded = unexpectedSuccessHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    let event = yield undefined;
+
+    is(event.type, "success", "Correct event type");
+  }
+
+  resetAllDatabases(continueToNextStepSync);
+  yield undefined;
+
+  for (let params of openParams) {
+    let request = openDatabase(params);
+    request.onerror = errorHandler;
+    request.onupgradeneeded = unexpectedSuccessHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    let event = yield undefined;
+
+    is(event.type, "success", "Correct event type");
+  }
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -5,41 +5,46 @@
 [DEFAULT]
 dupe-manifest =
 head = xpcshell-head-parent-process.js
 tail =
 skip-if = toolkit == 'gonk'
 support-files =
   bug1056939_profile.zip
   defaultStorageUpgrade_profile.zip
+  idbSubdirUpgrade1_profile.zip
+  idbSubdirUpgrade2_profile.zip
   mutableFileUpgrade_profile.zip
   GlobalObjectsChild.js
   GlobalObjectsComponent.js
   GlobalObjectsComponent.manifest
   GlobalObjectsModule.jsm
   GlobalObjectsSandbox.js
   metadataRestore_profile.zip
   schema18upgrade_profile.zip
   schema21upgrade_profile.zip
+  storagePersistentUpgrade_profile.zip
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_blob_file_backed.js]
 [test_bug1056939.js]
 [test_cleanup_transaction.js]
 [test_defaultStorageUpgrade.js]
+[test_idbSubdirUpgrade.js]
 [test_globalObjects_ipc.js]
 skip-if = toolkit == 'android'
 [test_idle_maintenance.js]
 [test_invalidate.js]
 # disabled for the moment.
 skip-if = true
 [test_lowDiskSpace.js]
 [test_metadataRestore.js]
 [test_mutableFileUpgrade.js]
 [test_quotaExceeded_recovery.js]
 [test_readwriteflush_disabled.js]
 [test_schema18upgrade.js]
 [test_schema21upgrade.js]
+[test_storagePersistentUpgrade.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=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 "ActorsParent.h"
 
+#include "mozIStorageConnection.h"
+#include "mozIStorageService.h"
 #include "nsIBinaryInputStream.h"
 #include "nsIBinaryOutputStream.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIRunnable.h"
@@ -38,16 +40,18 @@
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/unused.h"
+#include "mozStorageCID.h"
+#include "mozStorageHelper.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsEscape.h"
@@ -87,22 +91,16 @@
 #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
 
 // 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"
-
 #define KB * 1024ULL
 #define MB * 1024ULL KB
 #define GB * 1024ULL MB
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 
@@ -117,48 +115,203 @@ const bool QuotaManager::kRunningXPCShel
 const char QuotaManager::kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
 
 namespace {
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
+// The database schema version we store in the SQLite database is a (signed)
+// 32-bit integer.
+const int32_t kSQLiteSchemaVersion = 1;
+
+const uint32_t kSQLitePageSizeOverride = 512;
+
+// The storage version we store in the SQLite database is a (signed) 32-bit
+// integer.
+const int32_t kStorageVersion = 1;
+
 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.");
 
 static_assert(
   static_cast<uint32_t>(StorageType::Default) ==
   static_cast<uint32_t>(PERSISTENCE_TYPE_DEFAULT),
   "Enum values should match.");
 
 const char kChromeOrigin[] = "chrome";
-const char kAboutHomeOrigin[] = "moz-safe-about:home";
+const char kAboutHomeOriginPrefix[] = "moz-safe-about:home";
 const char kIndexedDBOriginPrefix[] = "indexeddb://";
 const char kResourceOriginPrefix[] = "resource://";
 
 #define INDEXEDDB_DIRECTORY_NAME "indexedDB"
 #define STORAGE_DIRECTORY_NAME "storage"
 #define PERSISTENT_DIRECTORY_NAME "persistent"
 #define PERMANENT_DIRECTORY_NAME "permanent"
 #define TEMPORARY_DIRECTORY_NAME "temporary"
 #define DEFAULT_DIRECTORY_NAME "default"
 
 enum AppId {
   kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
   kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
 };
 
+#define STORAGE_FILE_NAME "storage.sqlite"
+
+// The name of the file that we use to load/save the last access time of an
+// origin.
+#define METADATA_FILE_NAME ".metadata"
+#define METADATA_V2_FILE_NAME ".metadata-v2"
+
+/******************************************************************************
+ * SQLite functions
+ ******************************************************************************/
+
+#if 0
+nsresult
+UpgradeSchemaFrom1To2(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  nsresult rv = aConnection->SetSchemaVersion(2);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+#endif
+
+nsresult
+CreateStorageConnection(nsIFile* aStorageFile,
+                        mozIStorageConnection** aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aStorageFile);
+  MOZ_ASSERT(aConnection);
+
+  nsresult rv;
+
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageConnection> connection;
+  rv = ss->OpenUnsharedDatabase(aStorageFile, getter_AddRefs(connection));
+  if (rv == NS_ERROR_FILE_CORRUPTED) {
+    // Nuke the database file.
+    rv = aStorageFile->Remove(false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = ss->OpenUnsharedDatabase(aStorageFile, getter_AddRefs(connection));
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Check to make sure that the database schema is correct.
+  int32_t schemaVersion;
+  rv = connection->GetSchemaVersion(&schemaVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (schemaVersion > kSQLiteSchemaVersion) {
+    NS_WARNING("Unable to open storage database, schema is too high!");
+    return NS_ERROR_FAILURE;
+  }
+
+  if (schemaVersion != kSQLiteSchemaVersion) {
+    const bool newDatabase = !schemaVersion;
+
+    if (newDatabase) {
+      // Set the page size first.
+      if (kSQLitePageSizeOverride) {
+        rv = connection->ExecuteSimpleSQL(
+          nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
+        );
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      }
+    }
+
+    mozStorageTransaction transaction(connection, false,
+                                  mozIStorageConnection::TRANSACTION_IMMEDIATE);
+    if (newDatabase) {
+      rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+        "CREATE TABLE storage"
+          "( version INTEGER NOT NULL DEFAULT 0"
+          ");"
+      ));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = connection->SetSchemaVersion(kSQLiteSchemaVersion);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
+      MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+
+    } else {
+      // This logic needs to change next time we change the schema!
+      static_assert(kSQLiteSchemaVersion == 1,
+                    "Upgrade function needed due to schema version increase.");
+
+#if 0
+      while (schemaVersion != kSQLiteSchemaVersion) {
+        if (schemaVersion == 1) {
+          rv = UpgradeSchemaFrom1To2(connection);
+        } else {
+          NS_WARNING("Unable to open storage database, no upgrade path is "
+                     "available!");
+          return NS_ERROR_FAILURE;
+        }
+
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        rv = connection->GetSchemaVersion(&schemaVersion);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      }
+
+      MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
+#endif
+    }
+
+    rv = transaction.Commit();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  connection.forget(aConnection);
+  return NS_OK;
+}
+
 /******************************************************************************
  * Quota manager class declarations
  ******************************************************************************/
 
 } // namespace
 
 class DirectoryLockImpl final
   : public DirectoryLock
@@ -1292,16 +1445,17 @@ struct StorageDirectoryHelper::OriginPro
     eChrome,
     eContent
   };
 
   nsCOMPtr<nsIFile> mDirectory;
   nsCString mSpec;
   PrincipalOriginAttributes mAttrs;
   int64_t mTimestamp;
+  nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
 
   Type mType;
   bool mIsApp;
   bool mNeedsRestore;
   bool mIgnore;
 
@@ -1342,34 +1496,37 @@ class MOZ_STACK_CLASS OriginParser final
     eExpectingPort,
     eExpectingEmptyTokenOrDriveLetterOrPathnameComponent,
     eExpectingEmptyTokenOrPathnameComponent,
     eComplete,
     eHandledTrailingSeparator
   };
 
   const nsCString mOrigin;
+  const PrincipalOriginAttributes mOriginAttributes;
   Tokenizer mTokenizer;
 
   uint32_t mAppId;
   nsCString mSchema;
   nsCString mHost;
   Nullable<uint32_t> mPort;
   nsTArray<nsCString> mPathnameComponents;
   nsCString mHandledTokens;
 
   SchemaType mSchemaType;
   State mState;
   bool mInIsolatedMozBrowser;
   bool mMaybeDriveLetter;
   bool mError;
 
 public:
-  explicit OriginParser(const nsACString& aOrigin)
+  OriginParser(const nsACString& aOrigin,
+               const PrincipalOriginAttributes& aOriginAttributes)
     : mOrigin(aOrigin)
+    , mOriginAttributes(aOriginAttributes)
     , mTokenizer(aOrigin, '+')
     , mAppId(kNoAppId)
     , mPort()
     , mSchemaType(eNone)
     , mState(eExpectingAppIdOrSchema)
     , mInIsolatedMozBrowser(false)
     , mMaybeDriveLetter(false)
     , mError(false)
@@ -1409,40 +1566,70 @@ public:
     , mPersistent(aPersistent)
   { }
 
   nsresult
   CreateOrUpgradeMetadataFiles();
 
 private:
   nsresult
+  MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
+
+  nsresult
   GetDirectoryMetadata(nsIFile* aDirectory,
                        int64_t* aTimestamp,
                        nsACString& aGroup,
                        nsACString& aOrigin,
                        bool* aHasIsApp);
 
   virtual nsresult
   DoProcessOriginDirectories();
 };
 
-class RestoreDirectoryMetadataHelper final
+class UpgradeDirectoryMetadataFrom1To2Helper final
   : public StorageDirectoryHelper
 {
   const bool mPersistent;
 
 public:
-  RestoreDirectoryMetadataHelper(nsIFile* aDirectory,
-                                 bool aPersistent)
+  UpgradeDirectoryMetadataFrom1To2Helper(nsIFile* aDirectory,
+                                         bool aPersistent)
     : StorageDirectoryHelper(aDirectory)
     , mPersistent(aPersistent)
   { }
 
   nsresult
-  RestoreMetadataFile();
+  UpgradeMetadataFiles();
+
+private:
+  nsresult
+  GetDirectoryMetadata(nsIFile* aDirectory,
+                       int64_t* aTimestamp,
+                       nsACString& aGroup,
+                       nsACString& aOrigin,
+                       bool* aIsApp);
+
+  virtual nsresult
+  DoProcessOriginDirectories() override;
+};
+
+class RestoreDirectoryMetadata2Helper final
+  : public StorageDirectoryHelper
+{
+  const bool mPersistent;
+
+public:
+  RestoreDirectoryMetadata2Helper(nsIFile* aDirectory,
+                                  bool aPersistent)
+    : StorageDirectoryHelper(aDirectory)
+    , mPersistent(aPersistent)
+  { }
+
+  nsresult
+  RestoreMetadata2File();
 
 private:
   virtual nsresult
   DoProcessOriginDirectories();
 };
 
 class OriginKey : public nsAutoCString
 {
@@ -1540,16 +1727,17 @@ GetLastModifiedTime(nsIFile* aFile, int6
       if (!isDirectory) {
         nsString leafName;
         rv = aFile->GetLeafName(leafName);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
+            leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
             leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
           return NS_OK;
         }
 
         int64_t timestamp;
         rv = aFile->GetLastModifiedTime(&timestamp);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -1630,327 +1818,328 @@ EnsureDirectory(nsIFile* aDirectory, boo
 
 enum FileFlag {
   kTruncateFileFlag,
   kUpdateFileFlag,
   kAppendFileFlag
 };
 
 nsresult
-GetDirectoryMetadataOutputStream(nsIFile* aDirectory, FileFlag aFileFlag,
-                                 nsIBinaryOutputStream** aStream)
+GetOutputStream(nsIFile* aDirectory,
+                const nsAString& aFilename,
+                FileFlag aFileFlag,
+                nsIOutputStream** aStream)
 {
   AssertIsOnIOThread();
 
-  nsCOMPtr<nsIFile> metadataFile;
-  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
+  rv = file->Append(aFilename);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIOutputStream> outputStream;
   switch (aFileFlag) {
     case kTruncateFileFlag: {
       rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
-                                       metadataFile);
+                                       file);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       break;
     }
 
     case kUpdateFileFlag: {
       bool exists;
-      rv = metadataFile->Exists(&exists);
+      rv = file->Exists(&exists);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       if (!exists) {
         *aStream = nullptr;
         return NS_OK;
       }
 
       nsCOMPtr<nsIFileStream> stream;
-      rv = NS_NewLocalFileStream(getter_AddRefs(stream), metadataFile);
+      rv = NS_NewLocalFileStream(getter_AddRefs(stream), file);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       outputStream = do_QueryInterface(stream);
       if (NS_WARN_IF(!outputStream)) {
         return NS_ERROR_FAILURE;
       }
 
       break;
     }
 
     case kAppendFileFlag: {
       rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
-                                       metadataFile,
+                                       file,
                                        PR_WRONLY | PR_CREATE_FILE | PR_APPEND);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       break;
     }
 
     default:
       MOZ_CRASH("Should never get here!");
   }
 
+  outputStream.forget(aStream);
+  return NS_OK;
+}
+
+nsresult
+GetBinaryOutputStream(nsIFile* aDirectory,
+                      const nsAString& aFilename,
+                      FileFlag aFileFlag,
+                      nsIBinaryOutputStream** aStream)
+{
+  nsCOMPtr<nsIOutputStream> outputStream;
+  nsresult rv = GetOutputStream(aDirectory,
+                                aFilename,
+                                aFileFlag,
+                                getter_AddRefs(outputStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+
   nsCOMPtr<nsIBinaryOutputStream> binaryStream =
     do_CreateInstance("@mozilla.org/binaryoutputstream;1");
   if (NS_WARN_IF(!binaryStream)) {
     return NS_ERROR_FAILURE;
   }
 
   rv = binaryStream->SetOutputStream(outputStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   binaryStream.forget(aStream);
   return NS_OK;
 }
 
-nsresult
-CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
-                        const nsACString& aGroup, const nsACString& aOrigin,
-                        bool aIsApp)
-{
-  AssertIsOnIOThread();
-
-  nsCOMPtr<nsIBinaryOutputStream> stream;
-  nsresult rv =
-    GetDirectoryMetadataOutputStream(aDirectory, kTruncateFileFlag,
-                                     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);
-
-  rv = stream->WriteBoolean(aIsApp);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+void
+GetJarPrefix(uint32_t aAppId,
+             bool aInIsolatedMozBrowser,
+             nsACString& aJarPrefix)
+{
+  MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
+
+  if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    aAppId = nsIScriptSecurityManager::NO_APP_ID;
+  }
+
+  aJarPrefix.Truncate();
+
+  // Fallback.
+  if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInIsolatedMozBrowser) {
+    return;
+  }
+
+  // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
+  aJarPrefix.AppendInt(aAppId);
+  aJarPrefix.Append('+');
+  aJarPrefix.Append(aInIsolatedMozBrowser ? 't' : 'f');
+  aJarPrefix.Append('+');
 }
 
 nsresult
-RestoreDirectoryMetadata(nsIFile* aDirectory, bool aPersistent)
-{
-  RefPtr<RestoreDirectoryMetadataHelper> helper =
-    new RestoreDirectoryMetadataHelper(aDirectory, aPersistent);
-
-  nsresult rv = helper->RestoreMetadataFile();
+CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
+                        const nsACString& aSuffix, const nsACString& aGroup,
+                        const nsACString& aOrigin, bool aIsApp)
+{
+  AssertIsOnIOThread();
+
+  PrincipalOriginAttributes groupAttributes;
+
+  nsCString groupNoSuffix;
+  bool ok = groupAttributes.PopulateFromOrigin(aGroup, groupNoSuffix);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCString groupPrefix;
+  GetJarPrefix(groupAttributes.mAppId,
+               groupAttributes.mInIsolatedMozBrowser,
+               groupPrefix);
+
+  nsCString group = groupPrefix + groupNoSuffix;
+
+  PrincipalOriginAttributes originAttributes;
+
+  nsCString originNoSuffix;
+  ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCString originPrefix;
+  GetJarPrefix(originAttributes.mAppId,
+               originAttributes.mInIsolatedMozBrowser,
+               originPrefix);
+
+  nsCString origin = originPrefix + originNoSuffix;
+
+  MOZ_ASSERT(groupPrefix == originPrefix);
+
+  nsCOMPtr<nsIBinaryOutputStream> stream;
+  nsresult rv = GetBinaryOutputStream(aDirectory,
+                                      NS_LITERAL_STRING(METADATA_FILE_NAME),
+                                      kTruncateFileFlag,
+                                      getter_AddRefs(stream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(stream);
+
+  rv = stream->Write64(aTimestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteStringZ(group.get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteStringZ(origin.get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteBoolean(aIsApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-GetDirectoryMetadataInputStream(nsIFile* aDirectory,
-                                nsIBinaryInputStream** aStream)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aDirectory);
-  MOZ_ASSERT(aStream);
-
-  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);
-
-  binaryStream.forget(aStream);
-  return NS_OK;
-}
-
-nsresult
-GetDirectoryMetadataWithRestore(nsIFile* aDirectory,
-                                bool aPersistent,
-                                int64_t* aTimestamp,
-                                nsACString& aGroup,
-                                nsACString& aOrigin,
-                                bool* aIsApp)
-{
-  nsresult rv = QuotaManager::GetDirectoryMetadata(aDirectory,
-                                                   aTimestamp,
-                                                   aGroup,
-                                                   aOrigin,
-                                                   aIsApp);
+CreateDirectoryMetadata2(nsIFile* aDirectory, int64_t aTimestamp,
+                         const nsACString& aSuffix, const nsACString& aGroup,
+                         const nsACString& aOrigin, bool aIsApp)
+{
+  AssertIsOnIOThread();
+
+  nsCOMPtr<nsIBinaryOutputStream> stream;
+  nsresult rv = GetBinaryOutputStream(aDirectory,
+                                      NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
+                                      kTruncateFileFlag,
+                                      getter_AddRefs(stream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(stream);
+
+  rv = stream->Write64(aTimestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Reserved for navigator.persist()
+  rv = stream->WriteBoolean(false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    rv = RestoreDirectoryMetadata(aDirectory, aPersistent);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = QuotaManager::GetDirectoryMetadata(aDirectory,
-                                            aTimestamp,
-                                            aGroup,
-                                            aOrigin,
-                                            aIsApp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    return rv;
+  }
+
+  // Reserved data 1
+  rv = stream->Write32(0);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Reserved data 2
+  rv = stream->Write32(0);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteStringZ(PromiseFlatCString(aSuffix).get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->WriteBoolean(aIsApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp)
-{
-  AssertIsOnIOThread();
+GetBinaryInputStream(nsIFile* aDirectory,
+                     const nsAString& aFilename,
+                     nsIBinaryInputStream** aStream)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aDirectory);
-  MOZ_ASSERT(aTimestamp);
-
-  nsCOMPtr<nsIBinaryInputStream> binaryStream;
-  nsresult rv =
-    GetDirectoryMetadataInputStream(aDirectory, getter_AddRefs(binaryStream));
+  MOZ_ASSERT(aStream);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  uint64_t timestamp;
-  rv = binaryStream->Read64(&timestamp);
+  rv = file->Append(aFilename);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  *aTimestamp = timestamp;
-  return NS_OK;
-}
-
-nsresult
-GetDirectoryMetadataWithRestore(nsIFile* aDirectory,
-                                bool aPersistent,
-                                int64_t* aTimestamp)
-{
-  nsresult rv = GetDirectoryMetadata(aDirectory, aTimestamp);
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    rv = RestoreDirectoryMetadata(aDirectory, aPersistent);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = GetDirectoryMetadata(aDirectory, aTimestamp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  return NS_OK;
-}
-
-nsresult
-MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
-{
-  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 rv;
+  }
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream =
+    do_CreateInstance("@mozilla.org/binaryinputstream;1");
+  if (NS_WARN_IF(!binaryStream)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = binaryStream->SetInputStream(bufferedStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  binaryStream.forget(aStream);
   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).
 nsresult
 GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
@@ -2225,16 +2414,23 @@ CreateRunnable::RegisterObserver()
 
   nsresult rv = observerService->AddObserver(observer,
                                              PROFILE_BEFORE_CHANGE_OBSERVER_ID,
                                              false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  // This service has to be started on the main thread currently.
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   QuotaManagerService* qms = QuotaManagerService::GetOrCreate();
   if (NS_WARN_IF(!qms)) {
     return rv;
   }
 
   qms->NoteLiveManager(mManager);
 
   return NS_OK;
@@ -2658,17 +2854,17 @@ QuotaObject::EnableQuotaCheck()
  * Quota manager
  ******************************************************************************/
 
 QuotaManager::QuotaManager()
 : mQuotaMutex("QuotaManager.mQuotaMutex"),
   mTemporaryStorageLimit(0),
   mTemporaryStorageUsage(0),
   mTemporaryStorageInitialized(false),
-  mStorageAreaInitialized(false)
+  mStorageInitialized(false)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!gInstance);
 }
 
 QuotaManager::~QuotaManager()
 {
   AssertIsOnOwningThread();
@@ -3017,26 +3213,28 @@ QuotaManager::CollectOriginsForEviction(
 
     return sizeToBeFreed;
   }
 
   return 0;
 }
 
 nsresult
-QuotaManager::Init(const nsAString& aBaseDirPath)
+QuotaManager::Init(const nsAString& aBasePath)
 {
   nsresult rv;
 
+  mBasePath = aBasePath;
+
   nsCOMPtr<nsIFile> baseDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = baseDir->InitWithPath(aBaseDirPath);
+  rv = baseDir->InitWithPath(aBasePath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = CloneStoragePath(baseDir,
                         NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME),
                         mIndexedDBPath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -3402,16 +3600,186 @@ QuotaManager::GetDirectoryForOrigin(Pers
   rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
   NS_ENSURE_SUCCESS(rv, rv);
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 nsresult
+QuotaManager::RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(mStorageInitialized);
+
+  RefPtr<RestoreDirectoryMetadata2Helper> helper =
+    new RestoreDirectoryMetadata2Helper(aDirectory, aPersistent);
+
+  nsresult rv = helper->RestoreMetadata2File();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
+                                    int64_t* aTimestamp,
+                                    nsACString& aSuffix,
+                                    nsACString& aGroup,
+                                    nsACString& aOrigin,
+                                    bool* aIsApp)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aTimestamp);
+  MOZ_ASSERT(aIsApp);
+  MOZ_ASSERT(mStorageInitialized);
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream;
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool persisted;
+  rv = binaryStream->ReadBoolean(&persisted);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint32_t reservedData1;
+  rv = binaryStream->Read32(&reservedData1);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint32_t reservedData2;
+  rv = binaryStream->Read32(&reservedData2);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString suffix;
+  rv = binaryStream->ReadCString(suffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString group;
+  rv = binaryStream->ReadCString(group);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString origin;
+  rv = binaryStream->ReadCString(origin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isApp;
+  rv = binaryStream->ReadBoolean(&isApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  *aTimestamp = timestamp;
+  aSuffix = suffix;
+  aGroup = group;
+  aOrigin = origin;
+  *aIsApp = isApp;
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
+                                               bool aPersistent,
+                                               int64_t* aTimestamp,
+                                               nsACString& aSuffix,
+                                               nsACString& aGroup,
+                                               nsACString& aOrigin,
+                                               bool* aIsApp)
+{
+  nsresult rv = GetDirectoryMetadata2(aDirectory,
+                                      aTimestamp,
+                                      aSuffix,
+                                      aGroup,
+                                      aOrigin,
+                                      aIsApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = GetDirectoryMetadata2(aDirectory,
+                               aTimestamp,
+                               aSuffix,
+                               aGroup,
+                               aOrigin,
+                               aIsApp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aTimestamp);
+  MOZ_ASSERT(mStorageInitialized);
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream;
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  *aTimestamp = timestamp;
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
+                                               bool aPersistent,
+                                               int64_t* aTimestamp)
+{
+  nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = GetDirectoryMetadata2(aDirectory, aTimestamp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
 QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
 {
   MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY ||
              aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory =
@@ -3456,36 +3824,37 @@ QuotaManager::InitializeRepository(Persi
 
     if (!isDirectory) {
       nsString leafName;
       rv = childDirectory->GetLeafName(leafName);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
-          leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
+      if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
         continue;
       }
 
       QM_WARNING("Something (%s) in the repository that doesn't belong!",
                  NS_ConvertUTF16toUTF8(leafName).get());
       return NS_ERROR_UNEXPECTED;
     }
 
     int64_t timestamp;
+    nsCString suffix;
     nsCString group;
     nsCString origin;
     bool isApp;
-    rv = GetDirectoryMetadataWithRestore(childDirectory,
-                                         /* aPersistent */ false,
-                                         &timestamp,
-                                         group,
-                                         origin,
-                                         &isApp);
+    rv = GetDirectoryMetadata2WithRestore(childDirectory,
+                                          /* aPersistent */ false,
+                                          &timestamp,
+                                          suffix,
+                                          group,
+                                          origin,
+                                          &isApp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (IsTreatedAsPersistent(aPersistenceType, isApp)) {
       continue;
     }
 
@@ -3540,21 +3909,16 @@ QuotaManager::InitializeOrigin(Persisten
                                bool aIsApp,
                                int64_t aAccessTime,
                                nsIFile* aDirectory)
 {
   AssertIsOnIOThread();
 
   nsresult rv;
 
-  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
-    rv = MaybeUpgradeOriginDirectory(aDirectory);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   bool trackQuota = IsQuotaEnforced(aPersistenceType, aOrigin, aIsApp);
 
   // We need to initialize directories of all clients if they exists and also
   // get the total usage to initialize the quota.
   nsAutoPtr<UsageInfo> usageInfo;
   if (trackQuota) {
     usageInfo = new UsageInfo();
   }
@@ -3572,16 +3936,17 @@ QuotaManager::InitializeOrigin(Persisten
     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(METADATA_V2_FILE_NAME) ||
         leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
       continue;
     }
 
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -3799,35 +4164,228 @@ QuotaManager::MaybeUpgradePersistentStor
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-QuotaManager::MaybeUpgradeStorageArea()
+QuotaManager::UpgradeStorageFrom0To1(mozIStorageConnection* aConnection)
 {
   AssertIsOnIOThread();
-
-  if (mStorageAreaInitialized) {
+  MOZ_ASSERT(aConnection);
+
+  nsresult rv;
+
+  for (const PersistenceType persistenceType : kAllPersistenceTypes) {
+    nsCOMPtr<nsIFile> directory =
+      do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = directory->InitWithPath(GetStoragePath(persistenceType));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
+    RefPtr<UpgradeDirectoryMetadataFrom1To2Helper> helper =
+      new UpgradeDirectoryMetadataFrom1To2Helper(directory, persistent);
+
+    rv = helper->UpgradeMetadataFiles();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+#ifdef DEBUG
+  {
+    nsCOMPtr<mozIStorageStatement> stmt;
+    rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+      "SELECT version "
+      "FROM storage"
+    ), getter_AddRefs(stmt));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool hasResult;
+    rv = stmt->ExecuteStep(&hasResult);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(!hasResult);
+  }
+#endif
+
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO storage (version) "
+    "VALUES (:version)"
+  ), getter_AddRefs(stmt));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("version"), kStorageVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stmt->Execute();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+#if 0
+nsresult
+QuotaManager::UpgradeStorageFrom1To2(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE storage SET version = 2;"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+#endif
+
+nsresult
+QuotaManager::EnsureStorageIsInitialized()
+{
+  AssertIsOnIOThread();
+
+  if (mStorageInitialized) {
     return NS_OK;
   }
 
   nsresult rv = MaybeUpgradeIndexedDBDirectory();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = MaybeUpgradePersistentStorageDirectory();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mStorageAreaInitialized = true;
+  nsCOMPtr<nsIFile> storageFile =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = storageFile->InitWithPath(mBasePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<mozIStorageConnection> connection;
+  rv = CreateStorageConnection(storageFile, getter_AddRefs(connection));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Load version information.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = connection->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT version "
+    "FROM storage"
+  ), getter_AddRefs(stmt));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  int32_t storageVersion;
+  if (hasResult) {
+    rv = stmt->GetInt32(0, &storageVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    storageVersion = 0;
+  }
+
+  if (storageVersion > kStorageVersion) {
+    NS_WARNING("Unable to initialize storage, version is too high!");
+    return NS_ERROR_FAILURE;
+  }
+
+  if (storageVersion != kStorageVersion) {
+    const bool newDatabase = !storageVersion;
+
+    if (newDatabase) {
+      rv = UpgradeStorageFrom0To1(connection);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    } else {
+      // This logic needs to change next time we change the schema!
+      static_assert(kStorageVersion == 1,
+                    "Upgrade function needed due to storage version increase.");
+
+#if 0
+      while (storageVersion != kStorageVersion) {
+        if (storageVersion == 1) {
+          rv = UpgradeStorageFrom1To2(connection);
+        } else {
+          NS_WARNING("Unable to initialize storage, no upgrade path is "
+                     "available!");
+          return NS_ERROR_FAILURE;
+        }
+
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        stmt->Reset();
+
+        rv = stmt->ExecuteStep(&hasResult);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        if (NS_WARN_IF(!hasResult)) {
+          return NS_ERROR_FILE_CORRUPTED;
+        }
+
+        rv = stmt->GetInt32(1, &storageVersion);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      }
+
+      MOZ_ASSERT(storageVersion == kStorageVersion);
+#endif
+    }
+  }
+
+  mStorageInitialized = true;
 
   return NS_OK;
 }
 
 void
 QuotaManager::OpenDirectory(PersistenceType aPersistenceType,
                             const nsACString& aGroup,
                             const nsACString& aOrigin,
@@ -3911,24 +4469,25 @@ QuotaManager::OpenDirectoryInternal(Null
         mClients[index]->AbortOperations(iter.Get()->GetKey());
       }
     }
   }
 }
 
 nsresult
 QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
+                                        const nsACString& aSuffix,
                                         const nsACString& aGroup,
                                         const nsACString& aOrigin,
                                         bool aIsApp,
                                         nsIFile** aDirectory)
 {
   AssertIsOnIOThread();
 
-  nsresult rv = MaybeUpgradeStorageArea();
+  nsresult rv = EnsureStorageIsInitialized();
   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);
 
@@ -3984,41 +4543,69 @@ QuotaManager::EnsureOriginIsInitialized(
   bool created;
   rv = EnsureDirectory(directory, &created);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
     if (created) {
       timestamp = PR_Now();
 
-      rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin,
+      rv = CreateDirectoryMetadata(directory,
+                                   timestamp,
+                                   aSuffix,
+                                   aGroup,
+                                   aOrigin,
                                    aIsApp);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = CreateDirectoryMetadata2(directory,
+                                    timestamp,
+                                    aSuffix,
+                                    aGroup,
+                                    aOrigin,
+                                    aIsApp);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
-      rv = GetDirectoryMetadataWithRestore(directory,
-                                           persistent,
-                                           &timestamp);
+      rv = GetDirectoryMetadata2WithRestore(directory,
+                                            persistent,
+                                            &timestamp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(timestamp <= PR_Now());
     }
 
     rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
                           directory);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mInitializedOrigins.AppendElement(OriginKey(aPersistenceType, aOrigin));
   } else if (created) {
     timestamp = PR_Now();
 
-    rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin,
+    rv = CreateDirectoryMetadata(directory,
+                                 timestamp,
+                                 aSuffix,
+                                 aGroup,
+                                 aOrigin,
                                  aIsApp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = CreateDirectoryMetadata2(directory,
+                                  timestamp,
+                                  aSuffix,
+                                  aGroup,
+                                  aOrigin,
+                                  aIsApp);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
                           directory);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   directory.forget(aDirectory);
@@ -4043,17 +4630,17 @@ QuotaManager::OriginClearCompleted(Persi
 
 void
 QuotaManager::ResetOrClearCompleted()
 {
   AssertIsOnIOThread();
 
   mInitializedOrigins.Clear();
   mTemporaryStorageInitialized = false;
-  mStorageAreaInitialized = false;
+  mStorageInitialized = false;
 
   ReleaseIOThreadObjects();
 }
 
 Client*
 QuotaManager::GetClient(Client::Type aClientType)
 {
   MOZ_ASSERT(aClientType >= Client::IDB);
@@ -4150,58 +4737,61 @@ TryGetInfoForAboutURI(nsIPrincipal* aPri
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
 QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
+                                   nsACString* aSuffix,
                                    nsACString* aGroup,
                                    nsACString* aOrigin,
                                    bool* aIsApp)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
 
   if (aGroup && aOrigin) {
     nsresult rv =
       TryGetInfoForAboutURI(aPrincipal, *aGroup, *aOrigin, aIsApp);
     if (NS_SUCCEEDED(rv)) {
       return NS_OK;
     }
   }
 
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    GetInfoForChrome(aGroup, aOrigin, aIsApp);
+    GetInfoForChrome(aSuffix, aGroup, aOrigin, aIsApp);
     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;
-  rv = aPrincipal->GetOriginNoSuffix(origin);
+  rv = aPrincipal->GetOrigin(origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (origin.EqualsLiteral(kChromeOrigin)) {
     NS_WARNING("Non-chrome principal can't use chrome origin!");
     return NS_ERROR_FAILURE;
   }
 
-  nsCString jarPrefix;
-  if (aGroup || aOrigin) {
-    rv = aPrincipal->GetJarPrefix(jarPrefix);
-    NS_ENSURE_SUCCESS(rv, rv);
+  nsCString suffix;
+  BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(suffix);
+
+  if (aSuffix)
+  {
+    aSuffix->Assign(suffix);
   }
 
   if (aGroup) {
     nsCString baseDomain;
     rv = aPrincipal->GetBaseDomain(baseDomain);
     if (NS_FAILED(rv)) {
       // A hack for JetPack.
 
@@ -4215,66 +4805,71 @@ QuotaManager::GetInfoFromPrincipal(nsIPr
 
       if (isIndexedDBURI) {
         rv = NS_OK;
       }
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (baseDomain.IsEmpty()) {
-      aGroup->Assign(jarPrefix + origin);
-    }
-    else {
-      aGroup->Assign(jarPrefix + baseDomain);
+      aGroup->Assign(origin);
+    } else {
+      aGroup->Assign(baseDomain + suffix);
     }
   }
 
   if (aOrigin) {
-    aOrigin->Assign(jarPrefix + origin);
+    aOrigin->Assign(origin);
   }
 
   if (aIsApp) {
     *aIsApp = aPrincipal->GetAppStatus() !=
                 nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
 QuotaManager::GetInfoFromWindow(nsPIDOMWindowOuter* aWindow,
+                                nsACString* aSuffix,
                                 nsACString* aGroup,
                                 nsACString* aOrigin,
                                 bool* aIsApp)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   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 = GetInfoFromPrincipal(principal, aGroup, aOrigin, aIsApp);
+  nsresult rv =
+    GetInfoFromPrincipal(principal, aSuffix, aGroup, aOrigin, aIsApp);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 // static
 void
-QuotaManager::GetInfoForChrome(nsACString* aGroup,
+QuotaManager::GetInfoForChrome(nsACString* aSuffix,
+                               nsACString* aGroup,
                                nsACString* aOrigin,
                                bool* aIsApp)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
 
+  if (aSuffix) {
+    aSuffix->Assign(EmptyCString());
+  }
   if (aGroup) {
     ChromeOrigin(*aGroup);
   }
   if (aOrigin) {
     ChromeOrigin(*aOrigin);
   }
   if (aIsApp) {
     *aIsApp = false;
@@ -4283,17 +4878,17 @@ QuotaManager::GetInfoForChrome(nsACStrin
 
 // static
 bool
 QuotaManager::IsOriginWhitelistedForPersistentStorage(const nsACString& aOrigin)
 {
   // The first prompt and quota tracking is not required for these origins in
   // persistent storage.
   if (aOrigin.EqualsLiteral(kChromeOrigin) ||
-      aOrigin.EqualsLiteral(kAboutHomeOrigin) ||
+      StringBeginsWith(aOrigin, nsDependentCString(kAboutHomeOriginPrefix)) ||
       StringBeginsWith(aOrigin, nsDependentCString(kIndexedDBOriginPrefix)) ||
       StringBeginsWith(aOrigin, nsDependentCString(kResourceOriginPrefix))) {
     return true;
   }
 
   return false;
 }
 
@@ -4321,62 +4916,16 @@ QuotaManager::IsQuotaEnforced(Persistenc
 
 // static
 void
 QuotaManager::ChromeOrigin(nsACString& aOrigin)
 {
   aOrigin.AssignLiteral(kChromeOrigin);
 }
 
-// static
-nsresult
-QuotaManager::GetDirectoryMetadata(nsIFile* aDirectory,
-                                   int64_t* aTimestamp,
-                                   nsACString& aGroup,
-                                   nsACString& aOrigin,
-                                   bool* aIsApp)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aDirectory);
-  MOZ_ASSERT(aTimestamp);
-
-  nsCOMPtr<nsIBinaryInputStream> binaryStream;
-  nsresult rv =
-    GetDirectoryMetadataInputStream(aDirectory, getter_AddRefs(binaryStream));
-  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);
-
-  bool isApp;
-  if (aIsApp) {
-    rv = binaryStream->ReadBoolean(&isApp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  *aTimestamp = timestamp;
-  aGroup = group;
-  aOrigin = origin;
-  if (aIsApp) {
-    *aIsApp = isApp;
-  }
-  return NS_OK;
-}
-
 uint64_t
 QuotaManager::LockedCollectOriginsForEviction(
                                   uint64_t aMinSizeToBeFreed,
                                   nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
 {
   mQuotaMutex.AssertCurrentThreadOwns();
 
   RefPtr<CollectOriginsHelper> helper =
@@ -5014,17 +5563,26 @@ OriginOperationBase::DirectoryWork()
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == State_DirectoryWorkOpen);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   if (NS_WARN_IF(!quotaManager)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = DoDirectoryWork(quotaManager);
+  nsresult rv;
+
+  if (mNeedsQuotaManagerInit) {
+    rv = quotaManager->EnsureStorageIsInitialized();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = DoDirectoryWork(quotaManager);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Must set mState before dispatching otherwise we will race with the owning
   // thread.
   AdvanceState();
 
@@ -5166,18 +5724,20 @@ SaveOriginAccessTimeOp::DoDirectoryWork(
     aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
                                          mOriginScope,
                                          getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIBinaryOutputStream> stream;
-  rv = GetDirectoryMetadataOutputStream(directory, kUpdateFileFlag,
-                                        getter_AddRefs(stream));
+  rv = GetBinaryOutputStream(directory,
+                             NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
+                             kUpdateFileFlag,
+                             getter_AddRefs(stream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // The origin directory may not exist anymore.
   if (stream) {
     rv = stream->Write64(mTimestamp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -5439,17 +5999,17 @@ GetUsageOp::DoInitOnMainThread()
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Figure out which origin we're dealing with.
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &origin,
+  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &mGroup, &origin,
                                           &mIsApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mOriginScope.SetFromOrigin(origin);
 
   return NS_OK;
@@ -5478,21 +6038,16 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
 
     if (IsTreatedAsPersistent(aPersistenceType, mIsApp)) {
       nsCString originKey = OriginKey(aPersistenceType, mOriginScope);
       initialized = aQuotaManager->IsOriginInitialized(originKey);
     } else {
       initialized = aQuotaManager->IsTemporaryStorageInitialized();
     }
 
-    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT && !initialized) {
-      rv = 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 && !mUsageInfo.Canceled()) {
       nsCOMPtr<nsISupports> entry;
@@ -5502,16 +6057,17 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
       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(METADATA_V2_FILE_NAME) ||
           leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
         continue;
       }
 
       if (!initialized) {
         bool isDirectory;
         rv = file->IsDirectory(&isDirectory);
         NS_ENSURE_SUCCESS(rv, rv);
@@ -5664,33 +6220,61 @@ QuotaRequestBase::ActorDestroy(ActorDest
 
   NoteActorDestroyed();
 }
 
 void
 ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
-  NS_ASSERTION(aQuotaManager, "Don't pass me null!");
+  MOZ_ASSERT(aQuotaManager);
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory =
     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS_VOID(rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
 
   rv = directory->InitWithPath(aQuotaManager->GetStoragePath());
-  NS_ENSURE_SUCCESS_VOID(rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
 
   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!");
+    MOZ_ASSERT(false, "Failed to remove storage directory!");
+  }
+
+  nsCOMPtr<nsIFile> storageFile =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = storageFile->InitWithPath(aQuotaManager->GetBasePath());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = storageFile->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 the storage connection
+    // correctly...
+    MOZ_ASSERT(false, "Failed to remove storage file!");
   }
 }
 
 nsresult
 ResetOrClearOp::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
 
@@ -5778,17 +6362,18 @@ OriginClearOp::DoInitOnMainThread()
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Figure out which origin we're dealing with.
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &origin, nullptr);
+  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr, &origin,
+                                          nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   const mozilla::PrincipalOriginAttributes& attrs =
     mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef();
 
   nsAutoCString pattern;
@@ -5864,25 +6449,27 @@ OriginClearOp::DeleteFiles(QuotaManager*
     if (!PatternMatchesOrigin(originSanitized,
                               NS_ConvertUTF16toUTF8(leafName))) {
       continue;
     }
 
     bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
 
     int64_t timestamp;
+    nsCString suffix;
     nsCString group;
     nsCString origin;
     bool isApp;
-    rv = GetDirectoryMetadataWithRestore(file,
-                                         persistent,
-                                         &timestamp,
-                                         group,
-                                         origin,
-                                         &isApp);
+    rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
+                                                         persistent,
+                                                         &timestamp,
+                                                         suffix,
+                                                         group,
+                                                         origin,
+                                                         &isApp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     for (uint32_t index = 0; index < 10; index++) {
       // We can't guarantee that this will always succeed on Windows...
       if (NS_SUCCEEDED((rv = file->Remove(true)))) {
         break;
@@ -5936,16 +6523,17 @@ OriginClearOp::GetResponse(RequestRespon
     aResponse = ClearOriginResponse();
   }
 }
 
 nsresult
 StorageDirectoryHelper::AddOriginDirectory(nsIFile* aDirectory,
                                            OriginProps** aOriginProps)
 {
+  AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
 
   OriginProps* originProps;
 
   nsString leafName;
   nsresult rv = aDirectory->GetLeafName(leafName);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -6028,17 +6616,18 @@ StorageDirectoryHelper::RunOnMainThread(
 
   for (uint32_t count = mOriginProps.Length(), index = 0;
        index < count;
        index++) {
     OriginProps& originProps = mOriginProps[index];
 
     switch (originProps.mType) {
       case OriginProps::eChrome: {
-        QuotaManager::GetInfoForChrome(&originProps.mGroup,
+        QuotaManager::GetInfoForChrome(&originProps.mSuffix,
+                                       &originProps.mGroup,
                                        &originProps.mOrigin,
                                        &originProps.mIsApp);
         break;
       }
 
       case OriginProps::eContent: {
         nsCOMPtr<nsIURI> uri;
         rv = NS_NewURI(getter_AddRefs(uri), originProps.mSpec);
@@ -6048,16 +6637,17 @@ StorageDirectoryHelper::RunOnMainThread(
 
         nsCOMPtr<nsIPrincipal> principal =
           BasePrincipal::CreateCodebasePrincipal(uri, originProps.mAttrs);
         if (NS_WARN_IF(!principal)) {
           return NS_ERROR_FAILURE;
         }
 
         rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                                &originProps.mSuffix,
                                                 &originProps.mGroup,
                                                 &originProps.mOrigin,
                                                 &originProps.mIsApp);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         break;
@@ -6094,17 +6684,25 @@ StorageDirectoryHelper::Run()
 bool
 OriginParser::ParseOrigin(const nsACString& aOrigin,
                           nsCString& aSpec,
                           PrincipalOriginAttributes* aAttrs)
 {
   MOZ_ASSERT(!aOrigin.IsEmpty());
   MOZ_ASSERT(aAttrs);
 
-  OriginParser parser(aOrigin);
+  PrincipalOriginAttributes originAttributes;
+
+  nsCString originNoSuffix;
+  bool ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
+  if (!ok) {
+    return false;
+  }
+
+  OriginParser parser(originNoSuffix, originAttributes);
   return parser.Parse(aSpec, aAttrs);
 }
 
 bool
 OriginParser::Parse(nsACString& aSpec, PrincipalOriginAttributes* aAttrs)
 {
   MOZ_ASSERT(aAttrs);
 
@@ -6133,17 +6731,23 @@ OriginParser::Parse(nsACString& aSpec, P
     QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin.get(),
                mHandledTokens.get());
 
     return false;
   }
 
   MOZ_ASSERT(mState == eComplete || mState == eHandledTrailingSeparator);
 
-  *aAttrs = PrincipalOriginAttributes(mAppId, mInIsolatedMozBrowser);
+  if (mAppId == kNoAppId) {
+    *aAttrs = mOriginAttributes;
+  } else {
+    MOZ_ASSERT(mOriginAttributes.mAppId == kNoAppId);
+
+    *aAttrs = PrincipalOriginAttributes(mAppId, mInIsolatedMozBrowser);
+  }
 
   nsAutoCString spec(mSchema);
 
   if (mSchemaType == eFile) {
     spec.AppendLiteral("://");
 
     for (uint32_t count = mPathnameComponents.Length(), index = 0;
          index < count;
@@ -6457,18 +7061,28 @@ OriginParser::HandleTrailingSeparator()
   mState = eHandledTrailingSeparator;
 }
 
 nsresult
 CreateOrUpgradeDirectoryMetadataHelper::CreateOrUpgradeMetadataFiles()
 {
   AssertIsOnIOThread();
 
+  bool exists;
+  nsresult rv = mDirectory->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsISimpleEnumerator> entries;
-  nsresult rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
@@ -6570,31 +7184,140 @@ CreateOrUpgradeDirectoryMetadataHelper::
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
+CreateOrUpgradeDirectoryMetadataHelper::MaybeUpgradeOriginDirectory(
+                                                            nsIFile* aDirectory)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  nsCOMPtr<nsIFile> metadataFile;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool exists;
+  rv = metadataFile->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    // Directory structure upgrade needed.
+    // Move all files to IDB specific directory.
+
+    nsString idbDirectoryName;
+    rv = Client::TypeToText(Client::IDB, idbDirectoryName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> idbDirectory;
+    rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = idbDirectory->Append(idbDirectoryName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return 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);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (NS_WARN_IF(!isDirectory)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+    }
+    else {
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    nsCOMPtr<nsISimpleEnumerator> entries;
+    rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool hasMore;
+    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+      nsCOMPtr<nsISupports> entry;
+      rv = entries->GetNext(getter_AddRefs(entry));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+      if (NS_WARN_IF(!file)) {
+        return rv;
+      }
+
+      nsString leafName;
+      rv = file->GetLeafName(leafName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (!leafName.Equals(idbDirectoryName)) {
+        rv = file->MoveTo(idbDirectory, EmptyString());
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      }
+    }
+
+    rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
 CreateOrUpgradeDirectoryMetadataHelper::GetDirectoryMetadata(
                                                             nsIFile* aDirectory,
                                                             int64_t* aTimestamp,
                                                             nsACString& aGroup,
                                                             nsACString& aOrigin,
                                                             bool* aHasIsApp)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aTimestamp);
   MOZ_ASSERT(aHasIsApp);
+  MOZ_ASSERT(!mPersistent);
 
   nsCOMPtr<nsIBinaryInputStream> binaryStream;
-  nsresult rv =
-    GetDirectoryMetadataInputStream(aDirectory, getter_AddRefs(binaryStream));
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   uint64_t timestamp;
   rv = binaryStream->Read64(&timestamp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -6634,16 +7357,17 @@ CreateOrUpgradeDirectoryMetadataHelper::
   for (uint32_t count = mOriginProps.Length(), index = 0;
        index < count;
        index++) {
     OriginProps& originProps = mOriginProps[index];
 
     if (mPersistent) {
       rv = CreateDirectoryMetadata(originProps.mDirectory,
                                    originProps.mTimestamp,
+                                   originProps.mSuffix,
                                    originProps.mGroup,
                                    originProps.mOrigin,
                                    originProps.mIsApp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // Move whitelisted origins to new persistent storage.
@@ -6701,27 +7425,29 @@ CreateOrUpgradeDirectoryMetadataHelper::
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     } else if (originProps.mNeedsRestore) {
       rv = CreateDirectoryMetadata(originProps.mDirectory,
                                    originProps.mTimestamp,
+                                   originProps.mSuffix,
                                    originProps.mGroup,
                                    originProps.mOrigin,
                                    originProps.mIsApp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else if (!originProps.mIgnore) {
       nsCOMPtr<nsIBinaryOutputStream> stream;
-      rv = GetDirectoryMetadataOutputStream(originProps.mDirectory,
-                                            kAppendFileFlag,
-                                            getter_AddRefs(stream));
+      rv = GetBinaryOutputStream(originProps.mDirectory,
+                                 NS_LITERAL_STRING(METADATA_FILE_NAME),
+                                 kAppendFileFlag,
+                                 getter_AddRefs(stream));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(stream);
 
       rv = stream->WriteBoolean(originProps.mIsApp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -6729,29 +7455,226 @@ CreateOrUpgradeDirectoryMetadataHelper::
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-RestoreDirectoryMetadataHelper::RestoreMetadataFile()
+UpgradeDirectoryMetadataFrom1To2Helper::UpgradeMetadataFiles()
+{
+  AssertIsOnIOThread();
+
+  bool exists;
+  nsresult rv = mDirectory->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
+    MOZ_ASSERT(originDir);
+
+    bool isDirectory;
+    rv = originDir->IsDirectory(&isDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!isDirectory) {
+      nsString leafName;
+      rv = originDir->GetLeafName(leafName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
+        QM_WARNING("Something (%s) in the storage directory that doesn't belong!",
+                   NS_ConvertUTF16toUTF8(leafName).get());
+
+      }
+      continue;
+    }
+
+    OriginProps* originProps;
+    rv = AddOriginDirectory(originDir, &originProps);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    int64_t timestamp;
+    nsCString group;
+    nsCString origin;
+    bool isApp;
+    nsresult rv = GetDirectoryMetadata(originDir,
+                                       &timestamp,
+                                       group,
+                                       origin,
+                                       &isApp);
+    if (NS_FAILED(rv)) {
+      if (!mPersistent) {
+        rv = GetLastModifiedTime(originDir, &timestamp);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        originProps->mTimestamp = timestamp;
+      }
+      originProps->mNeedsRestore = true;
+    } else {
+      originProps->mTimestamp = timestamp;
+    }
+  }
+
+  if (mOriginProps.IsEmpty()) {
+    return NS_OK;
+  }
+
+  rv = ProcessOriginDirectories();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UpgradeDirectoryMetadataFrom1To2Helper::GetDirectoryMetadata(
+                                                            nsIFile* aDirectory,
+                                                            int64_t* aTimestamp,
+                                                            nsACString& aGroup,
+                                                            nsACString& aOrigin,
+                                                            bool* aIsApp)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aTimestamp);
+  MOZ_ASSERT(aIsApp);
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream;
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString group;
+  rv = binaryStream->ReadCString(group);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString origin;
+  rv = binaryStream->ReadCString(origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isApp;
+  rv = binaryStream->ReadBoolean(&isApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  *aTimestamp = timestamp;
+  aGroup = group;
+  aOrigin = origin;
+  *aIsApp = isApp;
+  return NS_OK;
+}
+
+nsresult
+UpgradeDirectoryMetadataFrom1To2Helper::DoProcessOriginDirectories()
+{
+  AssertIsOnIOThread();
+
+  nsCOMPtr<nsIFile> permanentStorageDir;
+
+  for (uint32_t count = mOriginProps.Length(), index = 0;
+       index < count;
+       index++) {
+    OriginProps& originProps = mOriginProps[index];
+
+    nsresult rv;
+
+    if (originProps.mNeedsRestore) {
+      rv = CreateDirectoryMetadata(originProps.mDirectory,
+                                   originProps.mTimestamp,
+                                   originProps.mSuffix,
+                                   originProps.mGroup,
+                                   originProps.mOrigin,
+                                   originProps.mIsApp);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    rv = CreateDirectoryMetadata2(originProps.mDirectory,
+                                  originProps.mTimestamp,
+                                  originProps.mSuffix,
+                                  originProps.mGroup,
+                                  originProps.mOrigin,
+                                  originProps.mIsApp);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsString oldName;
+    rv = originProps.mDirectory->GetLeafName(oldName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsAutoCString originSanitized(originProps.mOrigin);
+    SanitizeOriginString(originSanitized);
+
+    NS_ConvertASCIItoUTF16 newName(originSanitized);
+
+    if (!oldName.Equals(newName)) {
+      rv = originProps.mDirectory->RenameTo(nullptr, newName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+RestoreDirectoryMetadata2Helper::RestoreMetadata2File()
 {
   AssertIsOnIOThread();
 
   nsresult rv;
 
-  if (mPersistent) {
-    rv = MaybeUpgradeOriginDirectory(mDirectory);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
   OriginProps* originProps;
   rv = AddOriginDirectory(mDirectory, &originProps);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (!mPersistent) {
     int64_t timestamp = INT64_MIN;
@@ -6767,28 +7690,29 @@ RestoreDirectoryMetadataHelper::RestoreM
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-RestoreDirectoryMetadataHelper::DoProcessOriginDirectories()
+RestoreDirectoryMetadata2Helper::DoProcessOriginDirectories()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mOriginProps.Length() == 1);
 
   OriginProps& originProps = mOriginProps[0];
 
-  nsresult rv = CreateDirectoryMetadata(originProps.mDirectory,
-                                        originProps.mTimestamp,
-                                        originProps.mGroup,
-                                        originProps.mOrigin,
-                                        originProps.mIsApp);
+  nsresult rv = CreateDirectoryMetadata2(originProps.mDirectory,
+                                         originProps.mTimestamp,
+                                         originProps.mSuffix,
+                                         originProps.mGroup,
+                                         originProps.mOrigin,
+                                         originProps.mIsApp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 } // namespace quota
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -16,16 +16,17 @@
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #include "Client.h"
 #include "PersistenceType.h"
 
 #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1"
 
+class mozIStorageConnection;
 class nsIEventTarget;
 class nsIPrincipal;
 class nsIThread;
 class nsITimer;
 class nsIURI;
 class nsPIDOMWindowOuter;
 class nsIRunnable;
 
@@ -197,16 +198,44 @@ public:
   void
   AbortOperationsForProcess(ContentParentId aContentParentId);
 
   nsresult
   GetDirectoryForOrigin(PersistenceType aPersistenceType,
                         const nsACString& aASCIIOrigin,
                         nsIFile** aDirectory) const;
 
+  nsresult
+  RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent);
+
+  nsresult
+  GetDirectoryMetadata2(nsIFile* aDirectory,
+                        int64_t* aTimestamp,
+                        nsACString& aSuffix,
+                        nsACString& aGroup,
+                        nsACString& aOrigin,
+                        bool* aIsApp);
+
+  nsresult
+  GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
+                                   bool aPersistent,
+                                   int64_t* aTimestamp,
+                                   nsACString& aSuffix,
+                                   nsACString& aGroup,
+                                   nsACString& aOrigin,
+                                   bool* aIsApp);
+
+  nsresult
+  GetDirectoryMetadata2(nsIFile* aDirectory, int64_t* aTimestamp);
+
+  nsresult
+  GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
+                                   bool aPersistent,
+                                   int64_t* aTimestamp);
+
   // This is the main entry point into the QuotaManager API.
   // Any storage API implementation (quota client) that participates in
   // centralized quota and storage handling should call this method to get
   // a directory lock which will protect client's files from being deleted
   // while they are still in use.
   // After a lock is acquired, client is notified via the open listener's
   // method DirectoryLockAcquired. If the lock couldn't be acquired, client
   // gets DirectoryLockFailed notification.
@@ -235,17 +264,21 @@ public:
                         OpenDirectoryListener* aOpenListener);
 
   // Collect inactive and the least recently used origins.
   uint64_t
   CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
                             nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
 
   nsresult
+  EnsureStorageIsInitialized();
+
+  nsresult
   EnsureOriginIsInitialized(PersistenceType aPersistenceType,
+                            const nsACString& aSuffix,
                             const nsACString& aGroup,
                             const nsACString& aOrigin,
                             bool aIsApp,
                             nsIFile** aDirectory);
 
   void
   OriginClearCompleted(PersistenceType aPersistenceType,
                        const nsACString& aOrigin,
@@ -286,16 +319,22 @@ public:
     NS_ASSERTION(mIOThread, "This should never be null!");
     return mIOThread;
   }
 
   Client*
   GetClient(Client::Type aClientType);
 
   const nsString&
+  GetBasePath() const
+  {
+    return mBasePath;
+  }
+
+  const nsString&
   GetStoragePath() const
   {
     return mStoragePath;
   }
 
   const nsString&
   GetStoragePath(PersistenceType aPersistenceType) const
   {
@@ -318,28 +357,31 @@ public:
   static void
   GetStorageId(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                Client::Type aClientType,
                nsACString& aDatabaseId);
 
   static nsresult
   GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
+                       nsACString* aSuffix,
                        nsACString* aGroup,
                        nsACString* aOrigin,
                        bool* aIsApp);
 
   static nsresult
   GetInfoFromWindow(nsPIDOMWindowOuter* aWindow,
+                    nsACString* aSuffix,
                     nsACString* aGroup,
                     nsACString* aOrigin,
                     bool* aIsApp);
 
   static void
-  GetInfoForChrome(nsACString* aGroup,
+  GetInfoForChrome(nsACString* aSuffix,
+                   nsACString* aGroup,
                    nsACString* aOrigin,
                    bool* aIsApp);
 
   static bool
   IsOriginWhitelistedForPersistentStorage(const nsACString& aOrigin);
 
   static bool
   IsFirstPromptRequired(PersistenceType aPersistenceType,
@@ -367,23 +409,16 @@ public:
   GetOriginPatternStringMaybeIgnoreBrowser(uint32_t aAppId, bool aBrowserOnly,
                                            nsAutoCString& _retval)
   {
     return GetOriginPatternString(aAppId,
                                   aBrowserOnly ? MozBrowser : IgnoreMozBrowser,
                                   EmptyCString(), _retval);
   }
 
-  static nsresult
-  GetDirectoryMetadata(nsIFile* aDirectory,
-                       int64_t* aTimestamp,
-                       nsACString& aGroup,
-                       nsACString& aOrigin,
-                       bool* aIsApp);
-
 private:
   QuotaManager();
 
   virtual ~QuotaManager();
 
   nsresult
   Init(const nsAString& aBaseDirPath);
 
@@ -427,17 +462,22 @@ private:
 
   nsresult
   MaybeUpgradeIndexedDBDirectory();
 
   nsresult
   MaybeUpgradePersistentStorageDirectory();
 
   nsresult
-  MaybeUpgradeStorageArea();
+  UpgradeStorageFrom0To1(mozIStorageConnection* aConnection);
+
+#if 0
+  nsresult
+  UpgradeStorageFrom1To2(mozIStorageConnection* aConnection);
+#endif
 
   nsresult
   InitializeRepository(PersistenceType aPersistenceType);
 
   nsresult
   InitializeOrigin(PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
@@ -498,24 +538,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;
 
   AutoTArray<RefPtr<Client>, Client::TYPE_MAX> mClients;
 
+  nsString mBasePath;
   nsString mIndexedDBPath;
   nsString mStoragePath;
   nsString mPermanentStoragePath;
   nsString mTemporaryStoragePath;
   nsString mDefaultStoragePath;
 
   uint64_t mTemporaryStorageLimit;
   uint64_t mTemporaryStorageUsage;
   bool mTemporaryStorageInitialized;
 
-  bool mStorageAreaInitialized;
+  bool mStorageInitialized;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif /* mozilla_dom_quota_quotamanager_h__ */