Bug 1286798 - Part 3: New basic (memory only) implementation of LocalStorage; r=asuth,mccr8
authorJan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:47:20 +0100
changeset 505221 2a6e7e64cec1e3b8184a432c6f4c2351a87f9f43
parent 505220 bb1fc7fd2fd240e074d0348721c7cdea1a07bb5e
child 505222 2aaa8d6bbaf1a08d7825e7b2827a398955974e0d
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, mccr8
bugs1286798
milestone65.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 1286798 - Part 3: New basic (memory only) implementation of LocalStorage; r=asuth,mccr8 The implementation is based on a cache (datastore) living in the parent process and sync IPC calls initiated from content processes. IPC communication is done using per principal/origin database actors which connect to the datastore. The synchronous blocking of the main thread is done by creating a nested event target and spinning the event loop.
browser/components/contextualidentity/test/browser/browser.ini
browser/components/extensions/test/browser/browser-common.ini
browser/components/privatebrowsing/test/browser/browser.ini
browser/components/sessionstore/test/browser.ini
devtools/client/storage/test/browser.ini
devtools/server/tests/browser/browser.ini
dom/base/nsGlobalWindowInner.cpp
dom/base/test/mochitest.ini
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/localstorage/ActorsChild.cpp
dom/localstorage/ActorsChild.h
dom/localstorage/ActorsParent.cpp
dom/localstorage/ActorsParent.h
dom/localstorage/LSDatabase.cpp
dom/localstorage/LSDatabase.h
dom/localstorage/LSObject.cpp
dom/localstorage/LSObject.h
dom/localstorage/LocalStorageCommon.cpp
dom/localstorage/LocalStorageCommon.h
dom/localstorage/LocalStorageManager2.cpp
dom/localstorage/LocalStorageManager2.h
dom/localstorage/PBackgroundLSDatabase.ipdl
dom/localstorage/PBackgroundLSRequest.ipdl
dom/localstorage/PBackgroundLSSharedTypes.ipdlh
dom/localstorage/moz.build
dom/moz.build
dom/storage/LocalStorageManager.cpp
dom/storage/LocalStorageManager.h
dom/storage/StorageIPC.cpp
dom/storage/StorageObserver.cpp
dom/tests/browser/browser.ini
dom/tests/mochitest/localstorage/chrome.ini
dom/tests/mochitest/localstorage/mochitest.ini
dom/tests/mochitest/storageevent/mochitest.ini
dom/tests/moz.build
editor/libeditor/tests/mochitest.ini
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
ipc/ipdl/sync-messages.ini
layout/build/nsLayoutModule.cpp
modules/libpref/init/all.js
testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini
testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini
testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini
testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini
testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini
testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini
testing/web-platform/meta/webstorage/document-domain.html.ini
testing/web-platform/meta/webstorage/event_basic.html.ini
testing/web-platform/meta/webstorage/event_body_attribute.html.ini
testing/web-platform/meta/webstorage/event_case_sensitive.html.ini
testing/web-platform/meta/webstorage/event_local_key.html.ini
testing/web-platform/meta/webstorage/event_local_newvalue.html.ini
testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini
testing/web-platform/meta/webstorage/event_local_removeitem.html.ini
testing/web-platform/meta/webstorage/event_local_storagearea.html.ini
testing/web-platform/meta/webstorage/event_local_url.html.ini
testing/web-platform/meta/webstorage/event_no_duplicates.html.ini
testing/web-platform/meta/webstorage/event_setattribute.html.ini
testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/forgetaboutsite/moz.build
toolkit/forgetaboutsite/test/unit/xpcshell.ini
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -5,17 +5,17 @@ support-files =
   favicon-normal32.png
   file_set_storages.html
   serviceworker.html
   worker.js
 
 [browser_aboutURLs.js]
 [browser_eme.js]
 [browser_favicon.js]
-[browser_forgetaboutsite.js]
+#[browser_forgetaboutsite.js]
 [browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js]
 [browser_restore_getCookiesWithOriginAttributes.js]
 [browser_forgetAPI_EME_forgetThisSite.js]
 [browser_forgetAPI_quota_clearStoragesForPrincipal.js]
 skip-if = verify
 [browser_newtabButton.js]
 [browser_usercontext.js]
 [browser_usercontextid_tabdrop.js]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -60,17 +60,17 @@ skip-if = (os == 'win' && !debug) || (ve
 [browser_ext_browserAction_popup_resize.js]
 skip-if = (os == 'mac' && debug) # Bug 1482004, also fails in test-verify
 [browser_ext_browserAction_simple.js]
 [browser_ext_browserAction_telemetry.js]
 [browser_ext_browserAction_theme_icons.js]
 [browser_ext_browsingData_formData.js]
 [browser_ext_browsingData_history.js]
 [browser_ext_browsingData_indexedDB.js]
-[browser_ext_browsingData_localStorage.js]
+#[browser_ext_browsingData_localStorage.js]
 [browser_ext_browsingData_pluginData.js]
 [browser_ext_browsingData_serviceWorkers.js]
 [browser_ext_chrome_settings_overrides_home.js]
 [browser_ext_commands_execute_browser_action.js]
 [browser_ext_commands_execute_page_action.js]
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 [browser_ext_commands_execute_sidebar_action.js]
 [browser_ext_commands_getAll.js]
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -20,17 +20,17 @@ support-files =
   file_triggeringprincipal_oa.html
 
 [browser_privatebrowsing_DownloadLastDirWithCPS.js]
 [browser_privatebrowsing_about.js]
 tags = trackingprotection
 [browser_privatebrowsing_aboutSessionRestore.js]
 [browser_privatebrowsing_cache.js]
 [browser_privatebrowsing_certexceptionsui.js]
-[browser_privatebrowsing_concurrent.js]
+#[browser_privatebrowsing_concurrent.js]
 [browser_privatebrowsing_context_and_chromeFlags.js]
 [browser_privatebrowsing_crh.js]
 [browser_privatebrowsing_downloadLastDir.js]
 skip-if = verify
 [browser_privatebrowsing_downloadLastDir_c.js]
 [browser_privatebrowsing_downloadLastDir_toggle.js]
 [browser_privatebrowsing_favicon.js]
 [browser_privatebrowsing_geoprompt.js]
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -149,17 +149,17 @@ skip-if = true
 [browser_394759_purge.js]
 [browser_423132.js]
 [browser_447951.js]
 [browser_454908.js]
 [browser_456342.js]
 [browser_461634.js]
 [browser_463205.js]
 [browser_463206.js]
-[browser_464199.js]
+#[browser_464199.js]
 [browser_465215.js]
 [browser_465223.js]
 [browser_466937.js]
 [browser_467409-backslashplosion.js]
 [browser_477657.js]
 [browser_480893.js]
 [browser_485482.js]
 [browser_485563.js]
--- a/devtools/client/storage/test/browser.ini
+++ b/devtools/client/storage/test/browser.ini
@@ -40,33 +40,33 @@ tags = usercontextid
 [browser_storage_cookies_domain.js]
 [browser_storage_cookies_domain_port.js]
 [browser_storage_cookies_edit.js]
 [browser_storage_cookies_edit_keyboard.js]
 [browser_storage_cookies_hostOnly.js]
 [browser_storage_cookies_samesite.js]
 skip-if = true # Bug 1448484 - sameSite1 is "Unset" - Got undefined, expected Unset
 [browser_storage_cookies_tab_navigation.js]
-[browser_storage_delete.js]
-[browser_storage_delete_all.js]
-[browser_storage_delete_tree.js]
-[browser_storage_delete_usercontextid.js]
-tags = usercontextid
+#[browser_storage_delete.js]
+#[browser_storage_delete_all.js]
+#[browser_storage_delete_tree.js]
+#[browser_storage_delete_usercontextid.js]
+#tags = usercontextid
 [browser_storage_dom_cache_disabled.js]
 [browser_storage_dynamic_updates_cookies.js]
-[browser_storage_dynamic_updates_localStorage.js]
-[browser_storage_dynamic_updates_sessionStorage.js]
+#[browser_storage_dynamic_updates_localStorage.js]
+#[browser_storage_dynamic_updates_sessionStorage.js]
 [browser_storage_empty_objectstores.js]
 [browser_storage_file_url.js]
 [browser_storage_indexeddb_delete.js]
 [browser_storage_indexeddb_delete_blocked.js]
 [browser_storage_indexeddb_duplicate_names.js]
 [browser_storage_indexeddb_overflow.js]
-[browser_storage_localstorage_add.js]
-[browser_storage_localstorage_edit.js]
+#[browser_storage_localstorage_add.js]
+#[browser_storage_localstorage_edit.js]
 [browser_storage_localstorage_error.js]
 [browser_storage_localstorage_rapid_add_remove.js]
 [browser_storage_overflow.js]
 [browser_storage_search.js]
 [browser_storage_search_keyboard_trap.js]
 [browser_storage_sessionstorage_add.js]
 [browser_storage_sessionstorage_edit.js]
 [browser_storage_sidebar.js]
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -89,16 +89,16 @@ skip-if = true # Needs to be updated for
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-recording-actor-02.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_perf-samples-01.js]
 [browser_perf-samples-02.js]
 [browser_spawn_actor_in_parent.js]
 [browser_storage_browser_toolbox_indexeddb.js]
 [browser_storage_cookies-duplicate-names.js]
-[browser_storage_dynamic_windows.js]
+#[browser_storage_dynamic_windows.js]
 [browser_storage_listings.js]
-[browser_storage_updates.js]
-skip-if = (verify && debug && (os == 'mac' || os == 'linux'))
+#[browser_storage_updates.js]
+#skip-if = (verify && debug && (os == 'mac' || os == 'linux'))
 [browser_stylesheets_getTextEmpty.js]
 [browser_stylesheets_nested-iframes.js]
 [browser_register_actor.js]
 [browser_webextension_inspected_window.js]
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -18,16 +18,18 @@
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
 #include "mozilla/AutoplayPermissionManager.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/LocalStorage.h"
+#include "mozilla/dom/LocalStorageCommon.h"
+#include "mozilla/dom/LSObject.h"
 #include "mozilla/dom/PartitionedLocalStorage.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/StorageNotifierService.h"
 #include "mozilla/dom/StorageUtils.h"
@@ -4903,42 +4905,48 @@ nsGlobalWindowInner::GetLocalStorage(Err
 
   // Note that this behavior is observable: if we grant storage permission to a
   // tracker, we pass from the partitioned LocalStorage to the 'normal'
   // LocalStorage. The previous data is lost and the 2 window.localStorage
   // objects, before and after the permission granted, will be different.
   if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny &&
       (!mLocalStorage ||
         mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) {
-    nsresult rv;
-    nsCOMPtr<nsIDOMStorageManager> storageManager =
-      do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
-    if (NS_FAILED(rv)) {
-      aError.Throw(rv);
-      return nullptr;
-    }
-
-    nsString documentURI;
-    if (mDoc) {
-      aError = mDoc->GetDocumentURI(documentURI);
-      if (NS_WARN_IF(aError.Failed())) {
+    RefPtr<Storage> storage;
+
+    if (NextGenLocalStorageEnabled()) {
+      aError = LSObject::Create(this, getter_AddRefs(storage));
+    } else {
+      nsresult rv;
+      nsCOMPtr<nsIDOMStorageManager> storageManager =
+        do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
+      if (NS_FAILED(rv)) {
+        aError.Throw(rv);
         return nullptr;
       }
-    }
-
-    nsIPrincipal *principal = GetPrincipal();
-    if (!principal) {
-      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-      return nullptr;
-    }
-
-    RefPtr<Storage> storage;
-    aError = storageManager->CreateStorage(this, principal, documentURI,
-                                           IsPrivateBrowsing(),
-                                           getter_AddRefs(storage));
+
+      nsString documentURI;
+      if (mDoc) {
+        aError = mDoc->GetDocumentURI(documentURI);
+        if (NS_WARN_IF(aError.Failed())) {
+          return nullptr;
+        }
+      }
+
+      nsIPrincipal *principal = GetPrincipal();
+      if (!principal) {
+        aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return nullptr;
+      }
+
+      aError = storageManager->CreateStorage(this, principal, documentURI,
+                                             IsPrivateBrowsing(),
+                                             getter_AddRefs(storage));
+    }
+
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = storage;
     MOZ_ASSERT(mLocalStorage);
   }
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -572,17 +572,17 @@ skip-if = toolkit == 'android' || (verif
 [test_bug962251.html]
 [test_bug976673.html]
 [test_bug982153.html]
 [test_bug999456.html]
 [test_bug1022229.html]
 [test_bug1025933.html]
 [test_bug1037687.html]
 support-files = test_bug1037687_subframe.html
-[test_bug1043106.html]
+#[test_bug1043106.html]
 [test_bug1057176.html]
 [test_bug1060938.html]
 [test_bug1064481.html]
 [test_bug1070015.html]
 [test_bug1075702.html]
 [test_bug1091883.html]
 [test_bug1100912.html]
 support-files = file_bug1100912.html
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/LSObject.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/dom/PLoginReputationChild.h"
 #include "mozilla/dom/PushNotifier.h"
 #include "mozilla/dom/RemoteWorkerService.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/URLClassifierChild.h"
@@ -3806,25 +3807,31 @@ ContentChild::GetSpecificMessageEventTar
 
       return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));
 
     default:
       return nullptr;
   }
 }
 
-#ifdef NIGHTLY_BUILD
 void
 ContentChild::OnChannelReceivedMessage(const Message& aMsg)
 {
+  if (aMsg.is_sync()) {
+    LSObject::CancelSyncLoop();
+  }
+
+#ifdef NIGHTLY_BUILD
   if (nsContentUtils::IsMessageInputEvent(aMsg)) {
     mPendingInputEvents++;
   }
+#endif
 }
 
+#ifdef NIGHTLY_BUILD
 PContentChild::Result
 ContentChild::OnMessageReceived(const Message& aMsg)
 {
   if (nsContentUtils::IsMessageInputEvent(aMsg)) {
     DebugOnly<uint32_t> prevEvts = mPendingInputEvents--;
     MOZ_ASSERT(prevEvts > 0);
   }
 
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -763,20 +763,20 @@ private:
   virtual void ProcessingError(Result aCode, const char* aReason) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetConstructedEventTarget(const Message& aMsg) override;
 
   virtual already_AddRefed<nsIEventTarget>
   GetSpecificMessageEventTarget(const Message& aMsg) override;
 
-#ifdef NIGHTLY_BUILD
   virtual void
   OnChannelReceivedMessage(const Message& aMsg) override;
 
+#ifdef NIGHTLY_BUILD
   virtual PContentChild::Result
   OnMessageReceived(const Message& aMsg) override;
 
   virtual PContentChild::Result
   OnMessageReceived(const Message& aMsg, Message*& aReply) override;
 #endif
 
   InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/ActorsChild.cpp
@@ -0,0 +1,118 @@
+/* -*- 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 "ActorsChild.h"
+
+#include "LSDatabase.h"
+
+namespace mozilla {
+namespace dom {
+
+/*******************************************************************************
+ * LSDatabaseChild
+ ******************************************************************************/
+
+LSDatabaseChild::LSDatabaseChild(LSDatabase* aDatabase)
+  : mDatabase(aDatabase)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aDatabase);
+
+  MOZ_COUNT_CTOR(LSDatabaseChild);
+}
+
+LSDatabaseChild::~LSDatabaseChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(LSDatabaseChild);
+}
+
+void
+LSDatabaseChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mDatabase) {
+    mDatabase->ClearActor();
+    mDatabase = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundLSDatabaseChild::SendDeleteMe());
+  }
+}
+
+void
+LSDatabaseChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mDatabase) {
+    mDatabase->ClearActor();
+#ifdef DEBUG
+    mDatabase = nullptr;
+#endif
+  }
+}
+
+/*******************************************************************************
+ * LocalStorageRequestChild
+ ******************************************************************************/
+
+LSRequestChild::LSRequestChild(LSRequestChildCallback* aCallback)
+  : mCallback(aCallback)
+  , mFinishing(false)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(LSRequestChild);
+}
+
+LSRequestChild::~LSRequestChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(LSRequestChild);
+}
+
+bool
+LSRequestChild::Finishing() const
+{
+  AssertIsOnOwningThread();
+
+  return mFinishing;
+}
+
+void
+LSRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+}
+
+mozilla::ipc::IPCResult
+LSRequestChild::Recv__delete__(const LSRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mCallback);
+
+  mCallback->OnResponse(aResponse);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+LSRequestChild::RecvReady()
+{
+  AssertIsOnOwningThread();
+
+  mFinishing = true;
+
+  SendFinish();
+
+  return IPC_OK();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/ActorsChild.h
@@ -0,0 +1,116 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_ActorsChild_h
+#define mozilla_dom_localstorage_ActorsChild_h
+
+#include "mozilla/dom/PBackgroundLSDatabaseChild.h"
+#include "mozilla/dom/PBackgroundLSRequestChild.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class BackgroundChildImpl;
+
+} // namespace ipc
+
+namespace dom {
+
+class LSDatabase;
+class LSObject;
+class LSRequestChildCallback;
+
+class LSDatabaseChild final
+  : public PBackgroundLSDatabaseChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+  friend class LSDatabase;
+  friend class LSObject;
+
+  LSDatabase* mDatabase;
+
+  NS_DECL_OWNINGTHREAD
+
+public:
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(LSDatabaseChild);
+  }
+
+private:
+  // Only created by LSObject.
+  explicit LSDatabaseChild(LSDatabase* aDatabase);
+
+  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+  ~LSDatabaseChild();
+
+  void
+  SendDeleteMeInternal();
+
+  // IPDL methods are only called by IPDL.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+class LSRequestChild final
+  : public PBackgroundLSRequestChild
+{
+  friend class LSObject;
+  friend class LSObjectChild;
+
+  RefPtr<LSRequestChildCallback> mCallback;
+
+  bool mFinishing;
+
+  NS_DECL_OWNINGTHREAD
+
+public:
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(LSReqeustChild);
+  }
+
+  bool
+  Finishing() const;
+
+private:
+  // Only created by LSObject.
+  explicit LSRequestChild(LSRequestChildCallback* aCallback);
+
+  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+  ~LSRequestChild();
+
+  // IPDL methods are only called by IPDL.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const LSRequestResponse& aResponse) override;
+
+  mozilla::ipc::IPCResult
+  RecvReady() override;
+};
+
+class NS_NO_VTABLE LSRequestChildCallback
+{
+public:
+  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+  virtual void
+  OnResponse(const LSRequestResponse& aResponse) = 0;
+
+protected:
+  virtual ~LSRequestChildCallback()
+  { }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_ActorsChild_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/ActorsParent.cpp
@@ -0,0 +1,975 @@
+/* -*- 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 "LocalStorageCommon.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/PBackgroundLSDatabaseParent.h"
+#include "mozilla/dom/PBackgroundLSRequestParent.h"
+#include "mozilla/dom/PBackgroundLSSharedTypes.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsDataHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+#define DISABLE_ASSERTS_FOR_FUZZING 0
+
+#if DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::dom::quota;
+using namespace mozilla::ipc;
+
+namespace {
+
+/*******************************************************************************
+ * Non-actor class declarations
+ ******************************************************************************/
+
+class DatastoreOperationBase
+  : public Runnable
+{
+  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+  nsresult mResultCode;
+  Atomic<bool> mMayProceedOnNonOwningThread;
+  bool mMayProceed;
+
+public:
+  nsIEventTarget*
+  OwningEventTarget() const
+  {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    return mOwningEventTarget;
+  }
+
+  bool
+  IsOnOwningThread() const
+  {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    bool current;
+    return
+      NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) && current;
+  }
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    MOZ_ASSERT(IsOnBackgroundThread());
+    MOZ_ASSERT(IsOnOwningThread());
+  }
+
+  nsresult
+  ResultCode() const
+  {
+    return mResultCode;
+  }
+
+  void
+  MaybeSetFailureCode(nsresult aErrorCode)
+  {
+    MOZ_ASSERT(NS_FAILED(aErrorCode));
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = aErrorCode;
+    }
+  }
+
+  void
+  NoteComplete()
+  {
+    AssertIsOnOwningThread();
+
+    mMayProceed = false;
+    mMayProceedOnNonOwningThread = false;
+  }
+
+  bool
+  MayProceed() const
+  {
+    AssertIsOnOwningThread();
+
+    return mMayProceed;
+  }
+
+  // May be called on any thread, but you should call MayProceed() if you know
+  // you're on the background thread because it is slightly faster.
+  bool
+  MayProceedOnNonOwningThread() const
+  {
+    return mMayProceedOnNonOwningThread;
+  }
+
+protected:
+  DatastoreOperationBase()
+    : Runnable("dom::DatastoreOperationBase")
+    , mOwningEventTarget(GetCurrentThreadEventTarget())
+    , mResultCode(NS_OK)
+    , mMayProceedOnNonOwningThread(true)
+    , mMayProceed(true)
+  { }
+
+  ~DatastoreOperationBase() override
+  {
+    MOZ_ASSERT(!mMayProceed);
+  }
+};
+
+class Datastore final
+{
+  nsDataHashtable<nsStringHashKey, nsString> mValues;
+  const nsCString mOrigin;
+
+public:
+  // Created by PrepareDatastoreOp.
+  explicit Datastore(const nsACString& aOrigin);
+
+  const nsCString&
+  Origin() const
+  {
+    return mOrigin;
+  }
+
+  uint32_t
+  GetLength() const;
+
+  void
+  GetKey(uint32_t aIndex, nsString& aKey) const;
+
+  void
+  GetItem(const nsString& aKey, nsString& aValue) const;
+
+  void
+  SetItem(const nsString& aKey, const nsString& aValue);
+
+  void
+  RemoveItem(const nsString& aKey);
+
+  void
+  Clear();
+
+  void
+  GetKeys(nsTArray<nsString>& aKeys) const;
+
+  NS_INLINE_DECL_REFCOUNTING(Datastore)
+
+private:
+  // Reference counted.
+  ~Datastore();
+};
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class Database final
+  : public PBackgroundLSDatabaseParent
+{
+  RefPtr<Datastore> mDatastore;
+
+#ifdef DEBUG
+  bool mActorDestroyed;
+  bool mActorWasAlive;
+#endif
+
+public:
+  // Created in AllocPBackgroundLSDatabaseParent.
+  Database();
+
+  void
+  SetActorAlive(already_AddRefed<Datastore>&& aDatastore);
+
+  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Database)
+
+private:
+  // Reference counted.
+  ~Database();
+
+  // IPDL methods are only called by IPDL.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  RecvDeleteMe() override;
+
+  mozilla::ipc::IPCResult
+  RecvGetLength(uint32_t* aLength) override;
+
+  mozilla::ipc::IPCResult
+  RecvGetKey(const uint32_t& aIndex, nsString* aKey) override;
+
+  mozilla::ipc::IPCResult
+  RecvGetItem(const nsString& aKey, nsString* aValue) override;
+
+  mozilla::ipc::IPCResult
+  RecvGetKeys(nsTArray<nsString>* aKeys) override;
+
+  mozilla::ipc::IPCResult
+  RecvSetItem(const nsString& aKey, const nsString& aValue) override;
+
+  mozilla::ipc::IPCResult
+  RecvRemoveItem(const nsString& aKey) override;
+
+  mozilla::ipc::IPCResult
+  RecvClear() override;
+};
+
+class LSRequestBase
+  : public DatastoreOperationBase
+  , public PBackgroundLSRequestParent
+{
+public:
+  virtual void
+  Dispatch() = 0;
+
+private:
+  // IPDL methods.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  RecvCancel() override;
+};
+
+class PrepareDatastoreOp
+  : public LSRequestBase
+{
+  enum class State
+  {
+    // Just created on the PBackground thread. Next step is OpeningOnMainThread
+    // or OpeningOnOwningThread.
+    Initial,
+
+    // Waiting to open/opening on the main thread. Next step is
+    // SendingReadyMessage.
+    OpeningOnMainThread,
+
+    // Waiting to open/opening on the owning thread. Next step is
+    // SendingReadyMessage.
+    OpeningOnOwningThread,
+
+    // Waiting to send/sending the ready message on the PBackground thread. Next
+    // step is WaitingForFinish.
+    SendingReadyMessage,
+
+    // Waiting for the finish message on the PBackground thread. Next step is
+    // SendingResults.
+    WaitingForFinish,
+
+    // Waiting to send/sending results on the PBackground thread. Next step is
+    // Completed.
+    SendingResults,
+
+    // All done.
+    Completed
+  };
+
+  const LSRequestPrepareDatastoreParams mParams;
+  nsCString mSuffix;
+  nsCString mGroup;
+  nsCString mOrigin;
+  State mState;
+
+public:
+  explicit PrepareDatastoreOp(const LSRequestParams& aParams);
+
+  void
+  Dispatch() override;
+
+private:
+  ~PrepareDatastoreOp() override;
+
+  nsresult
+  OpenOnMainThread();
+
+  nsresult
+  OpenOnOwningThread();
+
+  void
+  SendReadyMessage();
+
+  void
+  SendResults();
+
+  NS_IMETHOD
+  Run() override;
+
+  // IPDL overrides.
+  mozilla::ipc::IPCResult
+  RecvFinish() override;
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+
+typedef nsDataHashtable<nsCStringHashKey, Datastore*> DatastoreHashtable;
+
+StaticAutoPtr<DatastoreHashtable> gDatastores;
+
+uint64_t gLastDatastoreId = 0;
+
+typedef nsRefPtrHashtable<nsUint64HashKey, Datastore>
+  TemporaryStrongDatastoreHashtable;
+
+StaticAutoPtr<TemporaryStrongDatastoreHashtable> gTemporaryStrongDatastores;
+
+typedef nsTArray<Database*> LiveDatabaseArray;
+
+StaticAutoPtr<LiveDatabaseArray> gLiveDatabases;
+
+} // namespace
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+
+PBackgroundLSDatabaseParent*
+AllocPBackgroundLSDatabaseParent(const uint64_t& aDatastoreId)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!gTemporaryStrongDatastores)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  Datastore* datastore = gTemporaryStrongDatastores->GetWeak(aDatastoreId);
+  if (NS_WARN_IF(!datastore)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  // If we ever decide to return null from this point on, we need to make sure
+  // that the prepared datastore is removed from the gTemporaryStrongDatastores
+  // hashtable.
+  // We also assume that IPDL must call RecvPBackgroundLSDatabaseConstructor
+  // once we return a valid actor in this method.
+
+  RefPtr<Database> database = new Database();
+
+  // Transfer ownership to IPDL.
+  return database.forget().take();
+}
+
+bool
+RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor,
+                                     const uint64_t& aDatastoreId)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(gTemporaryStrongDatastores);
+  MOZ_ASSERT(gTemporaryStrongDatastores->GetWeak(aDatastoreId));
+
+  // The actor is now completely built (it has a manager, channel and it's
+  // registered as a subprotocol).
+  // ActorDestroy will be called if we fail here.
+
+  RefPtr<Datastore> datastore;
+  gTemporaryStrongDatastores->Remove(aDatastoreId, datastore.StartAssignment());
+  MOZ_ASSERT(datastore);
+
+  auto* database = static_cast<Database*>(aActor);
+
+  database->SetActorAlive(datastore.forget());
+
+  return true;
+}
+
+bool
+DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  // Transfer ownership back from IPDL.
+  RefPtr<Database> actor = dont_AddRef(static_cast<Database*>(aActor));
+
+  return true;
+}
+
+PBackgroundLSRequestParent*
+AllocPBackgroundLSRequestParent(PBackgroundParent* aBackgroundActor,
+                                const LSRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+
+  RefPtr<LSRequestBase> actor;
+
+  switch (aParams.type()) {
+    case LSRequestParams::TLSRequestPrepareDatastoreParams: {
+      bool isOtherProcess =
+        BackgroundParent::IsOtherProcessActor(aBackgroundActor);
+
+      const LSRequestPrepareDatastoreParams& params =
+        aParams.get_LSRequestPrepareDatastoreParams();
+
+      const PrincipalOrQuotaInfo& info = params.info();
+
+      PrincipalOrQuotaInfo::Type infoType = info.type();
+
+      bool paramsOk =
+        (isOtherProcess && infoType == PrincipalOrQuotaInfo::TPrincipalInfo) ||
+        (!isOtherProcess && infoType == PrincipalOrQuotaInfo::TQuotaInfo);
+
+      if (NS_WARN_IF(!paramsOk)) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+
+      actor = new PrepareDatastoreOp(aParams);
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  // Transfer ownership to IPDL.
+  return actor.forget().take();
+}
+
+bool
+RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
+                                    const LSRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
+
+  // The actor is now completely built.
+
+  auto* op = static_cast<LSRequestBase*>(aActor);
+
+  op->Dispatch();
+
+  return true;
+}
+
+bool
+DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+
+  // Transfer ownership back from IPDL.
+  RefPtr<LSRequestBase> actor =
+    dont_AddRef(static_cast<LSRequestBase*>(aActor));
+
+  return true;
+}
+
+/*******************************************************************************
+ * DatastoreOperationBase
+ ******************************************************************************/
+
+/*******************************************************************************
+ * Datastore
+ ******************************************************************************/
+
+Datastore::Datastore(const nsACString& aOrigin)
+  : mOrigin(aOrigin)
+{
+  AssertIsOnBackgroundThread();
+}
+
+Datastore::~Datastore()
+{
+  AssertIsOnBackgroundThread();
+
+  MOZ_ASSERT(gDatastores);
+  MOZ_ASSERT(gDatastores->Get(mOrigin));
+  gDatastores->Remove(mOrigin);
+
+  if (!gDatastores->Count()) {
+    gDatastores = nullptr;
+  }
+}
+
+uint32_t
+Datastore::GetLength() const
+{
+  AssertIsOnBackgroundThread();
+
+  return mValues.Count();
+}
+
+void
+Datastore::GetKey(uint32_t aIndex, nsString& aKey) const
+{
+  AssertIsOnBackgroundThread();
+
+  aKey.SetIsVoid(true);
+  for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
+    if (aIndex == 0) {
+      aKey = iter.Key();
+      return;
+    }
+    aIndex--;
+  }
+}
+
+void
+Datastore::GetItem(const nsString& aKey, nsString& aValue) const
+{
+  AssertIsOnBackgroundThread();
+
+  if (!mValues.Get(aKey, &aValue)) {
+    aValue.SetIsVoid(true);
+  }
+}
+
+void
+Datastore::SetItem(const nsString& aKey, const nsString& aValue)
+{
+  AssertIsOnBackgroundThread();
+
+  mValues.Put(aKey, aValue);
+}
+
+void
+Datastore::RemoveItem(const nsString& aKey)
+{
+  AssertIsOnBackgroundThread();
+
+  mValues.Remove(aKey);
+}
+
+void
+Datastore::Clear()
+{
+  AssertIsOnBackgroundThread();
+
+  mValues.Clear();
+}
+
+void
+Datastore::GetKeys(nsTArray<nsString>& aKeys) const
+{
+  AssertIsOnBackgroundThread();
+
+  for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
+    aKeys.AppendElement(iter.Key());
+  }
+}
+
+/*******************************************************************************
+ * Database
+ ******************************************************************************/
+
+Database::Database()
+#ifdef DEBUG
+  : mActorDestroyed(false)
+  , mActorWasAlive(false)
+#endif
+{
+  AssertIsOnBackgroundThread();
+}
+
+Database::~Database()
+{
+  MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
+}
+
+void
+Database::SetActorAlive(already_AddRefed<Datastore>&& aDatastore)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorWasAlive);
+  MOZ_ASSERT(!mActorDestroyed);
+
+#ifdef DEBUG
+  mActorWasAlive = true;
+#endif
+
+  mDatastore = std::move(aDatastore);
+
+  if (!gLiveDatabases) {
+    gLiveDatabases = new LiveDatabaseArray();
+  }
+
+  gLiveDatabases->AppendElement(this);
+}
+
+void
+Database::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+  MOZ_ASSERT(mDatastore);
+
+#ifdef DEBUG
+  mActorDestroyed = true;
+#endif
+
+  mDatastore = nullptr;
+
+  MOZ_ASSERT(gLiveDatabases);
+  gLiveDatabases->RemoveElement(this);
+
+  if (gLiveDatabases->IsEmpty()) {
+    gLiveDatabases = nullptr;
+  }
+}
+
+mozilla::ipc::IPCResult
+Database::RecvDeleteMe()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  IProtocol* mgr = Manager();
+  if (!PBackgroundLSDatabaseParent::Send__delete__(this)) {
+    return IPC_FAIL_NO_REASON(mgr);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvGetLength(uint32_t* aLength)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aLength);
+  MOZ_ASSERT(mDatastore);
+
+  *aLength = mDatastore->GetLength();
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvGetKey(const uint32_t& aIndex, nsString* aKey)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aKey);
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->GetKey(aIndex, *aKey);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvGetItem(const nsString& aKey, nsString* aValue)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aValue);
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->GetItem(aKey, *aValue);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvSetItem(const nsString& aKey, const nsString& aValue)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->SetItem(aKey, aValue);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvRemoveItem(const nsString& aKey)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->RemoveItem(aKey);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvClear()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->Clear();
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+Database::RecvGetKeys(nsTArray<nsString>* aKeys)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aKeys);
+  MOZ_ASSERT(mDatastore);
+
+  mDatastore->GetKeys(*aKeys);
+
+  return IPC_OK();
+}
+
+/*******************************************************************************
+ * LSRequestBase
+ ******************************************************************************/
+
+void
+LSRequestBase::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  NoteComplete();
+}
+
+mozilla::ipc::IPCResult
+LSRequestBase::RecvCancel()
+{
+  AssertIsOnOwningThread();
+
+  IProtocol* mgr = Manager();
+  if (!PBackgroundLSRequestParent::Send__delete__(this, NS_ERROR_FAILURE)) {
+    return IPC_FAIL_NO_REASON(mgr);
+  }
+
+  return IPC_OK();
+}
+
+/*******************************************************************************
+ * PrepareDatastoreOp
+ ******************************************************************************/
+
+PrepareDatastoreOp::PrepareDatastoreOp(const LSRequestParams& aParams)
+  : mParams(aParams.get_LSRequestPrepareDatastoreParams())
+  , mState(State::Initial)
+{
+  MOZ_ASSERT(aParams.type() ==
+               LSRequestParams::TLSRequestPrepareDatastoreParams);
+}
+
+PrepareDatastoreOp::~PrepareDatastoreOp()
+{
+  MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
+                mState == State::Initial || mState == State::Completed);
+}
+
+void
+PrepareDatastoreOp::Dispatch()
+{
+  AssertIsOnOwningThread();
+
+  const PrincipalOrQuotaInfo& info = mParams.info();
+
+  if (info.type() == PrincipalOrQuotaInfo::TPrincipalInfo) {
+    mState = State::OpeningOnMainThread;
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+  } else {
+    MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TQuotaInfo);
+
+    mState = State::OpeningOnOwningThread;
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
+  }
+}
+
+nsresult
+PrepareDatastoreOp::OpenOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State::OpeningOnMainThread);
+
+  if (!MayProceedOnNonOwningThread()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const PrincipalOrQuotaInfo& info = mParams.info();
+
+  MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TPrincipalInfo);
+
+  const PrincipalInfo& principalInfo = info.get_PrincipalInfo();
+
+  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+    QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
+  } else {
+    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+
+    nsresult rv;
+    nsCOMPtr<nsIPrincipal> principal =
+      PrincipalInfoToPrincipal(principalInfo, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                            &mSuffix,
+                                            &mGroup,
+                                            &mOrigin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mState = State::SendingReadyMessage;
+  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+
+  return NS_OK;
+}
+
+nsresult
+PrepareDatastoreOp::OpenOnOwningThread()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::OpeningOnOwningThread);
+
+  if (!MayProceed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const PrincipalOrQuotaInfo& info = mParams.info();
+
+  MOZ_ASSERT(info.type() == PrincipalOrQuotaInfo::TQuotaInfo);
+
+  const QuotaInfo& quotaInfo = info.get_QuotaInfo();
+
+  mSuffix = quotaInfo.suffix();
+  mGroup = quotaInfo.group();
+  mOrigin = quotaInfo.origin();
+
+  mState = State::SendingReadyMessage;
+  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+
+  return NS_OK;
+}
+
+void
+PrepareDatastoreOp::SendReadyMessage()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::SendingReadyMessage);
+
+  if (!MayProceed()) {
+    MaybeSetFailureCode(NS_ERROR_FAILURE);
+
+    mState = State::Completed;
+  } else {
+    Unused << SendReady();
+
+    mState = State::WaitingForFinish;
+  }
+}
+
+void
+PrepareDatastoreOp::SendResults()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::SendingResults);
+
+  if (!MayProceed()) {
+    MaybeSetFailureCode(NS_ERROR_FAILURE);
+  } else {
+    LSRequestResponse response;
+
+    if (NS_SUCCEEDED(ResultCode())) {
+      RefPtr<Datastore> datastore;
+
+      if (gDatastores) {
+        datastore = gDatastores->Get(mOrigin);
+      }
+
+      if (!datastore) {
+        datastore = new Datastore(mOrigin);
+
+        if (!gDatastores) {
+          gDatastores = new DatastoreHashtable();
+        }
+
+        gDatastores->Put(mOrigin, datastore);
+      }
+
+      uint64_t datastoreId = ++gLastDatastoreId;
+
+      if (!gTemporaryStrongDatastores) {
+        gTemporaryStrongDatastores = new TemporaryStrongDatastoreHashtable();
+      }
+      gTemporaryStrongDatastores->Put(datastoreId, datastore);
+
+      LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
+      prepareDatastoreResponse.datastoreId() = datastoreId;
+
+      response = prepareDatastoreResponse;
+    } else {
+      response = ResultCode();
+    }
+
+    Unused <<
+      PBackgroundLSRequestParent::Send__delete__(this, response);
+  }
+
+  mState = State::Completed;
+}
+
+NS_IMETHODIMP
+PrepareDatastoreOp::Run()
+{
+  nsresult rv;
+
+  switch (mState) {
+    case State::OpeningOnMainThread:
+      rv = OpenOnMainThread();
+      break;
+
+    case State::OpeningOnOwningThread:
+      rv = OpenOnOwningThread();
+      break;
+
+    case State::SendingReadyMessage:
+      SendReadyMessage();
+      return NS_OK;
+
+    case State::SendingResults:
+      SendResults();
+      return NS_OK;
+
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingReadyMessage) {
+    MaybeSetFailureCode(rv);
+
+    // Must set mState before dispatching otherwise we will race with the owning
+    // thread.
+    mState = State::SendingReadyMessage;
+
+    if (IsOnOwningThread()) {
+      SendReadyMessage();
+    } else {
+      MOZ_ALWAYS_SUCCEEDS(
+        OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+    }
+  }
+
+  return NS_OK;
+}
+
+mozilla::ipc::IPCResult
+PrepareDatastoreOp::RecvFinish()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::WaitingForFinish);
+
+  mState = State::SendingResults;
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
+
+  return IPC_OK();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/ActorsParent.h
@@ -0,0 +1,49 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_ActorsParent_h
+#define mozilla_dom_localstorage_ActorsParent_h
+
+namespace mozilla {
+
+namespace ipc {
+
+class PBackgroundParent;
+
+} // namespace ipc
+
+namespace dom {
+
+class LSRequestParams;
+class PBackgroundLSDatabaseParent;
+class PBackgroundLSRequestParent;
+
+PBackgroundLSDatabaseParent*
+AllocPBackgroundLSDatabaseParent(const uint64_t& aDatastoreId);
+
+bool
+RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor,
+                                     const uint64_t& aDatastoreId);
+
+bool
+DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor);
+
+PBackgroundLSRequestParent*
+AllocPBackgroundLSRequestParent(
+                              mozilla::ipc::PBackgroundParent* aBackgroundActor,
+                              const LSRequestParams& aParams);
+
+bool
+RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
+                                    const LSRequestParams& aParams);
+
+bool
+DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor);
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_ActorsParent_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LSDatabase.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LSDatabase.h"
+
+namespace mozilla {
+namespace dom {
+
+LSDatabase::LSDatabase()
+  : mActor(nullptr)
+{
+  AssertIsOnOwningThread();
+}
+
+LSDatabase::~LSDatabase()
+{
+  AssertIsOnOwningThread();
+
+  if (mActor) {
+    mActor->SendDeleteMeInternal();
+    MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
+  }
+}
+
+void
+LSDatabase::SetActor(LSDatabaseChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!mActor);
+
+  mActor = aActor;
+}
+
+nsresult
+LSDatabase::GetLength(uint32_t* aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  uint32_t result;
+  if (NS_WARN_IF(!mActor->SendGetLength(&result))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = result;
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::GetKey(uint32_t aIndex,
+                   nsAString& aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  nsString result;
+  if (NS_WARN_IF(!mActor->SendGetKey(aIndex, &result))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aResult = result;
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::GetItem(const nsAString& aKey,
+                    nsAString& aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  nsString result;
+  if (NS_WARN_IF(!mActor->SendGetItem(nsString(aKey), &result))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aResult = result;
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::GetKeys(nsTArray<nsString>& aKeys)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  nsTArray<nsString> result;
+  if (NS_WARN_IF(!mActor->SendGetKeys(&result))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aKeys.SwapElements(result);
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::SetItem(const nsAString& aKey,
+                    const nsAString& aValue)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  if (NS_WARN_IF(!mActor->SendSetItem(nsString(aKey), nsString(aValue)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::RemoveItem(const nsAString& aKey)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  if (NS_WARN_IF(!mActor->SendRemoveItem(nsString(aKey)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+LSDatabase::Clear()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mActor);
+
+  if (NS_WARN_IF(!mActor->SendClear())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LSDatabase.h
@@ -0,0 +1,73 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_LSDatabase_h
+#define mozilla_dom_localstorage_LSDatabase_h
+
+namespace mozilla {
+namespace dom {
+
+class LSDatabaseChild;
+
+class LSDatabase final
+{
+  LSDatabaseChild* mActor;
+
+public:
+  LSDatabase();
+
+  NS_INLINE_DECL_REFCOUNTING(LSDatabase)
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(LSDatabase);
+  }
+
+  void
+  SetActor(LSDatabaseChild* aActor);
+
+  void
+  ClearActor()
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(mActor);
+
+    mActor = nullptr;
+  }
+
+  nsresult
+  GetLength(uint32_t* aResult);
+
+  nsresult
+  GetKey(uint32_t aIndex,
+         nsAString& aResult);
+
+  nsresult
+  GetItem(const nsAString& aKey,
+          nsAString& aResult);
+
+  nsresult
+  GetKeys(nsTArray<nsString>& aKeys);
+
+  nsresult
+  SetItem(const nsAString& aKey,
+          const nsAString& aValue);
+
+  nsresult
+  RemoveItem(const nsAString& aKey);
+
+  nsresult
+  Clear();
+
+private:
+  ~LSDatabase();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_LSDatabase_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LSObject.cpp
@@ -0,0 +1,629 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LSObject.h"
+
+#include "ActorsChild.h"
+#include "IPCBlobInputStreamThread.h"
+#include "LocalStorageCommon.h"
+#include "mozilla/ThreadEventQueue.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsContentUtils.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsThread.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+class RequestHelper;
+
+StaticMutex gRequestHelperMutex;
+RequestHelper* gRequestHelper = nullptr;
+
+class RequestHelper final
+  : public Runnable
+  , public LSRequestChildCallback
+{
+  enum class State
+  {
+    Initial,
+    ResponsePending,
+    Finishing,
+    Complete
+  };
+
+  RefPtr<LSObject> mObject;
+  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+  nsCOMPtr<nsIEventTarget> mNestedEventTarget;
+  LSRequestChild* mActor;
+  const LSRequestParams mParams;
+  LSRequestResponse mResponse;
+  nsresult mResultCode;
+  State mState;
+  bool mWaiting;
+
+public:
+  RequestHelper(LSObject* aObject,
+                const LSRequestParams& aParams)
+    : Runnable("dom::RequestHelper")
+    , mObject(aObject)
+    , mOwningEventTarget(GetCurrentThreadEventTarget())
+    , mActor(nullptr)
+    , mParams(aParams)
+    , mResultCode(NS_OK)
+    , mState(State::Initial)
+    , mWaiting(true)
+  {
+    StaticMutexAutoLock lock(gRequestHelperMutex);
+    gRequestHelper = this;
+  }
+
+  bool
+  IsOnOwningThread() const
+  {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    bool current;
+    return
+      NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) && current;
+  }
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsOnOwningThread());
+  }
+
+  nsresult
+  StartAndReturnResponse(LSRequestResponse& aResponse);
+
+  nsresult
+  CancelOnAnyThread();
+
+private:
+  ~RequestHelper()
+  {
+    StaticMutexAutoLock lock(gRequestHelperMutex);
+    gRequestHelper = nullptr;
+  }
+
+  nsresult
+  Start();
+
+  void
+  Finish();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIRUNNABLE
+
+  // LSRequestChildCallback
+  void
+  OnResponse(const LSRequestResponse& aResponse) override;
+};
+
+} // namespace
+
+LSObject::LSObject(nsPIDOMWindowInner* aWindow,
+                   nsIPrincipal* aPrincipal)
+  : Storage(aWindow, aPrincipal)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NextGenLocalStorageEnabled());
+}
+
+LSObject::~LSObject()
+{
+  AssertIsOnOwningThread();
+}
+
+// static
+nsresult
+LSObject::Create(nsPIDOMWindowInner* aWindow,
+                 Storage** aStorage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aStorage);
+  MOZ_ASSERT(NextGenLocalStorageEnabled());
+  MOZ_ASSERT(nsContentUtils::StorageAllowedForWindow(aWindow) >
+               nsContentUtils::StorageAccess::eDeny);
+
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
+  MOZ_ASSERT(sop);
+
+  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+  if (NS_WARN_IF(!principal)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+
+  nsAutoPtr<PrincipalOrQuotaInfo> info(new PrincipalOrQuotaInfo());
+  if (XRE_IsParentProcess()) {
+    nsCString suffix;
+    nsCString group;
+    nsCString origin;
+    rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                            &suffix,
+                                            &group,
+                                            &origin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    QuotaInfo quotaInfo;
+    quotaInfo.suffix() = suffix;
+    quotaInfo.group() = group;
+    quotaInfo.origin() = origin;
+
+    *info = quotaInfo;
+  } else {
+    PrincipalInfo principalInfo;
+    rv = PrincipalToPrincipalInfo(principal, &principalInfo);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+
+    *info = principalInfo;
+  }
+
+  RefPtr<LSObject> object = new LSObject(aWindow, principal);
+  object->mInfo = std::move(info);
+
+  object.forget(aStorage);
+  return NS_OK;
+}
+
+// static
+void
+LSObject::CancelSyncLoop()
+{
+  RefPtr<RequestHelper> helper;
+
+  {
+    StaticMutexAutoLock lock(gRequestHelperMutex);
+    helper = gRequestHelper;
+  }
+
+  if (helper) {
+    Unused << NS_WARN_IF(NS_FAILED(helper->CancelOnAnyThread()));
+  }
+}
+
+LSRequestChild*
+LSObject::StartRequest(nsIEventTarget* aMainEventTarget,
+                       const LSRequestParams& aParams,
+                       LSRequestChildCallback* aCallback)
+{
+  AssertIsOnDOMFileThread();
+
+  PBackgroundChild* backgroundActor =
+    BackgroundChild::GetOrCreateForCurrentThread(aMainEventTarget);
+  if (NS_WARN_IF(!backgroundActor)) {
+    return nullptr;
+  }
+
+  LSRequestChild* actor = new LSRequestChild(aCallback);
+
+  backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams);
+
+  return actor;
+}
+
+Storage::StorageType
+LSObject::Type() const
+{
+  AssertIsOnOwningThread();
+
+  return eLocalStorage;
+}
+
+bool
+LSObject::IsForkOf(const Storage* aStorage) const
+{
+  AssertIsOnOwningThread();
+
+  return false;
+}
+
+int64_t
+LSObject::GetOriginQuotaUsage() const
+{
+  AssertIsOnOwningThread();
+
+  return 0;
+}
+
+uint32_t
+LSObject::GetLength(nsIPrincipal& aSubjectPrincipal,
+                    ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return 0;
+  }
+
+  uint32_t result;
+  rv = mDatabase->GetLength(&result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return 0;
+  }
+
+  return result;
+}
+
+void
+LSObject::Key(uint32_t aIndex,
+              nsAString& aResult,
+              nsIPrincipal& aSubjectPrincipal,
+              ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  nsString result;
+  rv = mDatabase->GetKey(aIndex, result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  aResult = result;
+}
+
+void
+LSObject::GetItem(const nsAString& aKey,
+                  nsAString& aResult,
+                  nsIPrincipal& aSubjectPrincipal,
+                  ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  nsString result;
+  rv = mDatabase->GetItem(aKey, result);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  aResult = result;
+}
+
+void
+LSObject::GetSupportedNames(nsTArray<nsString>& aNames)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  rv = mDatabase->GetKeys(aNames);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+}
+
+void
+LSObject::SetItem(const nsAString& aKey,
+                  const nsAString& aValue,
+                  nsIPrincipal& aSubjectPrincipal,
+                  ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = mDatabase->SetItem(aKey, aValue);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+}
+
+void
+LSObject::RemoveItem(const nsAString& aKey,
+                     nsIPrincipal& aSubjectPrincipal,
+                     ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = mDatabase->RemoveItem(aKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+}
+
+void
+LSObject::Clear(nsIPrincipal& aSubjectPrincipal,
+                ErrorResult& aError)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = EnsureDatabase();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = mDatabase->Clear();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+}
+
+nsresult
+LSObject::EnsureDatabase()
+{
+  AssertIsOnOwningThread();
+
+  if (mDatabase) {
+    return NS_OK;
+  }
+
+  // We don't need this yet, but once the request successfully finishes, it's
+  // too late to initialize PBackground child on the owning thread, because
+  // it can fail and parent would keep an extra strong ref to the datastore.
+  PBackgroundChild* backgroundActor =
+    BackgroundChild::GetOrCreateForCurrentThread();
+  if (NS_WARN_IF(!backgroundActor)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  LSRequestPrepareDatastoreParams params;
+  params.info() = *mInfo;
+
+  RefPtr<RequestHelper> helper = new RequestHelper(this, params);
+
+  LSRequestResponse response;
+
+  // This will start and finish the request on the DOM File thread.
+  // The owning thread is synchronously blocked while the request is
+  // asynchronously processed on the DOM File thread.
+  nsresult rv = helper->StartAndReturnResponse(response);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (response.type() == LSRequestResponse::Tnsresult) {
+    return response.get_nsresult();
+  }
+
+  MOZ_ASSERT(response.type() ==
+               LSRequestResponse::TLSRequestPrepareDatastoreResponse);
+
+  const LSRequestPrepareDatastoreResponse& prepareDatastoreResponse =
+    response.get_LSRequestPrepareDatastoreResponse();
+
+  uint64_t datastoreId = prepareDatastoreResponse.datastoreId();
+
+  // The datastore is now ready on the parent side (prepared by the asynchronous
+  // request on the DOM File thread).
+  // Let's create a direct connection to the datastore (through a database
+  // actor) from the owning thread.
+  // Note that we now can't error out, otherwise parent will keep an extra
+  // strong reference to the datastore.
+  RefPtr<LSDatabase> database = new LSDatabase();
+
+  LSDatabaseChild* actor = new LSDatabaseChild(database);
+
+  MOZ_ALWAYS_TRUE(
+    backgroundActor->SendPBackgroundLSDatabaseConstructor(actor, datastoreId));
+
+  database->SetActor(actor);
+
+  mDatabase = std::move(database);
+
+  return NS_OK;
+}
+
+nsresult
+RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  // Normally, we would use the standard way of blocking the thread using
+  // a monitor.
+  // The problem is that BackgroundChild::GetOrCreateForCurrentThread()
+  // called on the DOM File thread may dispatch a runnable to the main
+  // thread to finish initialization of PBackground. A monitor would block
+  // the main thread and the runnable would never get executed causing the
+  // helper to be stuck in a wait loop.
+  // However, BackgroundChild::GetOrCreateForCurrentThread() supports passing
+  // a custom main event target, so we can create a nested event target and
+  // spin the event loop. Nothing can dispatch to the nested event target
+  // except BackgroundChild::GetOrCreateForCurrentThread(), so spinning of the
+  // event loop can't fire any other events.
+  // This way the thread is synchronously blocked in a safe manner and the
+  // runnable gets executed.
+  {
+    auto thread = static_cast<nsThread*>(NS_GetCurrentThread());
+
+    auto queue =
+      static_cast<ThreadEventQueue<EventQueue>*>(thread->EventQueue());
+
+    mNestedEventTarget = queue->PushEventQueue();
+    MOZ_ASSERT(mNestedEventTarget);
+
+    auto autoPopEventQueue = mozilla::MakeScopeExit([&] {
+      queue->PopEventQueue(mNestedEventTarget);
+    });
+
+    nsCOMPtr<nsIEventTarget> domFileThread =
+      IPCBlobInputStreamThread::GetOrCreate();
+    if (NS_WARN_IF(!domFileThread)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
+      return !mWaiting;
+    }));
+  }
+
+  if (NS_WARN_IF(NS_FAILED(mResultCode))) {
+    return mResultCode;
+  }
+
+  aResponse = std::move(mResponse);
+  return NS_OK;
+}
+
+nsresult
+RequestHelper::CancelOnAnyThread()
+{
+  RefPtr<RequestHelper> self = this;
+
+  RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+    "RequestHelper::CancelOnAnyThread",
+    [self] () {
+      LSRequestChild* actor = self->mActor;
+      if (actor && !actor->Finishing()) {
+        actor->SendCancel();
+      }
+    });
+
+  nsCOMPtr<nsIEventTarget> domFileThread =
+    IPCBlobInputStreamThread::GetOrCreate();
+  if (NS_WARN_IF(!domFileThread)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = domFileThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+RequestHelper::Start()
+{
+  AssertIsOnDOMFileThread();
+  MOZ_ASSERT(mState == State::Initial);
+
+  mState = State::ResponsePending;
+
+  LSRequestChild* actor =
+    mObject->StartRequest(mNestedEventTarget, mParams, this);
+  if (NS_WARN_IF(!actor)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mActor = actor;
+
+  return NS_OK;
+}
+
+void
+RequestHelper::Finish()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::Finishing);
+
+  mObject = nullptr;
+
+  mWaiting = false;
+
+  mState = State::Complete;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper, Runnable)
+
+NS_IMETHODIMP
+RequestHelper::Run()
+{
+  nsresult rv;
+
+  switch (mState) {
+    case State::Initial:
+      rv = Start();
+      break;
+
+    case State::Finishing:
+      Finish();
+      return NS_OK;
+
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = rv;
+    }
+
+    mState = State::Finishing;
+
+    if (IsOnOwningThread()) {
+      Finish();
+    } else {
+      MOZ_ALWAYS_SUCCEEDS(mNestedEventTarget->Dispatch(this,
+                                                       NS_DISPATCH_NORMAL));
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+RequestHelper::OnResponse(const LSRequestResponse& aResponse)
+{
+  AssertIsOnDOMFileThread();
+  MOZ_ASSERT(mState == State::ResponsePending);
+
+  mActor = nullptr;
+
+  mResponse = aResponse;
+
+  mState = State::Finishing;
+  MOZ_ALWAYS_SUCCEEDS(mNestedEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LSObject.h
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_LSObject_h
+#define mozilla_dom_localstorage_LSObject_h
+
+#include "mozilla/dom/Storage.h"
+
+class nsIPrincipal;
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class LSDatabase;
+class LSRequestChild;
+class LSRequestChildCallback;
+class LSRequestParams;
+class PrincipalOrQuotaInfo;
+
+class LSObject final
+  : public Storage
+{
+  nsAutoPtr<PrincipalOrQuotaInfo> mInfo;
+
+  RefPtr<LSDatabase> mDatabase;
+
+public:
+  static nsresult
+  Create(nsPIDOMWindowInner* aWindow,
+         Storage** aStorage);
+
+  static void
+  CancelSyncLoop();
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(LSObject);
+  }
+
+  LSRequestChild*
+  StartRequest(nsIEventTarget* aMainEventTarget,
+               const LSRequestParams& aParams,
+               LSRequestChildCallback* aCallback);
+
+  // Storage overrides.
+  StorageType
+  Type() const override;
+
+  bool
+  IsForkOf(const Storage* aStorage) const override;
+
+  int64_t
+  GetOriginQuotaUsage() const override;
+
+  uint32_t
+  GetLength(nsIPrincipal& aSubjectPrincipal,
+            ErrorResult& aError) override;
+
+  void
+  Key(uint32_t aIndex,
+      nsAString& aResult,
+      nsIPrincipal& aSubjectPrincipal,
+      ErrorResult& aError) override;
+
+  void
+  GetItem(const nsAString& aKey,
+          nsAString& aResult,
+          nsIPrincipal& aSubjectPrincipal,
+          ErrorResult& aError) override;
+
+  void
+  GetSupportedNames(nsTArray<nsString>& aNames) override;
+
+  void
+  SetItem(const nsAString& aKey,
+          const nsAString& aValue,
+          nsIPrincipal& aSubjectPrincipal,
+          ErrorResult& aError) override;
+
+  void
+  RemoveItem(const nsAString& aKey,
+             nsIPrincipal& aSubjectPrincipal,
+             ErrorResult& aError) override;
+
+  void
+  Clear(nsIPrincipal& aSubjectPrincipal,
+        ErrorResult& aError) override;
+
+private:
+  LSObject(nsPIDOMWindowInner* aWindow,
+           nsIPrincipal* aPrincipal);
+
+  ~LSObject();
+
+  nsresult
+  EnsureDatabase();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_LSObject_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LocalStorageCommon.cpp
@@ -0,0 +1,32 @@
+/* -*- 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 "LocalStorageCommon.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+int32_t gNextGenLocalStorageEnabled = -1;
+
+} // namespace
+
+bool
+NextGenLocalStorageEnabled()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (gNextGenLocalStorageEnabled == -1) {
+    bool enabled = Preferences::GetBool("dom.storage.next_gen", false);
+    gNextGenLocalStorageEnabled = enabled ? 1 : 0;
+  }
+
+  return !!gNextGenLocalStorageEnabled;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LocalStorageCommon.h
@@ -0,0 +1,193 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_LocalStorageCommon_h
+#define mozilla_dom_localstorage_LocalStorageCommon_h
+
+/*
+ * Local storage
+ * ~~~~~~~~~~~~~
+ *
+ * Implementation overview
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The implementation is based on a per principal/origin cache (datastore)
+ * living in the main process and synchronous calls initiated from content
+ * processes.
+ * The IPC communication is managed by database actors which link to the
+ * datastore.
+ * The synchronous blocking of the main thread is done by using a special
+ * technique or by using standard synchronous IPC calls.
+ *
+ * General architecture
+ * ~~~~~~~~~~~~~~~~~~~~
+ * The current browser architecture consists of one main process and multiple
+ * content processes (there are other processes but for simplicity's sake, they
+ * are not mentioned here). The processes use the IPC communication to talk to
+ * each other. Local storage implementation uses the client-server model, so
+ * the main process manages all the data and content processes then request
+ * particular data from the main process. The main process is also called the
+ * parent or the parent side, the content process is then called the child or
+ * the child side.
+ *
+ * Datastores
+ * ~~~~~~~~~~
+ *
+ * A datastore provides a convenient way to access data for given origin. The
+ * data is always preloaded into memory and indexed using a hash table. This
+ * enables very fast access to particular stored items. There can be only one
+ * datastore per origin and exists solely on the parent side. It is represented
+ * by the "Datastore" class. A datastore instance is a ref counted object and
+ * lives on the PBackground thread, it is kept alive by database objects. When
+ * the last database object for given origin is destroyed, the associated
+ * datastore object is destroyed too.
+ *
+ * Databases
+ * ~~~~~~~~~
+ *
+ * A database allows direct access to a datastore from a content process. There
+ * can be multiple databases for the same origin, but they all share the same
+ * datastore.
+ * Databases use the PBackgroundLSDatabase IPDL protocol for IPC communication.
+ * Given the nature of local storage, most of PBackgroundLSDatabase messages
+ * are synchronous.
+ *
+ * On the parent side, the database is represented by the "Database" class that
+ * is a parent actor as well (implements the "PBackgroundLSDatabaseParent"
+ * interface). A database instance is a ref counted object and lives on the
+ * PBackground thread.
+ * All live database actors are tracked in an array.
+ *
+ * On the child side, the database is represented by the "LSDatabase" class
+ * that provides indirect access to a child actor. An LSDatabase instance is a
+ * ref counted object and lives on the main thread.
+ * The actual child actor is represented by the "LSDatabaseChild" class that
+ * implements the "PBackgroundLSDatabaseChild" interface. An "LSDatabaseChild"
+ * instance is not ref counted and lives on the main thread too.
+ *
+ * Synchronous blocking
+ * ~~~~~~~~~~~~~~~~~~~~
+ *
+ * Local storage is synchronous in nature which means the execution can't move
+ * forward until there's a reply for given method call.
+ * Since we have to use IPC anyway, we could just always use synchronous IPC
+ * messages for all local storage method calls. Well, there's a problem with
+ * that approach.
+ * If the main process needs to do some off PBackground thread stuff like
+ * getting info from principals on the main thread or some asynchronous stuff
+ * like directory locking before sending a reply to a synchronous message, then
+ * we would have to block the thread or spin the event loop which is usually a
+ * bad idea, especially in the main process.
+ * Instead, we can use a special thread in the content process called DOM File
+ * thread for communication with the main process using asynchronous messages
+ * and synchronously block the main thread until the DOM File thread is done
+ * (the main thread blocking is a bit more complicated, see the comment in
+ * RequestHelper::StartAndReturnResponse for more details).
+ * Anyway, the extra hop to the DOM File thread brings another overhead and
+ * latency. The final solution is to use a combination of the special thread
+ * for complex stuff like datastore preparation and synchronous IPC messages
+ * sent directly from the main thread for database access when data is already
+ * loaded from disk into memory.
+ *
+ * Requests
+ * ~~~~~~~~
+ *
+ * Requests are used to handle asynchronous high level datastore operations
+ * which are initiated in a content process and then processed in the parent
+ * process (for example, preparation of a datastore).
+ * Requests use the "PBackgroundLSRequest" IPDL protocol for IPC communication.
+ *
+ * On the parent side, the request is represented by the "LSRequestBase" class
+ * that is a parent actor as well (implements the "PBackgroundLSRequestParent"
+ * interface). It's an abstract class (contains pure virtual functions) so it
+ * can't be used to create instances.
+ * It also inherits from the "DatastoreOperationBase" class which is a generic
+ * base class for all datastore operations. The "DatastoreOperationsBase" class
+ * inherits from the "Runnable" class, so derived class instances are ref
+ * counted, can be dispatched to multiple threads and thus they are used on
+ * multiple threads. However, derived class instances can be created on the
+ * PBackground thread only.
+ *
+ * On the child side, the request is represented by the "RequestHelper" class
+ * that covers all the complexity needed to start a new request, handle
+ * responses and do safe main thread blocking at the same time.
+ * It inherits from the "Runnable" class, so instances are ref counted and
+ * they are internally used on multiple threads (specifically on the main
+ * thread and on the DOM File thread). Anyway, users should create and use
+ * instances of this class only on the main thread (apart from a special case
+ * when we need to cancel the request from an internal chromium IPC thread to
+ * prevent a dead lock involving CPOWs).
+ * The actual child actor is represented by the "LSRequestChild" class that
+ * implements the "PBackgroundLSRequestChild" interface. An "LSRequestChild"
+ * instance is not ref counted and lives on the DOM File thread.
+ * Request responses are passed using the "LSRequestChildCallback" interface.
+ *
+ * Preparation of a datastore
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The datastore preparation is needed to make sure a datastore is fully loaded
+ * into memory. Every datastore preparation produces a unique id (even if the
+ * datastore for given origin already exists).
+ * On the parent side, the preparation is handled by the "PrepareDatastoreOp"
+ * class which inherits from the "LSRequestBase" class. The preparation process
+ * on the parent side is quite complicated, it happens sequentially on multiple
+ * threads and is managed by a state machine.
+ * On the child side, the preparation is done in the LSObject::EnsureDatabase
+ * method using the "RequestHelper" class. The method starts a new preparation
+ * request and obtains a unique id produced by the parent (or an error code if
+ * the requested failed to complete).
+ *
+ * Linking databases to a datastore
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * A datastore exists only on the parent side, but it can be accessed from the
+ * content via database actors. Database actors are initiated on the child side
+ * and they need to be linked to a datastore on the parent side via an id. The
+ * datastore preparation process gives us the required id.
+ * The linking is initiated on the child side in the LSObject::EnsureDatabase
+ * method by calling SendPBackgroundLSDatabaseConstructor and finished in
+ * RecvPBackgroundLSDatabaseConstructor on the parent side.
+ *
+ * Actor migration
+ * ~~~~~~~~~~~~~~~
+ *
+ * In theory, the datastore preparation request could return a database actor
+ * directly (instead of returning an id intended for database linking to a
+ * datastore). However, as it was explained above, the preparation must be done
+ * on the DOM File thread and database objects are used on the main thread. The
+ * returned actor would have to be migrated from the DOM File thread to the
+ * main thread and that's something which our IPDL doesn't support yet.
+ *
+ * Exposing local storage
+ * ~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The implementation is exposed to the DOM via window.localStorage attribute.
+ * Local storage's sibling, session storage shares the same WebIDL interface
+ * for exposing it to web content, therefore there's an abstract class called
+ * "Storage" that handles some of the common DOM bindings stuff. Local storage
+ * specific functionality is defined in the "LSObject" derived class.
+ * The "LSObject" class is also a starting point for the datastore preparation
+ * and database linking.
+ *
+ * Local storage manager
+ * ~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The local storage manager exposes some of the features that need to be
+ * available only in the chrome code or tests. The manager is represented by
+ * the "LocalStorageManager2" class that implements the "nsIDOMStorageManager"
+ * interface.
+ */
+
+namespace mozilla {
+namespace dom {
+
+bool
+NextGenLocalStorageEnabled();
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_LocalStorageCommon_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LocalStorageManager2.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LocalStorageManager2.h"
+
+#include "LSObject.h"
+
+namespace mozilla {
+namespace dom {
+
+LocalStorageManager2::LocalStorageManager2()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NextGenLocalStorageEnabled());
+}
+
+LocalStorageManager2::~LocalStorageManager2()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager)
+
+NS_IMETHODIMP
+LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
+                                      Storage** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(_retval);
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
+                                    nsIPrincipal* aPrincipal,
+                                    const nsAString& aDocumentURI,
+                                    bool aPrivate,
+                                    Storage** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(_retval);
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
+                                 nsIPrincipal* aPrincipal,
+                                 bool aPrivate,
+                                 Storage** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(_retval);
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aStorageToCloneFrom);
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal,
+                                   Storage *aStorage,
+                                   bool* _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aStorage);
+  MOZ_ASSERT(_retval);
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/LocalStorageManager2.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_localstorage_LocalStorageManager2_h
+#define mozilla_dom_localstorage_LocalStorageManager2_h
+
+#include "nsIDOMStorageManager.h"
+
+namespace mozilla {
+namespace dom {
+
+class LocalStorageManager2 final
+  : public nsIDOMStorageManager
+{
+public:
+  LocalStorageManager2();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMSTORAGEMANAGER
+
+private:
+  ~LocalStorageManager2();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_localstorage_LocalStorageManager2_h
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/PBackgroundLSDatabase.ipdl
@@ -0,0 +1,49 @@
+/* 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 protocol PBackground;
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PBackgroundLSDatabase
+{
+  manager PBackground;
+
+parent:
+  // The DeleteMe message is used to avoid a race condition between the parent
+  // actor and the child actor. The PBackgroundLSDatabase protocol could be
+  // simply destroyed by sending the __delete__ message from the child side.
+  // However, that would destroy the child actor immediatelly and the parent
+  // could be sending a message to the child at the same time resulting in a
+  // routing error since the child actor wouldn't exist anymore. A routing
+  // error typically causes a crash. The race can be prevented by doing the
+  // teardown in two steps. First, we send the DeleteMe message to the parent
+  // and the parent then sends the __delete__ message to the child.
+  async DeleteMe();
+
+  sync GetLength()
+    returns (uint32_t length);
+
+  sync GetKey(uint32_t index)
+    returns (nsString key);
+
+  sync GetItem(nsString key)
+    returns (nsString value);
+
+  sync GetKeys()
+    returns (nsString[] keys);
+
+  sync SetItem(nsString key, nsString value);
+
+  sync RemoveItem(nsString key);
+
+  sync Clear();
+
+child:
+  async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/PBackgroundLSRequest.ipdl
@@ -0,0 +1,49 @@
+/* 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 protocol PBackground;
+
+namespace mozilla {
+namespace dom {
+
+struct LSRequestPrepareDatastoreResponse
+{
+  uint64_t datastoreId;
+};
+
+union LSRequestResponse
+{
+  nsresult;
+  LSRequestPrepareDatastoreResponse;
+};
+
+protocol PBackgroundLSRequest
+{
+  manager PBackground;
+
+parent:
+  // The Cancel message is used to avoid a possible dead lock caused by a CPOW
+  // sending a synchronous message from the main thread in the chrome process
+  // to the main thread in the content process at the time we are blocking
+  // the main thread in the content process to handle a request.
+  // We use the PBackground thread on the parent side to handle requests, but
+  // sometimes we need to get information from principals and that's currently
+  // only possible on the main thread. So if the main thread in the chrome
+  // process is blocked by a CPOW operation, our request must wait for the CPOW
+  // operation to complete. However the CPOW operation can't complete either
+  // because we are blocking the main thread in the content process.
+  // The dead lock is prevented by canceling our nested event loop in the
+  // content process when we receive a synchronous IPC message from the parent.
+  async Cancel();
+
+  async Finish();
+
+child:
+  async __delete__(LSRequestResponse response);
+
+  async Ready();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh
@@ -0,0 +1,32 @@
+/* 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 PBackgroundSharedTypes;
+
+namespace mozilla {
+namespace dom {
+
+struct QuotaInfo {
+  nsCString suffix;
+  nsCString group;
+  nsCString origin;
+};
+
+union PrincipalOrQuotaInfo {
+  PrincipalInfo;
+  QuotaInfo;
+};
+
+struct LSRequestPrepareDatastoreParams
+{
+  PrincipalOrQuotaInfo info;
+};
+
+union LSRequestParams
+{
+  LSRequestPrepareDatastoreParams;
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom.localstorage += [
+    'ActorsParent.h',
+]
+
+EXPORTS.mozilla.dom += [
+    'LocalStorageCommon.h',
+    'LocalStorageManager2.h',
+    'LSObject.h',
+]
+
+UNIFIED_SOURCES += [
+    'ActorsChild.cpp',
+    'ActorsParent.cpp',
+    'LocalStorageCommon.cpp',
+    'LocalStorageManager2.cpp',
+    'LSDatabase.cpp',
+    'LSObject.cpp',
+]
+
+IPDL_SOURCES += [
+    'PBackgroundLSDatabase.ipdl',
+    'PBackgroundLSRequest.ipdl',
+    'PBackgroundLSSharedTypes.ipdlh',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wno-error=shadow']
+
+LOCAL_INCLUDES += [
+    '/dom/file/ipc',
+]
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -98,16 +98,17 @@ DIRS += [
     'xhr',
     'worklet',
     'script',
     'payments',
     'websocket',
     'serviceworkers',
     'simpledb',
     'reporting',
+    'localstorage',
 ]
 
 if CONFIG['MOZ_LIBPRIO']:
     DIRS += ['prio']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
--- a/dom/storage/LocalStorageManager.cpp
+++ b/dom/storage/LocalStorageManager.cpp
@@ -16,16 +16,17 @@
 #include "nsNetCID.h"
 #include "nsIURL.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/LocalStorageCommon.h"
 
 // Only allow relatively small amounts of data since performance of
 // the synchronous IO is very bad.
 // We are enforcing simple per-origin quota only.
 #define DEFAULT_QUOTA_LIMIT (5 * 1024)
 
 namespace mozilla {
 namespace dom {
@@ -56,16 +57,18 @@ LocalStorageManager::GetQuota()
 }
 
 NS_IMPL_ISUPPORTS(LocalStorageManager,
                   nsIDOMStorageManager)
 
 LocalStorageManager::LocalStorageManager()
   : mCaches(8)
 {
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
   StorageObserver* observer = StorageObserver::Self();
   NS_ASSERTION(observer, "No StorageObserver, cannot observe private data delete notifications!");
 
   if (observer) {
     observer->AddSink(this);
   }
 
   NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
@@ -468,19 +471,30 @@ LocalStorageManager::Observe(const char*
     return NS_OK;
   }
 #endif
 
   NS_ERROR("Unexpected topic");
   return NS_ERROR_UNEXPECTED;
 }
 
+// static
+LocalStorageManager*
+LocalStorageManager::Self()
+{
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
+  return sSelf;
+}
+
 LocalStorageManager*
 LocalStorageManager::Ensure()
 {
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
+
   if (sSelf) {
     return sSelf;
   }
 
   // Cause sSelf to be populated.
   nsCOMPtr<nsIDOMStorageManager> initializer =
     do_GetService("@mozilla.org/dom/localStorage-manager;1");
   MOZ_ASSERT(sSelf, "Didn't initialize?");
--- a/dom/storage/LocalStorageManager.h
+++ b/dom/storage/LocalStorageManager.h
@@ -112,17 +112,17 @@ private:
   typedef nsTHashtable<LocalStorageCacheHashKey> CacheOriginHashtable;
   nsClassHashtable<nsCStringHashKey, CacheOriginHashtable> mCaches;
 
   void ClearCaches(uint32_t aUnloadFlags,
                    const OriginAttributesPattern& aPattern,
                    const nsACString& aKeyPrefix);
 
   // Global getter of localStorage manager service
-  static LocalStorageManager* Self() { return sSelf; }
+  static LocalStorageManager* Self();
 
   // Like Self, but creates an instance if we're not yet initialized.
   static LocalStorageManager* Ensure();
 
 private:
   // Keeps usage cache objects for eTLD+1 scopes we have touched.
   nsDataHashtable<nsCStringHashKey, RefPtr<StorageUsage> > mUsages;
 
--- a/dom/storage/StorageIPC.cpp
+++ b/dom/storage/StorageIPC.cpp
@@ -142,36 +142,39 @@ StorageDBChild::ReleaseIPDLReference()
   Release();
 }
 
 StorageDBChild::StorageDBChild(LocalStorageManager* aManager)
   : mManager(aManager)
   , mStatus(NS_OK)
   , mIPCOpen(false)
 {
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
 }
 
 StorageDBChild::~StorageDBChild()
 {
 }
 
 // static
 StorageDBChild*
 StorageDBChild::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
 
   return sStorageChild;
 }
 
 // static
 StorageDBChild*
 StorageDBChild::GetOrCreate()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!NextGenLocalStorageEnabled());
 
   if (sStorageChild || sStorageChildDown) {
     // When sStorageChildDown is at true, sStorageChild is null.
     // Checking sStorageChildDown flag here prevents reinitialization of
     // the storage child after shutdown.
     return sStorageChild;
   }
 
--- a/dom/storage/StorageObserver.cpp
+++ b/dom/storage/StorageObserver.cpp
@@ -176,23 +176,25 @@ StorageObserver::ClearMatchingOrigin(con
   }
 
   nsCString originScope;
   rv = CreateReversedDomain(convertedDomain, 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;
+  if (!NextGenLocalStorageEnabled()) {
+    if (XRE_IsParentProcess()) {
+      StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
+      if (NS_WARN_IF(!storageChild)) {
+        return NS_ERROR_FAILURE;
+      }
+
+      storageChild->SendClearMatchingOrigin(originScope);
     }
-
-    storageChild->SendClearMatchingOrigin(originScope);
   }
 
   aOriginScope = originScope;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StorageObserver::Observe(nsISupports* aSubject,
@@ -200,26 +202,31 @@ StorageObserver::Observe(nsISupports* aS
                          const char16_t* aData)
 {
   nsresult rv;
 
   // Start the thread that opens the database.
   if (!strcmp(aTopic, kStartupTopic)) {
     MOZ_ASSERT(XRE_IsParentProcess());
 
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->RemoveObserver(this, kStartupTopic);
 
     return NS_NewTimerWithObserver(getter_AddRefs(mDBThreadStartDelayTimer),
                                    this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
   }
 
   // Timer callback used to start the database a short timer after startup
   if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
     MOZ_ASSERT(XRE_IsParentProcess());
+    MOZ_ASSERT(!NextGenLocalStorageEnabled());
 
     nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
     if (!timer) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (timer == mDBThreadStartDelayTimer) {
       mDBThreadStartDelayTimer = nullptr;
@@ -236,25 +243,27 @@ StorageObserver::Observe(nsISupports* aS
   }
 
   // Clear everything, caches + database
   if (!strcmp(aTopic, "cookie-changed")) {
     if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
       return NS_OK;
     }
 
-    StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
-    if (NS_WARN_IF(!storageChild)) {
-      return NS_ERROR_FAILURE;
-    }
+    if (!NextGenLocalStorageEnabled()) {
+      StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
+      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;
   }
 
   // Clear from caches everything that has been stored
@@ -306,16 +315,20 @@ StorageObserver::Observe(nsISupports* aS
 
     Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix),
            originScope);
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "extension:purge-localStorage")) {
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     const char topic[] = "extension:purge-localStorage-caches";
 
     if (aData) {
       nsCString originScope;
       rv = ClearMatchingOrigin(aData, originScope);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
@@ -350,25 +363,33 @@ StorageObserver::Observe(nsISupports* aS
 
     Notify("domain-data-cleared", EmptyString(), originScope);
 
     return NS_OK;
   }
 
   // Clear all private-browsing caches
   if (!strcmp(aTopic, "last-pb-context-exited")) {
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     Notify("private-browsing-data-cleared");
 
     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());
 
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     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)) {
@@ -386,16 +407,20 @@ StorageObserver::Observe(nsISupports* aS
     Notify("profile-change");
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "profile-before-change")) {
     MOZ_ASSERT(XRE_IsParentProcess());
 
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     if (mBackgroundThread) {
       bool done = false;
 
       RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
         new StorageDBThread::ShutdownRunnable(done);
       MOZ_ALWAYS_SUCCEEDS(
         mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
 
@@ -404,34 +429,46 @@ StorageObserver::Observe(nsISupports* aS
       mBackgroundThread = 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;
     }
 
     storageChild->SendAsyncFlush();
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "domstorage-test-flushed")) {
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     // Only used to propagate to IPC children
     Notify("test-flushed");
 
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "domstorage-test-reload")) {
+    if (NextGenLocalStorageEnabled()) {
+      return NS_OK;
+    }
+
     Notify("test-reload");
 
     return NS_OK;
   }
 #endif
 
   NS_ERROR("Unexpected topic");
   return NS_ERROR_UNEXPECTED;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -48,18 +48,18 @@ skip-if = e10s
 [browser_hasbeforeunload.js]
 support-files =
   beforeunload_test_page.html
 run-if = e10s
 [browser_largeAllocation_win32.js]
 skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
 [browser_largeAllocation_non_win32.js]
 skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) || (os == 'linux') || (os == 'mac' && debug)  # Large-Allocation requires e10s # Bug 1336075
-[browser_localStorage_e10s.js]
-skip-if = !e10s || verify # This is a test of e10s functionality.
+#[browser_localStorage_e10s.js]
+#skip-if = !e10s || verify # This is a test of e10s functionality.
 [browser_localStorage_privatestorageevent.js]
 [browser_persist_cookies.js]
 support-files =
   set-samesite-cookies-and-redirect.sjs
   mimeme.sjs
 [browser_persist_mixed_content_image.js]
 support-files =
   test_mixed_content_image.html
--- a/dom/tests/mochitest/localstorage/chrome.ini
+++ b/dom/tests/mochitest/localstorage/chrome.ini
@@ -2,10 +2,10 @@
 skip-if = os == 'android'
 support-files =
   page_blank.html
   frameQuota.html
   interOriginFrame.js
 
 [test_localStorageBasePrivateBrowsing_perwindowpb.html]
 skip-if = true # bug 1156725
-[test_localStorageFromChrome.xhtml]
-[test_localStorageQuotaPrivateBrowsing_perwindowpb.html]
+#[test_localStorageFromChrome.xhtml]
+#[test_localStorageQuotaPrivateBrowsing_perwindowpb.html]
--- a/dom/tests/mochitest/localstorage/mochitest.ini
+++ b/dom/tests/mochitest/localstorage/mochitest.ini
@@ -14,40 +14,40 @@ support-files =
   interOriginFrame.js
   interOriginTest.js
   interOriginTest2.js
   localStorageCommon.js
   frameLocalStorageSessionOnly.html
   file_tryAccessSessionStorage.html
 
 [test_brokenUTF-16.html]
-[test_bug600307-DBOps.html]
+#[test_bug600307-DBOps.html]
 [test_bug746272-1.html]
 [test_bug746272-2.html]
 skip-if = os == "android" || verify # bug 962029
 [test_cookieBlock.html]
-[test_cookieSession.html]
+#[test_cookieSession.html]
 [test_embededNulls.html]
 [test_keySync.html]
-[test_localStorageBase.html]
-skip-if = e10s
-[test_localStorageBaseSessionOnly.html]
+#[test_localStorageBase.html]
+#skip-if = e10s
+#[test_localStorageBaseSessionOnly.html]
 [test_localStorageCookieSettings.html]
-[test_localStorageEnablePref.html]
+#[test_localStorageEnablePref.html]
 [test_localStorageKeyOrder.html]
 [test_localStorageOriginsDiff.html]
 [test_localStorageOriginsDomainDiffs.html]
 [test_localStorageOriginsEquals.html]
 skip-if = toolkit == 'android'
 [test_localStorageOriginsPortDiffs.html]
 [test_localStorageOriginsSchemaDiffs.html]
 skip-if = toolkit == 'android' #TIMED_OUT
-[test_localStorageQuota.html]
-skip-if = toolkit == 'android' #TIMED_OUT
-[test_localStorageQuotaSessionOnly.html]
-skip-if = toolkit == 'android' || (verify && (os == 'linux' || os == 'win')) #TIMED_OUT
+#[test_localStorageQuota.html]
+#skip-if = toolkit == 'android' #TIMED_OUT
+#[test_localStorageQuotaSessionOnly.html]
+#skip-if = toolkit == 'android' || (verify && (os == 'linux' || os == 'win')) #TIMED_OUT
 [test_localStorageQuotaSessionOnly2.html]
 skip-if = true # bug 1347690
 [test_localStorageReplace.html]
 skip-if = toolkit == 'android'
 [test_storageConstructor.html]
-[test_localStorageSessionPrefOverride.html]
+#[test_localStorageSessionPrefOverride.html]
 [test_firstPartyOnlyPermission.html]
--- a/dom/tests/mochitest/storageevent/mochitest.ini
+++ b/dom/tests/mochitest/storageevent/mochitest.ini
@@ -6,12 +6,12 @@ support-files =
   frameSessionStorageMasterEqual.html
   frameSessionStorageMasterNotEqual.html
   frameSessionStorageSlaveEqual.html
   frameSessionStorageSlaveNotEqual.html
   interOriginFrame.js
   interOriginTest2.js
 
 [test_storageLocalStorageEventCheckNoPropagation.html]
-[test_storageLocalStorageEventCheckPropagation.html]
-[test_storageNotifications.html]
+#[test_storageLocalStorageEventCheckPropagation.html]
+#[test_storageNotifications.html]
 [test_storageSessionStorageEventCheckNoPropagation.html]
 [test_storageSessionStorageEventCheckPropagation.html]
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -174,17 +174,17 @@ MOCHITEST_MANIFESTS += [
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'mochitest/beacon/chrome.ini',
     'mochitest/chrome/chrome.ini',
     'mochitest/general/chrome.ini',
     'mochitest/geolocation/chrome.ini',
     'mochitest/keyhandling/chrome.ini',
-    'mochitest/localstorage/chrome.ini',
+#    'mochitest/localstorage/chrome.ini',
     'mochitest/sessionstorage/chrome.ini',
     'mochitest/webcomponents/chrome.ini',
     'mochitest/whatwg/chrome.ini',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['browser/browser.ini', 'browser/perfmetrics/browser.ini',
                              'mochitest/ajax/offline/browser.ini']
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -136,19 +136,19 @@ skip-if = toolkit == 'android' #bug 9577
 skip-if = os == 'android'
 [test_bug636465.html]
 skip-if = os == 'android'
 [test_bug638596.html]
 [test_bug641466.html]
 [test_bug645914.html]
 [test_bug646194.html]
 [test_bug668599.html]
-[test_bug674770-1.html]
-subsuite = clipboard
-skip-if = toolkit == 'android' || verify
+#[test_bug674770-1.html]
+#subsuite = clipboard
+#skip-if = toolkit == 'android' || verify
 [test_bug674770-2.html]
 subsuite = clipboard
 skip-if = toolkit == 'android'
 [test_bug674861.html]
 [test_bug676401.html]
 [test_bug677752.html]
 [test_bug681229.html]
 subsuite = clipboard
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -11,16 +11,18 @@
 #include "FileDescriptorSetChild.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasChild.h"
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/dom/ClientManagerActors.h"
+#include "mozilla/dom/PBackgroundLSDatabaseChild.h"
+#include "mozilla/dom/PBackgroundLSRequestChild.h"
 #include "mozilla/dom/PBackgroundSDBConnectionChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
@@ -218,16 +220,68 @@ BackgroundChildImpl::DeallocPBackgroundI
                                          PBackgroundIndexedDBUtilsChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+BackgroundChildImpl::PBackgroundSDBConnectionChild*
+BackgroundChildImpl::AllocPBackgroundSDBConnectionChild(
+                                            const PrincipalInfo& aPrincipalInfo)
+{
+  MOZ_CRASH("PBackgroundSDBConnectionChild actor should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundSDBConnectionChild(
+                                          PBackgroundSDBConnectionChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
+BackgroundChildImpl::PBackgroundLSDatabaseChild*
+BackgroundChildImpl::AllocPBackgroundLSDatabaseChild(
+                                                    const uint64_t& aDatastoreId)
+{
+  MOZ_CRASH("PBackgroundLSDatabaseChild actor should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundLSDatabaseChild(
+                                             PBackgroundLSDatabaseChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
+BackgroundChildImpl::PBackgroundLSRequestChild*
+BackgroundChildImpl::AllocPBackgroundLSRequestChild(
+                                                 const LSRequestParams& aParams)
+{
+  MOZ_CRASH("PBackgroundLSRequestChild actor should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundLSRequestChild(
+                                              PBackgroundLSRequestChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
 BackgroundChildImpl::PBackgroundLocalStorageCacheChild*
 BackgroundChildImpl::AllocPBackgroundLocalStorageCacheChild(
                                             const PrincipalInfo& aPrincipalInfo,
                                             const nsCString& aOriginKey,
                                             const uint32_t& aPrivateBrowsingId)
 {
   MOZ_CRASH("PBackgroundLocalStorageChild actors should be manually "
             "constructed!");
@@ -238,34 +292,16 @@ BackgroundChildImpl::DeallocPBackgroundL
                                       PBackgroundLocalStorageCacheChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
-BackgroundChildImpl::PBackgroundSDBConnectionChild*
-BackgroundChildImpl::AllocPBackgroundSDBConnectionChild(
-                                            const PrincipalInfo& aPrincipalInfo)
-{
-  MOZ_CRASH("PBackgroundSDBConnectionChild actor should be manually "
-            "constructed!");
-}
-
-bool
-BackgroundChildImpl::DeallocPBackgroundSDBConnectionChild(
-                                          PBackgroundSDBConnectionChild* aActor)
-{
-  MOZ_ASSERT(aActor);
-
-  delete aActor;
-  return true;
-}
-
 BackgroundChildImpl::PBackgroundStorageChild*
 BackgroundChildImpl::AllocPBackgroundStorageChild(const nsString& aProfilePath)
 {
   MOZ_CRASH("PBackgroundStorageChild actors should be manually constructed!");
 }
 
 bool
 BackgroundChildImpl::DeallocPBackgroundStorageChild(
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -73,16 +73,29 @@ protected:
   virtual PBackgroundSDBConnectionChild*
   AllocPBackgroundSDBConnectionChild(const PrincipalInfo& aPrincipalInfo)
                                      override;
 
   virtual bool
   DeallocPBackgroundSDBConnectionChild(PBackgroundSDBConnectionChild* aActor)
                                        override;
 
+  virtual PBackgroundLSDatabaseChild*
+  AllocPBackgroundLSDatabaseChild(const uint64_t& aCacheId) override;
+
+  virtual bool
+  DeallocPBackgroundLSDatabaseChild(PBackgroundLSDatabaseChild* aActor)
+                                    override;
+
+  virtual PBackgroundLSRequestChild*
+  AllocPBackgroundLSRequestChild(const LSRequestParams& aParams) override;
+
+  virtual bool
+  DeallocPBackgroundLSRequestChild(PBackgroundLSRequestChild* aActor) override;
+
   virtual PBackgroundLocalStorageCacheChild*
   AllocPBackgroundLocalStorageCacheChild(const PrincipalInfo& aPrincipalInfo,
                                          const nsCString& aOriginKey,
                                          const uint32_t& aPrivateBrowsingId)
                                          override;
 
   virtual bool
   DeallocPBackgroundLocalStorageCacheChild(
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -28,16 +28,17 @@
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/StorageActivityService.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/PendingIPCBlobParent.h"
 #include "mozilla/dom/ipc/TemporaryIPCBlobParent.h"
+#include "mozilla/dom/localstorage/ActorsParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/dom/simpledb/ActorsParent.h"
 #include "mozilla/dom/RemoteWorkerParent.h"
 #include "mozilla/dom/RemoteWorkerServiceParent.h"
 #include "mozilla/dom/SharedWorkerParent.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/MIDIManagerParent.h"
 #include "mozilla/dom/MIDIPortParent.h"
@@ -288,16 +289,89 @@ BackgroundParentImpl::DeallocPBackground
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   return mozilla::dom::DeallocPBackgroundSDBConnectionParent(aActor);
 }
 
+BackgroundParentImpl::PBackgroundLSDatabaseParent*
+BackgroundParentImpl::AllocPBackgroundLSDatabaseParent(
+                                                    const uint64_t& aDatastoreId)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return mozilla::dom::AllocPBackgroundLSDatabaseParent(aDatastoreId);
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPBackgroundLSDatabaseConstructor(
+                                            PBackgroundLSDatabaseParent* aActor,
+                                            const uint64_t& aDatastoreId)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  if (!mozilla::dom::RecvPBackgroundLSDatabaseConstructor(aActor,
+                                                          aDatastoreId)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundLSDatabaseParent(
+                                            PBackgroundLSDatabaseParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  return mozilla::dom::DeallocPBackgroundLSDatabaseParent(aActor);
+}
+
+BackgroundParentImpl::PBackgroundLSRequestParent*
+BackgroundParentImpl::AllocPBackgroundLSRequestParent(
+                                                 const LSRequestParams& aParams)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return mozilla::dom::AllocPBackgroundLSRequestParent(this, aParams);
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPBackgroundLSRequestConstructor(
+                                             PBackgroundLSRequestParent* aActor,
+                                             const LSRequestParams& aParams)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  if (!mozilla::dom::RecvPBackgroundLSRequestConstructor(aActor, aParams)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundLSRequestParent(
+                                             PBackgroundLSRequestParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  return mozilla::dom::DeallocPBackgroundLSRequestParent(aActor);
+}
+
 BackgroundParentImpl::PBackgroundLocalStorageCacheParent*
 BackgroundParentImpl::AllocPBackgroundLocalStorageCacheParent(
                                             const PrincipalInfo& aPrincipalInfo,
                                             const nsCString& aOriginKey,
                                             const uint32_t& aPrivateBrowsingId)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -72,16 +72,38 @@ protected:
                                          PBackgroundSDBConnectionParent* aActor,
                                          const PrincipalInfo& aPrincipalInfo)
                                          override;
 
   virtual bool
   DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor)
                                         override;
 
+  virtual PBackgroundLSDatabaseParent*
+  AllocPBackgroundLSDatabaseParent(const uint64_t& aCacheId) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor,
+                                       const uint64_t& aDatastoreId) override;
+
+  virtual bool
+  DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor)
+                                     override;
+
+  virtual PBackgroundLSRequestParent*
+  AllocPBackgroundLSRequestParent(const LSRequestParams& aParams) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
+                                      const LSRequestParams& aParams) override;
+
+  virtual bool
+  DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor)
+                                    override;
+
   virtual PBackgroundLocalStorageCacheParent*
   AllocPBackgroundLocalStorageCacheParent(const PrincipalInfo& aPrincipalInfo,
                                           const nsCString& aOriginKey,
                                           const uint32_t& aPrivateBrowsingId)
                                           override;
 
   virtual mozilla::ipc::IPCResult
   RecvPBackgroundLocalStorageCacheConstructor(
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -1,16 +1,18 @@
 /* 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 protocol PAsmJSCacheEntry;
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundIndexedDBUtils;
 include protocol PBackgroundSDBConnection;
+include protocol PBackgroundLSDatabase;
+include protocol PBackgroundLSRequest;
 include protocol PBackgroundLocalStorageCache;
 include protocol PBackgroundStorage;
 include protocol PBackgroundTest;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PClientManager;
@@ -39,16 +41,17 @@ include protocol PServiceWorkerRegistrat
 include protocol PWebAuthnTransaction;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include IPCBlob;
 include IPCServiceWorkerDescriptor;
 include IPCServiceWorkerRegistrationDescriptor;
+include PBackgroundLSSharedTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 include PFileSystemParams;
 include ProtocolTypes;
 include RemoteWorkerTypes;
 include MIDITypes;
 
 include "mozilla/dom/cache/IPCUtils.h";
@@ -67,16 +70,18 @@ namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PAsmJSCacheEntry;
   manages PBackgroundIDBFactory;
   manages PBackgroundIndexedDBUtils;
   manages PBackgroundSDBConnection;
+  manages PBackgroundLSDatabase;
+  manages PBackgroundLSRequest;
   manages PBackgroundLocalStorageCache;
   manages PBackgroundStorage;
   manages PBackgroundTest;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PClientManager;
@@ -114,16 +119,20 @@ parent:
 
   async PBackgroundIndexedDBUtils();
 
   // Use only for testing!
   async FlushPendingFileDeletions();
 
   async PBackgroundSDBConnection(PrincipalInfo principalInfo);
 
+  async PBackgroundLSDatabase(uint64_t datastoreId);
+
+  async PBackgroundLSRequest(LSRequestParams params);
+
   async PBackgroundLocalStorageCache(PrincipalInfo principalInfo,
                                      nsCString originKey,
                                      uint32_t privateBrowsingId);
 
   async PBackgroundStorage(nsString profilePath);
 
   async PVsync();
 
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -917,16 +917,30 @@ description =
 [PRemoteDecoderManager::PRemoteVideoDecoder]
 description = See Bug 1505976 - investigate changing to async instead of matching GPU pattern
 [PVideoDecoderManager::PVideoDecoder]
 description =
 [PVideoDecoderManager::Readback]
 description =
 [PBackgroundStorage::Preload]
 description =
+[PBackgroundLSDatabase::GetLength]
+description =
+[PBackgroundLSDatabase::GetKey]
+description =
+[PBackgroundLSDatabase::GetItem]
+description =
+[PBackgroundLSDatabase::GetKeys]
+description =
+[PBackgroundLSDatabase::SetItem]
+description =
+[PBackgroundLSDatabase::RemoveItem]
+description =
+[PBackgroundLSDatabase::Clear]
+description =
 [PRemoteSpellcheckEngine::Check]
 description =
 [PRemoteSpellcheckEngine::CheckAndSuggest]
 description =
 [PRemoteSpellcheckEngine::SetDictionary]
 description =
 [PGPU::AddLayerTreeIdMapping]
 description =
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -58,17 +58,19 @@
 #include "nsGlobalWindowCommands.h"
 #include "nsJSProtocolHandler.h"
 #include "nsIControllerContext.h"
 #include "nsZipArchive.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobURL.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/SDBConnection.h"
+#include "mozilla/dom/LocalStorageCommon.h"
 #include "mozilla/dom/LocalStorageManager.h"
+#include "mozilla/dom/LocalStorageManager2.h"
 #include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/StorageActivityService.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/OSFileConstants.h"
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
@@ -160,17 +162,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsParserU
 #define PRESENTATION_TCP_SESSION_TRANSPORT_CID \
 { 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
 
 already_AddRefed<nsIPresentationService> NS_CreatePresentationService();
 
 // Factory Constructor
 typedef mozilla::dom::BlobURL::Mutator BlobURLMutator;
 NS_GENERIC_FACTORY_CONSTRUCTOR(BlobURLMutator)
-NS_GENERIC_FACTORY_CONSTRUCTOR(LocalStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService,
                                          QuotaManagerService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager,
                                          ServiceWorkerManager::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WorkerDebuggerManager,
                                          WorkerDebuggerManager::GetInstance)
@@ -489,16 +490,29 @@ NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_S
 NS_DEFINE_NAMED_CID(PRESENTATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID);
 NS_DEFINE_NAMED_CID(PRESENTATION_TCP_SESSION_TRANSPORT_CID);
 
 NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID);
 
 NS_DEFINE_NAMED_CID(NS_SCRIPTERROR_CID);
 
+static nsresult
+LocalStorageManagerConstructor(nsISupports *aOuter, REFNSIID aIID,
+                               void **aResult)
+{
+  if (NextGenLocalStorageEnabled()) {
+    RefPtr<LocalStorageManager2> manager = new LocalStorageManager2();
+    return manager->QueryInterface(aIID, aResult);
+  }
+
+  RefPtr<LocalStorageManager> manager = new LocalStorageManager();
+  return manager->QueryInterface(aIID, aResult);
+}
+
 static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
   // clang-format off
   XPCONNECT_CIDENTRIES
 #ifdef DEBUG
   { &kNS_LAYOUT_DEBUGGER_CID, false, nullptr, CreateNewLayoutDebugger },
 #endif
   { &kNS_FRAMETRAVERSAL_CID, false, nullptr, CreateNewFrameTraversal },
   { &kIN_DEEPTREEWALKER_CID, false, nullptr, inDeepTreeWalkerConstructor },
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1280,16 +1280,22 @@ pref("dom.require_user_interaction_for_b
 pref("dom.disable_open_during_load",                false);
 pref("dom.popup_maximum",                           20);
 pref("dom.popup_allowed_events", "change click dblclick mouseup pointerup notificationclick reset submit touchend");
 
 pref("dom.disable_open_click_delay", 1000);
 pref("dom.serviceWorkers.disable_open_click_delay", 1000);
 
 pref("dom.storage.enabled", true);
+// Whether or not LSNG (Next Generation Local Storage) is enabled.
+#ifdef NIGHTLY_BUILD
+pref("dom.storage.next_gen", true);
+#else
+pref("dom.storage.next_gen", false);
+#endif
 pref("dom.storage.default_quota",      5120);
 pref("dom.storage.testing", false);
 
 pref("dom.send_after_paint_to_content", false);
 
 // Timeout clamp in ms for timeouts we clamp
 pref("dom.min_timeout_value", 4);
 // And for background windows
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-closed.html.ini
@@ -0,0 +1,2 @@
+[opener-closed.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noopener.html.ini
@@ -0,0 +1,2 @@
+[opener-noopener.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/auxiliary-browsing-contexts/opener-noreferrer.html.ini
@@ -0,0 +1,2 @@
+[opener-noreferrer.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_blank-002.html.ini
@@ -0,0 +1,2 @@
+[choose-_blank-002.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_parent-004.html.ini
@@ -0,0 +1,2 @@
+[choose-_parent-004.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_self-002.html.ini
@@ -0,0 +1,2 @@
+[choose-_self-002.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-001.html.ini
@@ -0,0 +1,2 @@
+[choose-_top-001.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-002.html.ini
@@ -0,0 +1,2 @@
+[choose-_top-002.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/browsing-context-names/choose-_top-003.html.ini
@@ -0,0 +1,2 @@
+[choose-_top-003.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/windows/noreferrer-null-opener.html.ini
@@ -0,0 +1,2 @@
+[noreferrer-null-opener.html]
+  disabled: temporary
--- a/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini
+++ b/testing/web-platform/meta/html/browsers/windows/noreferrer-window-name.html.ini
@@ -1,3 +1,2 @@
 [noreferrer-window-name.html]
-  disabled:
-    if verify: fails in verify mode
+  disabled: temporary
--- a/testing/web-platform/meta/webstorage/document-domain.html.ini
+++ b/testing/web-platform/meta/webstorage/document-domain.html.ini
@@ -1,2 +1,2 @@
 [document-domain.html]
-  expected: TIMEOUT
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_basic.html.ini
@@ -0,0 +1,2 @@
+[event_basic.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_body_attribute.html.ini
@@ -0,0 +1,2 @@
+[event_body_attribute.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_case_sensitive.html.ini
@@ -0,0 +1,2 @@
+[event_case_sensitive.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_key.html.ini
@@ -0,0 +1,2 @@
+[event_local_key.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_newvalue.html.ini
@@ -0,0 +1,2 @@
+[event_local_newvalue.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_oldvalue.html.ini
@@ -0,0 +1,2 @@
+[event_local_oldvalue.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_removeitem.html.ini
@@ -0,0 +1,2 @@
+[event_local_removeitem.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_storagearea.html.ini
@@ -0,0 +1,2 @@
+[event_local_storagearea.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_local_url.html.ini
@@ -0,0 +1,2 @@
+[event_local_url.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_no_duplicates.html.ini
@@ -0,0 +1,2 @@
+[event_no_duplicates.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/event_setattribute.html.ini
@@ -0,0 +1,2 @@
+[event_setattribute.html]
+  disabled: temporary
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webstorage/storage_local_setitem_quotaexceedederr.html.ini
@@ -0,0 +1,2 @@
+[storage_local_setitem_quotaexceedederr.html]
+  disabled: temporary
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -63,13 +63,13 @@ skip-if = serviceworker_e10s
 [browser_storageAccessPromiseResolveHandlerUserInteraction.js]
 [browser_storageAccessRemovalNavigateSubframe.js]
 skip-if = serviceworker_e10s
 [browser_storageAccessSandboxed.js]
 skip-if = serviceworker_e10s
 [browser_storageAccessWithHeuristics.js]
 [browser_allowPermissionForTracker.js]
 [browser_denyPermissionForTracker.js]
-[browser_localStorageEvents.js]
-support-files = localStorage.html
+#[browser_localStorageEvents.js]
+#support-files = localStorage.html
 [browser_partitionedLocalStorage.js]
-[browser_partitionedLocalStorage_events.js]
-support-files = localStorageEvents.html
+#[browser_partitionedLocalStorage_events.js]
+#support-files = localStorageEvents.html
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -107,21 +107,21 @@ skip-if = (toolkit == 'android') # bug 1
 [test_ext_sendmessage_reply.html]
 [test_ext_sendmessage_reply2.html]
 skip-if = true # Bug 1258897
 [test_ext_storage_manager_capabilities.html]
 scheme=https
 [test_ext_subframes_privileges.html]
 skip-if = os == 'android' || verify # bug 1489771
 [test_ext_test.html]
-[test_ext_unlimitedStorage.html]
-[test_ext_unlimitedStorage_legacy_persistent_indexedDB.html]
+#[test_ext_unlimitedStorage.html]
+#[test_ext_unlimitedStorage_legacy_persistent_indexedDB.html]
 # IndexedDB persistent storage mode is not allowed on Fennec from a non-chrome privileged code
 # (it has only been enabled for apps and privileged code). See Bug 1119462 for additional info.
-skip-if = os == 'android'
+#skip-if = os == 'android'
 [test_ext_web_accessible_resources.html]
 skip-if = os == 'android' && debug # bug 1397615
 [test_ext_webnavigation.html]
 skip-if = os == 'android' && debug # bug 1397615
 [test_ext_webnavigation_filters.html]
 skip-if = (os == 'android' && debug) || (verify && (os == 'linux' || os == 'mac')) # bug 1397615
 [test_ext_webrequest_auth.html]
 skip-if = os == 'android'
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -1,8 +1,8 @@
 [DEFAULT]
 tags = webextensions in-process-webextensions
 
-[test_ext_storage_cleanup.html]
+#[test_ext_storage_cleanup.html]
 # Bug 1426514 storage_cleanup: clearing localStorage fails with oop
 
 [include:mochitest-common.ini]
 skip-if = os == 'win' # Windows WebExtensions always run OOP
--- a/toolkit/forgetaboutsite/moz.build
+++ b/toolkit/forgetaboutsite/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
-XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
+#XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'ForgetAboutSite.jsm',
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Data Sanitization')
--- a/toolkit/forgetaboutsite/test/unit/xpcshell.ini
+++ b/toolkit/forgetaboutsite/test/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 head = head_forgetaboutsite.js ../../../../dom/push/test/xpcshell/head.js
 skip-if = toolkit == 'android'
 support-files =
   !/dom/push/test/xpcshell/head.js
 
-[test_removeDataFromDomain.js]
+#[test_removeDataFromDomain.js]