Bug 1669437 - Add necessary infrastructure for independent in-memory only local storage database; r=asuth,dom-workers-and-storage-reviewers
authorJan Varga <jvarga@mozilla.com>
Wed, 11 Nov 2020 13:12:57 +0000
changeset 556739 661f0d8ae4c44db58e668c831b555dbc038b77d0
parent 556738 4806fdbc8d1781fffa7f4aed8e15dbe770e3ba3b
child 556740 95c4cfea94a24d7573b36826ba3a015ca82b8a6d
push id37940
push userccoroiu@mozilla.com
push dateWed, 11 Nov 2020 16:38:50 +0000
treeherdermozilla-central@661f0d8ae4c4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, dom-workers-and-storage-reviewers
bugs1669437
milestone84.0a1
first release with
nightly linux32
661f0d8ae4c4 / 84.0a1 / 20201111163850 / files
nightly linux64
661f0d8ae4c4 / 84.0a1 / 20201111163850 / files
nightly mac
661f0d8ae4c4 / 84.0a1 / 20201111163850 / files
nightly win32
661f0d8ae4c4 / 84.0a1 / 20201111163850 / files
nightly win64
661f0d8ae4c4 / 84.0a1 / 20201111163850 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1669437 - Add necessary infrastructure for independent in-memory only local storage database; r=asuth,dom-workers-and-storage-reviewers The new infrastructure consists of a separate bridge between the content and the parent process and a separate local storage database in the parent process. The new infrastructure can be used for storing and sharing of private browsing data across content processes. This patch only creates necessary infrastructure, actual enabling of storing and sharing of data across content processes will be done in a follow-up patch. Differential Revision: https://phabricator.services.mozilla.com/D96562
dom/storage/LocalStorageCache.cpp
dom/storage/LocalStorageCache.h
dom/storage/LocalStorageManager.cpp
dom/storage/LocalStorageManager.h
dom/storage/StorageDBThread.cpp
dom/storage/StorageDBThread.h
dom/storage/StorageIPC.cpp
dom/storage/StorageIPC.h
dom/storage/StorageObserver.cpp
dom/storage/StorageObserver.h
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js
--- a/dom/storage/LocalStorageCache.cpp
+++ b/dom/storage/LocalStorageCache.cpp
@@ -124,16 +124,17 @@ void LocalStorageCache::Init(LocalStorag
                              nsIPrincipal* aPrincipal,
                              const nsACString& aQuotaOriginScope) {
   if (mInitialized) {
     return;
   }
 
   mInitialized = true;
   aPrincipal->OriginAttributesRef().CreateSuffix(mOriginSuffix);
+  mPrivateBrowsingId = aPrincipal->GetPrivateBrowsingId();
   mPersistent = aPersistent;
   if (aQuotaOriginScope.IsEmpty()) {
     mQuotaOriginScope = Origin();
   } else {
     mQuotaOriginScope = aQuotaOriginScope;
   }
 
   if (mPersistent) {
@@ -142,17 +143,17 @@ void LocalStorageCache::Init(LocalStorag
   }
 
   // Check the quota string has (or has not) the identical origin suffix as
   // this storage cache is bound to.
   MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
   MOZ_ASSERT(mOriginSuffix.IsEmpty() !=
              StringBeginsWith(mQuotaOriginScope, "^"_ns));
 
-  mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
+  mUsage = aManager->GetOriginUsage(mQuotaOriginScope, mPrivateBrowsingId);
 }
 
 void LocalStorageCache::NotifyObservers(const LocalStorage* aStorage,
                                         const nsString& aKey,
                                         const nsString& aOldValue,
                                         const nsString& aNewValue) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aStorage);
@@ -209,17 +210,18 @@ bool LocalStorageCache::ProcessUsageDelt
   return true;
 }
 
 void LocalStorageCache::Preload() {
   if (mLoaded || !mPersistent) {
     return;
   }
 
-  StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
+  StorageDBChild* storageChild =
+      StorageDBChild::GetOrCreate(mPrivateBrowsingId);
   if (!storageChild) {
     mLoaded = true;
     mLoadResult = NS_ERROR_FAILURE;
     return;
   }
 
   storageChild->AsyncPreload(this);
 }
@@ -249,17 +251,17 @@ void LocalStorageCache::WaitForPreload(T
   // SyncPreload will just wait for it to finish rather then synchronously
   // read from the database.  It seems to me more optimal.
 
   // TODO place for A/B testing (force main thread load vs. let preload finish)
 
   // No need to check sDatabase for being non-null since preload is either
   // done before we've shut the DB down or when the DB could not start,
   // preload has not even be started.
-  StorageDBChild::Get()->SyncPreload(this);
+  StorageDBChild::Get(mPrivateBrowsingId)->SyncPreload(this);
 }
 
 nsresult LocalStorageCache::GetLength(const LocalStorage* aStorage,
                                       uint32_t* aRetval) {
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
@@ -369,17 +371,17 @@ nsresult LocalStorageCache::SetItem(cons
     return NS_OK;
   }
 
 #if !defined(MOZ_WIDGET_ANDROID)
   NotifyObservers(aStorage, nsString(aKey), aOld, aValue);
 #endif
 
   if (Persist(aStorage)) {
-    StorageDBChild* storageChild = StorageDBChild::Get();
+    StorageDBChild* storageChild = StorageDBChild::Get(mPrivateBrowsingId);
     if (!storageChild) {
       NS_ERROR(
           "Writing to localStorage after the database has been shut down"
           ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     if (DOMStringIsNull(aOld)) {
@@ -418,17 +420,17 @@ nsresult LocalStorageCache::RemoveItem(c
     return NS_OK;
   }
 
 #if !defined(MOZ_WIDGET_ANDROID)
   NotifyObservers(aStorage, nsString(aKey), aOld, VoidString());
 #endif
 
   if (Persist(aStorage)) {
-    StorageDBChild* storageChild = StorageDBChild::Get();
+    StorageDBChild* storageChild = StorageDBChild::Get(mPrivateBrowsingId);
     if (!storageChild) {
       NS_ERROR(
           "Writing to localStorage after the database has been shut down"
           ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return storageChild->AsyncRemoveItem(this, aKey);
@@ -469,17 +471,17 @@ nsresult LocalStorageCache::Clear(const 
 
 #if !defined(MOZ_WIDGET_ANDROID)
   if (hadData) {
     NotifyObservers(aStorage, VoidString(), VoidString(), VoidString());
   }
 #endif
 
   if (Persist(aStorage) && (refresh || hadData)) {
-    StorageDBChild* storageChild = StorageDBChild::Get();
+    StorageDBChild* storageChild = StorageDBChild::Get(mPrivateBrowsingId);
     if (!storageChild) {
       NS_ERROR(
           "Writing to localStorage after the database has been shut down"
           ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
     }
 
     return storageChild->AsyncClear(this);
--- a/dom/storage/LocalStorageCache.h
+++ b/dom/storage/LocalStorageCache.h
@@ -251,16 +251,19 @@ class LocalStorageCache : public LocalSt
   // all keys and coresponding values are loaded from the database.
   // This flag never goes from true back to false.  Since this flag is
   // critical for mData hashtable synchronization, it's made atomic.
   Atomic<bool, ReleaseAcquire> mLoaded;
 
   // Result of load from the database.  Valid after mLoaded flag has been set.
   nsresult mLoadResult;
 
+  // Expected to be only 0 or 1.
+  uint32_t mPrivateBrowsingId;
+
   // Init() method has been called
   bool mInitialized : 1;
 
   // This cache is about to be bound with the database (i.e. it has
   // to load from the DB first and has to persist when modifying the
   // default data set.)
   bool mPersistent : 1;
 
--- a/dom/storage/LocalStorageManager.cpp
+++ b/dom/storage/LocalStorageManager.cpp
@@ -56,17 +56,19 @@ LocalStorageManager::LocalStorageManager
                "Somebody is trying to "
                "do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
   sSelf = this;
 
   if (!XRE_IsParentProcess()) {
     // Do this only on the child process.  The thread IPC bridge
     // is also used to communicate chrome observer notifications.
     // Note: must be called after we set sSelf
-    StorageDBChild::GetOrCreate();
+    for (const uint32_t id : {0, 1}) {
+      StorageDBChild::GetOrCreate(id);
+    }
   }
 }
 
 LocalStorageManager::~LocalStorageManager() {
   StorageObserver* observer = StorageObserver::Self();
   if (observer) {
     observer->RemoveSink(this);
   }
@@ -95,25 +97,26 @@ LocalStorageCache* LocalStorageManager::
   if (!entry) {
     return nullptr;
   }
 
   return entry->cache();
 }
 
 already_AddRefed<StorageUsage> LocalStorageManager::GetOriginUsage(
-    const nsACString& aOriginNoSuffix) {
+    const nsACString& aOriginNoSuffix, const uint32_t aPrivateBrowsingId) {
   RefPtr<StorageUsage> usage;
   if (mUsages.Get(aOriginNoSuffix, &usage)) {
     return usage.forget();
   }
 
   usage = new StorageUsage(aOriginNoSuffix);
 
-  StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
+  StorageDBChild* storageChild =
+      StorageDBChild::GetOrCreate(aPrivateBrowsingId);
   if (storageChild) {
     storageChild->AsyncGetUsage(usage);
   }
 
   mUsages.Put(aOriginNoSuffix, usage);
 
   return usage.forget();
 }
@@ -162,19 +165,22 @@ nsresult LocalStorageManager::GetStorage
   // Get or create a cache for the given scope
   if (!cache) {
     if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
       *aRetval = nullptr;
       return NS_OK;
     }
 
     if (aCreateMode == CreateMode::CreateIfShouldPreload) {
+      const uint32_t privateBrowsingId =
+          aStoragePrincipal->GetPrivateBrowsingId();
+
       // This is a demand to just preload the cache, if the scope has
       // no data stored, bypass creation and preload of the cache.
-      StorageDBChild* db = StorageDBChild::Get();
+      StorageDBChild* db = StorageDBChild::Get(privateBrowsingId);
       if (db) {
         if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(
                 originAttrSuffix, originKey))) {
           return NS_OK;
         }
       } else {
         if (originKey.EqualsLiteral("knalb.:about")) {
           return NS_OK;
--- a/dom/storage/LocalStorageManager.h
+++ b/dom/storage/LocalStorageManager.h
@@ -40,17 +40,17 @@ class LocalStorageManager final : public
   static uint32_t GetQuota();
 
   // Gets (but not ensures) cache for the given scope
   LocalStorageCache* GetCache(const nsACString& aOriginSuffix,
                               const nsACString& aOriginNoSuffix);
 
   // Returns object keeping usage cache for the scope.
   already_AddRefed<StorageUsage> GetOriginUsage(
-      const nsACString& aOriginNoSuffix);
+      const nsACString& aOriginNoSuffix, uint32_t aPrivateBrowsingId);
 
   static nsAutoCString CreateOrigin(const nsACString& aOriginSuffix,
                                     const nsACString& aOriginNoSuffix);
 
  private:
   ~LocalStorageManager();
 
   // StorageObserverSink, handler to various chrome clearing notification
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -47,20 +47,20 @@
 
 namespace mozilla {
 namespace dom {
 
 using namespace StorageUtils;
 
 namespace {  // anon
 
-StorageDBThread* sStorageThread = nullptr;
+StorageDBThread* sStorageThread[2] = {nullptr, nullptr};
 
 // False until we shut the storage thread down.
-bool sStorageThreadDown = false;
+bool sStorageThreadDown[2] = {false, false};
 
 }  // namespace
 
 // XXX Fix me!
 #if 0
 StorageDBBridge::StorageDBBridge()
 {
 }
@@ -92,69 +92,79 @@ class StorageDBThread::InitHelper final 
   ~InitHelper() override = default;
 
   nsresult RunOnMainThread();
 
   NS_DECL_NSIRUNNABLE
 };
 
 class StorageDBThread::NoteBackgroundThreadRunnable final : public Runnable {
+  // Expected to be only 0 or 1.
+  const uint32_t mPrivateBrowsingId;
   nsCOMPtr<nsIEventTarget> mOwningThread;
 
  public:
-  NoteBackgroundThreadRunnable()
+  explicit NoteBackgroundThreadRunnable(const uint32_t aPrivateBrowsingId)
       : Runnable("dom::StorageDBThread::NoteBackgroundThreadRunnable"),
+        mPrivateBrowsingId(aPrivateBrowsingId),
         mOwningThread(GetCurrentEventTarget()) {}
 
  private:
   ~NoteBackgroundThreadRunnable() override = default;
 
   NS_DECL_NSIRUNNABLE
 };
 
-StorageDBThread::StorageDBThread()
+StorageDBThread::StorageDBThread(const uint32_t aPrivateBrowsingId)
     : mThread(nullptr),
       mThreadObserver(new ThreadObserver()),
       mStopIOThread(false),
       mWALModeEnabled(false),
       mDBReady(false),
       mStatus(NS_OK),
       mWorkerStatements(mWorkerConnection),
       mReaderStatements(mReaderConnection),
       mFlushImmediately(false),
-      mPriorityCounter(0) {}
+      mPrivateBrowsingId(aPrivateBrowsingId),
+      mPriorityCounter(0) {
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
+}
 
 // static
-StorageDBThread* StorageDBThread::Get() {
+StorageDBThread* StorageDBThread::Get(const uint32_t aPrivateBrowsingId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
 
-  return sStorageThread;
+  return sStorageThread[aPrivateBrowsingId];
 }
 
 // static
-StorageDBThread* StorageDBThread::GetOrCreate(const nsString& aProfilePath) {
+StorageDBThread* StorageDBThread::GetOrCreate(
+    const nsString& aProfilePath, const uint32_t aPrivateBrowsingId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
 
-  if (sStorageThread || sStorageThreadDown) {
+  StorageDBThread*& storageThread = sStorageThread[aPrivateBrowsingId];
+  if (storageThread || sStorageThreadDown[aPrivateBrowsingId]) {
     // When sStorageThreadDown is at true, sStorageThread is null.
     // Checking sStorageThreadDown flag here prevents reinitialization of
     // the storage thread after shutdown.
-    return sStorageThread;
+    return storageThread;
   }
 
-  auto storageThread = MakeUnique<StorageDBThread>();
+  auto newStorageThread = MakeUnique<StorageDBThread>(aPrivateBrowsingId);
 
-  nsresult rv = storageThread->Init(aProfilePath);
+  nsresult rv = newStorageThread->Init(aProfilePath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
-  sStorageThread = storageThread.release();
+  storageThread = newStorageThread.release();
 
-  return sStorageThread;
+  return storageThread;
 }
 
 // static
 nsresult StorageDBThread::GetProfilePath(nsString& aProfilePath) {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
 
   // Need to determine location on the main thread, since
@@ -180,66 +190,66 @@ nsresult StorageDBThread::GetProfilePath
   }
 
   return NS_OK;
 }
 
 nsresult StorageDBThread::Init(const nsString& aProfilePath) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
-  nsresult rv;
+  if (mPrivateBrowsingId == 0) {
+    nsresult rv;
+
+    nsString profilePath;
+    if (aProfilePath.IsEmpty()) {
+      RefPtr<InitHelper> helper = new InitHelper();
 
-  nsString profilePath;
-  if (aProfilePath.IsEmpty()) {
-    RefPtr<InitHelper> helper = new InitHelper();
+      rv = helper->SyncDispatchAndReturnProfilePath(profilePath);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    } else {
+      profilePath = aProfilePath;
+    }
 
-    rv = helper->SyncDispatchAndReturnProfilePath(profilePath);
+    mDatabaseFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-  } else {
-    profilePath = aProfilePath;
-  }
 
-  mDatabaseFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+    rv = mDatabaseFile->InitWithPath(profilePath);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = mDatabaseFile->Append(u"webappsstore.sqlite"_ns);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  rv = mDatabaseFile->InitWithPath(profilePath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mDatabaseFile->Append(u"webappsstore.sqlite"_ns);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Need to keep the lock to avoid setting mThread later then
   // the thread body executes.
   MonitorAutoLock monitor(mThreadObserver->GetMonitor());
 
   mThread = PR_CreateThread(PR_USER_THREAD, &StorageDBThread::ThreadFunc, this,
                             PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
                             PR_JOINABLE_THREAD, 262144);
   if (!mThread) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   RefPtr<NoteBackgroundThreadRunnable> runnable =
-      new NoteBackgroundThreadRunnable();
+      new NoteBackgroundThreadRunnable(mPrivateBrowsingId);
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
 
   return NS_OK;
 }
 
 nsresult StorageDBThread::Shutdown() {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
-  sStorageThreadDown = true;
-
   if (!mThread) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS> timer;
 
   {
     MonitorAutoLock monitor(mThreadObserver->GetMonitor());
@@ -517,24 +527,34 @@ nsresult StorageDBThread::OpenDatabaseCo
   nsresult rv;
 
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<mozIStorageService> service =
       do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = service->OpenUnsharedDatabase(mDatabaseFile,
-                                     getter_AddRefs(mWorkerConnection));
-  if (rv == NS_ERROR_FILE_CORRUPTED) {
-    // delete the db and try opening again
-    rv = mDatabaseFile->Remove(false);
-    NS_ENSURE_SUCCESS(rv, rv);
+  if (mPrivateBrowsingId == 0) {
+    MOZ_ASSERT(mDatabaseFile);
+
     rv = service->OpenUnsharedDatabase(mDatabaseFile,
                                        getter_AddRefs(mWorkerConnection));
+    if (rv == NS_ERROR_FILE_CORRUPTED) {
+      // delete the db and try opening again
+      rv = mDatabaseFile->Remove(false);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = service->OpenUnsharedDatabase(mDatabaseFile,
+                                         getter_AddRefs(mWorkerConnection));
+    }
+  } else {
+    MOZ_ASSERT(mPrivateBrowsingId == 1);
+
+    rv = service->OpenSpecialDatabase(kMozStorageMemoryStorageKey,
+                                      "lsprivatedb"_ns,
+                                      getter_AddRefs(mWorkerConnection));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult StorageDBThread::OpenAndUpdateDatabase() {
   nsresult rv;
@@ -557,26 +577,28 @@ nsresult StorageDBThread::InitDatabase()
   // Here we are on the worker thread. This opens the worker connection.
   MOZ_ASSERT(!NS_IsMainThread());
 
   rv = OpenAndUpdateDatabase();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = StorageDBUpdater::Update(mWorkerConnection);
   if (NS_FAILED(rv)) {
-    // Update has failed, rather throw the database away and try
-    // opening and setting it up again.
-    rv = mWorkerConnection->Close();
-    mWorkerConnection = nullptr;
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (mPrivateBrowsingId == 0) {
+      // Update has failed, rather throw the database away and try
+      // opening and setting it up again.
+      rv = mWorkerConnection->Close();
+      mWorkerConnection = nullptr;
+      NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = mDatabaseFile->Remove(false);
-    NS_ENSURE_SUCCESS(rv, rv);
+      rv = mDatabaseFile->Remove(false);
+      NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = OpenAndUpdateDatabase();
+      rv = OpenAndUpdateDatabase();
+    }
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Create a read-only clone
   (void)mWorkerConnection->Clone(true, getter_AddRefs(mReaderConnection));
   NS_ENSURE_TRUE(mReaderConnection, NS_ERROR_FAILURE);
 
   // Database open and all initiation operation are done.  Switching this flag
@@ -1524,36 +1546,39 @@ StorageDBThread::InitHelper::Run() {
 
 NS_IMETHODIMP
 StorageDBThread::NoteBackgroundThreadRunnable::Run() {
   MOZ_ASSERT(NS_IsMainThread());
 
   StorageObserver* observer = StorageObserver::Self();
   MOZ_ASSERT(observer);
 
-  observer->NoteBackgroundThread(mOwningThread);
+  observer->NoteBackgroundThread(mPrivateBrowsingId, mOwningThread);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StorageDBThread::ShutdownRunnable::Run() {
   if (NS_IsMainThread()) {
     mDone = true;
 
     return NS_OK;
   }
 
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
-  if (sStorageThread) {
-    sStorageThread->Shutdown();
+  StorageDBThread*& storageThread = sStorageThread[mPrivateBrowsingId];
+  if (storageThread) {
+    sStorageThreadDown[mPrivateBrowsingId] = true;
 
-    delete sStorageThread;
-    sStorageThread = nullptr;
+    storageThread->Shutdown();
+
+    delete storageThread;
+    storageThread = nullptr;
   }
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
 
   return NS_OK;
 }
 
 }  // namespace dom
--- a/dom/storage/StorageDBThread.h
+++ b/dom/storage/StorageDBThread.h
@@ -287,38 +287,43 @@ class StorageDBThread final {
     Monitor mMonitor;
   };
 
   class InitHelper;
 
   class NoteBackgroundThreadRunnable;
 
   class ShutdownRunnable : public Runnable {
+    // Expected to be only 0 or 1.
+    const uint32_t mPrivateBrowsingId;
     // Only touched on the main thread.
     bool& mDone;
 
    public:
-    explicit ShutdownRunnable(bool& aDone)
-        : Runnable("dom::StorageDBThread::ShutdownRunnable"), mDone(aDone) {
+    explicit ShutdownRunnable(const uint32_t aPrivateBrowsingId, bool& aDone)
+        : Runnable("dom::StorageDBThread::ShutdownRunnable"),
+          mPrivateBrowsingId(aPrivateBrowsingId),
+          mDone(aDone) {
       MOZ_ASSERT(NS_IsMainThread());
     }
 
    private:
     ~ShutdownRunnable() = default;
 
     NS_DECL_NSIRUNNABLE
   };
 
  public:
-  StorageDBThread();
+  explicit StorageDBThread(uint32_t aPrivateBrowsingId);
   virtual ~StorageDBThread() = default;
 
-  static StorageDBThread* Get();
+  static StorageDBThread* Get(uint32_t aPrivateBrowsingId);
 
-  static StorageDBThread* GetOrCreate(const nsString& aProfilePath);
+  static StorageDBThread* GetOrCreate(const nsString& aProfilePath,
+                                      uint32_t aPrivateBrowsingId);
 
   static nsresult GetProfilePath(nsString& aProfilePath);
 
   virtual nsresult Init(const nsString& aProfilePath);
 
   // Flushes all uncommited data and stops the I/O thread.
   virtual nsresult Shutdown();
 
@@ -424,16 +429,19 @@ class StorageDBThread final {
 
   // List of preloading operations, in chronological or priority order.
   // Executed prioritly over pending update operations.
   nsTArray<DBOperation*> mPreloads;
 
   // Collector of pending update operations
   PendingOperations mPendingTasks;
 
+  // Expected to be only 0 or 1.
+  const uint32_t mPrivateBrowsingId;
+
   // Counter of calls for thread priority rising.
   int32_t mPriorityCounter;
 
   // Helper to direct an operation to one of the arrays above;
   // also checks IsOriginClearPending for preloads
   nsresult InsertDBOp(DBOperation* aOperation);
 
   // Opens the database, first thing we do after start of the thread.
--- a/dom/storage/StorageIPC.cpp
+++ b/dom/storage/StorageIPC.cpp
@@ -27,20 +27,20 @@ namespace dom {
 
 namespace {
 
 typedef nsClassHashtable<nsCStringHashKey, nsTArray<LocalStorageCacheParent*>>
     LocalStorageCacheParentHashtable;
 
 StaticAutoPtr<LocalStorageCacheParentHashtable> gLocalStorageCacheParents;
 
-StorageDBChild* sStorageChild = nullptr;
+StorageDBChild* sStorageChild[2] = {nullptr, nullptr};
 
 // False until we shut the storage child down.
-bool sStorageChildDown = false;
+bool sStorageChildDown[2] = {false, false};
 
 }  // namespace
 
 LocalStorageCacheChild::LocalStorageCacheChild(LocalStorageCache* aCache)
     : mCache(aCache) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCache);
   aCache->AssertIsOnOwningThread();
@@ -106,18 +106,24 @@ mozilla::ipc::IPCResult LocalStorageCach
   return IPC_OK();
 }
 
 // ----------------------------------------------------------------------------
 // Child
 // ----------------------------------------------------------------------------
 
 class StorageDBChild::ShutdownObserver final : public nsIObserver {
+  // Expected to be only 0 or 1.
+  const uint32_t mPrivateBrowsingId;
+
  public:
-  ShutdownObserver() { MOZ_ASSERT(NS_IsMainThread()); }
+  explicit ShutdownObserver(const uint32_t aPrivateBrowsingId)
+      : mPrivateBrowsingId(aPrivateBrowsingId) {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
  private:
   ~ShutdownObserver() { MOZ_ASSERT(NS_IsMainThread()); }
 };
 
@@ -128,57 +134,65 @@ void StorageDBChild::AddIPDLReference() 
 }
 
 void StorageDBChild::ReleaseIPDLReference() {
   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
   mIPCOpen = false;
   Release();
 }
 
-StorageDBChild::StorageDBChild(LocalStorageManager* aManager)
-    : mManager(aManager), mStatus(NS_OK), mIPCOpen(false) {
+StorageDBChild::StorageDBChild(LocalStorageManager* aManager,
+                               const uint32_t aPrivateBrowsingId)
+    : mManager(aManager),
+      mPrivateBrowsingId(aPrivateBrowsingId),
+      mStatus(NS_OK),
+      mIPCOpen(false) {
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
   MOZ_ASSERT(!NextGenLocalStorageEnabled());
 }
 
 StorageDBChild::~StorageDBChild() = default;
 
 // static
-StorageDBChild* StorageDBChild::Get() {
+StorageDBChild* StorageDBChild::Get(const uint32_t aPrivateBrowsingId) {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
   MOZ_ASSERT(!NextGenLocalStorageEnabled());
 
-  return sStorageChild;
+  return sStorageChild[aPrivateBrowsingId];
 }
 
 // static
-StorageDBChild* StorageDBChild::GetOrCreate() {
+StorageDBChild* StorageDBChild::GetOrCreate(const uint32_t aPrivateBrowsingId) {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
   MOZ_ASSERT(!NextGenLocalStorageEnabled());
 
-  if (sStorageChild || sStorageChildDown) {
+  StorageDBChild*& storageChild = sStorageChild[aPrivateBrowsingId];
+  if (storageChild || sStorageChildDown[aPrivateBrowsingId]) {
     // When sStorageChildDown is at true, sStorageChild is null.
     // Checking sStorageChildDown flag here prevents reinitialization of
     // the storage child after shutdown.
-    return sStorageChild;
+    return storageChild;
   }
 
   // Use LocalStorageManager::Ensure in case we're called from
   // DOMSessionStorageManager's initializer and we haven't yet initialized the
   // local storage manager.
-  RefPtr<StorageDBChild> storageChild =
-      new StorageDBChild(LocalStorageManager::Ensure());
+  RefPtr<StorageDBChild> newStorageChild =
+      new StorageDBChild(LocalStorageManager::Ensure(), aPrivateBrowsingId);
 
-  nsresult rv = storageChild->Init();
+  nsresult rv = newStorageChild->Init();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
-  storageChild.forget(&sStorageChild);
+  newStorageChild.forget(&storageChild);
 
-  return sStorageChild;
+  return storageChild;
 }
 
 nsTHashtable<nsCStringHashKey>& StorageDBChild::OriginsHavingData() {
   if (!mOriginsHavingData) {
     mOriginsHavingData = MakeUnique<nsTHashtable<nsCStringHashKey>>();
   }
 
   return *mOriginsHavingData;
@@ -189,31 +203,32 @@ nsresult StorageDBChild::Init() {
 
   ::mozilla::ipc::PBackgroundChild* actor =
       ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!actor)) {
     return NS_ERROR_FAILURE;
   }
 
   nsString profilePath;
-  if (XRE_IsParentProcess()) {
+  if (XRE_IsParentProcess() && mPrivateBrowsingId == 0) {
     nsresult rv = StorageDBThread::GetProfilePath(profilePath);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   AddIPDLReference();
 
-  actor->SendPBackgroundStorageConstructor(this, profilePath);
+  actor->SendPBackgroundStorageConstructor(this, profilePath,
+                                           mPrivateBrowsingId);
 
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   MOZ_ASSERT(observerService);
 
-  nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+  nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mPrivateBrowsingId);
 
   MOZ_ALWAYS_SUCCEEDS(
       observerService->AddObserver(observer, "xpcom-shutdown", false));
 
   return NS_OK;
 }
 
 nsresult StorageDBChild::Shutdown() {
@@ -375,17 +390,17 @@ mozilla::ipc::IPCResult StorageDBChild::
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBChild::RecvLoadUsage(
     const nsCString& aOriginNoSuffix, const int64_t& aUsage) {
   RefPtr<StorageUsageBridge> scopeUsage =
-      mManager->GetOriginUsage(aOriginNoSuffix);
+      mManager->GetOriginUsage(aOriginNoSuffix, mPrivateBrowsingId);
   scopeUsage->LoadUsage(aUsage);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBChild::RecvError(const nsresult& aRv) {
   mStatus = aRv;
   return IPC_OK();
 }
@@ -402,23 +417,24 @@ StorageDBChild::ShutdownObserver::Observ
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
   if (NS_WARN_IF(!observerService)) {
     return NS_ERROR_FAILURE;
   }
 
   Unused << observerService->RemoveObserver(this, "xpcom-shutdown");
 
-  if (sStorageChild) {
-    sStorageChildDown = true;
+  StorageDBChild*& storageChild = sStorageChild[mPrivateBrowsingId];
+  if (storageChild) {
+    sStorageChildDown[mPrivateBrowsingId] = true;
 
-    MOZ_ALWAYS_TRUE(sStorageChild->PBackgroundStorageChild::SendDeleteMe());
+    MOZ_ALWAYS_TRUE(storageChild->PBackgroundStorageChild::SendDeleteMe());
 
-    NS_RELEASE(sStorageChild);
-    sStorageChild = nullptr;
+    NS_RELEASE(storageChild);
+    storageChild = nullptr;
   }
 
   return NS_OK;
 }
 
 SessionStorageObserverChild::SessionStorageObserverChild(
     SessionStorageObserver* aObserver)
     : mObserver(aObserver) {
@@ -662,18 +678,21 @@ void StorageDBParent::AddIPDLReference()
 void StorageDBParent::ReleaseIPDLReference() {
   MOZ_ASSERT(mIPCOpen, "Attempting to release non-existent IPDL reference");
   mIPCOpen = false;
   Release();
 }
 
 namespace {}  // namespace
 
-StorageDBParent::StorageDBParent(const nsString& aProfilePath)
-    : mProfilePath(aProfilePath), mIPCOpen(false) {
+StorageDBParent::StorageDBParent(const nsString& aProfilePath,
+                                 const uint32_t aPrivateBrowsingId)
+    : mProfilePath(aProfilePath),
+      mPrivateBrowsingId(aPrivateBrowsingId),
+      mIPCOpen(false) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
   // We are always open by IPC only
   AddIPDLReference();
 }
 
 StorageDBParent::~StorageDBParent() {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
@@ -690,17 +709,17 @@ void StorageDBParent::Init() {
   PBackgroundParent* actor = Manager();
   MOZ_ASSERT(actor);
 
   if (::mozilla::ipc::BackgroundParent::IsOtherProcessActor(actor)) {
     mObserverSink = new ObserverSink(this);
     mObserverSink->Start();
   }
 
-  StorageDBThread* storageThread = StorageDBThread::Get();
+  StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId);
   if (storageThread) {
     nsTArray<nsCString> scopes;
     storageThread->GetOriginsHavingData(&scopes);
     mozilla::Unused << SendOriginsHavingData(scopes);
   }
 }
 
 StorageDBParent::CacheParentBridge* StorageDBParent::NewCache(
@@ -720,30 +739,32 @@ mozilla::ipc::IPCResult StorageDBParent:
     return IPC_FAIL_NO_REASON(mgr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncPreload(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
     const bool& aPriority) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   storageThread->AsyncPreload(NewCache(aOriginSuffix, aOriginNoSuffix),
                               aPriority);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncGetUsage(
     const nsCString& aOriginNoSuffix) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   // The object releases it self in LoadUsage method
   RefPtr<UsageParentBridge> usage =
       new UsageParentBridge(this, aOriginNoSuffix);
 
@@ -828,143 +849,152 @@ class SyncLoadCacheHelper : public Local
 };
 
 }  // namespace
 
 mozilla::ipc::IPCResult StorageDBParent::RecvPreload(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
     const uint32_t& aAlreadyLoadedCount, nsTArray<nsString>* aKeys,
     nsTArray<nsString>* aValues, nsresult* aRv) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   RefPtr<SyncLoadCacheHelper> cache(
       new SyncLoadCacheHelper(aOriginSuffix, aOriginNoSuffix,
                               aAlreadyLoadedCount, aKeys, aValues, aRv));
 
   storageThread->SyncPreload(cache, true);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncAddItem(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
     const nsString& aKey, const nsString& aValue) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   nsresult rv = storageThread->AsyncAddItem(
       NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncUpdateItem(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
     const nsString& aKey, const nsString& aValue) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   nsresult rv = storageThread->AsyncUpdateItem(
       NewCache(aOriginSuffix, aOriginNoSuffix), aKey, aValue);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncRemoveItem(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix,
     const nsString& aKey) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   nsresult rv = storageThread->AsyncRemoveItem(
       NewCache(aOriginSuffix, aOriginNoSuffix), aKey);
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncClear(
     const nsCString& aOriginSuffix, const nsCString& aOriginNoSuffix) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   nsresult rv =
       storageThread->AsyncClear(NewCache(aOriginSuffix, aOriginNoSuffix));
   if (NS_FAILED(rv) && mIPCOpen) {
     mozilla::Unused << SendError(rv);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvAsyncFlush() {
-  StorageDBThread* storageThread = StorageDBThread::Get();
+  StorageDBThread* storageThread = StorageDBThread::Get(mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   storageThread->AsyncFlush();
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvStartup() {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvClearAll() {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   storageThread->AsyncClearAll();
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOrigin(
     const nsCString& aOriginNoSuffix) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   storageThread->AsyncClearMatchingOrigin(aOriginNoSuffix);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult StorageDBParent::RecvClearMatchingOriginAttributes(
     const OriginAttributesPattern& aPattern) {
-  StorageDBThread* storageThread = StorageDBThread::GetOrCreate(mProfilePath);
+  StorageDBThread* storageThread =
+      StorageDBThread::GetOrCreate(mProfilePath, mPrivateBrowsingId);
   if (!storageThread) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   storageThread->AsyncClearMatchingOriginAttributes(aPattern);
 
   return IPC_OK();
 }
@@ -1454,24 +1484,25 @@ bool DeallocPBackgroundLocalStorageCache
   // Transfer ownership back from IPDL.
   RefPtr<LocalStorageCacheParent> actor =
       dont_AddRef(static_cast<LocalStorageCacheParent*>(aActor));
 
   return true;
 }
 
 PBackgroundStorageParent* AllocPBackgroundStorageParent(
-    const nsString& aProfilePath) {
+    const nsString& aProfilePath, const uint32_t& aPrivateBrowsingId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
 
-  return new StorageDBParent(aProfilePath);
+  return new StorageDBParent(aProfilePath, aPrivateBrowsingId);
 }
 
 mozilla::ipc::IPCResult RecvPBackgroundStorageConstructor(
-    PBackgroundStorageParent* aActor, const nsString& aProfilePath) {
+    PBackgroundStorageParent* aActor, const nsString& aProfilePath,
+    const uint32_t& aPrivateBrowsingId) {
   ::mozilla::ipc::AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   auto* actor = static_cast<StorageDBParent*>(aActor);
   actor->Init();
   return IPC_OK();
 }
 
--- a/dom/storage/StorageIPC.h
+++ b/dom/storage/StorageIPC.h
@@ -95,21 +95,21 @@ class LocalStorageCacheChild final : pub
 // and expects asynchronous answers. Those are then transparently
 // forwarded back to consumers on the child process.
 class StorageDBChild final : public PBackgroundStorageChild {
   class ShutdownObserver;
 
   virtual ~StorageDBChild();
 
  public:
-  explicit StorageDBChild(LocalStorageManager* aManager);
+  StorageDBChild(LocalStorageManager* aManager, uint32_t aPrivateBrowsingId);
 
-  static StorageDBChild* Get();
+  static StorageDBChild* Get(uint32_t aPrivateBrowsingId);
 
-  static StorageDBChild* GetOrCreate();
+  static StorageDBChild* GetOrCreate(uint32_t aPrivateBrowsingId);
 
   NS_INLINE_DECL_REFCOUNTING(StorageDBChild);
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
   virtual nsresult Init();
   virtual nsresult Shutdown();
@@ -173,16 +173,19 @@ class StorageDBChild final : public PBac
 
   // Origins having data hash, for optimization purposes only
   UniquePtr<nsTHashtable<nsCStringHashKey>> mOriginsHavingData;
 
   // List of caches waiting for preload.  This ensures the contract that
   // AsyncPreload call references the cache for time of the preload.
   nsTHashtable<nsRefPtrHashKey<LocalStorageCacheBridge>> mLoadingCaches;
 
+  // Expected to be only 0 or 1.
+  const uint32_t mPrivateBrowsingId;
+
   // Status of the remote database
   nsresult mStatus;
 
   bool mIPCOpen;
 };
 
 class SessionStorageObserverChild final : public PSessionStorageObserverChild {
   friend class SessionStorageManager;
@@ -327,17 +330,17 @@ class LocalStorageCacheParent final
 // Also responsible for forwardning all chrome operation notifications
 // such as cookie cleaning etc to the child process.
 class StorageDBParent final : public PBackgroundStorageParent {
   class ObserverSink;
 
   virtual ~StorageDBParent();
 
  public:
-  explicit StorageDBParent(const nsString& aProfilePath);
+  StorageDBParent(const nsString& aProfilePath, uint32_t aPrivateBrowsingId);
 
   void Init();
 
   NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
   NS_IMETHOD_(MozExternalRefCountType) Release(void);
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
@@ -464,16 +467,19 @@ class StorageDBParent final : public PBa
 
   // A hack to deal with deadlock between the parent process main thread and
   // background thread when invoking StorageDBThread::GetOrCreate because it
   // cannot safely perform a synchronous dispatch back to the main thread
   // (because we are already synchronously doing things on the stack).
   // Populated for the same process actors, empty for other process actors.
   nsString mProfilePath;
 
+  // Expected to be only 0 or 1.
+  const uint32_t mPrivateBrowsingId;
+
   ThreadSafeAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
   // True when IPC channel is open and Send*() methods are OK to use.
   bool mIPCOpen;
 };
 
 class SessionStorageObserverParent final : public PSessionStorageObserverParent,
@@ -569,20 +575,21 @@ mozilla::ipc::IPCResult RecvPBackgroundL
     PBackgroundLocalStorageCacheParent* aActor,
     const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
     const nsCString& aOriginKey, const uint32_t& aPrivateBrowsingId);
 
 bool DeallocPBackgroundLocalStorageCacheParent(
     PBackgroundLocalStorageCacheParent* aActor);
 
 PBackgroundStorageParent* AllocPBackgroundStorageParent(
-    const nsString& aProfilePath);
+    const nsString& aProfilePath, const uint32_t& aPrivateBrowsingId);
 
 mozilla::ipc::IPCResult RecvPBackgroundStorageConstructor(
-    PBackgroundStorageParent* aActor, const nsString& aProfilePath);
+    PBackgroundStorageParent* aActor, const nsString& aProfilePath,
+    const uint32_t& aPrivateBrowsingId);
 
 bool DeallocPBackgroundStorageParent(PBackgroundStorageParent* aActor);
 
 PSessionStorageObserverParent* AllocPSessionStorageObserverParent();
 
 bool RecvPSessionStorageObserverConstructor(
     PSessionStorageObserverParent* aActor);
 
--- a/dom/storage/StorageObserver.cpp
+++ b/dom/storage/StorageObserver.cpp
@@ -32,16 +32,18 @@ namespace dom {
 
 using namespace StorageUtils;
 
 static const char kStartupTopic[] = "sessionstore-windows-restored";
 static const uint32_t kStartupDelay = 0;
 
 const char kTestingPref[] = "dom.storage.testing";
 
+constexpr auto kPrivateBrowsingPattern = u"{ \"privateBrowsingId\": 1 }"_ns;
+
 NS_IMPL_ISUPPORTS(StorageObserver, nsIObserver, nsISupportsWeakReference)
 
 StorageObserver* StorageObserver::sSelf = nullptr;
 
 // static
 nsresult StorageObserver::Init() {
   if (sSelf) {
     return NS_OK;
@@ -124,18 +126,21 @@ void StorageObserver::RemoveSink(Storage
 void StorageObserver::Notify(const char* aTopic,
                              const nsAString& aOriginAttributesPattern,
                              const nsACString& aOriginScope) {
   for (auto* sink : mSinks.ForwardRange()) {
     sink->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
   }
 }
 
-void StorageObserver::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) {
-  mBackgroundThread = aBackgroundThread;
+void StorageObserver::NoteBackgroundThread(const uint32_t aPrivateBrowsingId,
+                                           nsIEventTarget* aBackgroundThread) {
+  MOZ_ASSERT(aPrivateBrowsingId <= 1);
+
+  mBackgroundThread[aPrivateBrowsingId] = aBackgroundThread;
 }
 
 nsresult StorageObserver::GetOriginScope(const char16_t* aData,
                                          nsACString& aOriginScope) {
   nsresult rv;
 
   NS_ConvertUTF16toUTF8 domain(aData);
 
@@ -193,43 +198,47 @@ StorageObserver::Observe(nsISupports* aS
     nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
     if (!timer) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (timer == mDBThreadStartDelayTimer) {
       mDBThreadStartDelayTimer = nullptr;
 
-      StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-      if (NS_WARN_IF(!storageChild)) {
-        return NS_ERROR_FAILURE;
+      for (const uint32_t id : {0, 1}) {
+        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+        if (NS_WARN_IF(!storageChild)) {
+          return NS_ERROR_FAILURE;
+        }
+
+        storageChild->SendStartup();
       }
-
-      storageChild->SendStartup();
     }
 
     return NS_OK;
   }
 
   // Clear everything, caches + database
   if (!strcmp(aTopic, "cookie-changed")) {
     if (!u"cleared"_ns.Equals(aData)) {
       return NS_OK;
     }
 
     if (!NextGenLocalStorageEnabled()) {
-      StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-      if (NS_WARN_IF(!storageChild)) {
-        return NS_ERROR_FAILURE;
-      }
+      for (const uint32_t id : {0, 1}) {
+        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+        if (NS_WARN_IF(!storageChild)) {
+          return NS_ERROR_FAILURE;
+        }
 
-      storageChild->AsyncClearAll();
+        storageChild->AsyncClearAll();
 
-      if (XRE_IsParentProcess()) {
-        storageChild->SendClearAll();
+        if (XRE_IsParentProcess()) {
+          storageChild->SendClearAll();
+        }
       }
     }
 
     Notify("cookie-cleared");
 
     return NS_OK;
   }
 
@@ -292,35 +301,39 @@ StorageObserver::Observe(nsISupports* aS
       nsCString originScope;
 
       rv = GetOriginScope(aData, originScope);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       if (XRE_IsParentProcess()) {
-        StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-        if (NS_WARN_IF(!storageChild)) {
-          return NS_ERROR_FAILURE;
+        for (const uint32_t id : {0, 1}) {
+          StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+          if (NS_WARN_IF(!storageChild)) {
+            return NS_ERROR_FAILURE;
+          }
+
+          storageChild->SendClearMatchingOrigin(originScope);
         }
-
-        storageChild->SendClearMatchingOrigin(originScope);
       }
 
       Notify(topic, u""_ns, originScope);
     } else {
-      StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-      if (NS_WARN_IF(!storageChild)) {
-        return NS_ERROR_FAILURE;
-      }
+      for (const uint32_t id : {0, 1}) {
+        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+        if (NS_WARN_IF(!storageChild)) {
+          return NS_ERROR_FAILURE;
+        }
 
-      storageChild->AsyncClearAll();
+        storageChild->AsyncClearAll();
 
-      if (XRE_IsParentProcess()) {
-        storageChild->SendClearAll();
+        if (XRE_IsParentProcess()) {
+          storageChild->SendClearAll();
+        }
       }
 
       Notify(topic);
     }
 
     return NS_OK;
   }
 
@@ -341,17 +354,43 @@ StorageObserver::Observe(nsISupports* aS
   }
 
   // Clear all private-browsing caches
   if (!strcmp(aTopic, "last-pb-context-exited")) {
     if (NextGenLocalStorageEnabled()) {
       return NS_OK;
     }
 
-    Notify("private-browsing-data-cleared", u"{ \"privateBrowsingId\": 1 }"_ns);
+    // We get the notification in both processes (parent and content), but the
+    // clearing of the in-memory database should be triggered from the parent
+    // process only to avoid creation of redundant clearing operations.
+    // Also, if we create a new StorageDBChild instance late during content
+    // process shutdown, then it might be leaked in debug builds because it
+    // could happen that there is no chance to properly destroy it.
+    if (XRE_IsParentProcess()) {
+      // This doesn't use a loop with privateBrowsingId 0 and 1, since we only
+      // need to clear the in-memory database which is represented by
+      // privateBrowsingId 1.
+      static const uint32_t id = 1;
+
+      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+      if (NS_WARN_IF(!storageChild)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      OriginAttributesPattern pattern;
+      if (!pattern.Init(kPrivateBrowsingPattern)) {
+        NS_ERROR("Cannot parse origin attributes pattern");
+        return NS_ERROR_FAILURE;
+      }
+
+      storageChild->SendClearMatchingOriginAttributes(pattern);
+    }
+
+    Notify("private-browsing-data-cleared", kPrivateBrowsingPattern);
 
     return NS_OK;
   }
 
   // Clear data of the origins whose prefixes will match the suffix.
   if (!strcmp(aTopic, "clear-origin-attributes-data")) {
     MOZ_ASSERT(XRE_IsParentProcess());
 
@@ -360,23 +399,25 @@ StorageObserver::Observe(nsISupports* aS
     }
 
     OriginAttributesPattern pattern;
     if (!pattern.Init(nsDependentString(aData))) {
       NS_ERROR("Cannot parse origin attributes pattern");
       return NS_ERROR_FAILURE;
     }
 
-    StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-    if (NS_WARN_IF(!storageChild)) {
-      return NS_ERROR_FAILURE;
+    for (const uint32_t id : {0, 1}) {
+      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+      if (NS_WARN_IF(!storageChild)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      storageChild->SendClearMatchingOriginAttributes(pattern);
     }
 
-    storageChild->SendClearMatchingOriginAttributes(pattern);
-
     Notify("origin-attr-pattern-cleared", nsDependentString(aData));
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "profile-after-change")) {
     Notify("profile-change");
 
@@ -385,45 +426,49 @@ StorageObserver::Observe(nsISupports* aS
 
   if (!strcmp(aTopic, "profile-before-change")) {
     MOZ_ASSERT(XRE_IsParentProcess());
 
     if (NextGenLocalStorageEnabled()) {
       return NS_OK;
     }
 
-    if (mBackgroundThread) {
-      bool done = false;
+    for (const uint32_t id : {0, 1}) {
+      if (mBackgroundThread[id]) {
+        bool done = false;
 
-      RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
-          new StorageDBThread::ShutdownRunnable(done);
-      MOZ_ALWAYS_SUCCEEDS(
-          mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
+        RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
+            new StorageDBThread::ShutdownRunnable(id, done);
+        MOZ_ALWAYS_SUCCEEDS(mBackgroundThread[id]->Dispatch(
+            shutdownRunnable, NS_DISPATCH_NORMAL));
 
-      MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done; }));
+        MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done; }));
 
-      mBackgroundThread = nullptr;
+        mBackgroundThread[id] = nullptr;
+      }
     }
 
     return NS_OK;
   }
 
 #ifdef DOM_STORAGE_TESTS
   if (!strcmp(aTopic, "domstorage-test-flush-force")) {
     if (NextGenLocalStorageEnabled()) {
       return NS_OK;
     }
 
-    StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-    if (NS_WARN_IF(!storageChild)) {
-      return NS_ERROR_FAILURE;
+    for (const uint32_t id : {0, 1}) {
+      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
+      if (NS_WARN_IF(!storageChild)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      storageChild->SendAsyncFlush();
     }
 
-    storageChild->SendAsyncFlush();
-
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "domstorage-test-flushed")) {
     if (NextGenLocalStorageEnabled()) {
       return NS_OK;
     }
 
--- a/dom/storage/StorageObserver.h
+++ b/dom/storage/StorageObserver.h
@@ -45,28 +45,29 @@ class StorageObserver : public nsIObserv
   static StorageObserver* Self() { return sSelf; }
 
   void AddSink(StorageObserverSink* aObs);
   void RemoveSink(StorageObserverSink* aObs);
   void Notify(const char* aTopic,
               const nsAString& aOriginAttributesPattern = u""_ns,
               const nsACString& aOriginScope = ""_ns);
 
-  void NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
+  void NoteBackgroundThread(uint32_t aPrivateBrowsingId,
+                            nsIEventTarget* aBackgroundThread);
 
  private:
   virtual ~StorageObserver() = default;
 
   nsresult GetOriginScope(const char16_t* aData, nsACString& aOriginScope);
 
   static void TestingPrefChanged(const char* aPrefName, void* aClosure);
 
   static StorageObserver* sSelf;
 
-  nsCOMPtr<nsIEventTarget> mBackgroundThread;
+  nsCOMPtr<nsIEventTarget> mBackgroundThread[2];
 
   // Weak references
   nsTObserverArray<StorageObserverSink*> mSinks;
   nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
 };
 
 }  // namespace dom
 }  // namespace mozilla
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -279,17 +279,17 @@ bool BackgroundChildImpl::DeallocPBackgr
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
 BackgroundChildImpl::PBackgroundStorageChild*
 BackgroundChildImpl::AllocPBackgroundStorageChild(
-    const nsString& aProfilePath) {
+    const nsString& aProfilePath, const uint32_t& aPrivateBrowsingId) {
   MOZ_CRASH("PBackgroundStorageChild actors should be manually constructed!");
 }
 
 bool BackgroundChildImpl::DeallocPBackgroundStorageChild(
     PBackgroundStorageChild* aActor) {
   MOZ_ASSERT(aActor);
 
   StorageDBChild* child = static_cast<StorageDBChild*>(aActor);
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -102,17 +102,18 @@ class BackgroundChildImpl : public PBack
   AllocPBackgroundLocalStorageCacheChild(
       const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey,
       const uint32_t& aPrivateBrowsingId) override;
 
   virtual bool DeallocPBackgroundLocalStorageCacheChild(
       PBackgroundLocalStorageCacheChild* aActor) override;
 
   virtual PBackgroundStorageChild* AllocPBackgroundStorageChild(
-      const nsString& aProfilePath) override;
+      const nsString& aProfilePath,
+      const uint32_t& aPrivateBrowsingId) override;
 
   virtual bool DeallocPBackgroundStorageChild(
       PBackgroundStorageChild* aActor) override;
 
   virtual already_AddRefed<PRemoteLazyInputStreamChild>
   AllocPRemoteLazyInputStreamChild(const nsID& aID,
                                    const uint64_t& aSize) override;
 
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -447,30 +447,34 @@ bool BackgroundParentImpl::DeallocPBackg
   AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundLocalStorageCacheParent(aActor);
 }
 
 auto BackgroundParentImpl::AllocPBackgroundStorageParent(
-    const nsString& aProfilePath) -> PBackgroundStorageParent* {
+    const nsString& aProfilePath, const uint32_t& aPrivateBrowsingId)
+    -> PBackgroundStorageParent* {
   AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
 
-  return mozilla::dom::AllocPBackgroundStorageParent(aProfilePath);
+  return mozilla::dom::AllocPBackgroundStorageParent(aProfilePath,
+                                                     aPrivateBrowsingId);
 }
 
 mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBackgroundStorageConstructor(
-    PBackgroundStorageParent* aActor, const nsString& aProfilePath) {
+    PBackgroundStorageParent* aActor, const nsString& aProfilePath,
+    const uint32_t& aPrivateBrowsingId) {
   AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
-  return mozilla::dom::RecvPBackgroundStorageConstructor(aActor, aProfilePath);
+  return mozilla::dom::RecvPBackgroundStorageConstructor(aActor, aProfilePath,
+                                                         aPrivateBrowsingId);
 }
 
 bool BackgroundParentImpl::DeallocPBackgroundStorageParent(
     PBackgroundStorageParent* aActor) {
   AssertIsInMainOrSocketProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -125,20 +125,22 @@ class BackgroundParentImpl : public PBac
       PBackgroundLocalStorageCacheParent* aActor,
       const PrincipalInfo& aPrincipalInfo, const nsCString& aOriginKey,
       const uint32_t& aPrivateBrowsingId) override;
 
   bool DeallocPBackgroundLocalStorageCacheParent(
       PBackgroundLocalStorageCacheParent* aActor) override;
 
   PBackgroundStorageParent* AllocPBackgroundStorageParent(
-      const nsString& aProfilePath) override;
+      const nsString& aProfilePath,
+      const uint32_t& aPrivateBrowsingId) override;
 
   mozilla::ipc::IPCResult RecvPBackgroundStorageConstructor(
-      PBackgroundStorageParent* aActor, const nsString& aProfilePath) override;
+      PBackgroundStorageParent* aActor, const nsString& aProfilePath,
+      const uint32_t& aPrivateBrowsingId) override;
 
   bool DeallocPBackgroundStorageParent(
       PBackgroundStorageParent* aActor) override;
 
   already_AddRefed<PBackgroundSessionStorageManagerParent>
   AllocPBackgroundSessionStorageManagerParent(
       const uint64_t& aTopContextId) override;
 
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -170,17 +170,17 @@ parent:
   async LSClearPrivateBrowsing();
 
   async PBackgroundLocalStorageCache(PrincipalInfo principalInfo,
                                      nsCString originKey,
                                      uint32_t privateBrowsingId);
 
   async PBackgroundSessionStorageManager(uint64_t aTopContextId);
 
-  async PBackgroundStorage(nsString profilePath);
+  async PBackgroundStorage(nsString profilePath, uint32_t privateBrowsingId);
 
   async PVsync();
 
   async PCameras();
 
   async PUDPSocket(PrincipalInfo? pInfo, nsCString filter);
   async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
 
--- a/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js
+++ b/security/manager/ssl/tests/unit/test_sts_preloadlist_perwindowpb.js
@@ -19,16 +19,18 @@ var secInfo = Cc[
 ].createInstance(Ci.nsITransportSecurityInfo);
 
 function cleanup() {
   Services.obs.removeObserver(gObserver, "last-pb-context-exited");
   gSSService.clearAll();
 }
 
 function run_test() {
+  do_get_profile();
+
   registerCleanupFunction(cleanup);
   Services.obs.addObserver(gObserver, "last-pb-context-exited");
 
   add_test(test_part1);
   add_test(test_private_browsing1);
   add_test(test_private_browsing2);
 
   run_next_test();