Merge autoland to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Thu, 19 Mar 2020 23:55:44 +0200
changeset 519936 018964cefa63a14d1e43b31be1b91526a9d708fc
parent 519647 e1342040e7eb59de587efc614980577d53e591da (current diff)
parent 519623 15522501b0f117b31ea64945046d24665474ba33 (diff)
child 519937 32d6a3f1f83cec54b8190f1993c7fa343406ce20
push id110774
push userccoroiu@mozilla.com
push dateFri, 20 Mar 2020 22:03:28 +0000
treeherderautoland@3d0ebb8bfed3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone76.0a1
first release with
nightly linux32
018964cefa63 / 76.0a1 / 20200319215651 / files
nightly linux64
018964cefa63 / 76.0a1 / 20200319215651 / files
nightly mac
018964cefa63 / 76.0a1 / 20200319215651 / files
nightly win32
018964cefa63 / 76.0a1 / 20200319215651 / files
nightly win64
018964cefa63 / 76.0a1 / 20200319215651 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
browser/components/search/extensions/engines.json
build/clang-plugin/TempRefPtrChecker.cpp
build/clang-plugin/TempRefPtrChecker.h
build/clang-plugin/tests/TestTempRefPtr.cpp
--- a/.babel-eslint.rc.js
+++ b/.babel-eslint.rc.js
@@ -1,6 +1,10 @@
+/* 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/. */
+
 module.exports = {
   plugins: [
     "@babel/plugin-syntax-optional-chaining",
     "@babel/plugin-syntax-nullish-coalescing-operator",
   ],
 };
--- a/accessible/tests/mochitest/textselection/test_general.html
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -121,16 +121,22 @@
                                 aTests) {
       this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
 
       this.eventSeq = [
         new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID),
       ];
 
       this.invoke = function changeDOMSelection_invoke() {
+        // HyperTextAccessible::GetSelectionDOMRanges ignores hidden selections.
+        // Here we may be focusing an editable element (and thus hiding the
+        // main document selection), so blur it so that we test what we want to
+        // test.
+        document.activeElement.blur();
+
         var sel = window.getSelection();
         var range = document.createRange();
         range.setStart(getNode(aNodeID1), aNodeOffset1);
         range.setEnd(getNode(aNodeID2), aNodeOffset2);
         sel.addRange(range);
       };
 
       this.finalCheck = function changeDOMSelection_finalCheck() {
--- a/browser/.eslintrc.js
+++ b/browser/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // XXX Bug 1326071 - This should be reduced down - probably to 20 or to
     // be removed & synced with the mozilla/recommended value.
     complexity: ["error", { max: 44 }],
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -483,21 +483,26 @@ pref("browser.tabs.extraDragSpace", fals
 // false  return to the adjacent tab (old default)
 pref("browser.tabs.selectOwnerOnClose", true);
 
 pref("browser.tabs.showAudioPlayingIcon", true);
 // This should match Chromium's audio indicator delay.
 pref("browser.tabs.delayHidingAudioPlayingIconMS", 3000);
 
 #if defined(NIGHTLY_BUILD) && !defined(MOZ_ASAN)
-  // Pref to control whether we use a separate privileged content process
-  // for about: pages. This pref name did not age well: we will have multiple
-  // types of privileged content processes, each with different privileges.
-  // types of privleged content processes, each with different privleges.
+// Pref to control whether we use a separate privileged content process
+// for about: pages. This pref name did not age well: we will have multiple
+// types of privileged content processes, each with different privileges.
+// types of privleged content processes, each with different privleges.
+#if defined(MOZ_CODE_COVERAGE) && defined(XP_LINUX)
+  // Disabled on Linux ccov builds due to bug 1621269.
+  pref("browser.tabs.remote.separatePrivilegedContentProcess", false);
+#else
   pref("browser.tabs.remote.separatePrivilegedContentProcess", true);
+#endif
   // This pref will cause assertions when a remoteType triggers a process switch
   // to a new remoteType it should not be able to trigger.
   pref("browser.tabs.remote.enforceRemoteTypeRestrictions", true);
 #endif
 
 // Pref to control whether we use a separate privileged content process
 // for certain mozilla webpages (which are listed in the pref
 // browser.tabs.remote.separatedMozillaDomains).
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -1734,19 +1734,23 @@ var gIdentityHandler = {
     text.textContent = gNavigatorBundle.getFormattedString(
       "geolocationLastAccessIndicatorText",
       [timeFormat.formatBestUnit(lastAccess)]
     );
 
     indicator.appendChild(icon);
     indicator.appendChild(text);
 
-    document
-      .getElementById("identity-popup-geo-container")
-      .appendChild(indicator);
+    let geoContainer = document.getElementById("identity-popup-geo-container");
+
+    // Check whether geoContainer still exists.
+    // We are async, the identity popup could have been closed already.
+    if (geoContainer) {
+      geoContainer.appendChild(indicator);
+    }
   },
 
   _createBlockedPopupIndicator(aTotalBlockedPopups) {
     let indicator = document.createXULElement("hbox");
     indicator.setAttribute("class", "identity-popup-permission-item");
     indicator.setAttribute("align", "center");
     indicator.setAttribute("id", "blocked-popup-indicator-item");
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6201,23 +6201,28 @@ nsBrowserAccess.prototype = {
           browsingContext = browser.browsingContext;
         }
         break;
       default:
         // OPEN_CURRENTWINDOW or an illegal value
         browsingContext =
           window.content && BrowsingContext.getFromWindow(window.content);
         if (aURI) {
-          let loadflags = isExternal
-            ? Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL
-            : Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+          let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+          if (isExternal) {
+            loadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
+          } else if (!aTriggeringPrincipal.isSystemPrincipal) {
+            // XXX this code must be reviewed and changed when bug 1616353
+            // lands.
+            loadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIRST_LOAD;
+          }
           gBrowser.loadURI(aURI.spec, {
             triggeringPrincipal: aTriggeringPrincipal,
             csp: aCsp,
-            flags: loadflags,
+            loadFlags,
             referrerInfo,
           });
         }
         if (
           !Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground")
         ) {
           window.focus();
         }
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2840,16 +2840,20 @@
 
           let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
           if (allowThirdPartyFixup) {
             flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
             flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
           }
           if (fromExternal) {
             flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
+          } else if (!triggeringPrincipal.isSystemPrincipal) {
+            // XXX this code must be reviewed and changed when bug 1616353
+            // lands.
+            flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIRST_LOAD;
           }
           if (allowMixedContent) {
             flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
           }
           if (!allowInheritPrincipal) {
             flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
           }
           if (disableTRR) {
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -32,17 +32,16 @@ const whitelist = {
     "resource://gre/modules/Timer.jsm",
     "resource://gre/modules/XPCOMUtils.jsm",
 
     // Logging related
     "resource://gre/modules/Log.jsm",
 
     // Session store
     "resource:///modules/sessionstore/ContentSessionStore.jsm",
-    "resource://gre/modules/sessionstore/SessionHistory.jsm",
 
     // Browser front-end
     "resource:///actors/AboutReaderChild.jsm",
     "resource:///actors/BrowserTabChild.jsm",
     "resource:///actors/LinkHandlerChild.jsm",
     "resource:///actors/SearchTelemetryChild.jsm",
     "resource://gre/actors/AutoCompleteChild.jsm",
     "resource://gre/modules/ActorChild.jsm",
@@ -81,16 +80,20 @@ const whitelist = {
 // Items on this list are allowed to be loaded but not
 // required, as opposed to items in the main whitelist,
 // which are all required.
 const intermittently_loaded_whitelist = {
   modules: new Set([
     "resource://gre/modules/nsAsyncShutdown.jsm",
     "resource://gre/modules/sessionstore/Utils.jsm",
 
+    // Session store.
+    "resource://gre/modules/sessionstore/SessionHistory.jsm",
+    "resource:///modules/sessionstore/SessionHistoryListener.jsm",
+
     "resource://specialpowers/SpecialPowersChild.jsm",
     "resource://specialpowers/WrapPrivileged.jsm",
 
     // Webcompat about:config front-end. This is presently nightly-only and
     // part of a system add-on which may not load early enough for the test.
     "resource://webcompat/AboutCompat.jsm",
 
     // Test related
--- a/browser/base/content/test/permissions/browser.ini
+++ b/browser/base/content/test/permissions/browser.ini
@@ -2,17 +2,16 @@
 support-files=
   head.js
   permissions.html
   temporary_permissions_subframe.html
   temporary_permissions_frame.html
 [browser_canvas_fingerprinting_resistance.js]
 skip-if = debug || os == "linux" && asan # Bug 1522069
 [browser_permissions.js]
-skip-if = debug && os == "mac" # Bug 1601576
 [browser_permissions_delegate_vibrate.js]
 support-files=
   empty.html
 [browser_permission_delegate_geo.js]
 skip-if = fission # Bug 1587743
 [browser_permissions_postPrompt.js]
 support-files=
   dummy.js
--- a/browser/components/.eslintrc.js
+++ b/browser/components/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // XXX Bug 1326071 - This should be reduced down - probably to 20 or to
     // be removed & synced with the mozilla/recommended value.
     complexity: ["error", 61],
   },
--- a/browser/components/aboutlogins/content/.eslintrc.js
+++ b/browser/components/aboutlogins/content/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   parserOptions: {
     sourceType: "module",
   },
 };
--- a/browser/components/aboutlogins/content/components/.eslintrc.js
+++ b/browser/components/aboutlogins/content/components/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   parserOptions: {
     sourceType: "module",
   },
 };
--- a/browser/components/customizableui/content/.eslintrc.js
+++ b/browser/components/customizableui/content/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: {
     "mozilla/browser-window": true,
   },
 
   plugins: ["mozilla"],
--- a/browser/components/extensions/.eslintrc.js
+++ b/browser/components/extensions/.eslintrc.js
@@ -1,5 +1,9 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   extends: "../../../toolkit/components/extensions/.eslintrc.js",
 };
--- a/browser/components/extensions/child/.eslintrc.js
+++ b/browser/components/extensions/child/.eslintrc.js
@@ -1,5 +1,9 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   extends: "../../../../toolkit/components/extensions/child/.eslintrc.js",
 };
--- a/browser/components/extensions/parent/.eslintrc.js
+++ b/browser/components/extensions/parent/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   extends: "../../../../toolkit/components/extensions/parent/.eslintrc.js",
 
   globals: {
     Tab: true,
     TabContext: true,
--- a/browser/components/migration/.eslintrc.js
+++ b/browser/components/migration/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     "block-scoped-var": "error",
     complexity: ["error", { max: 22 }],
     "max-nested-callbacks": ["error", 3],
     "no-extend-native": "error",
--- a/browser/components/newtab/.eslintrc.js
+++ b/browser/components/newtab/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 module.exports = {
   // When adding items to this file please check for effects on sub-directories.
   parserOptions: {
     ecmaVersion: 2018,
     ecmaFeatures: {
       jsx: true,
     },
     sourceType: "module",
--- a/browser/components/newtab/content-src/.eslintrc.js
+++ b/browser/components/newtab/content-src/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 /* eslint-disable import/no-commonjs */
 
 module.exports = {
   rules: {
     "import/no-commonjs": 2,
   },
 };
--- a/browser/components/payments/.eslintrc.js
+++ b/browser/components/payments/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   overrides: [
     {
       files: [
         "res/components/*.js",
         "res/containers/*.js",
--- a/browser/components/protections/.eslintrc.js
+++ b/browser/components/protections/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   parserOptions: {
     sourceType: "module",
   },
 };
--- a/browser/components/search/.eslintrc.js
+++ b/browser/components/search/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     "mozilla/var-only-at-top-level": "error",
     "require-jsdoc": [
       "error",
       {
deleted file mode 100644
--- a/browser/components/search/extensions/engines.json
+++ /dev/null
@@ -1,1470 +0,0 @@
-{
-  "data": [
-    {
-      "orderHint": 1000,
-      "webExtension": {
-        "id": "google@search.mozilla.org"
-      },
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "client", "value": "firefox-b-d" },
-          { "name": "q", "value": "{searchTerms}" }
-        ]
-      },
-      "extraParams": [{
-        "name": "channel",
-        "condition": "pref",
-        "pref": "google_channel_row"
-      }],
-      "telemetryId": "google-b-d",
-      "appliesTo": [{
-        "included": { "everywhere": true },
-        "default": "yes"
-      }, {
-        "included": { "everywhere": true },
-        "application": {
-          "channel": ["esr"]
-        },
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "client", "value": "firefox-b-e" },
-            { "name": "q", "value": "{searchTerms}" }
-          ]
-        },
-        "telemetryId": "google-b-e"
-      }, {
-        "included": {
-          "regions": ["ru", "tr", "by", "kz"],
-          "locales": {
-            "matches": ["ru", "tr", "be", "kk"],
-            "startsWith": ["en"]
-          },
-          "telemetryId": "google"
-        },
-        "default": "no"
-      }, {
-        "included": {
-          "regions": ["ru", "tr", "by", "kz"],
-          "locales": {
-            "matches": ["ru", "tr", "be", "kk"],
-            "startsWith": ["en"]
-          },
-          "telemetryId": "google"
-        },
-        "application": {
-          "channel": ["esr"]
-        },
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "client", "value": "firefox-b-e" },
-            { "name": "q", "value": "{searchTerms}" }
-          ]
-        },
-        "default": "no"
-      }, {
-        "included": {
-          "regions": ["cn"],
-          "locales": {
-            "matches": ["zh-CN"]
-          }
-        },
-        "default": "no"
-      }, {
-        "included": {
-          "regions": ["cn"],
-          "locales": {
-            "matches": ["zh-CN"]
-          }
-        },
-        "application": {
-          "channel": ["esr"]
-        },
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "client", "value": "firefox-b-e" },
-            { "name": "q", "value": "{searchTerms}" }
-          ]
-        },
-        "default": "no"
-      }, {
-        "included": { "regions": ["us"] },
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "client", "value": "firefox-b-1-d" },
-            { "name": "q", "value": "{searchTerms}" }
-          ]
-        },
-        "extraParams": [{
-          "name": "channel",
-          "condition": "pref",
-          "pref": "google_channel_us"
-        }],
-        "telemetryId": "google-b-1-d"
-      },  {
-        "included": { "regions": ["us"] },
-        "application": {
-          "channel": ["esr"]
-        },
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "client", "value": "firefox-b-1-e" },
-            { "name": "q", "value": "{searchTerms}" }
-          ]
-        },
-        "extraParams": [{
-          "name": "channel",
-          "condition": "pref",
-          "pref": "google_channel_us"
-        }],
-        "telemetryId": "google-b-1-e"
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "bing@search.mozilla.org"
-      },
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "pc", "value": "MOZI" },
-          { "name": "q", "value": "{searchTerms}" }
-        ]
-      },
-      "extraParams": [{
-        "name": "form",
-        "condition": "purpose",
-        "purpose": "contextmenu",
-        "value": "MOZCON"
-      }, {
-        "name": "form",
-        "condition": "purpose",
-        "purpose": "searchbar",
-        "value": "MOZSBR"
-      }, {
-        "name": "form",
-        "condition": "purpose",
-        "purpose": "homepage",
-        "value": "MOZSPG"
-      }, {
-        "name": "form",
-        "condition": "purpose",
-        "purpose": "keyword",
-        "value": "MOZLBR"
-      }, {
-        "name": "form",
-        "condition": "purpose",
-        "purpose": "newtab",
-        "value": "MOZTSB"
-      }],
-      "appliesTo": [{
-        "included": {
-          "locales": {
-            "matches": [
-              "ach", "af", "an", "ar", "ast", "az", "ca", "ca-valencia",
-              "cak", "da", "de", "dsb", "el", "eo", "es-CL", "es-ES",
-              "es-MX", "eu", "fa", "ff", "fi", "fr", "fy-NL", "gn",
-              "gu-IN", "hi-IN", "hr", "hsb", "ia", "is", "it",
-              "ja-JP-macos", "ja", "ka", "kab", "km", "kn", "lij", "lo",
-              "lt", "mk", "ms", "my", "nb-NO", "ne-NP", "nl",
-              "nn-NO", "oc", "pa-IN", "pt-BR", "rm", "ro", "son",
-              "sq", "sr", "sv-SE", "th", "tl", "trs", "uk", "ur", "uz",
-              "wo", "xh", "zh-CN"
-            ],
-            "startsWith": ["bn", "en"]
-          }
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "baidu@search.mozilla.org"
-      },
-      "telemetryId": "baidu",
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "tn", "value": "monline_7_dg" },
-          { "name": "ie", "value": "utf-8" },
-          { "name": "wd", "value": "{searchTerms}" }
-        ],
-        "suggestUrlGetParams": [
-          { "name": "tn", "value": "monline_7_dg" },
-          { "name": "ie", "value": "utf-8" },
-          { "name": "action", "value": "opensearch" },
-          { "name": "wd", "value": "{searchTerms}" }
-        ]
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["zh-CN"] } }
-      }, {
-        "included": {
-          "regions": ["cn"],
-          "locales": { "matches": ["zh-CN"] }
-        },
-        "default": "yes"
-      }]
-    },
-    {
-      "orderHint": 500,
-      "webExtension": {
-        "id": "amazondotcom@search.mozilla.org"
-      },
-      "telemetryId": "amazondotcom",
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "field-keywords", "value": "{searchTerms}" },
-          { "name": "ie", "value": "{inputEncoding}" },
-          { "name": "mode", "value": "blended" },
-          { "name": "tag", "value": "mozilla-20" },
-          { "name": "sourceid", "value": "Mozilla-search" }
-        ]
-      },
-      "appliesTo": [{
-        "included": {
-          "locales": {
-            "matches": [
-              "ach", "af", "ar", "az", "bg", "cak", "en-US", "eo",
-              "es-AR", "fa", "gn", "hy-AM", "ia", "is", "ka", "km",
-              "lt", "mk", "ms", "my", "ro", "si", "th", "tl",
-              "trs", "uz"
-            ]
-          }
-        }
-      }, {
-        "included": {
-          "regions": ["au"],
-          "locales": {
-            "matches": [
-              "ach", "af", "ar", "az", "bg", "cak", "en-US", "eo",
-              "es-AR", "fa", "gn", "hy-AM", "ia", "is", "ka", "km",
-              "lt", "mk", "ms", "my", "ro", "si", "th", "tl",
-              "trs", "uz"
-            ]
-          }
-        },
-        "webExtension": {
-          "id": "amazon@search.mozilla.org",
-          "locales": ["au"]
-        },
-        "telemetryId": "amazon-au"
-      }, {
-        "included": {
-          "regions": ["ca"],
-          "locales": {
-            "matches": [
-              "ach", "af", "ar", "az", "bg", "cak", "en-US", "eo",
-              "es-AR", "fa", "gn", "hy-AM", "ia", "is", "ka", "km",
-              "lt", "mk", "ms", "my", "ro", "si", "th", "tl",
-              "trs", "uz"
-            ]
-          }
-        },
-        "webExtension": {
-          "id": "amazon@search.mozilla.org",
-          "locales": ["ca"]
-        },
-        "telemetryId": "amazon-ca"
-      }, {
-        "included": {
-          "regions": ["fr"],
-          "locales": {
-            "matches": [
-              "ach", "af", "ar", "az", "bg", "cak", "en-US", "eo",
-              "es-AR", "fa", "gn", "hy-AM", "ia", "is", "ka", "km",
-              "lt", "mk", "ms", "my", "ro", "si", "th", "tl",
-              "trs", "uz"
-            ]
-          }
-        },
-        "webExtension": {
-          "id": "amazon@search.mozilla.org",
-          "locales": ["france"]
-        },
-        "telemetryId": "amazon-france",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefox-fr-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": {
-          "regions": ["gb"],
-          "locales": {
-            "matches": [
-              "ach", "af", "ar", "az", "bg", "cak", "en-US", "eo",
-              "es-AR", "fa", "gn", "hy-AM", "ia", "is", "ka", "km",
-              "lt", "mk", "ms", "my", "ro", "si", "th", "tl",
-              "trs", "uz"
-            ]
-          }
-        },
-        "webExtension": {
-          "id": "amazon@search.mozilla.org",
-          "locales": ["en-GB"]
-        },
-        "telemetryId": "amazon-en-GB",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefox-uk-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }]
-    },
-    {
-      "orderHint": 500,
-      "webExtension": {
-        "id": "amazon@search.mozilla.org"
-      },
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "field-keywords", "value": "{searchTerms}" },
-          { "name": "ie", "value": "{inputEncoding}" },
-          { "name": "mode", "value": "blended" }
-        ]
-      },
-      "appliesTo": [{
-        "included": {
-          "locales": { "matches": [
-            "bn", "bn-IN", "kn", "gu-IN", "mr", "pa-IN", "ta", "te", "ur"
-          ]}
-        },
-        "webExtension": {
-          "locales": ["in"]
-        },
-        "telemetryId": "amazon-in"
-      }, {
-        "included": {
-          "locales": { "matches": ["br", "ff", "fr", "son", "wo"] }
-        },
-        "webExtension": {
-          "locales": ["france"]
-        },
-        "telemetryId": "amazon-france",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefox-fr-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": {
-          "regions": ["ca"],
-          "locales": { "matches": ["br", "ff", "fr", "son", "wo"] }
-        },
-        "webExtension": {
-          "locales": ["ca"]
-        },
-        "telemetryId": "amazon-ca"
-      }, {
-        "included": { "locales": { "matches": ["en-CA"] } },
-        "webExtension": {
-          "locales": ["ca"]
-        },
-        "telemetryId": "amazon-ca"
-      }, {
-        "included": { "locales": { "matches": ["ja-JP-macos", "ja"] } },
-        "webExtension": {
-          "locales": ["jp"]
-        },
-        "telemetryId": "amazon-jp",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "mozillajapan-fx-22" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["it", "lij"] } },
-        "webExtension": {
-          "locales": ["it"]
-        },
-        "telemetryId": "amazon-it",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefoxit-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["de", "dsb", "hsb"] } },
-        "webExtension": {
-          "locales": ["de"]
-        },
-        "telemetryId": "amazon-de",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefox-de-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": {
-            "matches": [
-              "cy", "da", "el", "en-GB", "eu", "ga-IE", "gd", "gl", "hr",
-              "nb-NO", "nn-NO", "pt-PT", "sq", "sr"
-            ]
-          }
-        },
-        "webExtension": {
-          "locales": ["en-GB"]
-        },
-        "telemetryId": "amazon-en-GB",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "field-keywords", "value": "{searchTerms}" },
-            { "name": "ie", "value": "{inputEncoding}" },
-            { "name": "mode", "value": "blended" },
-            { "name": "tag", "value": "firefox-uk-21" },
-            { "name": "sourceid", "value": "Mozilla-search" }
-          ]
-        }
-      }, {
-        "included": {
-          "regions": ["au"],
-          "locales": {
-            "matches": [
-              "cy", "da", "el", "en-GB", "eu", "ga-IE", "gd", "gl", "hr",
-              "nb-NO", "nn-NO", "pt-PT", "sq", "sr"
-            ]
-          }
-        },
-        "webExtension": {
-          "locales": ["au"]
-        },
-        "telemetryId": "amazon-au"
-      }]
-    },
-    {
-      "orderHint": 500,
-      "webExtension": {
-        "id": "amazondotcn@search.mozilla.org"
-      },
-      "telemetryId": "amazondotcn",
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "keywords", "value": "{searchTerms}" },
-          { "name": "ix", "value": "sunray" },
-          { "name": "pageletid", "value": "headsearch" },
-          { "name": "searchType", "value": "" },
-          { "name": "Go.x", "value": "0" },
-          { "name": "Go.y", "value": "0" },
-          { "name": "bestSaleNum", "value": "0" }
-        ]
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["zh-CN"] } }
-      }]
-    },
-    {
-      "orderHint": 500,
-      "webExtension": {
-        "id": "ebay@search.mozilla.org"
-      },
-      "params": {
-        "searchUrlGetParams": [
-          { "name": "ff3", "value": "4" },
-          { "name": "toolid", "value": "20004" },
-          { "name": "campid", "value": "5338192028" },
-          { "name": "customid", "value": "" },
-          { "name": "mpre", "value": "https://www.ebay.com/sch/{searchTerms}" }
-        ]
-      },
-      "appliesTo": [{
-        "included": {
-          "locales": { "matches": ["en-US"] },
-          "regions": ["us"]
-        }
-      }, {
-        "included": { "locales": {
-          "matches": ["an", "ast", "ca", "ca-valencia", "es-ES", "eu", "gl"]
-        }},
-        "webExtension": {
-          "locales": ["es"]
-        },
-        "telemetryId": "ebay-es",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.es/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["br", "fr", "wo"] }},
-        "webExtension": {
-          "locales": ["fr"]
-        },
-        "telemetryId": "ebay-fr",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.fr/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["br", "en-US", "fr", "wo"] },
-          "regions": ["be"]
-        },
-        "webExtension": {
-          "locales": ["be"]
-        },
-        "telemetryId": "ebay-be",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.befr.ebay.be/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["br", "en-US", "fr", "wo"] },
-          "regions": ["ca"]
-        },
-        "webExtension": {
-          "locales": ["ca"]
-        },
-        "telemetryId": "ebay-ca",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ca/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["br", "en-US", "fr", "wo"] },
-          "regions": ["ch"]
-        },
-        "webExtension": {
-          "locales": ["ch"]
-        },
-        "telemetryId": "ebay-ch",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ch/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["cy", "en-GB", "gd"] }},
-        "webExtension": {
-          "locales": ["uk"]
-        },
-        "telemetryId": "ebay-uk",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.co.uk/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["en-US"] },
-          "regions": ["gb"]
-        },
-        "webExtension": {
-          "locales": ["uk"]
-        },
-        "telemetryId": "ebay-uk",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.co.uk/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["cy", "en-GB", "en-US", "gd"] },
-          "regions": ["au"]
-        },
-        "webExtension": {
-          "locales": ["au"]
-        },
-        "telemetryId": "ebay-au",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.com.au/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["cy", "en-GB", "en-US", "gd"] },
-          "regions": ["ie"]
-        },
-        "webExtension": {
-          "locales": ["ie"]
-        },
-        "telemetryId": "ebay-ie",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ie/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["de", "dsb", "hsb"] }},
-        "webExtension": {
-          "locales": ["de"]
-        },
-        "telemetryId": "ebay-de",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.de/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["de", "dsb", "hsb"] },
-          "regions": ["at"]
-        },
-        "webExtension": {
-          "locales": ["at"]
-        },
-        "telemetryId": "ebay-at",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.at/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["de", "dsb", "hsb"] },
-          "regions": ["ch"]
-        },
-        "webExtension": {
-          "locales": ["ch"]
-        },
-        "telemetryId": "ebay-ch"
-      }, {
-        "included": { "locales": { "matches": ["en-CA"] }},
-        "webExtension": {
-          "locales": ["ca"]
-        },
-        "telemetryId": "ebay-ca",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ca/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["fy-NL", "nl"] }},
-        "webExtension": {
-          "locales": ["nl"]
-        },
-        "telemetryId": "ebay-nl",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.nl/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["en-US"] },
-          "regions": ["nl"]
-        },
-        "webExtension": {
-          "locales": ["nl"]
-        },
-        "telemetryId": "ebay-nl",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.nl/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": {
-          "locales": { "matches": ["fy-NL", "nl"] },
-          "regions": ["be"]
-        },
-        "webExtension": {
-          "locales": ["be"]
-        },
-        "telemetryId": "ebay-be",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.befr.ebay.be/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["ga-IE"] }},
-        "webExtension": {
-          "locales": ["ie"]
-        },
-        "telemetryId": "ebay-ie",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ie/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["it", "lij"] }},
-        "webExtension": {
-          "locales": ["it"]
-        },
-        "telemetryId": "ebay-it",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.it/sch/{searchTerms}" }
-          ]
-        }
-      }, {
-        "included": { "locales": { "matches": ["rm"] }},
-        "webExtension": {
-          "locales": ["ch"]
-        },
-        "telemetryId": "ebay-ch",
-        "params": {
-          "searchUrlGetParams": [
-            { "name": "ff3", "value": "4" },
-            { "name": "toolid", "value": "20004" },
-            { "name": "campid", "value": "5338192028" },
-            { "name": "customid", "value": "" },
-            { "name": "mpre", "value": "https://www.ebay.ch/sch/{searchTerms}" }
-          ]
-        }
-      }]
-    },
-    {
-      "orderHint": 500,
-      "webExtension": {
-        "id": "ddg@search.mozilla.org"
-      },
-      "telemetryId": "ddg",
-      "extraParams": [{
-        "name": "t",
-        "condition": "purpose",
-        "purpose": "contextmenu",
-        "value": "ffcm"
-      }, {
-        "name": "t",
-        "condition": "purpose",
-        "purpose": "keyword",
-        "value": "ffab"
-      }, {
-        "name": "t",
-        "condition": "purpose",
-        "purpose": "searchbar",
-        "value": "ffsb"
-      }, {
-        "name": "t",
-        "condition": "purpose",
-        "purpose": "homepage",
-        "value": "ffhp"
-      }, {
-        "name": "t",
-        "condition": "purpose",
-        "purpose": "newtab",
-        "value": "ffnt"
-      }],
-      "appliesTo": [{
-        "included": { "everywhere": true }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "yandex@search.mozilla.org"
-      },
-      "extraParams": [{
-        "name": "clid",
-        "condition": "purpose",
-        "purpose": "searchbar",
-        "value": "2186618"
-      }, {
-        "name": "clid",
-        "condition": "purpose",
-        "purpose": "keyword",
-        "value": "2186621"
-      }, {
-        "name": "clid",
-        "condition": "purpose",
-        "purpose": "contextmenu",
-        "value": "2186623"
-      }, {
-        "name": "clid",
-        "condition": "purpose",
-        "purpose": "homepage",
-        "value": "2186617"
-      }, {
-        "name": "clid",
-        "condition": "purpose",
-        "purpose": "newtab",
-        "value": "2186620"
-      }],
-      "appliesTo": [{
-        "default": "yes",
-        "included": {
-          "regions": ["ru", "tr", "by", "kz"],
-          "locales": {
-            "matches": ["ru", "tr", "be", "kk"],
-            "startsWith": ["en"]
-          }
-        },
-        "webExtension": {
-          "locales": ["en"]
-        },
-        "telemetryId": "yandex-en"
-      }, {
-        "included": { "locales": { "matches": ["az"] }},
-        "webExtension": {
-          "locales": ["az"]
-        },
-        "telemetryId": "yandex-az"
-      }, {
-        "included": { "locales": { "matches": ["be"] }},
-        "webExtension": {
-          "locales": ["by"]
-        },
-        "telemetryId": "yandex-by"
-      }, {
-        "included": { "locales": { "matches": ["kk"] }},
-        "webExtension": {
-          "locales": ["kk"]
-        },
-        "telemetryId": "yandex-kk"
-      }, {
-        "included": { "locales": { "matches": ["ru"] }},
-        "webExtension": {
-          "locales": ["ru"]
-        },
-        "telemetryId": "yandex-ru"
-      }, {
-        "included": { "locales": { "matches": ["tr"] }},
-        "webExtension": {
-          "locales": ["tr"]
-        },
-        "telemetryId": "yandex-tr"
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "allaannonser-sv-SE@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sv-SE"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "allegro-pl@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["pl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "atlas-sk@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sk"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "azerdict@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["az"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "azet-sk@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sk"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "bbc-alba@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["gd"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "bok-NO@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": {
-        "locales": { "matches": ["nb-NO", "nn-NO"]}
-      }}]
-    },
-    {
-      "webExtension": {
-        "id": "bolcom@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["fy-NL"]}},
-        "webExtension": {
-          "locales": ["fy-NL"]
-        }
-      }, {
-        "included": { "locales": { "matches": ["nl"]}},
-        "webExtension": {
-          "locales": ["nl"]
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "ceneji@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "chambers-en-GB@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["en-GB"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "coccoc@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["vi"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "daum-kr@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ko"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "diec2@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["ca", "ca-valencia"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "drae@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["es-AR", "es-CL", "es-ES"]}}
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "ecosia@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["de"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "eki-ee@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["et"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "eudict@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["hr"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "faclair-beag@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["gd"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "flip@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["kk"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "freelang@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["br"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "gulesider-NO@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["nn-NO", "nb-NO"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "heureka-cz@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["cs"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "hotline-ua@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["uk"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "kannadastore@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["kn"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "leo_ende_de@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["de", "dsb", "hsb" , "rm"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "list-am@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["hy-AM"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "longdo@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["th"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "mailru@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ru"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "mapy-cz@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["cs"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "marktplaats@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["fy-NL"]}},
-        "webExtension": {
-          "locales": ["fy-NL"]
-        }
-      }, {
-        "included": { "locales": { "matches": ["nl"]}},
-        "webExtension": {
-          "locales": ["nl"]
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "mercadolibre@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["es-AR"]}},
-        "webExtension": {
-          "locales": ["ar"]
-        }
-      }, {
-        "included": { "locales": { "matches": ["es-CL"]}},
-        "webExtension": {
-          "locales": ["cl"]
-        }
-      }, {
-        "included": { "locales": { "matches": ["es-MX"]}},
-        "webExtension": {
-          "locales": ["mx"]
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "mercadolivre@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["pt-BR"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "morfix-dic@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["he"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "najdi-si@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "naver-kr@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ko"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "neti-ee@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["et"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "odpiralni@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "olx@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["bs"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "oshiete-goo@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ja-JP-macos", "jp"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "osta-ee@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["et"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "ozonru@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ru"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "palasprint@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["cy"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "pazaruvaj@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["bg"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "pogodak@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sr"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "priberam@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["pt-PT"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "priceru@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ru"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "prisjakt-sv-SE@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sv-SE"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "pwn-pl@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["pl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "qwant@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["fr"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "qxl-NO@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["nb-NO", "nn-NO"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "rakuten@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ja-JP-macos", "jp"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "readmoo@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["zh-TW"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "salidzinilv@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ltg", "lv"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "seznam-cz@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["cs"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "sslv@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ltg", "lv"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "tearma@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ga-IE"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "twitter@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": [
-          "default", "en-US", "ach", "an", "bs", "ca", "ca-valencia",
-          "en-CA", "ga-IE", "gn", "hr", "ia", "ka", "kk", "km",
-          "lo", "lt", "ms", "my", "ne-NP", "oc", "pt-BR",
-          "sl", "tl", "tr", "ur", "uz", "wo"
-        ]}}
-      }, {
-        "included": { "locales": { "matches": ["ja-JP-macos", "ja"]}},
-        "webExtension": {
-          "locales": ["ja"]
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "tyda-sv-SE@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sv-SE"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "vatera@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["hu"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "wiktionary@search.mozilla.org"
-      },
-      "appliesTo": [{
-        "included": { "locales": { "matches": ["te"]}},
-        "webExtension": {
-          "locales": ["te"]
-        }
-      }, {
-        "included": { "locales": { "matches": ["oc"]}},
-        "webExtension": {
-          "locales": ["oc"]
-        }
-      }]
-    },
-    {
-      "webExtension": {
-        "id": "wolnelektury-pl@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["pl"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "yahoo-jp@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ja-JP-macos", "ja"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "yahoo-jp-auctions@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["ja-JP-macos", "ja"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "zoznam-sk@search.mozilla.org"
-      },
-      "appliesTo": [{ "included": { "locales": { "matches": ["sk"]}}}]
-    },
-    {
-      "webExtension": {
-        "id": "wikipedia@search.mozilla.org"
-      },
-      "appliesTo": [ {
-        "included": { "everywhere": true }
-      }, {
-        "included": { "locales": { "matches": [
-          "af", "an", "ar", "ast", "az", "bg",
-          "br", "bs", "cy", "da", "de", "dsb",
-          "el", "eo", "et", "eu", "fa", "fi", "fy-NL", "ga-IE",
-          "gd", "gl", "gn", "he", "hr", "hsb", "hu", "ia",
-          "id", "is", "it", "ka", "kab", "kk", "km", "kn",
-          "lij", "lo", "lt", "ltg", "lv", "mk", "mr",
-          "ms", "my", "nl", "oc", "pl", "rm", "ro", "ru",
-          "si", "sk", "sl", "sq", "sr", "sv-SE", "ta", "te",
-          "th", "tl", "tr", "uk", "ur", "uz", "vi", "wo",
-          "zh-CN", "zh-TW"
-        ]}},
-        "webExtension": {
-          "locales": ["$USER_LOCALE"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "be" ] } },
-        "webExtension": {
-          "locales": ["be", "be-tarask"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "bn", "bn-BD", "bn-IN" ] } },
-        "webExtension": {
-          "locales": ["bn"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "ca", "ca-valencia" ] } },
-        "webExtension": {
-          "locales": ["ca"]
-        }
-      }, {
-        "included": { "locales": { "matches": [
-          "cak", "es-AR", "es-CL", "es-ES", "es-MX", "trs"
-        ]}},
-        "webExtension": {
-          "locales": ["es"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "cs" ] } },
-        "webExtension": {
-          "locales": ["cz"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "ff", "fr", "son" ] } },
-        "webExtension": {
-          "locales": ["fr"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "gu-IN" ] } },
-        "webExtension": {
-          "locales": ["gu"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "hi-IN" ] } },
-        "webExtension": {
-          "locales": ["hi"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "hy-AM" ] } },
-        "webExtension": {
-          "locales": ["hy"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "ja-JP-macos", "ja" ] } },
-        "webExtension": {
-          "locales": ["ja"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "ko" ] } },
-        "webExtension": {
-          "locales": ["kr"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "nb-NO" ] } },
-        "webExtension": {
-          "locales": ["NO"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "ne-NP" ] } },
-        "webExtension": {
-          "locales": ["ne"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "nn-NO" ] } },
-        "webExtension": {
-          "locales": ["NN"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "pa-IN" ] } },
-        "webExtension": {
-          "locales": ["pa"]
-        }
-      }, {
-        "included": { "locales": { "matches": [ "pt-BR", "pt-PT" ] } },
-        "webExtension": {
-          "locales": ["pt"]
-        }
-      }]
-    }
-  ]
-}
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -53,16 +53,18 @@ ChromeUtils.defineModuleGetter(
  * legal to begin another restore.
  */
 function ContentRestore(chromeGlobal) {
   let internal = new ContentRestoreInternal(chromeGlobal);
   let external = {};
 
   let EXPORTED_METHODS = [
     "restoreHistory",
+    "finishRestoreHistory",
+    "restoreOnNewEntry",
     "restoreTabContent",
     "restoreDocument",
     "resetRestore",
   ];
 
   for (let method of EXPORTED_METHODS) {
     external[method] = internal[method].bind(internal);
   }
@@ -89,16 +91,35 @@ function ContentRestoreInternal(chromeGl
   // restoreHistory and removed in restoreTabContent.
   this._historyListener = null;
 
   // This listener detects when a pending tab starts loading (when not
   // initiated by sessionstore) and when a restoring tab has finished loading
   // data from the network. Set in restoreHistory() and restoreTabContent(),
   // removed in resetRestore().
   this._progressListener = null;
+
+  this._shistoryInParent = false;
+}
+
+function kickOffNewLoadFromBlankPage(webNavigation, newURI) {
+  // Reset the tab's URL to what it's actually showing. Without this loadURI()
+  // would use the current document and change the displayed URL only.
+  webNavigation.setCurrentURI(Services.io.newURI("about:blank"));
+
+  // Kick off a new load so that we navigate away from about:blank to the
+  // new URL that was passed to loadURI(). The new load will cause a
+  // STATE_START notification to be sent and the ProgressListener will then
+  // notify the parent and do the rest.
+  let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+  let loadURIOptions = {
+    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    loadFlags,
+  };
+  webNavigation.loadURI(newURI, loadURIOptions);
 }
 
 /**
  * The API for the ContentRestore module. Methods listed in EXPORTED_METHODS are
  * public.
  */
 ContentRestoreInternal.prototype = {
   get docShell() {
@@ -106,17 +127,17 @@ ContentRestoreInternal.prototype = {
   },
 
   /**
    * Starts the process of restoring a tab. The tabData to be restored is passed
    * in here and used throughout the restoration. The epoch (which must be
    * non-zero) is passed through to all the callbacks. If a load in the tab
    * is started while it is pending, the appropriate callbacks are called.
    */
-  restoreHistory(tabData, loadArguments, callbacks) {
+  restoreHistory(tabData, loadArguments, callbacks, shistoryInParent) {
     this._tabData = tabData;
 
     // In case about:blank isn't done yet.
     let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
     webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);
 
     // Make sure currentURI is set so that switch-to-tab works before the tab is
     // restored. We'll reset this to about:blank when we try to restore the tab
@@ -125,69 +146,102 @@ ContentRestoreInternal.prototype = {
     // URL bar to be temporarily blank.
     let activeIndex = tabData.index - 1;
     let activePageData = tabData.entries[activeIndex] || {};
     let uri = activePageData.url || null;
     if (uri && !loadArguments) {
       webNavigation.setCurrentURI(Services.io.newURI(uri));
     }
 
-    SessionHistory.restore(this.docShell, tabData);
+    this._shistoryInParent = shistoryInParent;
+
+    if (this._shistoryInParent) {
+      callbacks.requestRestoreSHistory();
+    } else {
+      SessionHistory.restore(this.docShell, tabData);
 
-    // Add a listener to watch for reloads.
-    let listener = new HistoryListener(this.docShell, () => {
-      // On reload, restore tab contents.
-      this.restoreTabContent(null, false, callbacks.onLoadFinished);
-    });
+      // Add a listener to watch for reloads.
+      let listener = new HistoryListener(this.docShell, () => {
+        // On reload, restore tab contents.
+        this.restoreTabContent(
+          null,
+          false,
+          callbacks.onLoadFinished,
+          null,
+          null
+        );
+      });
 
-    webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener);
-    this._historyListener = listener;
+      webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener);
+      this._historyListener = listener;
 
+      this.finishRestoreHistory(callbacks);
+    }
+  },
+
+  finishRestoreHistory(callbacks) {
     // Make sure to reset the capabilities and attributes in case this tab gets
     // reused.
     SessionStoreUtils.restoreDocShellCapabilities(
       this.docShell,
-      tabData.disallow
+      this._tabData.disallow
     );
 
-    if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
-      SessionStoreUtils.restoreSessionStorage(this.docShell, tabData.storage);
-      delete tabData.storage;
+    if (this._tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
+      SessionStoreUtils.restoreSessionStorage(
+        this.docShell,
+        this._tabData.storage
+      );
+      delete this._tabData.storage;
     }
 
     // Add a progress listener to correctly handle browser.loadURI()
     // calls from foreign code.
     this._progressListener = new ProgressListener(this.docShell, {
       onStartRequest: () => {
         // Some code called browser.loadURI() on a pending tab. It's safe to
         // assume we don't care about restoring scroll or form data.
         this._tabData = null;
 
         // Listen for the tab to finish loading.
-        this.restoreTabContentStarted(callbacks.onLoadFinished);
+        this.restoreTabContentStarted(
+          callbacks.onLoadFinished,
+          callbacks.removeRestoreListener
+        );
 
         // Notify the parent.
         callbacks.onLoadStarted();
       },
     });
   },
 
+  restoreOnNewEntry(newURI) {
+    let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
+    kickOffNewLoadFromBlankPage(webNavigation, newURI);
+  },
+
   /**
    * Start loading the current page. When the data has finished loading from the
    * network, finishCallback is called. Returns true if the load was successful.
    */
-  restoreTabContent(loadArguments, isRemotenessUpdate, finishCallback) {
+  restoreTabContent(
+    loadArguments,
+    isRemotenessUpdate,
+    finishCallback,
+    removeListenerCallback,
+    reloadSHistoryCallback
+  ) {
     let tabData = this._tabData;
     this._tabData = null;
 
     let webNavigation = this.docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.legacySHistory;
 
     // Listen for the tab to finish loading.
-    this.restoreTabContentStarted(finishCallback);
+    this.restoreTabContentStarted(finishCallback, removeListenerCallback);
 
     // Reset the current URI to about:blank. We changed it above for
     // switch-to-tab, but now it must go back to the correct value before the
     // load happens. Don't bother doing this if we're restoring immediately
     // due to a process switch.
     if (!isRemotenessUpdate) {
       webNavigation.setCurrentURI(Services.io.newURI("about:blank"));
     }
@@ -268,17 +322,21 @@ ContentRestoreInternal.prototype = {
           entry: tabData.entries[activeIndex] || {},
           formdata: tabData.formdata || {},
           scrollPositions: tabData.scroll || {},
         };
 
         // In order to work around certain issues in session history, we need to
         // force session history to update its internal index and call reload
         // instead of gotoIndex. See bug 597315.
-        history.reloadCurrentEntry();
+        if (this._shistoryInParent) {
+          reloadSHistoryCallback();
+        } else {
+          history.reloadCurrentEntry();
+        }
       } else {
         // If there's nothing to restore, we should still blank the page.
         let loadURIOptions = {
           triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
           loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_HISTORY,
         };
         webNavigation.loadURI("about:blank", loadURIOptions);
       }
@@ -293,20 +351,24 @@ ContentRestoreInternal.prototype = {
     }
     return null;
   },
 
   /**
    * To be called after restoreHistory(). Removes all listeners needed for
    * pending tabs and makes sure to notify when the tab finished loading.
    */
-  restoreTabContentStarted(finishCallback) {
+  restoreTabContentStarted(finishCallback, removeListenerCallback) {
     // The reload listener is no longer needed.
-    this._historyListener.uninstall();
-    this._historyListener = null;
+    if (!this._shistoryInParent) {
+      this._historyListener.uninstall();
+      this._historyListener = null;
+    } else {
+      removeListenerCallback();
+    }
 
     // Remove the old progress listener.
     this._progressListener.uninstall();
 
     // We're about to start a load. This listener will be called when the load
     // has finished getting everything from the network.
     this._progressListener = new ProgressListener(this.docShell, {
       onStopRequest: () => {
@@ -410,30 +472,17 @@ HistoryListener.prototype = {
 
     // Ignore new SHistory entries with the same URI as those do not indicate
     // a navigation inside a document by changing the #hash part of the URL.
     // We usually hit this when purging session history for browsers.
     if (currentURI && currentURI.spec == newURI.spec) {
       return;
     }
 
-    // Reset the tab's URL to what it's actually showing. Without this loadURI()
-    // would use the current document and change the displayed URL only.
-    this.webNavigation.setCurrentURI(Services.io.newURI("about:blank"));
-
-    // Kick off a new load so that we navigate away from about:blank to the
-    // new URL that was passed to loadURI(). The new load will cause a
-    // STATE_START notification to be sent and the ProgressListener will then
-    // notify the parent and do the rest.
-    let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
-    let loadURIOptions = {
-      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
-      loadFlags,
-    };
-    this.webNavigation.loadURI(newURI.spec, loadURIOptions);
+    kickOffNewLoadFromBlankPage(this.webNavigation, newURI);
   },
 
   OnHistoryReload() {
     this.callback();
 
     // Cancel the load.
     return false;
   },
--- a/browser/components/sessionstore/ContentSessionStore.jsm
+++ b/browser/components/sessionstore/ContentSessionStore.jsm
@@ -16,29 +16,26 @@ function debug(msg) {
 
 ChromeUtils.defineModuleGetter(
   this,
   "ContentRestore",
   "resource:///modules/sessionstore/ContentRestore.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
-  "SessionHistory",
-  "resource://gre/modules/sessionstore/SessionHistory.jsm"
+  "SessionHistoryListener",
+  "resource:///modules/sessionstore/SessionHistoryListener.jsm"
 );
 
 // This pref controls whether or not we send updates to the parent on a timeout
 // or not, and should only be used for tests or debugging.
 const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";
 
 const PREF_INTERVAL = "browser.sessionstore.interval";
 
-const kNoIndex = Number.MAX_SAFE_INTEGER;
-const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
-
 class Handler {
   constructor(store) {
     this.store = store;
   }
 
   get contentRestore() {
     return this.store.contentRestore;
   }
@@ -49,94 +46,19 @@ class Handler {
 
   get mm() {
     return this.store.mm;
   }
 
   get messageQueue() {
     return this.store.messageQueue;
   }
-
-  get stateChangeNotifier() {
-    return this.store.stateChangeNotifier;
-  }
 }
 
 /**
- * Listens for state change notifcations from webProgress and notifies each
- * registered observer for either the start of a page load, or its completion.
- */
-class StateChangeNotifier extends Handler {
-  constructor(store) {
-    super(store);
-
-    this._observers = new Set();
-    let ifreq = this.mm.docShell.QueryInterface(Ci.nsIInterfaceRequestor);
-    let webProgress = ifreq.getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(
-      this,
-      Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
-    );
-  }
-
-  /**
-   * Adds a given observer |obs| to the set of observers that will be notified
-   * when when a new document starts or finishes loading.
-   *
-   * @param obs (object)
-   */
-  addObserver(obs) {
-    this._observers.add(obs);
-  }
-
-  /**
-   * Notifies all observers that implement the given |method|.
-   *
-   * @param method (string)
-   */
-  notifyObservers(method) {
-    for (let obs of this._observers) {
-      if (typeof obs[method] == "function") {
-        obs[method]();
-      }
-    }
-  }
-
-  /**
-   * @see nsIWebProgressListener.onStateChange
-   */
-  onStateChange(webProgress, request, stateFlags, status) {
-    // Ignore state changes for subframes because we're only interested in the
-    // top-document starting or stopping its load.
-    if (!webProgress.isTopLevel || webProgress.DOMWindow != this.mm.content) {
-      return;
-    }
-
-    // onStateChange will be fired when loading the initial about:blank URI for
-    // a browser, which we don't actually care about. This is particularly for
-    // the case of unrestored background tabs, where the content has not yet
-    // been restored: we don't want to accidentally send any updates to the
-    // parent when the about:blank placeholder page has loaded.
-    if (!this.mm.docShell.hasLoadedNonBlankURI) {
-      return;
-    }
-
-    if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
-      this.notifyObservers("onPageLoadStarted");
-    } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-      this.notifyObservers("onPageLoadCompleted");
-    }
-  }
-}
-StateChangeNotifier.prototype.QueryInterface = ChromeUtils.generateQI([
-  Ci.nsIWebProgressListener,
-  Ci.nsISupportsWeakReference,
-]);
-
-/**
  * Listens for and handles content events that we need for the
  * session store service to be notified of state changes in content.
  */
 class EventListener extends Handler {
   constructor(store) {
     super(store);
 
     SessionStoreUtils.addDynamicFrameFilteredListener(
@@ -173,149 +95,16 @@ class EventListener extends Handler {
       // Restore the form data and scroll position. If we're not currently
       // restoring a tab state then this call will simply be a noop.
       this.contentRestore.restoreDocument();
     }
   }
 }
 
 /**
- * Listens for changes to the session history. Whenever the user navigates
- * we will collect URLs and everything belonging to session history.
- *
- * Causes a SessionStore:update message to be sent that contains the current
- * session history.
- *
- * Example:
- *   {entries: [{url: "about:mozilla", ...}, ...], index: 1}
- */
-class SessionHistoryListener extends Handler {
-  constructor(store) {
-    super(store);
-
-    this._fromIdx = kNoIndex;
-
-    // The state change observer is needed to handle initial subframe loads.
-    // It will redundantly invalidate with the SHistoryListener in some cases
-    // but these invalidations are very cheap.
-    this.stateChangeNotifier.addObserver(this);
-
-    // By adding the SHistoryListener immediately, we will unfortunately be
-    // notified of every history entry as the tab is restored. We don't bother
-    // waiting to add the listener later because these notifications are cheap.
-    // We will likely only collect once since we are batching collection on
-    // a delay.
-    this.mm.docShell
-      .QueryInterface(Ci.nsIWebNavigation)
-      .sessionHistory.legacySHistory.addSHistoryListener(this);
-
-    // Collect data if we start with a non-empty shistory.
-    if (!SessionHistory.isEmpty(this.mm.docShell)) {
-      this.collect();
-      // When a tab is detached from the window, for the new window there is a
-      // new SessionHistoryListener created. Normally it is empty at this point
-      // but in a test env. the initial about:blank might have a children in which
-      // case we fire off a history message here with about:blank in it. If we
-      // don't do it ASAP then there is going to be a browser swap and the parent
-      // will be all confused by that message.
-      this.messageQueue.send();
-    }
-
-    // Listen for page title changes.
-    this.mm.addEventListener("DOMTitleChanged", this);
-  }
-
-  uninit() {
-    let sessionHistory = this.mm.docShell.QueryInterface(Ci.nsIWebNavigation)
-      .sessionHistory;
-    if (sessionHistory) {
-      sessionHistory.legacySHistory.removeSHistoryListener(this);
-    }
-  }
-
-  collect() {
-    // We want to send down a historychange even for full collects in case our
-    // session history is a partial session history, in which case we don't have
-    // enough information for a full update. collectFrom(-1) tells the collect
-    // function to collect all data avaliable in this process.
-    if (this.mm.docShell) {
-      this.collectFrom(-1);
-    }
-  }
-
-  // History can grow relatively big with the nested elements, so if we don't have to, we
-  // don't want to send the entire history all the time. For a simple optimization
-  // we keep track of the smallest index from after any change has occured and we just send
-  // the elements from that index. If something more complicated happens we just clear it
-  // and send the entire history. We always send the additional info like the current selected
-  // index (so for going back and forth between history entries we set the index to kLastIndex
-  // if nothing else changed send an empty array and the additonal info like the selected index)
-  collectFrom(idx) {
-    if (this._fromIdx <= idx) {
-      // If we already know that we need to update history fromn index N we can ignore any changes
-      // tha happened with an element with index larger than N.
-      // Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which means we don't ignore anything
-      // here, and in case of navigation in the history back and forth we use kLastIndex which ignores
-      // only the subsequent navigations, but not any new elements added.
-      return;
-    }
-
-    this._fromIdx = idx;
-    this.messageQueue.push("historychange", () => {
-      if (this._fromIdx === kNoIndex) {
-        return null;
-      }
-
-      let history = SessionHistory.collect(this.mm.docShell, this._fromIdx);
-      this._fromIdx = kNoIndex;
-      return history;
-    });
-  }
-
-  handleEvent(event) {
-    this.collect();
-  }
-
-  onPageLoadCompleted() {
-    this.collect();
-  }
-
-  onPageLoadStarted() {
-    this.collect();
-  }
-
-  OnHistoryNewEntry(newURI, oldIndex) {
-    // We ought to collect the previously current entry as well, see bug 1350567.
-    this.collectFrom(oldIndex);
-  }
-
-  OnHistoryGotoIndex() {
-    // We ought to collect the previously current entry as well, see bug 1350567.
-    this.collectFrom(kLastIndex);
-  }
-
-  OnHistoryPurge() {
-    this.collect();
-  }
-
-  OnHistoryReload() {
-    this.collect();
-    return true;
-  }
-
-  OnHistoryReplaceEntry() {
-    this.collect();
-  }
-}
-SessionHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
-  Ci.nsISHistoryListener,
-  Ci.nsISupportsWeakReference,
-]);
-
-/**
  * A message queue that takes collected data and will take care of sending it
  * to the chrome process. It allows flushing using synchronous messages and
  * takes care of any race conditions that might occur because of that. Changes
  * will be batched if they're pushed in quick succession to avoid a message
  * flood.
  */
 class MessageQueue extends Handler {
   constructor(store) {
@@ -545,43 +334,53 @@ class MessageQueue extends Handler {
   }
 }
 
 /**
  * Listens for and handles messages sent by the session store service.
  */
 const MESSAGES = [
   "SessionStore:restoreHistory",
+  "SessionStore:finishRestoreHistory",
+  "SessionStore:OnHistoryReload",
+  "SessionStore:OnHistoryNewEntry",
   "SessionStore:restoreTabContent",
   "SessionStore:resetRestore",
   "SessionStore:flush",
   "SessionStore:becomeActiveProcess",
 ];
 
 class ContentSessionStore {
   constructor(mm) {
     this.mm = mm;
     this.messageQueue = new MessageQueue(this);
-    this.stateChangeNotifier = new StateChangeNotifier(this);
 
     this.epoch = 0;
 
     this.contentRestoreInitialized = false;
 
+    this.waitRestoreSHistoryInParent = false;
+    this.restoreTabContentData = null;
+
     XPCOMUtils.defineLazyGetter(this, "contentRestore", () => {
       this.contentRestoreInitialized = true;
       return new ContentRestore(mm);
     });
 
-    this.handlers = [
-      new EventListener(this),
-      new SessionHistoryListener(this),
-      this.stateChangeNotifier,
-      this.messageQueue,
-    ];
+    this.handlers = [new EventListener(this), this.messageQueue];
+
+    this._shistoryInParent = Services.prefs.getBoolPref(
+      "fission.sessionHistoryInParent",
+      false
+    );
+    if (this._shistoryInParent) {
+      this.mm.sendAsyncMessage("SessionStore:addSHistoryListener");
+    } else {
+      this.handlers.push(new SessionHistoryListener(this));
+    }
 
     MESSAGES.forEach(m => mm.addMessageListener(m, this));
 
     // If we're browsing from the tab crashed UI to a blacklisted URI that keeps
     // this browser non-remote, we'll handle that in a pagehide event.
     mm.addEventListener("pagehide", this);
     mm.addEventListener("unload", this);
   }
@@ -592,102 +391,223 @@ class ContentSessionStore {
     if (!this.mm.docShell) {
       return;
     }
 
     // A fresh tab always starts with epoch=0. The parent has the ability to
     // override that to signal a new era in this tab's life. This enables it
     // to ignore async messages that were already sent but not yet received
     // and would otherwise confuse the internal tab state.
-    if (data.epoch && data.epoch != this.epoch) {
+    if (data && data.epoch && data.epoch != this.epoch) {
       this.epoch = data.epoch;
     }
 
     switch (name) {
       case "SessionStore:restoreHistory":
         this.restoreHistory(data);
         break;
+      case "SessionStore:finishRestoreHistory":
+        this.finishRestoreHistory();
+        break;
+      case "SessionStore:OnHistoryNewEntry":
+        this.contentRestore.restoreOnNewEntry(data.uri);
+        break;
+      case "SessionStore:OnHistoryReload":
+        // On reload, restore tab contents.
+        this.contentRestore.restoreTabContent(
+          null,
+          false,
+          () => {
+            // Tell SessionStore.jsm that it may want to restore some more tabs,
+            // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
+            this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
+              epoch: this.epoch,
+            });
+          },
+          () => {
+            // Tell SessionStore.jsm to remove restoreListener.
+            this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
+              epoch: this.epoch,
+            });
+          },
+          () => {
+            // Tell SessionStore.jsm to reload currentEntry.
+            this.mm.sendAsyncMessage("SessionStore:reloadCurrentEntry", {
+              epoch: this.epoch,
+            });
+          }
+        );
+        break;
       case "SessionStore:restoreTabContent":
-        this.restoreTabContent(data);
+        if (this.waitRestoreSHistoryInParent) {
+          // Queue the TabContentData if we haven't finished sHistoryRestore yet.
+          this.restoreTabContentData = data;
+        } else {
+          this.restoreTabContent(data);
+        }
         break;
       case "SessionStore:resetRestore":
         this.contentRestore.resetRestore();
         break;
       case "SessionStore:flush":
         this.flush(data);
         break;
       case "SessionStore:becomeActiveProcess":
-        SessionHistoryListener.collect();
+        if (!this._shistoryInParent) {
+          SessionHistoryListener.collect();
+        }
         break;
       default:
         debug("received unknown message '" + name + "'");
         break;
     }
   }
 
   restoreHistory({ epoch, tabData, loadArguments, isRemotenessUpdate }) {
-    this.contentRestore.restoreHistory(tabData, loadArguments, {
-      // Note: The callbacks passed here will only be used when a load starts
-      // that was not initiated by sessionstore itself. This can happen when
-      // some code calls browser.loadURI() or browser.reload() on a pending
-      // browser/tab.
+    this.contentRestore.restoreHistory(
+      tabData,
+      loadArguments,
+      {
+        // Note: The callbacks passed here will only be used when a load starts
+        // that was not initiated by sessionstore itself. This can happen when
+        // some code calls browser.loadURI() or browser.reload() on a pending
+        // browser/tab.
+
+        onLoadStarted: () => {
+          // Notify the parent that the tab is no longer pending.
+          this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
+            epoch,
+          });
+        },
+
+        onLoadFinished: () => {
+          // Tell SessionStore.jsm that it may want to restore some more tabs,
+          // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
+          this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
+            epoch,
+          });
+        },
 
-      onLoadStarted: () => {
-        // Notify the parent that the tab is no longer pending.
-        this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
-          epoch,
-        });
-      },
+        removeRestoreListener: () => {
+          if (!this._shistoryInParent) {
+            return;
+          }
+
+          // Notify the parent that the tab is no longer pending.
+          this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
+            epoch,
+          });
+        },
 
-      onLoadFinished: () => {
-        // Tell SessionStore.jsm that it may want to restore some more tabs,
-        // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
-        this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
-          epoch,
-        });
+        requestRestoreSHistory: () => {
+          if (!this._shistoryInParent) {
+            return;
+          }
+
+          this.waitRestoreSHistoryInParent = true;
+          // Send tabData to the parent process.
+          this.mm.sendAsyncMessage("SessionStore:restoreSHistoryInParent", {
+            epoch,
+          });
+        },
       },
-    });
+      this._shistoryInParent
+    );
 
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
       // For non-remote tabs, when restoreHistory finishes, we send a synchronous
       // message to SessionStore.jsm so that it can run SSTabRestoring. Users of
       // SSTabRestoring seem to get confused if chrome and content are out of
       // sync about the state of the restore (particularly regarding
       // docShell.currentURI). Using a synchronous message is the easiest way
       // to temporarily synchronize them.
       //
       // For remote tabs, because all nsIWebProgress notifications are sent
       // asynchronously using messages, we get the same-order guarantees of the
       // message manager, and can use an async message.
       this.mm.sendSyncMessage("SessionStore:restoreHistoryComplete", {
         epoch,
         isRemotenessUpdate,
       });
-    } else {
+    } else if (!this._shistoryInParent) {
       this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", {
         epoch,
         isRemotenessUpdate,
       });
     }
   }
 
+  finishRestoreHistory() {
+    this.contentRestore.finishRestoreHistory({
+      // Note: The callbacks passed here will only be used when a load starts
+      // that was not initiated by sessionstore itself. This can happen when
+      // some code calls browser.loadURI() or browser.reload() on a pending
+      // browser/tab.
+      onLoadStarted: () => {
+        // Notify the parent that the tab is no longer pending.
+        this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
+          epoch: this.epoch,
+        });
+      },
+
+      onLoadFinished: () => {
+        // Tell SessionStore.jsm that it may want to restore some more tabs,
+        // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
+        this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
+          epoch: this.epoch,
+        });
+      },
+
+      removeRestoreListener: () => {
+        if (!this._shistoryInParent) {
+          return;
+        }
+
+        // Notify the parent that the tab is no longer pending.
+        this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
+          epoch: this.epoch,
+        });
+      },
+    });
+
+    this.mm.sendAsyncMessage("SessionStore:restoreHistoryComplete", {
+      epoch: this.epoch,
+    });
+    if (this.restoreTabContentData) {
+      this.restoreTabContent(this.restoreTabContentData);
+      this.restoreTabContentData = null;
+    }
+    this.waitRestoreSHistoryInParent = false;
+  }
+
   restoreTabContent({ loadArguments, isRemotenessUpdate, reason }) {
     let epoch = this.epoch;
 
     // We need to pass the value of didStartLoad back to SessionStore.jsm.
     let didStartLoad = this.contentRestore.restoreTabContent(
       loadArguments,
       isRemotenessUpdate,
       () => {
         // Tell SessionStore.jsm that it may want to restore some more tabs,
         // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
         this.mm.sendAsyncMessage("SessionStore:restoreTabContentComplete", {
           epoch,
           isRemotenessUpdate,
         });
+      },
+      () => {
+        // Tell SessionStore.jsm to remove restore listener.
+        this.mm.sendAsyncMessage("SessionStore:removeRestoreListener", {
+          epoch,
+        });
+      },
+      () => {
+        this.mm.sendAsyncMessage("SessionStore:reloadCurrentEntry", {
+          epoch,
+        });
       }
     );
 
     this.mm.sendAsyncMessage("SessionStore:restoreTabContentStarted", {
       epoch,
       isRemotenessUpdate,
       reason,
     });
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/SessionHistoryListener.jsm
@@ -0,0 +1,232 @@
+/* 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/. */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["SessionHistoryListener"];
+
+ChromeUtils.defineModuleGetter(
+  this,
+  "SessionHistory",
+  "resource://gre/modules/sessionstore/SessionHistory.jsm"
+);
+
+const kNoIndex = Number.MAX_SAFE_INTEGER;
+const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
+
+/**
+ * Listens for state change notifcations from webProgress and notifies each
+ * registered observer for either the start of a page load, or its completion.
+ */
+class StateChangeNotifier {
+  constructor(store) {
+    this.store = store;
+
+    this._observers = new Set();
+
+    let webProgress = this.mm.docShell
+      .QueryInterface(Ci.nsIInterfaceRequestor)
+      .getInterface(Ci.nsIWebProgress);
+
+    webProgress.addProgressListener(
+      this,
+      Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
+    );
+  }
+
+  get mm() {
+    return this.store.mm;
+  }
+
+  /**
+   * Adds a given observer |obs| to the set of observers that will be notified
+   * when when a new document starts or finishes loading.
+   *
+   * @param obs (object)
+   */
+  addObserver(obs) {
+    this._observers.add(obs);
+  }
+
+  /**
+   * Notifies all observers that implement the given |method|.
+   *
+   * @param method (string)
+   */
+  notifyObservers(method) {
+    for (let obs of this._observers) {
+      if (typeof obs[method] == "function") {
+        obs[method]();
+      }
+    }
+  }
+
+  /**
+   * @see nsIWebProgressListener.onStateChange
+   */
+  onStateChange(webProgress, request, stateFlags, status) {
+    // Ignore state changes for subframes because we're only interested in the
+    // top-document starting or stopping its load.
+    if (!webProgress.isTopLevel || webProgress.DOMWindow != this.mm.content) {
+      return;
+    }
+
+    // onStateChange will be fired when loading the initial about:blank URI for
+    // a browser, which we don't actually care about. This is particularly for
+    // the case of unrestored background tabs, where the content has not yet
+    // been restored: we don't want to accidentally send any updates to the
+    // parent when the about:blank placeholder page has loaded.
+    if (!this.mm.docShell.hasLoadedNonBlankURI) {
+      return;
+    }
+
+    if (stateFlags & Ci.nsIWebProgressListener.STATE_START) {
+      this.notifyObservers("onPageLoadStarted");
+    } else if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+      this.notifyObservers("onPageLoadCompleted");
+    }
+  }
+}
+StateChangeNotifier.prototype.QueryInterface = ChromeUtils.generateQI([
+  Ci.nsIWebProgressListener,
+  Ci.nsISupportsWeakReference,
+]);
+
+/**
+ * Listens for changes to the session history. Whenever the user navigates
+ * we will collect URLs and everything belonging to session history.
+ *
+ * Causes a SessionStore:update message to be sent that contains the current
+ * session history.
+ *
+ * Example:
+ *   {entries: [{url: "about:mozilla", ...}, ...], index: 1}
+ */
+class SessionHistoryListener {
+  constructor(store) {
+    this.store = store;
+
+    this._fromIdx = kNoIndex;
+
+    // The state change observer is needed to handle initial subframe loads.
+    // It will redundantly invalidate with the SHistoryListener in some cases
+    // but these invalidations are very cheap.
+    this.stateChangeNotifier = new StateChangeNotifier(this);
+    this.stateChangeNotifier.addObserver(this);
+
+    // By adding the SHistoryListener immediately, we will unfortunately be
+    // notified of every history entry as the tab is restored. We don't bother
+    // waiting to add the listener later because these notifications are cheap.
+    // We will likely only collect once since we are batching collection on
+    // a delay.
+    this.mm.docShell
+      .QueryInterface(Ci.nsIWebNavigation)
+      .sessionHistory.legacySHistory.addSHistoryListener(this);
+
+    // Collect data if we start with a non-empty shistory.
+    if (!SessionHistory.isEmpty(this.mm.docShell)) {
+      this.collect();
+      // When a tab is detached from the window, for the new window there is a
+      // new SessionHistoryListener created. Normally it is empty at this point
+      // but in a test env. the initial about:blank might have a children in which
+      // case we fire off a history message here with about:blank in it. If we
+      // don't do it ASAP then there is going to be a browser swap and the parent
+      // will be all confused by that message.
+      this.store.messageQueue.send();
+    }
+
+    // Listen for page title changes.
+    this.mm.addEventListener("DOMTitleChanged", this);
+  }
+
+  get mm() {
+    return this.store.mm;
+  }
+
+  uninit() {
+    let sessionHistory = this.mm.docShell.QueryInterface(Ci.nsIWebNavigation)
+      .sessionHistory;
+    if (sessionHistory) {
+      sessionHistory.legacySHistory.removeSHistoryListener(this);
+    }
+  }
+
+  collect() {
+    // We want to send down a historychange even for full collects in case our
+    // session history is a partial session history, in which case we don't have
+    // enough information for a full update. collectFrom(-1) tells the collect
+    // function to collect all data avaliable in this process.
+    if (this.mm.docShell) {
+      this.collectFrom(-1);
+    }
+  }
+
+  // History can grow relatively big with the nested elements, so if we don't have to, we
+  // don't want to send the entire history all the time. For a simple optimization
+  // we keep track of the smallest index from after any change has occured and we just send
+  // the elements from that index. If something more complicated happens we just clear it
+  // and send the entire history. We always send the additional info like the current selected
+  // index (so for going back and forth between history entries we set the index to kLastIndex
+  // if nothing else changed send an empty array and the additonal info like the selected index)
+  collectFrom(idx) {
+    if (this._fromIdx <= idx) {
+      // If we already know that we need to update history fromn index N we can ignore any changes
+      // tha happened with an element with index larger than N.
+      // Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which means we don't ignore anything
+      // here, and in case of navigation in the history back and forth we use kLastIndex which ignores
+      // only the subsequent navigations, but not any new elements added.
+      return;
+    }
+
+    this._fromIdx = idx;
+    this.store.messageQueue.push("historychange", () => {
+      if (this._fromIdx === kNoIndex) {
+        return null;
+      }
+
+      let history = SessionHistory.collect(this.mm.docShell, this._fromIdx);
+      this._fromIdx = kNoIndex;
+      return history;
+    });
+  }
+
+  handleEvent(event) {
+    this.collect();
+  }
+
+  onPageLoadCompleted() {
+    this.collect();
+  }
+
+  onPageLoadStarted() {
+    this.collect();
+  }
+
+  OnHistoryNewEntry(newURI, oldIndex) {
+    // We ought to collect the previously current entry as well, see bug 1350567.
+    this.collectFrom(oldIndex);
+  }
+
+  OnHistoryGotoIndex() {
+    // We ought to collect the previously current entry as well, see bug 1350567.
+    this.collectFrom(kLastIndex);
+  }
+
+  OnHistoryPurge() {
+    this.collect();
+  }
+
+  OnHistoryReload() {
+    this.collect();
+    return true;
+  }
+
+  OnHistoryReplaceEntry() {
+    this.collect();
+  }
+}
+SessionHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
+  Ci.nsISHistoryListener,
+  Ci.nsISupportsWeakReference,
+]);
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -92,41 +92,63 @@ const MESSAGES = [
   "SessionStore:restoreTabContentComplete",
 
   // A crashed tab was revived by navigating to a different page. Remove its
   // browser from the list of crashed browsers to stop ignoring its messages.
   "SessionStore:crashedTabRevived",
 
   // The content script encountered an error.
   "SessionStore:error",
+
+  // The content script asks us to add the session history listener in the
+  // parent process when sessionHistory is in the parent process.
+  "SessionStore:addSHistoryListener",
+
+  // The content script asks us to remove the session history listener which
+  // is added in the restore process when sessionHistory is in the parent process.
+  "SessionStore:removeRestoreListener",
+
+  // The content script asks us to restore session history in the parent process
+  // when sessionHistory is in the parent process.
+  "SessionStore:restoreSHistoryInParent",
+
+  // The content script asks us to reload the current session history entry when
+  // sessionHistory is in the parent process.
+  "SessionStore:reloadCurrentEntry",
 ];
 
 // The list of messages we accept from <xul:browser>s that have no tab
 // assigned, or whose windows have gone away. Those are for example the
 // ones that preload about:newtab pages, or from browsers where the window
 // has just been closed.
 const NOTAB_MESSAGES = new Set([
   // For a description see above.
   "SessionStore:crashedTabRevived",
 
   // For a description see above.
   "SessionStore:update",
 
   // For a description see above.
   "SessionStore:error",
+
+  // For a description see above.
+  "SessionStore:addSHistoryListener",
 ]);
 
 // The list of messages we accept without an "epoch" parameter.
 // See getCurrentEpoch() and friends to find out what an "epoch" is.
 const NOEPOCH_MESSAGES = new Set([
   // For a description see above.
   "SessionStore:crashedTabRevived",
 
   // For a description see above.
   "SessionStore:error",
+
+  // For a description see above.
+  "SessionStore:addSHistoryListener",
 ]);
 
 // The list of messages we want to receive even during the short period after a
 // frame has been removed from the DOM and before its frame script has finished
 // unloading.
 const CLOSED_MESSAGES = new Set([
   // For a description see above.
   "SessionStore:crashedTabRevived",
@@ -170,23 +192,33 @@ const RESTORE_TAB_CONTENT_REASON = {
    * us to do a remoteness-flip.
    */
   NAVIGATE_AND_RESTORE: 1,
 };
 
 // 'browser.startup.page' preference value to resume the previous session.
 const BROWSER_STARTUP_RESUME_SESSION = 3;
 
+// Used by SessionHistoryListener.
+const kNoIndex = Number.MAX_SAFE_INTEGER;
+const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
+
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
 ChromeUtils.import("resource://gre/modules/Timer.jsm", this);
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
 ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
 
+ChromeUtils.defineModuleGetter(
+  this,
+  "SessionHistory",
+  "resource://gre/modules/sessionstore/SessionHistory.jsm"
+);
+
 XPCOMUtils.defineLazyServiceGetters(this, {
   gScreenManager: ["@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"],
   Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
 });
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
@@ -496,16 +528,25 @@ var SessionStoreInternal = {
 
   // A monotonic value used to generate a unique ID for each process switch.
   _switchIdMonotonic: 0,
 
   // During the initial restore and setBrowserState calls tracks the number of
   // windows yet to be restored
   _restoreCount: -1,
 
+  // For each <browser> element, records the SHistoryListener.
+  _browserSHistoryListener: new WeakMap(),
+
+  // For each <browser> element, records the SHistoryListener.
+  _browserSHistoryListenerForRestore: new WeakMap(),
+
+  // The history data needed to be restored in the parent.
+  _shistoryToRestore: new WeakMap(),
+
   // For each <browser> element, records the current epoch.
   _browserEpochs: new WeakMap(),
 
   // Any browsers that fires the oop-browser-crashed event gets stored in
   // here - that way we know which browsers to ignore messages from (until
   // they get restored).
   _crashedBrowsers: new WeakSet(),
 
@@ -819,16 +860,21 @@ var SessionStoreInternal = {
       "sessionstore.restore_on_demand"
     );
     this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);
 
     gResistFingerprintingEnabled = Services.prefs.getBoolPref(
       "privacy.resistFingerprinting"
     );
     Services.prefs.addObserver("privacy.resistFingerprinting", this);
+
+    this._shistoryInParent = Services.prefs.getBoolPref(
+      "fission.sessionHistoryInParent",
+      false
+    );
   },
 
   /**
    * Called on application shutdown, after notifications:
    * quit-application-granted, quit-application
    */
   _uninit: function ssi_uninit() {
     if (!this._initialized) {
@@ -902,26 +948,291 @@ var SessionStoreInternal = {
         }
         break;
       case "channel-on-may-change-process":
         this.onMayChangeProcess(aSubject);
         break;
     }
   },
 
+  // Create a sHistoryLister and register it.
+  // We also need to save the SHistoryLister into this._browserSHistoryListener.
+  addSHistoryListener(aBrowser) {
+    function SHistoryListener(browser) {
+      browser.frameLoader.browsingContext.sessionHistory.addSHistoryListener(
+        this
+      );
+
+      this.browser = browser;
+      this.frameLoader = browser.frameLoader;
+      this._fromIdx = kNoIndex;
+      this._sHistoryChanges = false;
+      if (this.browser.currentURI && this.browser.ownerGlobal) {
+        this._lastKnownUri = browser.currentURI.displaySpec;
+        this._lastKnownBody = browser.ownerGlobal.document.body;
+        this._lastKnownUserContextId =
+          browser.contentPrincipal.originAttributes.userContextId;
+      }
+    }
+    SHistoryListener.prototype = {
+      QueryInterface: ChromeUtils.generateQI([
+        Ci.nsISHistoryListener,
+        Ci.nsISupportsWeakReference,
+      ]),
+
+      notifySHistoryChanges(index) {
+        if (this._fromIdx <= index) {
+          // If we already know that we need to update history from index N we can ignore any changes
+          // that happened with an element with index larger than N.
+          // Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which means we don't ignore anything
+          // here, and in case of navigation in the history back and forth we use kLastIndex which ignores
+          // only the subsequent navigations, but not any new elements added.
+          return;
+        }
+
+        if (!this._sHistoryChanges) {
+          this.frameLoader.requestSHistoryUpdate(/*aImmediately*/ false);
+          this._sHistoryChanges = true;
+        }
+        this._fromIdx = index;
+        if (this.browser.currentURI && this.browser.ownerGlobal) {
+          this._lastKnownUri = this.browser.currentURI.displaySpec;
+          this._lastKnownBody = this.browser.ownerGlobal.document.body;
+          this._lastKnownUserContextId = this.browser.contentPrincipal.originAttributes.userContextId;
+        }
+      },
+
+      uninstall() {
+        if (this.frameLoader.browsingContext) {
+          let shistory = this.frameLoader.browsingContext.sessionHistory;
+          if (shistory) {
+            shistory.removeSHistoryListener(this);
+          }
+        }
+      },
+
+      OnHistoryNewEntry(newURI, oldIndex) {
+        this.notifySHistoryChanges(oldIndex);
+      },
+
+      OnHistoryGotoIndex() {
+        this.notifySHistoryChanges(kLastIndex);
+      },
+      OnHistoryPurge() {
+        this.notifySHistoryChanges(-1);
+      },
+
+      OnHistoryReload() {
+        this.notifySHistoryChanges(-1);
+        return true;
+      },
+
+      OnHistoryReplaceEntry() {
+        this.notifySHistoryChanges(-1);
+
+        let win = this.browser.ownerGlobal;
+        let tab = win ? win.gBrowser.getTabForBrowser(this.browser) : null;
+        if (tab) {
+          let event = tab.ownerDocument.createEvent("CustomEvent");
+          event.initCustomEvent("SSHistoryReplaceEntry", true, false);
+          tab.dispatchEvent(event);
+        }
+      },
+    };
+
+    let spec = null;
+    if (aBrowser.currentURI) {
+      spec = aBrowser.currentURI.displaySpec;
+    }
+
+    if (!aBrowser.frameLoader) {
+      debug(
+        "addSHistoryListener(), aBrowser.frameLoader doesn't exist" +
+          ",browser.currentURI.displaySpec=" +
+          spec
+      );
+      return;
+    }
+    if (!aBrowser.frameLoader.browsingContext) {
+      debug(
+        "addSHistoryListener(), aBrowser.fl.browsingContext doesn't exists" +
+          ",browser.currentURI.displaySpec=" +
+          spec
+      );
+      return;
+    }
+    if (!aBrowser.frameLoader.browsingContext.sessionHistory) {
+      debug(
+        "addSHistoryListener(), aBrowser.fl.bc.sessionHistory doesn't exists" +
+          ",browser.currentURI.displaySpec=" +
+          spec
+      );
+      return;
+    }
+
+    let listener = new SHistoryListener(aBrowser);
+    this._browserSHistoryListener.set(aBrowser.permanentKey, listener);
+
+    // Collect data if we start with a non-empty shistory.
+    let uri = aBrowser.currentURI.displaySpec;
+    let history = aBrowser.frameLoader.browsingContext.sessionHistory;
+    if (uri != "about:blank" || history.count != 0) {
+      aBrowser.frameLoader.requestSHistoryUpdate(/*aImmediately*/ true);
+    }
+  },
+
+  /**
+   * This listener detects when a page being restored is reloaded. It triggers a
+   * callback and cancels the reload. The callback will send a message to
+   * SessionStore.jsm so that it can restore the content immediately.
+   */
+  addSHistoryListenerForRestore(aBrowser) {
+    function SHistoryListener(browser) {
+      browser.frameLoader.browsingContext.sessionHistory.addSHistoryListener(
+        this
+      );
+      this.browser = browser;
+    }
+    SHistoryListener.prototype = {
+      QueryInterface: ChromeUtils.generateQI([
+        Ci.nsISHistoryListener,
+        Ci.nsISupportsWeakReference,
+      ]),
+
+      uninstall() {
+        let shistory = this.browser.frameLoader.browsingContext.sessionHistory;
+        if (shistory) {
+          shistory.removeSHistoryListener(this);
+        }
+      },
+
+      OnHistoryGotoIndex() {},
+      OnHistoryPurge() {},
+      OnHistoryReplaceEntry() {},
+
+      // This will be called for a pending tab when loadURI(uri) is called where
+      // the given |uri| only differs in the fragment.
+      OnHistoryNewEntry(newURI) {
+        let currentURI = this.browser.currentURI;
+
+        // Ignore new SHistory entries with the same URI as those do not indicate
+        // a navigation inside a document by changing the #hash part of the URL.
+        // We usually hit this when purging session history for browsers.
+        if (currentURI && currentURI.displaySpec == newURI.spec) {
+          return;
+        }
+
+        // Notify ContentSessionStore.jsm to restore on new entry.
+        this.browser.messageManager.sendAsyncMessage(
+          "SessionStore:OnHistoryNewEntry",
+          { uri: newURI.spec }
+        );
+      },
+
+      OnHistoryReload() {
+        // Notify ContentSessionStore.jsm to restore tab contents.
+        this.browser.messageManager.sendAsyncMessage(
+          "SessionStore:OnHistoryReload"
+        );
+        // Cancel the load.
+        return false;
+      },
+    };
+
+    if (
+      !aBrowser.frameLoader ||
+      !aBrowser.frameLoader.browsingContext ||
+      !aBrowser.frameLoader.browsingContext.sessionHistory
+    ) {
+      return;
+    }
+
+    let listener = new SHistoryListener(aBrowser);
+    this._browserSHistoryListenerForRestore.set(
+      aBrowser.permanentKey,
+      listener
+    );
+  },
+
   updateSessionStoreFromTablistener(aBrowser, aData) {
     if (aBrowser.permanentKey == undefined) {
       return;
     }
 
     // Ignore sessionStore update from previous epochs
     if (!this.isCurrentEpoch(aBrowser, aData.epoch)) {
       return;
     }
 
+    let sHistoryChangedInListener = false;
+    let listener = this._browserSHistoryListener.get(aBrowser.permanentKey);
+    if (listener) {
+      sHistoryChangedInListener = listener._sHistoryChanges;
+    }
+
+    if (aData.sHistoryNeeded || sHistoryChangedInListener) {
+      if (!listener) {
+        debug(
+          "updateSessionStoreFromTablistener() with aData.sHistoryNeeded, but no SHlistener. Add again!!!"
+        );
+        this.addSHistoryListener(aBrowser);
+        listener = this._browserSHistoryListener.get(aBrowser.permanentKey);
+      }
+
+      if (listener) {
+        if (!aData.sHistoryNeeded && listener._fromIdx == kNoIndex) {
+          // No shistory changes needed.
+          listener._sHistoryChanges = false;
+        } else {
+          // |browser.frameLoader| might be empty if the browser was already
+          // destroyed and its tab removed. In that case we still have the last
+          // frameLoader we know about to compare.
+          let frameLoader =
+            aBrowser.frameLoader ||
+            this._lastKnownFrameLoader.get(aBrowser.permanentKey);
+          if (
+            frameLoader &&
+            frameLoader.browsingContext &&
+            frameLoader.browsingContext.sessionHistory
+          ) {
+            let uri = aBrowser.currentURI
+              ? aBrowser.currentURI.displaySpec
+              : listener._lastKnownUri;
+            let body = aBrowser.ownerGlobal
+              ? aBrowser.ownerGlobal.document.body
+              : listener._lastKnownBody;
+            let userContextId = aBrowser.contentPrincipal
+              ? aBrowser.contentPrincipal.originAttributes.userContextId
+              : listener._lastKnownUserContextId;
+            aData.data.historychange = SessionHistory.collectFromParent(
+              uri,
+              body,
+              frameLoader.browsingContext.sessionHistory,
+              userContextId,
+              listener._sHistoryChanges ? listener._fromIdx : -1
+            );
+            listener._sHistoryChanges = false;
+            listener._fromIdx = kNoIndex;
+          } else {
+            debug(
+              "updateSessionStoreFromTablistener() with sHistoryNeeded, but no fL.bC.sessionHistory."
+            );
+          }
+        }
+      } else {
+        debug(
+          "updateSessionStoreFromTablistener() with sHistoryNeeded, but no sHlistener."
+        );
+      }
+    }
+
+    if ("sHistoryNeeded" in aData) {
+      delete aData.sHistoryNeeded;
+    }
+
     TabState.update(aBrowser, aData);
     let win = aBrowser.ownerGlobal;
     this.saveStateDelayed(win);
 
     if (aData.flushID) {
       // This is an update kicked off by an async flush request. Notify the
       // TabStateFlusher so that it can finish the request and notify its
       // consumer that's waiting for the flush to be done.
@@ -960,16 +1271,44 @@ var SessionStoreInternal = {
     }
 
     // Ignore messages from previous epochs.
     if (hasEpoch && !this.isCurrentEpoch(browser, data.epoch)) {
       return;
     }
 
     switch (aMessage.name) {
+      case "SessionStore:addSHistoryListener":
+        this.addSHistoryListener(browser);
+        break;
+      case "SessionStore:restoreSHistoryInParent":
+        if (
+          browser.frameLoader &&
+          browser.frameLoader.browsingContext &&
+          browser.frameLoader.browsingContext.sessionHistory
+        ) {
+          let tabData = this._shistoryToRestore.get(browser.permanentKey);
+          if (tabData) {
+            this._shistoryToRestore.delete(browser.permanentKey);
+            SessionHistory.restoreFromParent(
+              browser.frameLoader.browsingContext.sessionHistory,
+              tabData
+            );
+          }
+          this.addSHistoryListenerForRestore(browser);
+        } else {
+          debug(
+            "receive SessionStore:restoreSHistoryInParent: but cannot find sessionHistory from bc."
+          );
+        }
+        browser.messageManager.sendAsyncMessage(
+          "SessionStore:finishRestoreHistory"
+        );
+        break;
+
       case "SessionStore:update":
         // |browser.frameLoader| might be empty if the browser was already
         // destroyed and its tab removed. In that case we still have the last
         // frameLoader we know about to compare.
         let frameLoader =
           browser.frameLoader ||
           this._lastKnownFrameLoader.get(browser.permanentKey);
 
@@ -979,16 +1318,23 @@ var SessionStoreInternal = {
         }
 
         if (aMessage.data.isFinal) {
           // If this the final message we need to resolve all pending flush
           // requests for the given browser as they might have been sent too
           // late and will never respond. If they have been sent shortly after
           // switching a browser's remoteness there isn't too much data to skip.
           TabStateFlusher.resolveAll(browser);
+          let listener = this._browserSHistoryListener.get(
+            browser.permanentKey
+          );
+          if (listener) {
+            listener.uninstall();
+            this._browserSHistoryListener.delete(browser.permanentKey);
+          }
         } else if (aMessage.data.flushID) {
           // This is an update kicked off by an async flush request. Notify the
           // TabStateFlusher so that it can finish the request and notify its
           // consumer that's waiting for the flush to be done.
           TabStateFlusher.resolve(browser, aMessage.data.flushID);
         }
 
         // Ignore messages from <browser> elements that have crashed
@@ -1068,16 +1414,49 @@ var SessionStoreInternal = {
         // Update tab label and icon again after the tab history was updated.
         this.updateTabLabelAndIcon(tab, tabData);
 
         let event = win.document.createEvent("Events");
         event.initEvent("SSTabRestoring", true, false);
         tab.dispatchEvent(event);
         break;
       }
+      case "SessionStore:removeRestoreListener":
+        let listener = this._browserSHistoryListenerForRestore.get(
+          browser.permanentKey
+        );
+        if (listener) {
+          listener.uninstall();
+          this._browserSHistoryListenerForRestore.delete(browser.permanentKey);
+        }
+        break;
+      case "SessionStore:reloadCurrentEntry":
+        let fL =
+          browser.frameLoader ||
+          this._lastKnownFrameLoader.get(browser.permanentKey);
+        if (fL) {
+          if (fL.browsingContext) {
+            if (fL.browsingContext.sessionHistory) {
+              fL.browsingContext.sessionHistory.reloadCurrentEntry();
+            } else {
+              debug(
+                "receive SessionStore:reloadCurrentEntry browser.fL.bC.sessionHistory is null."
+              );
+            }
+          } else {
+            debug(
+              "receive SessionStore:reloadCurrentEntry browser.fL.browsingContext is null."
+            );
+          }
+        } else {
+          debug(
+            "receive SessionStore:reloadCurrentEntry browser.frameLoader is null."
+          );
+        }
+        break;
       case "SessionStore:restoreTabContentStarted":
         if (TAB_STATE_FOR_BROWSER.get(browser) == TAB_STATE_NEEDS_RESTORE) {
           // If a load not initiated by sessionstore was started in a
           // previously pending tab. Mark the tab as no longer pending.
           this.markTabAsRestoring(tab);
         } else if (
           data.reason != RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE
         ) {
@@ -1208,16 +1587,21 @@ var SessionStoreInternal = {
           );
         this.setCurrentEpoch(target, newEpoch);
         target.messageManager.sendAsyncMessage(
           "SessionStore:becomeActiveProcess",
           {
             epoch: newEpoch,
           }
         );
+
+        let listener = this._browserSHistoryListener.get(target.permanentKey);
+        if (listener) {
+          listener.notifySHistoryChanges(-1);
+        }
         break;
       default:
         throw new Error(`unhandled event ${aEvent.type}?`);
     }
     this._clearRestoringWindows();
   },
 
   /**
@@ -5794,16 +6178,21 @@ var SessionStoreInternal = {
             frameLoader.remoteTab.transmitPermissionsForPrincipal(principal);
           }
         } catch (e) {
           console.error(e);
         }
       }
     }
 
+    if (this._shistoryInParent) {
+      // Save the history data for restoring in the parent process.
+      this._shistoryToRestore.set(browser.permanentKey, options.tabData);
+    }
+
     browser.messageManager.sendAsyncMessage(
       "SessionStore:restoreHistory",
       options
     );
 
     if (browser && browser.frameLoader) {
       browser.frameLoader.requestEpochUpdate(options.epoch);
     }
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -12,16 +12,17 @@ JAR_MANIFESTS += ['jar.mn']
 EXTRA_JS_MODULES.sessionstore = [
     'ContentRestore.jsm',
     'ContentSessionStore.jsm',
     'GlobalState.jsm',
     'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
     'RunState.jsm',
     'SessionCookies.jsm',
     'SessionFile.jsm',
+    'SessionHistoryListener.jsm',
     'SessionMigration.jsm',
     'SessionSaver.jsm',
     'SessionStartup.jsm',
     'SessionStore.jsm',
     'SessionWorker.js',
     'SessionWorker.jsm',
     'StartupPerformance.jsm',
     'TabAttributes.jsm',
--- a/browser/components/sessionstore/test/browser_async_remove_tab.js
+++ b/browser/components/sessionstore/test/browser_async_remove_tab.js
@@ -27,22 +27,36 @@ function restoreClosedTabWithValue(rval)
 
   if (index == -1) {
     throw new Error("no closed tab found for given rval");
   }
 
   return ss.undoCloseTab(window, index);
 }
 
-function promiseNewLocationAndHistoryEntryReplaced(browser, snippet) {
+function promiseNewLocationAndHistoryEntryReplaced(tab, snippet) {
+  let browser = tab.linkedBrowser;
+
+  if (Services.prefs.getBoolPref("fission.sessionHistoryInParent", false)) {
+    SpecialPowers.spawn(browser, [snippet], async function(codeSnippet) {
+      // Need to define 'webNavigation' for 'codeSnippet'
+      // eslint-disable-next-line no-unused-vars
+      let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
+      // Evaluate the snippet that changes the location.
+      // eslint-disable-next-line no-eval
+      eval(codeSnippet);
+    });
+    return promiseOnHistoryReplaceEntry(tab);
+  }
+
   return SpecialPowers.spawn(browser, [snippet], async function(codeSnippet) {
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let shistory = webNavigation.sessionHistory.legacySHistory;
 
-    // Evaluate the snippet that the changes the location.
+    // Evaluate the snippet that changes the location.
     // eslint-disable-next-line no-eval
     eval(codeSnippet);
 
     return new Promise(resolve => {
       let listener = {
         OnHistoryReplaceEntry() {
           shistory.removeSHistoryListener(this);
           resolve();
@@ -116,17 +130,17 @@ add_task(async function save_worthy_tabs
   let { tab, r } = await createTabWithRandomValue("about:blank");
   let browser = tab.linkedBrowser;
   ok(browser.isRemoteBrowser, "browser is remote");
 
   // Replace about:blank with a new remote page.
   let snippet =
     'webNavigation.loadURI("https://example.com/",\
     {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()})';
-  await promiseNewLocationAndHistoryEntryReplaced(browser, snippet);
+  await promiseNewLocationAndHistoryEntryReplaced(tab, snippet);
 
   // Remotness shouldn't have changed.
   ok(browser.isRemoteBrowser, "browser is still remote");
 
   // Remove the tab before the update arrives.
   let promise = promiseRemoveTabAndSessionState(tab);
 
   // No tab state worth saving (that we know about yet).
@@ -159,21 +173,20 @@ add_task(async function save_worthy_tabs
   await promise;
 
   // Turns out there is a tab state worth saving.
   ok(isValueInClosedData(r), "closed tab saved");
 });
 
 add_task(async function dont_save_empty_tabs_final() {
   let { tab, r } = await createTabWithRandomValue("https://example.com/");
-  let browser = tab.linkedBrowser;
 
   // Replace the current page with an about:blank entry.
   let snippet = 'content.location.replace("about:blank")';
-  await promiseNewLocationAndHistoryEntryReplaced(browser, snippet);
+  await promiseNewLocationAndHistoryEntryReplaced(tab, snippet);
 
   // Remove the tab before the update arrives.
   let promise = promiseRemoveTabAndSessionState(tab);
 
   // Tab state deemed worth saving (yet).
   ok(isValueInClosedData(r), "closed tab saved");
   await promise;
 
--- a/browser/components/sessionstore/test/browser_async_window_flushing.js
+++ b/browser/components/sessionstore/test/browser_async_window_flushing.js
@@ -33,18 +33,22 @@ add_task(async function test_add_interes
 
   // Send a message that will cause the content to change its location
   // to someplace more interesting. We've disabled auto updates from
   // the browser, so the parent won't know about this
   await SpecialPowers.spawn(browser, [PAGE], async function(newPage) {
     content.location = newPage;
   });
 
-  await promiseContentMessage(browser, "ss-test:OnHistoryReplaceEntry");
-
+  if (Services.prefs.getBoolPref("fission.sessionHistoryInParent", false)) {
+    let tab = newWin.gBrowser.selectedTab;
+    await promiseOnHistoryReplaceEntry(tab);
+  } else {
+    await promiseContentMessage(browser, "ss-test:OnHistoryReplaceEntry");
+  }
   // Clear out the userTypedValue so that the new window looks like
   // it's really not worth restoring.
   browser.userTypedValue = null;
 
   // Once this windowClosed Promise resolves, we should have finished
   // the flush and revisited our decision to put this window into
   // the closed windows array.
   let windowClosed = BrowserTestUtils.windowClosed(newWin);
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -551,16 +551,20 @@ function whenDelayedStartupFinished(aWin
       executeSoon(aCallback);
     }
   }, "browser-delayed-startup-finished");
 }
 function promiseDelayedStartupFinished(aWindow) {
   return new Promise(resolve => whenDelayedStartupFinished(aWindow, resolve));
 }
 
+function promiseOnHistoryReplaceEntry(tab) {
+  return BrowserTestUtils.waitForEvent(tab, "SSHistoryReplaceEntry");
+}
+
 function promiseTabRestored(tab) {
   return BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
 }
 
 function promiseTabRestoring(tab) {
   return BrowserTestUtils.waitForEvent(tab, "SSTabRestoring");
 }
 
--- a/browser/components/translation/content/.eslintrc.js
+++ b/browser/components/translation/content/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: {
     "mozilla/browser-window": true,
   },
 
   plugins: ["mozilla"],
--- a/browser/components/urlbar/.eslintrc.js
+++ b/browser/components/urlbar/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     "mozilla/var-only-at-top-level": "error",
     "require-jsdoc": [
       "error",
       {
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -643,24 +643,25 @@ class UrlbarInput {
           // start a new search so that the offer appears in the view by itself
           // to make it even clearer to the user what's going on.
           this.startQuery();
           return;
         }
 
         if (
           result.heuristic &&
+          this.window.gKeywordURIFixup &&
           UrlbarUtils.looksLikeSingleWordHost(originalUntrimmedValue)
         ) {
-          // The docshell when fixes a single word to a search, also checks the
-          // dns and prompts the user whether they wanted to rather visit that
-          // as a host. On a positive answer, it adds to the domains whitelist
-          // that we use to make decisions. Because here we are directly asking
-          // for a search, bypassing the docshell, we must do it here.
-          // See URIFixupChild.jsm and keyword-uri-fixup.
+          // When fixing a single word to a search, the docShell also checks the
+          // DNS and asks the user whether they would rather visit that as a
+          // host. On a positive answer, it adds to the domain whitelist that
+          // we use to make decisions. Because we are directly asking for a
+          // search here, bypassing the docShell, we need invoke the same check
+          // ourselves. See also URIFixupChild.jsm and keyword-uri-fixup.
           let flags =
             Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
             Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
           if (PrivateBrowsingUtils.isWindowPrivate(this.window)) {
             flags |= Ci.nsIURIFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
           }
           // Don't interrupt the load action in case of errors.
           try {
--- a/browser/extensions/formautofill/.eslintrc.js
+++ b/browser/extensions/formautofill/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // Rules from the mozilla plugin
     "mozilla/balanced-listeners": "error",
     "mozilla/no-aArgs": "error",
     "mozilla/var-only-at-top-level": "error",
--- a/browser/extensions/report-site-issue/.eslintrc.js
+++ b/browser/extensions/report-site-issue/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // Rules from the mozilla plugin
     "mozilla/balanced-listeners": "error",
     "mozilla/no-aArgs": "error",
     "mozilla/var-only-at-top-level": "error",
--- a/browser/tools/mozscreenshots/.eslintrc.js
+++ b/browser/tools/mozscreenshots/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   extends: ["plugin:mozilla/browser-test"],
 
   rules: {
     "no-unused-vars": [
       "error",
--- a/build/clang-plugin/Checks.inc
+++ b/build/clang-plugin/Checks.inc
@@ -33,11 +33,10 @@ CHECK(NonParamInsideFunctionDeclChecker,
 CHECK(NonTrivialTypeInFfiChecker, "non-trivial-type-in-ffi-boundary")
 CHECK(OverrideBaseCallChecker, "override-base-call")
 CHECK(OverrideBaseCallUsageChecker, "override-base-call-usage")
 CHECK(ParamTraitsEnumChecker, "paramtraits-enum")
 CHECK(RefCountedCopyConstructorChecker, "refcounted-copy-constructor")
 CHECK(RefCountedInsideLambdaChecker, "refcounted-inside-lambda")
 CHECK(ScopeChecker, "scope")
 CHECK(SprintfLiteralChecker, "sprintf-literal")
-CHECK(TempRefPtrChecker, "performance-temp-refptr")
 CHECK(TrivialCtorDtorChecker, "trivial-constructor-destructor")
 CHECK(TrivialDtorChecker, "trivial-destructor")
--- a/build/clang-plugin/ChecksIncludes.inc
+++ b/build/clang-plugin/ChecksIncludes.inc
@@ -34,11 +34,10 @@
 #include "NoUsingNamespaceMozillaJavaChecker.h"
 #include "OverrideBaseCallChecker.h"
 #include "OverrideBaseCallUsageChecker.h"
 #include "ParamTraitsEnumChecker.h"
 #include "RefCountedCopyConstructorChecker.h"
 #include "RefCountedInsideLambdaChecker.h"
 #include "ScopeChecker.h"
 #include "SprintfLiteralChecker.h"
-#include "TempRefPtrChecker.h"
 #include "TrivialCtorDtorChecker.h"
 #include "TrivialDtorChecker.h"
--- a/build/clang-plugin/alpha/AlphaChecks.inc
+++ b/build/clang-plugin/alpha/AlphaChecks.inc
@@ -1,8 +1,9 @@
 /* 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/. */
 
 // The list of checker classes that are compatible with clang-tidy and are considered
 // to be in alpha stage development.
 
 // CHECK(AlphaChecker, "alpha-checker")
+CHECK(TempRefPtrChecker, "performance-temp-refptr")
--- a/build/clang-plugin/alpha/AlphaIncludes.inc
+++ b/build/clang-plugin/alpha/AlphaIncludes.inc
@@ -0,0 +1,1 @@
+#include "TempRefPtrChecker.h"
rename from build/clang-plugin/TempRefPtrChecker.cpp
rename to build/clang-plugin/alpha/TempRefPtrChecker.cpp
rename from build/clang-plugin/TempRefPtrChecker.h
rename to build/clang-plugin/alpha/TempRefPtrChecker.h
--- a/build/clang-plugin/alpha/sources.mozbuild
+++ b/build/clang-plugin/alpha/sources.mozbuild
@@ -1,9 +1,10 @@
 # -*- 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/.
 
 HOST_SOURCES += [
     # 'AlphaChecker.cpp',
+    'TempRefPtrChecker.cpp',
 ]
\ No newline at end of file
rename from build/clang-plugin/tests/TestTempRefPtr.cpp
rename to build/clang-plugin/alpha/tests/TestTempRefPtr.cpp
--- a/build/clang-plugin/alpha/tests/sources.mozbuild
+++ b/build/clang-plugin/alpha/tests/sources.mozbuild
@@ -1,9 +1,10 @@
 # -*- 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/.
 
 SOURCES += [
     # 'AlphaTest.cpp',
+    'TestTempRefPtr.cpp',
 ]
\ No newline at end of file
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -38,17 +38,16 @@ HOST_SOURCES += [
     'NoUsingNamespaceMozillaJavaChecker.cpp',
     'OverrideBaseCallChecker.cpp',
     'OverrideBaseCallUsageChecker.cpp',
     'ParamTraitsEnumChecker.cpp',
     'RefCountedCopyConstructorChecker.cpp',
     'RefCountedInsideLambdaChecker.cpp',
     'ScopeChecker.cpp',
     'SprintfLiteralChecker.cpp',
-    'TempRefPtrChecker.cpp',
     'TrivialCtorDtorChecker.cpp',
     'TrivialDtorChecker.cpp',
     'VariableUsageHelpers.cpp',
 ]
 
 # Ideally, we wouldn't have compile-time choices wrt checkes. bug 1617153.
 if CONFIG['OS_ARCH'] == 'WINNT':
     HOST_DEFINES['TARGET_IS_WINDOWS'] = True
--- a/build/clang-plugin/tests/TestCanRunScript.cpp
+++ b/build/clang-plugin/tests/TestCanRunScript.cpp
@@ -370,17 +370,21 @@ struct DisallowConstNonRefPtrMemberArgs 
     mRefCounted->method_test(); // expected-error {{arguments must all be strong refs or caller's parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument).  'mRefCounted' is neither.}}
   }
   MOZ_CAN_RUN_SCRIPT void bar() {
     test2(mRefCounted); // expected-error {{arguments must all be strong refs or caller's parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument).  'mRefCounted' is neither.}}
   }
 };
 
 MOZ_CAN_RUN_SCRIPT void test_temporary_1() {
+#ifdef MOZ_CLANG_PLUGIN_ALPHA
   RefPtr<RefCountedBase>(new RefCountedBase())->method_test(); // expected-warning {{performance issue: temporary 'RefPtr<RefCountedBase>' is only dereferenced here once which involves short-lived AddRef/Release calls}}
+#else
+  RefPtr<RefCountedBase>(new RefCountedBase())->method_test();
+#endif
 }
 
 MOZ_CAN_RUN_SCRIPT void test_temporary_2() {
   test_ref(*RefPtr<RefCountedBase>(new RefCountedBase()));
 }
 
 struct WeakSmartPtr {
   RefCountedBase* member;
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -44,17 +44,16 @@ SOURCES += [
     'TestOverrideBaseCall.cpp',
     'TestOverrideBaseCallAnnotation.cpp',
     'TestParamTraitsEnum.cpp',
     'TestRefCountedCopyConstructor.cpp',
     'TestSprintfLiteral.cpp',
     'TestStackClass.cpp',
     'TestStaticLocalClass.cpp',
     'TestTemporaryClass.cpp',
-    'TestTempRefPtr.cpp',
     'TestTrivialCtorDtor.cpp',
     'TestTrivialDtor.cpp',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
         'TestFopenUsage.cpp',
         'TestLoadLibraryUsage.cpp',
--- a/devtools/.eslintrc.js
+++ b/devtools/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   plugins: ["react"],
   globals: {
     exports: true,
     isWorker: true,
     loader: true,
--- a/devtools/.eslintrc.mochitests.js
+++ b/devtools/.eslintrc.mochitests.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 // Parent config file for all devtools browser mochitest files.
 module.exports = {
   "extends": [
     "plugin:mozilla/browser-test"
   ],
   // All globals made available in the test environment.
   "globals": {
     "DevToolsUtils": true,
--- a/devtools/.eslintrc.xpcshell.js
+++ b/devtools/.eslintrc.xpcshell.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 // Parent config file for all devtools xpcshell files.
 module.exports = {
   "extends": [
     "plugin:mozilla/xpcshell-test"
   ],
   "rules": {
     // Allow non-camelcase so that run_test doesn't produce a warning.
     "camelcase": "off",
--- a/devtools/client/.eslintrc.js
+++ b/devtools/client/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: { browser: true },
   globals: {
     define: true,
   },
   rules: {
--- a/devtools/client/accessibility/accessibility-proxy.js
+++ b/devtools/client/accessibility/accessibility-proxy.js
@@ -17,16 +17,19 @@ const PARENT_ACCESSIBILITY_EVENTS = [
 /**
  * Component responsible for tracking all Accessibility fronts in parent and
  * content processes.
  */
 class AccessibilityProxy {
   constructor(toolbox) {
     this.toolbox = toolbox;
 
+    this.accessibilityEventsMap = new Map();
+    this.accessibleWalkerEventsMap = new Map();
+
     this.audit = this.audit.bind(this);
     this.disableAccessibility = this.disableAccessibility.bind(this);
     this.enableAccessibility = this.enableAccessibility.bind(this);
     this.getAccessibilityTreeRoot = this.getAccessibilityTreeRoot.bind(this);
     this.resetAccessiblity = this.resetAccessiblity.bind(this);
     this.startListeningForAccessibilityEvents = this.startListeningForAccessibilityEvents.bind(
       this
     );
@@ -34,26 +37,27 @@ class AccessibilityProxy {
       this
     );
     this.stopListeningForAccessibilityEvents = this.stopListeningForAccessibilityEvents.bind(
       this
     );
     this.stopListeningForLifecycleEvents = this.stopListeningForLifecycleEvents.bind(
       this
     );
-  }
-
-  get target() {
-    return this.toolbox.target;
+    this._onTargetAvailable = this._onTargetAvailable.bind(this);
   }
 
   get enabled() {
     return this.accessibilityFront && this.accessibilityFront.enabled;
   }
 
+  get currentTarget() {
+    return this._currentTarget;
+  }
+
   /**
    * Perform an audit for a given filter.
    *
    * @param  {String} filter
    *         Type of an audit to perform.
    * @param  {Function} onError
    *         Audit error callback.
    * @param  {Function} onProgress
@@ -62,52 +66,54 @@ class AccessibilityProxy {
    *         Audit completion callback.
    *
    * @return {Promise}
    *         Resolves when the audit for a top document, that the walker
    *         traverses, completes.
    */
   audit(filter, onError, onProgress, onCompleted) {
     return new Promise(resolve => {
+      const front = this.accessibleWalkerFront;
       const types =
         filter === FILTERS.ALL ? Object.values(AUDIT_TYPE) : [filter];
       const auditEventHandler = ({ type, ancestries, progress }) => {
         switch (type) {
           case "error":
-            this.accessibleWalkerFront.off("audit-event", auditEventHandler);
+            this._off(front, "audit-event", auditEventHandler);
             onError();
             resolve();
             break;
           case "completed":
-            this.accessibleWalkerFront.off("audit-event", auditEventHandler);
+            this._off(front, "audit-event", auditEventHandler);
             onCompleted(ancestries);
             resolve();
             break;
           case "progress":
             onProgress(progress);
             break;
           default:
             break;
         }
       };
 
-      this.accessibleWalkerFront.on("audit-event", auditEventHandler);
-      this.accessibleWalkerFront.startAudit({ types });
+      this._on(front, "audit-event", auditEventHandler);
+      front.startAudit({ types });
     });
   }
 
   /**
    * Stop picking and remove all walker listeners.
    */
   async cancelPick(onHovered, onPicked, onPreviewed, onCanceled) {
-    await this.accessibleWalkerFront.cancelPick();
-    this.accessibleWalkerFront.off("picker-accessible-hovered", onHovered);
-    this.accessibleWalkerFront.off("picker-accessible-picked", onPicked);
-    this.accessibleWalkerFront.off("picker-accessible-previewed", onPreviewed);
-    this.accessibleWalkerFront.off("picker-accessible-canceled", onCanceled);
+    const front = this.accessibleWalkerFront;
+    await front.cancelPick();
+    this._off(front, "picker-accessible-hovered", onHovered);
+    this._off(front, "picker-accessible-picked", onPicked);
+    this._off(front, "picker-accessible-previewed", onPreviewed);
+    this._off(front, "picker-accessible-canceled", onCanceled);
   }
 
   async disableAccessibility() {
     // Accessibility service is shut down using the parent accessibility front.
     // That, in turn, shuts down accessibility service in all content processes.
     // We need to wait until that happens to be sure platform  accessibility is
     // fully disabled.
     // TODO: Remove this after Firefox 75 and use parentAccessibilityFront.
@@ -147,111 +153,207 @@ class AccessibilityProxy {
   }
 
   /**
    * Start picking and add walker listeners.
    * @param  {Boolean} doFocus
    *         If true, move keyboard focus into content.
    */
   async pick(doFocus, onHovered, onPicked, onPreviewed, onCanceled) {
-    this.accessibleWalkerFront.on("picker-accessible-hovered", onHovered);
-    this.accessibleWalkerFront.on("picker-accessible-picked", onPicked);
-    this.accessibleWalkerFront.on("picker-accessible-previewed", onPreviewed);
-    this.accessibleWalkerFront.on("picker-accessible-canceled", onCanceled);
-    await this.accessibleWalkerFront.pick(doFocus);
+    const front = this.accessibleWalkerFront;
+    this._on(front, "picker-accessible-hovered", onHovered);
+    this._on(front, "picker-accessible-picked", onPicked);
+    this._on(front, "picker-accessible-previewed", onPreviewed);
+    this._on(front, "picker-accessible-canceled", onCanceled);
+    await front.pick(doFocus);
   }
 
   async resetAccessiblity() {
     const { enabled } = this.accessibilityFront;
     const { canBeEnabled, canBeDisabled } =
       this.parentAccessibilityFront || this.accessibilityFront;
     return { enabled, canBeDisabled, canBeEnabled };
   }
 
   startListeningForAccessibilityEvents(eventMap) {
     for (const [type, listener] of Object.entries(eventMap)) {
-      this.accessibleWalkerFront.on(type, listener);
+      this._on(this.accessibleWalkerFront, type, listener);
     }
   }
 
   stopListeningForAccessibilityEvents(eventMap) {
     for (const [type, listener] of Object.entries(eventMap)) {
-      this.accessibleWalkerFront.off(type, listener);
+      this._off(this.accessibleWalkerFront, type, listener);
     }
   }
 
   startListeningForLifecycleEvents(eventMap) {
-    for (let [type, listeners] of Object.entries(eventMap)) {
-      listeners = Array.isArray(listeners) ? listeners : [listeners];
+    for (const [type, listeners] of Object.entries(eventMap)) {
       const accessibilityFront =
         // TODO: Remove parentAccessibilityFront check after Firefox 75.
         this.parentAccessibilityFront &&
         PARENT_ACCESSIBILITY_EVENTS.includes(type)
           ? this.parentAccessibilityFront
           : this.accessibilityFront;
-      for (const listener of listeners) {
-        accessibilityFront.on(type, listener);
-      }
+      this._on(accessibilityFront, type, listeners);
     }
   }
 
   stopListeningForLifecycleEvents(eventMap) {
-    for (let [type, listeners] of Object.entries(eventMap)) {
-      listeners = Array.isArray(listeners) ? listeners : [listeners];
+    for (const [type, listeners] of Object.entries(eventMap)) {
       // TODO: Remove parentAccessibilityFront check after Firefox 75.
       const accessibilityFront =
         this.parentAccessibilityFront &&
         PARENT_ACCESSIBILITY_EVENTS.includes(type)
           ? this.parentAccessibilityFront
           : this.accessibilityFront;
-      for (const listener of listeners) {
-        accessibilityFront.off(type, listener);
-      }
+      this._off(accessibilityFront, type, listeners);
     }
   }
 
-  async ensureReady() {
-    const { mainRoot } = this.target.client;
+  /**
+   * Part of the proxy initialization only needs to be done when the accessibility panel starts.
+   * To avoid performance issues, the panel will explicitly call this method every time a new
+   * target becomes available.
+   */
+  async initializeProxyForPanel(targetFront) {
+    await this._updateTarget(targetFront);
+
+    const { mainRoot } = this._currentTarget.client;
     if (await mainRoot.hasActor("parentAccessibility")) {
       this.parentAccessibilityFront = await mainRoot.getFront(
         "parentaccessibility"
       );
     }
 
     this.accessibleWalkerFront = this.accessibilityFront.accessibleWalkerFront;
     this.simulatorFront = this.accessibilityFront.simulatorFront;
     if (this.simulatorFront) {
       this.simulate = types => this.simulatorFront.simulate({ types });
     }
+
+    // Move front listeners to new front.
+    for (const [type, listeners] of this.accessibilityEventsMap.entries()) {
+      const accessibilityFront =
+        // TODO: Remove parentAccessibilityFront check after Firefox 75.
+        this.parentAccessibilityFront &&
+        PARENT_ACCESSIBILITY_EVENTS.includes(type)
+          ? this.parentAccessibilityFront
+          : this.accessibilityFront;
+      for (const listener of listeners) {
+        accessibilityFront.on(type, listener);
+      }
+    }
+
+    for (const [type, listeners] of this.accessibleWalkerEventsMap.entries()) {
+      for (const listener of listeners) {
+        this.accessibleWalkerFront.on(type, listener);
+      }
+    }
   }
 
   async initialize() {
     try {
-      this.accessibilityFront = await this.target.getFront("accessibility");
+      await this.toolbox.targetList.watchTargets(
+        [this.toolbox.targetList.TYPES.FRAME],
+        this._onTargetAvailable
+      );
+      return true;
+    } catch (e) {
+      // toolbox may be destroyed during this step.
+      return false;
+    }
+  }
+
+  destroy() {
+    this.toolbox.targetList.unwatchTargets(
+      [this.toolbox.targetList.TYPES.FRAME],
+      this._onTargetAvailable
+    );
+
+    this.accessibilityEventsMap = null;
+    this.accessibleWalkerEventsMap = null;
+
+    this.accessibilityFront = null;
+    this.parentAccessibilityFront = null;
+    this.accessibleWalkerFront = null;
+    this.simulatorFront = null;
+    this.simulate = null;
+    this.toolbox = null;
+  }
+
+  _getEventsMap(front) {
+    return front === this.accessibleWalkerFront
+      ? this.accessibleWalkerEventsMap
+      : this.accessibilityEventsMap;
+  }
+
+  async _onTargetAvailable({ targetFront, isTopLevel }) {
+    if (isTopLevel) {
+      await this._updateTarget(targetFront);
+    }
+  }
+
+  _on(front, type, listeners) {
+    listeners = Array.isArray(listeners) ? listeners : [listeners];
+
+    for (const listener of listeners) {
+      front.on(type, listener);
+    }
+
+    const eventsMap = this._getEventsMap(front);
+    const eventsMapListeners = eventsMap.has(type)
+      ? [...eventsMap.get(type), ...listeners]
+      : listeners;
+    eventsMap.set(type, eventsMapListeners);
+  }
+
+  _off(front, type, listeners) {
+    listeners = Array.isArray(listeners) ? listeners : [listeners];
+
+    for (const listener of listeners) {
+      front.off(type, listener);
+    }
+
+    const eventsMap = this._getEventsMap(front);
+    if (!eventsMap.has(type)) {
+      return;
+    }
+
+    const eventsMapListeners = eventsMap
+      .get(type)
+      .filter(l => !listeners.includes(l));
+    if (eventsMapListeners.length) {
+      eventsMap.set(type, eventsMapListeners);
+    } else {
+      eventsMap.delete(type);
+    }
+  }
+
+  async _updateTarget(targetFront) {
+    if (this._updatePromise && this._currentTarget === targetFront) {
+      return this._updatePromise;
+    }
+
+    this._currentTarget = targetFront;
+
+    this._updatePromise = (async () => {
+      this.accessibilityFront = await this._currentTarget.getFront(
+        "accessibility"
+      );
       // Finalize accessibility front initialization. See accessibility front
       // bootstrap method description.
       await this.accessibilityFront.bootstrap();
       this.supports = {};
       // To add a check for backward compatibility add something similar to the
       // example below:
       //
       // [this.supports.simulation] = await Promise.all([
       //   // Please specify the version of Firefox when the feature was added.
-      //   this.target.actorHasMethod("accessibility", "getSimulator"),
+      //   this._currentTarget.actorHasMethod("accessibility", "getSimulator"),
       // ]);
-      return true;
-    } catch (e) {
-      // toolbox may be destroyed during this step.
-      return false;
-    }
-  }
+    })();
 
-  destroy() {
-    this.accessibilityFront = null;
-    this.parentAccessibilityFront = null;
-    this.accessibleWalkerFront = null;
-    this.simulatorFront = null;
-    this.simulate = null;
-    this.toolbox = null;
+    return this._updatePromise;
   }
 }
 
 exports.AccessibilityProxy = AccessibilityProxy;
--- a/devtools/client/accessibility/panel.js
+++ b/devtools/client/accessibility/panel.js
@@ -35,16 +35,17 @@ const EVENTS = {
  * displays current relevant accessible details.
  */
 function AccessibilityPanel(iframeWindow, toolbox, startup) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this.startup = startup;
 
   this.onTabNavigated = this.onTabNavigated.bind(this);
+  this.onTargetAvailable = this.onTargetAvailable.bind(this);
   this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
   this.onNewAccessibleFrontSelected = this.onNewAccessibleFrontSelected.bind(
     this
   );
   this.onAccessibilityInspectorUpdated = this.onAccessibilityInspectorUpdated.bind(
     this
   );
   this.updateA11YServiceDurationTimer = this.updateA11YServiceDurationTimer.bind(
@@ -68,34 +69,38 @@ AccessibilityPanel.prototype = {
     let resolver;
     this._opening = new Promise(resolve => {
       resolver = resolve;
     });
 
     this._telemetry = new Telemetry();
     this.panelWin.gTelemetry = this._telemetry;
 
-    this.target.on("navigate", this.onTabNavigated);
     this._toolbox.on("select", this.onPanelVisibilityChange);
 
     this.panelWin.EVENTS = EVENTS;
     EventEmitter.decorate(this.panelWin);
     this.panelWin.on(
       EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
       this.onNewAccessibleFrontSelected
     );
     this.panelWin.on(
       EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
       this.onAccessibilityInspectorUpdated
     );
 
     this.shouldRefresh = true;
 
     await this.startup.initAccessibility();
-    await this.accessibilityProxy.ensureReady();
+
+    await this._toolbox.targetList.watchTargets(
+      [this._toolbox.targetList.TYPES.FRAME],
+      this.onTargetAvailable
+    );
+
     this.picker = new Picker(this);
     this.fluentBundles = await this.createFluentBundles();
 
     this.updateA11YServiceDurationTimer();
     this.accessibilityProxy.startListeningForLifecycleEvents({
       init: [this.updateA11YServiceDurationTimer, this.forceUpdatePickerButton],
       shutdown: [
         this.updateA11YServiceDurationTimer,
@@ -142,16 +147,27 @@ AccessibilityPanel.prototype = {
    * refreshed immediatelly if it's currently selected or lazily when the user
    * actually selects it.
    */
   onTabNavigated() {
     this.shouldRefresh = true;
     this._opening.then(() => this.refresh());
   },
 
+  async onTargetAvailable({ targetFront, isTopLevel, isTargetSwitching }) {
+    if (isTopLevel) {
+      await this.accessibilityProxy.initializeProxyForPanel(targetFront);
+      this.accessibilityProxy.currentTarget.on("navigate", this.onTabNavigated);
+    }
+
+    if (isTargetSwitching) {
+      this.onTabNavigated();
+    }
+  },
+
   /**
    * Make sure the panel is refreshed (if needed) when it's selected.
    */
   onPanelVisibilityChange() {
     this._opening.then(() => this.refresh());
   },
 
   refresh() {
@@ -261,29 +277,30 @@ AccessibilityPanel.prototype = {
 
   /**
    * Return true if the Accessibility panel is currently selected.
    */
   get isVisible() {
     return this._toolbox.currentToolId === "accessibility";
   },
 
-  get target() {
-    return this._toolbox.target;
-  },
-
   destroy() {
     if (this._destroyed) {
       return;
     }
     this._destroyed = true;
 
+    this._toolbox.targetList.unwatchTargets(
+      [this._toolbox.targetList.TYPES.FRAME],
+      this.onTargetAvailable
+    );
+
     this.postContentMessage("destroy");
 
-    this.target.off("navigate", this.onTabNavigated);
+    this.accessibilityProxy.currentTarget.off("navigate", this.onTabNavigated);
     this._toolbox.off("select", this.onPanelVisibilityChange);
 
     this.panelWin.off(
       EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
       this.onNewAccessibleFrontSelected
     );
     this.panelWin.off(
       EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
--- a/devtools/client/accessibility/test/browser/browser.ini
+++ b/devtools/client/accessibility/test/browser/browser.ini
@@ -9,16 +9,17 @@ support-files =
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
 
 [browser_accessibility_context_menu_browser.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533184
 [browser_accessibility_context_menu_inspector.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533484
+[browser_accessibility_fission_switch_target.js]
 [browser_accessibility_mutations.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533534
 [browser_accessibility_panel_highlighter.js]
 [browser_accessibility_panel_highlighter_multi_tab.js]
 skip-if = (os == 'linux' && debug && bits == 64) # Bug 1511247
 [browser_accessibility_panel_toolbar_checks.js]
 [browser_accessibility_panel_toolbar_pref_scroll.js]
 [browser_accessibility_relation_navigation.js]
--- a/devtools/client/accessibility/test/browser/browser_accessibility_context_menu_browser.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_context_menu_browser.js
@@ -34,16 +34,19 @@ add_task(async function testNoShowAccess
   contextMenu.hidePopup();
   gBrowser.removeCurrentTab();
 });
 
 addA11YPanelTask(
   "Test show accessibility properties context menu in browser.",
   TEST_URI,
   async function({ panel, toolbox, browser }) {
+    // Load the inspector to ensure it to use in this test.
+    await toolbox.loadTool("inspector");
+
     const headerSelector = "#h1";
 
     const contextMenu = document.getElementById("contentAreaContextMenu");
     const awaitPopupShown = BrowserTestUtils.waitForEvent(
       contextMenu,
       "popupshown"
     );
     await BrowserTestUtils.synthesizeMouse(
--- a/devtools/client/accessibility/test/browser/browser_accessibility_context_menu_inspector.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_context_menu_inspector.js
@@ -78,16 +78,19 @@ async function checkAccessibleObjectSele
     "Selected row is visible."
   );
 }
 
 addA11YPanelTask(
   "Test show accessibility properties context menu.",
   TEST_URI,
   async function testShowAccessibilityPropertiesContextMenu(env) {
+    // Load the inspector to ensure it to use in this test.
+    await env.toolbox.loadTool("inspector");
+
     let allMenuItems = await openContextMenuForNode(env);
     let showA11YPropertiesNode = checkShowA11YPropertiesNode(
       allMenuItems,
       true
     );
 
     allMenuItems = await openContextMenuForNode(env, "#h1");
     showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, false);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_fission_switch_target.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test switching for the top-level target.
+
+const MAIN_PROCESS_URL = "about:robots";
+const MAIN_PROCESS_EXPECTED = [
+  {
+    expected: {
+      sidebar: {
+        name: "Gort! Klaatu barada nikto!",
+        role: "document",
+      },
+    },
+  },
+];
+
+const CONTENT_PROCESS_URL = buildURL(`<title>Test page</title>`);
+const CONTENT_PROCESS_EXPECTED = [
+  {
+    expected: {
+      sidebar: {
+        name: "Test page",
+        role: "document",
+      },
+    },
+  },
+];
+
+add_task(async () => {
+  await pushPref("devtools.target-switching.enabled", true);
+
+  info(
+    "Open a test page running on the content process and accessibility panel"
+  );
+  const env = await addTestTab(CONTENT_PROCESS_URL);
+  await runA11yPanelTests(CONTENT_PROCESS_EXPECTED, env);
+
+  info("Navigate to a page running on the main process");
+  await navigateTo(MAIN_PROCESS_URL);
+  await runA11yPanelTests(MAIN_PROCESS_EXPECTED, env);
+
+  info("Back to a page running on the content process");
+  await navigateTo(CONTENT_PROCESS_URL);
+  await runA11yPanelTests(CONTENT_PROCESS_EXPECTED, env);
+
+  await disableAccessibilityInspector(env);
+});
--- a/devtools/client/accessibility/test/browser/browser_accessibility_reload.js
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_reload.js
@@ -54,17 +54,17 @@ const tests = [
       sidebar: {
         name: "Accessibility Panel Test",
         role: "document",
       },
     },
   },
   {
     desc: "Reload the page.",
-    setup: async ({ panel }) => reload(panel.target),
+    setup: async ({ panel }) => reload(panel.accessibilityProxy.currentTarget),
     expected: {
       tree: [
         {
           role: "document",
           name: `"Accessibility Panel Test"`,
         },
       ],
       sidebar: {
--- a/devtools/client/accessibility/test/browser/head.js
+++ b/devtools/client/accessibility/test/browser/head.js
@@ -135,20 +135,16 @@ async function addTestTab(url) {
   await waitUntilState(
     store,
     state =>
       state.accessibles.size === 1 &&
       state.details.accessible &&
       state.details.accessible.role === "document"
   );
 
-  // Wait for inspector load here to avoid protocol errors on shutdown, since
-  // accessibility panel test can be too fast.
-  await panel._toolbox.loadTool("inspector");
-
   return {
     tab,
     browser: tab.linkedBrowser,
     panel,
     win,
     toolbox: panel._toolbox,
     doc,
     store,
--- a/devtools/client/debugger/.prettierrc.js
+++ b/devtools/client/debugger/.prettierrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 const fs = require("fs");
 
 module.exports = {
   ...JSON.parse(fs.readFileSync(__dirname + "/../../../.prettierrc")),
   overrides: [
     {
       files: [
         "src/**/*.js",
--- a/devtools/client/dom/.eslintrc.js
+++ b/devtools/client/dom/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   globals: {
     XMLHttpRequest: true,
     window: true,
     define: true,
     addEventListener: true,
--- a/devtools/client/inspector/.eslintrc.js
+++ b/devtools/client/inspector/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   // Extend from the devtools eslintrc.
   extends: "../../.eslintrc.js",
 
   rules: {
     // The inspector is being migrated to HTML and cleaned of
--- a/devtools/client/jsonview/.eslintrc.js
+++ b/devtools/client/jsonview/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   globals: {
     define: true,
     document: true,
     window: true,
     CustomEvent: true,
--- a/devtools/client/memory/.eslintrc.js
+++ b/devtools/client/memory/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: {
     browser: true,
   },
   globals: {
     d3: true,
--- a/devtools/client/performance-new/.eslintrc.js
+++ b/devtools/client/performance-new/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // Props are checked by TypeScript, so we don't need dynamic type checking here.
     "react/prop-types": "off",
   },
 };
--- a/devtools/client/shared/components/.eslintrc.js
+++ b/devtools/client/shared/components/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   globals: {
     define: true,
   },
 };
--- a/devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js
+++ b/devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   extends: "../../../../../.eslintrc.mochitests.js",
   globals: {
     run_test: true,
     run_next_test: true,
--- a/devtools/client/shared/sourceeditor/.eslintrc.js
+++ b/devtools/client/shared/sourceeditor/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   // Extend from the devtools eslintrc.
   extends: "../../../.eslintrc.js",
 
   rules: {
     // The inspector is being migrated to HTML and cleaned of
--- a/devtools/shared/.eslintrc.js
+++ b/devtools/shared/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // See bug 1288147, the devtools front-end wants to be able to run in
     // content privileged windows, where ownerGlobal doesn't exist.
     "mozilla/use-ownerGlobal": "off",
   },
--- a/devtools/shared/adb/xpcshell/.eslintrc.js
+++ b/devtools/shared/adb/xpcshell/.eslintrc.js
@@ -1,5 +1,9 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   extends: "../../../.eslintrc.xpcshell.js",
 };
--- a/devtools/shared/performance/xpcshell/.eslintrc.js
+++ b/devtools/shared/performance/xpcshell/.eslintrc.js
@@ -1,6 +1,10 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
   extends: "../../../.eslintrc.xpcshell.js",
 };
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -5,17 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDocShell.h"
 #include "nsDSURIContentListener.h"
 #include "nsIChannel.h"
 #include "nsServiceManagerUtils.h"
 #include "nsDocShellCID.h"
 #include "nsIWebNavigationInfo.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
 #include "mozilla/dom/Document.h"
+#include "mozilla/Unused.h"
 #include "nsError.h"
 #include "nsContentSecurityManager.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsGlobalWindowOuter.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIMultiPartChannel.h"
 #include "nsWebNavigationInfo.h"
 
@@ -36,33 +38,62 @@ MaybeCloseWindowHelper::MaybeCloseWindow
 
 MaybeCloseWindowHelper::~MaybeCloseWindowHelper() {}
 
 void MaybeCloseWindowHelper::SetShouldCloseWindow(bool aShouldCloseWindow) {
   mShouldCloseWindow = aShouldCloseWindow;
 }
 
 BrowsingContext* MaybeCloseWindowHelper::MaybeCloseWindow() {
-  if (mShouldCloseWindow) {
-    // Reset the window context to the opener window so that the dependent
-    // dialogs have a parent
-    RefPtr<BrowsingContext> opener = mBrowsingContext->GetOpener();
+  if (!mShouldCloseWindow) {
+    return mBrowsingContext;
+  }
+
+  // This method should not be called more than once, but it's better to avoid
+  // closing the current window again.
+  mShouldCloseWindow = false;
 
-    if (opener && !opener->IsDiscarded()) {
-      mBCToClose = mBrowsingContext;
-      mBrowsingContext = opener;
+  // Reset the window context to the opener window so that the dependent
+  // dialogs have a parent
+  RefPtr<BrowsingContext> newBC = ChooseNewBrowsingContext(mBrowsingContext);
+
+  if (newBC != mBrowsingContext && newBC && !newBC->IsDiscarded()) {
+    mBCToClose = mBrowsingContext;
+    mBrowsingContext = newBC;
+
+    // Now close the old window.  Do it on a timer so that we don't run
+    // into issues trying to close the window before it has fully opened.
+    NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
+    NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 0,
+                            nsITimer::TYPE_ONE_SHOT);
+  }
 
-      // Now close the old window.  Do it on a timer so that we don't run
-      // into issues trying to close the window before it has fully opened.
-      NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
-      NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 0,
-                              nsITimer::TYPE_ONE_SHOT);
-    }
+  return mBrowsingContext;
+}
+
+already_AddRefed<BrowsingContext>
+MaybeCloseWindowHelper::ChooseNewBrowsingContext(BrowsingContext* aBC) {
+  RefPtr<BrowsingContext> bc = aBC;
+
+  RefPtr<BrowsingContext> opener = bc->GetOpener();
+  if (opener && !opener->IsDiscarded()) {
+    return opener.forget();
   }
-  return mBrowsingContext;
+
+  if (!XRE_IsParentProcess()) {
+    return bc.forget();
+  }
+
+  CanonicalBrowsingContext* cbc = CanonicalBrowsingContext::Cast(aBC);
+  RefPtr<WindowGlobalParent> wgp = cbc->GetEmbedderWindowGlobal();
+  if (!wgp) {
+    return bc.forget();
+  }
+
+  return do_AddRef(wgp->BrowsingContext());
 }
 
 NS_IMETHODIMP
 MaybeCloseWindowHelper::Notify(nsITimer* timer) {
   NS_ASSERTION(mBCToClose, "No window to close after timer fired");
 
   mBCToClose->Close(CallerType::System, IgnoreErrors());
   mBCToClose = nullptr;
@@ -130,17 +161,17 @@ nsDSURIContentListener::DoContent(const 
       // logging to console happens within AllowTopLevelNavigationToDataURI
       aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
       *aAbortProcess = true;
       // close the window since the navigation to a data URI was blocked
       if (mDocShell && mDocShell->GetBrowsingContext()) {
         RefPtr<MaybeCloseWindowHelper> maybeCloseWindowHelper =
             new MaybeCloseWindowHelper(mDocShell->GetBrowsingContext());
         maybeCloseWindowHelper->SetShouldCloseWindow(true);
-        maybeCloseWindowHelper->MaybeCloseWindow();
+        Unused << maybeCloseWindowHelper->MaybeCloseWindow();
       }
       return NS_OK;
     }
   }
 
   if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI) {
     // XXX: Why does this not stop the content too?
     mDocShell->Stop(nsIWebNavigation::STOP_NETWORK);
--- a/docshell/base/nsDSURIContentListener.h
+++ b/docshell/base/nsDSURIContentListener.h
@@ -12,38 +12,44 @@
 #include "nsWeakReference.h"
 #include "nsITimer.h"
 
 class nsDocShell;
 class nsIInterfaceRequestor;
 class nsIWebNavigationInfo;
 class nsPIDOMWindowOuter;
 
-// Helper Class to eventually close an already openend window
+// Helper Class to eventually close an already opened window
 class MaybeCloseWindowHelper final : public nsITimerCallback {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
   explicit MaybeCloseWindowHelper(
       mozilla::dom::BrowsingContext* aContentContext);
 
   /**
-   * Closes the provided window async (if mShouldCloseWindow is true)
-   * and returns its opener if the window was just opened. Otherwise
-   * returns the BrowsingContext provided in the constructor.
+   * Closes the provided window async (if mShouldCloseWindow is true) and
+   * returns a valid browsingContext to be used instead as parent for dialogs or
+   * similar things.
+   * In case mShouldCloseWindow is true, the final browsing context will be the
+   * a valid new chrome window to use. It can be the opener, or the opener's
+   * top, or the top chrome window.
    */
   mozilla::dom::BrowsingContext* MaybeCloseWindow();
 
   void SetShouldCloseWindow(bool aShouldCloseWindow);
 
  protected:
   ~MaybeCloseWindowHelper();
 
  private:
+  already_AddRefed<mozilla::dom::BrowsingContext> ChooseNewBrowsingContext(
+      mozilla::dom::BrowsingContext* aBC);
+
   /**
    * The dom window associated to handle content.
    */
   RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
 
   /**
    * Used to close the window on a timer, to avoid any exceptions that are
    * thrown if we try to close the window before it's fully loaded.
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -5928,16 +5928,39 @@ nsresult Document::GetSrcdocData(nsAStri
 Nullable<WindowProxyHolder> Document::GetDefaultView() const {
   nsPIDOMWindowOuter* win = GetWindow();
   if (!win) {
     return nullptr;
   }
   return WindowProxyHolder(win->GetBrowsingContext());
 }
 
+nsIContent* Document::GetUnretargetedFocusedContent() const {
+  nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
+  if (!window) {
+    return nullptr;
+  }
+  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+  nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
+      window, nsFocusManager::eOnlyCurrentWindow,
+      getter_AddRefs(focusedWindow));
+  if (!focusedContent) {
+    return nullptr;
+  }
+  // be safe and make sure the element is from this document
+  if (focusedContent->OwnerDoc() != this) {
+    return nullptr;
+  }
+
+  if (focusedContent->ChromeOnlyAccess()) {
+    return focusedContent->FindFirstNonChromeOnlyAccessContent();
+  }
+  return focusedContent;
+}
+
 Element* Document::GetActiveElement() {
   // Get the focused element.
   Element* focusedElement = GetRetargetedFocusedElement();
   if (focusedElement) {
     return focusedElement;
   }
 
   // No focused element anywhere in this document.  Try to get the BODY.
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3288,16 +3288,17 @@ class Document : public nsINode,
       mozilla::ErrorResult& rv);
   void Close(mozilla::ErrorResult& rv);
   void Write(const mozilla::dom::Sequence<nsString>& aText,
              mozilla::ErrorResult& rv);
   void Writeln(const mozilla::dom::Sequence<nsString>& aText,
                mozilla::ErrorResult& rv);
   Nullable<WindowProxyHolder> GetDefaultView() const;
   Element* GetActiveElement();
+  nsIContent* GetUnretargetedFocusedContent() const;
   bool HasFocus(ErrorResult& rv) const;
   void GetDesignMode(nsAString& aDesignMode);
   void SetDesignMode(const nsAString& aDesignMode,
                      nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& rv);
   void SetDesignMode(const nsAString& aDesignMode,
                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   MOZ_CAN_RUN_SCRIPT
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -251,35 +251,23 @@ nsIContent* DocumentOrShadowRoot::Retarg
     if (cur->SubtreeRoot() == &AsNode()) {
       return cur;
     }
   }
   return nullptr;
 }
 
 Element* DocumentOrShadowRoot::GetRetargetedFocusedElement() {
-  if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
-    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
-    nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
-        window, nsFocusManager::eOnlyCurrentWindow,
-        getter_AddRefs(focusedWindow));
-    // be safe and make sure the element is from this document
-    if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
-      if (focusedContent->ChromeOnlyAccess()) {
-        focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
-      }
-
-      if (focusedContent) {
-        if (nsIContent* retarget = Retarget(focusedContent)) {
-          return retarget->AsElement();
-        }
-      }
-    }
+  auto* content = AsNode().OwnerDoc()->GetUnretargetedFocusedContent();
+  if (!content) {
+    return nullptr;
   }
-
+  if (nsIContent* retarget = Retarget(content)) {
+    return retarget->AsElement();
+  }
   return nullptr;
 }
 
 Element* DocumentOrShadowRoot::GetPointerLockElement() {
   nsCOMPtr<Element> pointerLockedElement =
       do_QueryReferent(EventStateManager::sPointerLockedElement);
   if (!pointerLockedElement) {
     return nullptr;
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -352,18 +352,21 @@ class Selection final : public nsSupport
    * @param granularity can be one of { "character", "word",
    *                                    "line", "lineboundary" }
    *
    * @throws NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
    * "sentenceboundary", "paragraph", "paragraphboundary", or
    * "documentboundary".  Throws NS_ERROR_INVALID_ARG if alter, direction,
    * or granularity has an unrecognized value.
    */
-  void Modify(const nsAString& aAlter, const nsAString& aDirection,
-              const nsAString& aGranularity, mozilla::ErrorResult& aRv);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Modify(const nsAString& aAlter,
+                                          const nsAString& aDirection,
+                                          const nsAString& aGranularity,
+                                          mozilla::ErrorResult& aRv);
 
   MOZ_CAN_RUN_SCRIPT
   void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
                           nsINode& aFocusNode, uint32_t aFocusOffset,
                           mozilla::ErrorResult& aRv);
 
   bool GetInterlinePosition(mozilla::ErrorResult& aRv);
   void SetInterlinePosition(bool aValue, mozilla::ErrorResult& aRv);
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -12,16 +12,17 @@
 #include "nsIFormControl.h"
 #include "nsWidgetsCID.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsRange.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsFocusManager.h"
+#include "nsFrameSelection.h"
 #include "mozilla/dom/DataTransfer.h"
 
 #include "nsIDocShell.h"
 #include "nsIContentViewerEdit.h"
 #include "nsISelectionController.h"
 
 #include "nsPIDOMWindow.h"
 #include "mozilla/dom/Document.h"
@@ -668,61 +669,50 @@ static nsresult AppendImagePromise(nsITr
   aTransferable->SetRequestingPrincipal(node->NodePrincipal());
   aTransferable->SetContentPolicyType(nsIContentPolicy::TYPE_INTERNAL_IMAGE);
 
   // add the dataless file promise flavor
   return aTransferable->AddDataFlavor(kFilePromiseMime);
 }
 #endif  // XP_WIN
 
-nsIContent* nsCopySupport::GetSelectionForCopy(Document* aDocument,
-                                               Selection** aSelection) {
-  *aSelection = nullptr;
-
+already_AddRefed<Selection> nsCopySupport::GetSelectionForCopy(
+    Document* aDocument) {
   PresShell* presShell = aDocument->GetPresShell();
   if (!presShell) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIContent> focusedContent;
-  nsCOMPtr<nsISelectionController> selectionController =
-      presShell->GetSelectionControllerForFocusedContent(
-          getter_AddRefs(focusedContent));
-  if (!selectionController) {
+  RefPtr<nsFrameSelection> frameSel = presShell->GetLastFocusedFrameSelection();
+  if (!frameSel) {
     return nullptr;
   }
 
-  RefPtr<Selection> sel = selectionController->GetSelection(
-      nsISelectionController::SELECTION_NORMAL);
-  sel.forget(aSelection);
-  return focusedContent;
+  RefPtr<Selection> sel = frameSel->GetSelection(SelectionType::eNormal);
+  return sel.forget();
 }
 
 bool nsCopySupport::CanCopy(Document* aDocument) {
   if (!aDocument) return false;
 
-  RefPtr<Selection> sel;
-  GetSelectionForCopy(aDocument, getter_AddRefs(sel));
-  NS_ENSURE_TRUE(sel, false);
-
-  return !sel->IsCollapsed();
+  RefPtr<Selection> sel = GetSelectionForCopy(aDocument);
+  return sel && !sel->IsCollapsed();
 }
 
 static bool IsInsideRuby(nsINode* aNode) {
   for (; aNode; aNode = aNode->GetParent()) {
     if (aNode->IsHTMLElement(nsGkAtoms::ruby)) {
       return true;
     }
   }
   return false;
 }
 
 static bool IsSelectionInsideRuby(Selection* aSelection) {
   uint32_t rangeCount = aSelection->RangeCount();
-  ;
   for (auto i : IntegerRange(rangeCount)) {
     nsRange* range = aSelection->GetRangeAt(i);
     if (!IsInsideRuby(range->GetClosestCommonInclusiveAncestor())) {
       return false;
     }
   }
   return true;
 }
@@ -771,17 +761,17 @@ bool nsCopySupport::FireClipboardEvent(E
 
   // Event target of clipboard events should be an element node which
   // contains selection start container.
   RefPtr<Element> targetElement;
 
   // If a selection was not supplied, try to find it.
   RefPtr<Selection> sel = aSelection;
   if (!sel) {
-    GetSelectionForCopy(doc, getter_AddRefs(sel));
+    sel = GetSelectionForCopy(doc);
   }
 
   // Retrieve the event target node from the start of the selection.
   if (sel) {
     nsRange* range = sel->GetRangeAt(0);
     if (range) {
       targetElement = GetElementOrNearestFlattenedTreeParentElement(
           range->GetStartContainer());
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -64,18 +64,18 @@ class nsCopySupport {
                                          mozilla::dom::Document* aDoc,
                                          nsITransferable** aTransferable);
   /**
    * Retrieve the selection for the given document. If the current focus
    * within the document has its own selection, aSelection will be set to it
    * and this focused content node returned. Otherwise, aSelection will be
    * set to the document's selection and null will be returned.
    */
-  static nsIContent* GetSelectionForCopy(mozilla::dom::Document* aDocument,
-                                         mozilla::dom::Selection** aSelection);
+  static already_AddRefed<mozilla::dom::Selection> GetSelectionForCopy(
+      mozilla::dom::Document* aDocument);
 
   /**
    * Returns true if a copy operation is currently permitted based on the
    * current focus and selection within the specified document.
    */
   static bool CanCopy(mozilla::dom::Document* aDocument);
 
   /**
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -3204,16 +3204,28 @@ void nsFrameLoader::RequestEpochUpdate(u
   }
 
   // If remote browsing (e10s), handle this with the BrowserParent.
   if (auto* browserParent = GetBrowserParent()) {
     Unused << browserParent->SendUpdateEpoch(aEpoch);
   }
 }
 
+void nsFrameLoader::RequestSHistoryUpdate(bool aImmediately) {
+  if (mSessionStoreListener) {
+    mSessionStoreListener->UpdateSHistoryChanges(aImmediately);
+    return;
+  }
+
+  // If remote browsing (e10s), handle this with the BrowserParent.
+  if (auto* browserParent = GetBrowserParent()) {
+    Unused << browserParent->SendUpdateSHistory(aImmediately);
+  }
+}
+
 void nsFrameLoader::Print(uint64_t aOuterWindowID,
                           nsIPrintSettings* aPrintSettings,
                           nsIWebProgressListener* aProgressListener,
                           ErrorResult& aRv) {
 #if defined(NS_PRINTING)
   if (auto* browserParent = GetBrowserParent()) {
     RefPtr<embedding::PrintingParent> printingParent =
         browserParent->Manager()->GetPrintingParent();
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -206,16 +206,18 @@ class nsFrameLoader final : public nsStu
   void RequestNotifyAfterRemotePaint();
 
   void RequestUpdatePosition(mozilla::ErrorResult& aRv);
 
   bool RequestTabStateFlush(uint32_t aFlushId, bool aIsFinal = false);
 
   void RequestEpochUpdate(uint32_t aEpoch);
 
+  void RequestSHistoryUpdate(bool aImmediately = false);
+
   void Print(uint64_t aOuterWindowID, nsIPrintSettings* aPrintSettings,
              nsIWebProgressListener* aProgressListener,
              mozilla::ErrorResult& aRv);
 
   void StartPersistence(uint64_t aOuterWindowID,
                         nsIWebBrowserPersistDocumentReceiver* aRecv,
                         mozilla::ErrorResult& aRv);
 
--- a/dom/base/nsISelectionController.idl
+++ b/dom/base/nsISelectionController.idl
@@ -78,16 +78,33 @@ interface nsISelectionController : nsISe
     Selection getSelection(in short type);
 
    /**
    * Return the selection object corresponding to a selection type.
    */
     [noscript,nostdcall,notxpcom,binaryname(GetSelection)]
     Selection getDOMSelection(in short aType);
 
+   /**
+    * Called when the selection controller should take the focus.
+    *
+    * This will take care to hide the previously-focused selection, show this
+    * selection, and repaint both.
+    */
+   [noscript,nostdcall,notxpcom]
+   void selectionWillTakeFocus();
+
+   /**
+    * Called when the selection controller has lost the focus.
+    *
+    * This will take care to hide and repaint the selection.
+    */
+   [noscript,nostdcall,notxpcom]
+   void selectionWillLoseFocus();
+
    const short SCROLL_SYNCHRONOUS = 1<<1;
    const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
    const short SCROLL_CENTER_VERTICALLY = 1<<4;
    const short SCROLL_OVERFLOW_HIDDEN = 1<<5;
    const short SCROLL_FOR_CARET_MOVE = 1<<6;
 
    /**
    * ScrollSelectionIntoView scrolls a region of the selection,
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -630,16 +630,17 @@ skip-if = toolkit == 'android' # Timeout
 [test_content_iterator_pre_order.html]
 [test_content_iterator_subtree.html]
 [test_copyimage.html]
 skip-if = toolkit == 'android' || headless #bug 904183
 [test_copypaste.html]
 skip-if = toolkit == 'android' || headless #bug 904183
 [test_copypaste.xhtml]
 skip-if = headless  #bug 904183
+[test_copypaste_disabled.html]
 [test_createHTMLDocument.html]
 [test_data_uri.html]
 skip-if = verify
 [test_document.all_iteration.html]
 [test_document.all_unqualified.html]
 [test_document_constructor.html]
 [test_document_importNode_document.html]
 [test_custom_element.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_copypaste_disabled.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="copypaste.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<style>
+@font-face {
+  font-family: Ahem;
+  src: url("Ahem.ttf");
+}
+body { font-family: Ahem; font-size: 20px; }
+input, textarea {
+  font: inherit;
+  -moz-appearance: none;
+  padding: 0;
+  border: 0;
+  scrollbar-width: none;
+}
+</style>
+
+<input id="disabled-input" disabled value="abcd"> efgh <br> <textarea rows=1 id="disabled-textarea" disabled>ijkl</textarea> mnop
+
+<script>
+function dragSelect(e, x1, x2, x3) {
+  dir = x2 > x1 ? 1 : -1;
+  synthesizeMouse(e, x1, 5, { type: "mousedown" });
+  synthesizeMouse(e, x1 + dir, 5, { type: "mousemove" });
+  if (x3)
+    synthesizeMouse(e, x3, 5, { type: "mousemove" });
+  synthesizeMouse(e, x2 - dir, 5, { type: "mousemove" });
+  synthesizeMouse(e, x2, 5, { type: "mouseup" });
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async function() {
+  const docShell = SpecialPowers.wrap(window).docShell;
+
+  const documentViewer = docShell.contentViewer.QueryInterface(
+    SpecialPowers.Ci.nsIContentViewerEdit
+  );
+
+  const clipboard = SpecialPowers.Services.clipboard;
+
+  function copySelectionToClipboard() {
+    return SimpleTest.promiseClipboardChange(
+      () => true,
+      () => {
+        documentViewer.copySelection();
+      }
+    );
+  }
+
+  function getLoadContext() {
+    return docShell.QueryInterface(SpecialPowers.Ci.nsILoadContext);
+  }
+
+  function getClipboardData(mime) {
+    var transferable = SpecialPowers.Cc[
+      "@mozilla.org/widget/transferable;1"
+    ].createInstance(SpecialPowers.Ci.nsITransferable);
+    transferable.init(getLoadContext());
+    transferable.addDataFlavor(mime);
+    clipboard.getData(transferable, 1);
+    var data = SpecialPowers.createBlankObject();
+    transferable.getTransferData(mime, data);
+    return data;
+  }
+
+  function testClipboardValue(mime, expected) {
+    var data = SpecialPowers.wrap(getClipboardData(mime));
+    is(
+      data.value == null
+        ? data.value
+        : data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data,
+      expected,
+      mime + " value in the clipboard"
+    );
+    return data.value;
+  }
+
+  for (let id of ["disabled-input", "disabled-textarea"]) {
+    let element = document.getElementById(id);
+    dragSelect(element, 0, 60);
+    await copySelectionToClipboard();
+    testClipboardValue("text/unicode", element.value.substr(0, 3));
+  }
+
+  SimpleTest.finish();
+});
+</script>
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -312,36 +312,34 @@ nsresult ContentEventHandler::InitCommon
 
   mSelection = nullptr;
   mRootContent = nullptr;
   mFirstSelectedRawRange.Clear();
 
   nsresult rv = InitBasic(aRequireFlush);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsISelectionController> selectionController;
+  RefPtr<nsFrameSelection> frameSel;
   if (PresShell* presShell = mDocument->GetPresShell()) {
-    selectionController = presShell->GetSelectionControllerForFocusedContent();
+    frameSel = presShell->GetLastFocusedFrameSelection();
   }
-  if (NS_WARN_IF(!selectionController)) {
+  if (NS_WARN_IF(!frameSel)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  mSelection =
-      selectionController->GetSelection(ToRawSelectionType(aSelectionType));
+  mSelection = frameSel->GetSelection(aSelectionType);
   if (NS_WARN_IF(!mSelection)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<Selection> normalSelection;
   if (mSelection->Type() == SelectionType::eNormal) {
     normalSelection = mSelection;
   } else {
-    normalSelection = selectionController->GetSelection(
-        nsISelectionController::SELECTION_NORMAL);
+    normalSelection = frameSel->GetSelection(SelectionType::eNormal);
     if (NS_WARN_IF(!normalSelection)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
   }
 
   rv = InitRootContent(normalSelection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -5160,17 +5160,17 @@ nsresult EventStateManager::HandleMiddle
     if (NS_WARN_IF(!selection)) {
       return NS_ERROR_FAILURE;
     }
   } else {
     Document* document = aPresShell->GetDocument();
     if (NS_WARN_IF(!document)) {
       return NS_ERROR_FAILURE;
     }
-    nsCopySupport::GetSelectionForCopy(document, getter_AddRefs(selection));
+    selection = nsCopySupport::GetSelectionForCopy(document);
     if (NS_WARN_IF(!selection)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   // Move selection to the clicked point.
   nsCOMPtr<nsIContent> container;
   int32_t offset;
@@ -5209,18 +5209,17 @@ nsresult EventStateManager::HandleMiddle
 
   // Although we've fired "paste" event, there is no editor to accept the
   // clipboard content.
   if (!aTextEditor) {
     return NS_OK;
   }
 
   // Check if the editor is still the good target to paste.
-  if (aTextEditor->Destroyed() || aTextEditor->IsReadonly() ||
-      aTextEditor->IsDisabled()) {
+  if (aTextEditor->Destroyed() || aTextEditor->IsReadonly()) {
     // XXX Should we consume the event when the editor is readonly and/or
     //     disabled?
     return NS_OK;
   }
 
   // The selection may have been modified during reflow.  Therefore, we
   // should adjust event target to pass IsAcceptableInputEvent().
   nsRange* range = selection->GetRangeAt(0);
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6634,28 +6634,23 @@ void HTMLInputElement::OnValueChanged(Va
   // :placeholder-shown pseudo-class may change when the value changes.
   // However, we don't want to waste cycles if the state doesn't apply.
   if (PlaceholderApplies() && HasAttr(nsGkAtoms::placeholder)) {
     UpdateState(true);
   }
 }
 
 bool HTMLInputElement::HasCachedSelection() {
-  bool isCached = false;
   TextControlState* state = GetEditorState();
-  if (state) {
-    isCached = state->IsSelectionCached() &&
-               state->HasNeverInitializedBefore() &&
-               state->GetSelectionProperties().GetStart() !=
-                   state->GetSelectionProperties().GetEnd();
-    if (isCached) {
-      state->WillInitEagerly();
-    }
-  }
-  return isCached;
+  if (!state) {
+    return false;
+  }
+  return state->IsSelectionCached() && state->HasNeverInitializedBefore() &&
+         state->GetSelectionProperties().GetStart() !=
+             state->GetSelectionProperties().GetEnd();
 }
 
 void HTMLInputElement::FieldSetDisabledChanged(bool aNotify) {
   // This *has* to be called *before* UpdateBarredFromConstraintValidation and
   // UpdateValueMissingValidityState because these two functions depend on our
   // disabled state.
   nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
 
--- a/dom/html/TextControlState.cpp
+++ b/dom/html/TextControlState.cpp
@@ -169,20 +169,16 @@ class RestoreSelectionState : public Run
       // 642800).
       nsAutoScriptBlocker scriptBlocker;
       TextControlState::SelectionProperties& properties =
           mTextControlState->GetSelectionProperties();
       if (properties.IsDirty()) {
         mFrame->SetSelectionRange(properties.GetStart(), properties.GetEnd(),
                                   properties.GetDirection());
       }
-      if (!mTextControlState->mSelectionRestoreEagerInit) {
-        mTextControlState->HideSelectionIfBlurred();
-      }
-      mTextControlState->mSelectionRestoreEagerInit = false;
     }
 
     if (mTextControlState) {
       mTextControlState->FinishedRestoringSelection();
     }
     return NS_OK;
   }
 
@@ -212,17 +208,16 @@ class MOZ_RAII AutoRestoreEditorState fi
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTextEditor);
 
     // EditorBase::SetFlags() is a virtual method.  Even though it does nothing
     // if new flags and current flags are same, the calling cost causes
     // appearing the method in profile.  So, this class should check if it's
     // necessary to call.
     uint32_t flags = mSavedFlags;
-    flags &= ~(nsIEditor::eEditorDisabledMask);
     flags &= ~(nsIEditor::eEditorReadonlyMask);
     flags |= nsIEditor::eEditorDontEchoPassword;
     if (mSavedFlags != flags) {
       mTextEditor->SetFlags(flags);
     }
     mTextEditor->SetMaxTextLength(-1);
   }
 
@@ -356,16 +351,18 @@ class TextInputSelectionController final
   NS_IMETHOD ScrollCharacter(bool aRight) override;
   NS_IMETHOD SelectAll(void) override;
   NS_IMETHOD CheckVisibility(nsINode* node, int16_t startOffset,
                              int16_t EndOffset, bool* _retval) override;
   virtual nsresult CheckVisibilityContent(nsIContent* aNode,
                                           int16_t aStartOffset,
                                           int16_t aEndOffset,
                                           bool* aRetval) override;
+  void SelectionWillTakeFocus() override;
+  void SelectionWillLoseFocus() override;
 
  private:
   RefPtr<nsFrameSelection> mFrameSelection;
   nsCOMPtr<nsIContent> mLimiter;
   nsIScrollableFrame* mScrollFrame;
   nsWeakPtr mPresShellWeak;
 };
 
@@ -766,16 +763,32 @@ NS_IMETHODIMP
 TextInputSelectionController::SelectAll() {
   if (!mFrameSelection) {
     return NS_ERROR_NULL_POINTER;
   }
   RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
   return frameSelection->SelectAll();
 }
 
+void TextInputSelectionController::SelectionWillTakeFocus() {
+  if (mFrameSelection) {
+    if (PresShell* shell = mFrameSelection->GetPresShell()) {
+      shell->FrameSelectionWillTakeFocus(*mFrameSelection);
+    }
+  }
+}
+
+void TextInputSelectionController::SelectionWillLoseFocus() {
+  if (mFrameSelection) {
+    if (PresShell* shell = mFrameSelection->GetPresShell()) {
+      shell->FrameSelectionWillLoseFocus(*mFrameSelection);
+    }
+  }
+}
+
 NS_IMETHODIMP
 TextInputSelectionController::CheckVisibility(nsINode* node,
                                               int16_t startOffset,
                                               int16_t EndOffset,
                                               bool* _retval) {
   if (!mPresShellWeak) {
     return NS_ERROR_NOT_INITIALIZED;
   }
@@ -1391,17 +1404,16 @@ bool TextControlState::sHasShutDown = fa
 
 TextControlState::TextControlState(TextControlElement* aOwningElement)
     : mTextCtrlElement(aOwningElement),
       mBoundFrame(nullptr),
       mEverInited(false),
       mEditorInitialized(false),
       mValueTransferInProgress(false),
       mSelectionCached(true),
-      mSelectionRestoreEagerInit(false),
       mPlaceholderVisibility(false),
       mPreviewVisibility(false)
 // When adding more member variable initializations here, add the same
 // also to ::Construct.
 {
   MOZ_COUNT_CTOR(TextControlState);
   static_assert(sizeof(*this) <= 128,
                 "Please keep small TextControlState as far as possible");
@@ -1414,17 +1426,16 @@ TextControlState* TextControlState::Cons
     sReleasedInstances->RemoveLastElement();
     state->mTextCtrlElement = aOwningElement;
     state->mBoundFrame = nullptr;
     state->mSelectionProperties = SelectionProperties();
     state->mEverInited = false;
     state->mEditorInitialized = false;
     state->mValueTransferInProgress = false;
     state->mSelectionCached = true;
-    state->mSelectionRestoreEagerInit = false;
     state->mPlaceholderVisibility = false;
     state->mPreviewVisibility = false;
     // When adding more member variable initializations here, add the same
     // also to the constructor.
     return state;
   }
 
   return new TextControlState(aOwningElement);
@@ -1635,17 +1646,19 @@ nsresult TextControlState::BindToFrame(n
   MOZ_ASSERT(presShell);
 
   // Create a SelectionController
   mSelCon = new TextInputSelectionController(presShell, rootNode);
   MOZ_ASSERT(!mTextListener, "Should not overwrite the object");
   mTextListener = new TextInputListener(mTextCtrlElement);
 
   mTextListener->SetFrame(mBoundFrame);
-  mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+
+  // Editor will override this as needed from InitializeSelection.
+  mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
 
   // Get the caret and make it a selection listener.
   // FYI: It's safe to use raw pointer for calling
   //      Selection::AddSelectionListner() because it only appends the listener
   //      to its internal array.
   Selection* selection = mSelCon->GetSelection(SelectionType::eNormal);
   if (selection) {
     RefPtr<nsCaret> caret = presShell->GetCaret();
@@ -1879,31 +1892,23 @@ nsresult TextControlState::PrepareEditor
   }
 
   // Set max text field length
   newTextEditor->SetMaxTextLength(mTextCtrlElement->UsedMaxLength());
 
   editorFlags = newTextEditor->Flags();
 
   // Check if the readonly attribute is set.
-  if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
+  //
+  // TODO: Should probably call IsDisabled(), as it is cheaper.
+  if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) ||
+      mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
     editorFlags |= nsIEditor::eEditorReadonlyMask;
   }
 
-  // Check if the disabled attribute is set.
-  // TODO: call IsDisabled() here!
-  if (mTextCtrlElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    editorFlags |= nsIEditor::eEditorDisabledMask;
-  }
-
-  // Disable the selection if necessary.
-  if (newTextEditor->IsDisabled()) {
-    mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
-  }
-
   SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
 
   if (shouldInitializeEditor) {
     // Hold on to the newly created editor
     preDestroyer.Swap(mTextEditor);
   }
 
   // If we have a default value, insert it under the div we created
@@ -2380,16 +2385,20 @@ void TextControlState::UnbindFromFrame(n
   MOZ_ASSERT(aFrame == mBoundFrame, "Unbinding from the wrong frame");
   if (aFrame && aFrame != mBoundFrame) {
     return;
   }
 
   AutoTextControlHandlingState handlingUnbindFromFrame(
       *this, TextControlAction::UnbindFromFrame);
 
+  if (mSelCon) {
+    mSelCon->SelectionWillLoseFocus();
+  }
+
   // We need to start storing the value outside of the editor if we're not
   // going to use it anymore, so retrieve it for now.
   nsAutoString value;
   GetValue(value, true);
 
   if (mRestoringSelection) {
     mRestoringSelection->Revoke();
     mRestoringSelection = nullptr;
@@ -3082,20 +3091,13 @@ void TextControlState::UpdateOverlayText
         !nsContentUtils::IsFocusedContent(mTextCtrlElement);
   }
 
   if (mBoundFrame && aNotify) {
     mBoundFrame->InvalidateFrame();
   }
 }
 
-void TextControlState::HideSelectionIfBlurred() {
-  MOZ_ASSERT(mSelCon, "Should have a selection controller if we have a frame!");
-  if (!nsContentUtils::IsFocusedContent(mTextCtrlElement)) {
-    mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
-  }
-}
-
 bool TextControlState::EditorHasComposition() {
   return mTextEditor && mTextEditor->IsIMEComposing();
 }
 
 }  // namespace mozilla
--- a/dom/html/TextControlState.h
+++ b/dom/html/TextControlState.h
@@ -269,17 +269,16 @@ class TextControlState final : public Su
 
   // placeholder methods
   bool GetPlaceholderVisibility() { return mPlaceholderVisibility; }
 
   // preview methods
   void SetPreviewText(const nsAString& aValue, bool aNotify);
   void GetPreviewText(nsAString& aValue);
   bool GetPreviewVisibility() { return mPreviewVisibility; }
-  void HideSelectionIfBlurred();
 
   struct SelectionProperties {
    public:
     SelectionProperties()
         : mStart(0), mEnd(0), mDirection(nsITextControlFrame::eForward) {}
     bool IsDefault() const {
       return mStart == 0 && mEnd == 0 &&
              mDirection == nsITextControlFrame::eForward;
@@ -310,17 +309,16 @@ class TextControlState final : public Su
     uint32_t mStart, mEnd;
     bool mIsDirty = false;
     nsITextControlFrame::SelectionDirection mDirection;
   };
 
   bool IsSelectionCached() const { return mSelectionCached; }
   SelectionProperties& GetSelectionProperties() { return mSelectionProperties; }
   MOZ_CAN_RUN_SCRIPT void SetSelectionProperties(SelectionProperties& aProps);
-  void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
   bool HasNeverInitializedBefore() const { return !mEverInited; }
   // Sync up our selection properties with our editor prior to being destroyed.
   // This will invoke UnbindFromFrame() to ensure that we grab whatever
   // selection state may be at the moment.
   MOZ_CAN_RUN_SCRIPT void SyncUpSelectionPropertiesBeforeDestruction();
 
   // Get the selection range start and end points in our text.
   void GetSelectionRange(uint32_t* aSelectionStart, uint32_t* aSelectionEnd,
@@ -451,18 +449,16 @@ class TextControlState final : public Su
   RefPtr<TextInputListener> mTextListener;
   Maybe<nsString> mValue;
   SelectionProperties mSelectionProperties;
   bool mEverInited;  // Have we ever been initialized?
   bool mEditorInitialized;
   bool mValueTransferInProgress;  // Whether a value is being transferred to the
                                   // frame
   bool mSelectionCached;          // Whether mSelectionProperties is valid
-  mutable bool mSelectionRestoreEagerInit;  // Whether we're eager initing
-                                            // because of selection restore
   bool mPlaceholderVisibility;
   bool mPreviewVisibility;
 
   /**
    * For avoiding allocation cost of the instance, we should reuse instances
    * as far as possible.
    *
    * FYI: `25` is just a magic number considered without enough investigation,
--- a/dom/ipc/BrowserChild.cpp
+++ b/dom/ipc/BrowserChild.cpp
@@ -2010,16 +2010,24 @@ mozilla::ipc::IPCResult BrowserChild::Re
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult BrowserChild::RecvUpdateEpoch(const uint32_t& aEpoch) {
   mSessionStoreListener->SetEpoch(aEpoch);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory(
+    const bool& aImmediately) {
+  if (mSessionStoreListener) {
+    mSessionStoreListener->UpdateSHistoryChanges(aImmediately);
+  }
+  return IPC_OK();
+}
+
 // In case handling repeated keys takes much time, we skip firing new ones.
 bool BrowserChild::SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent) {
   if (mRepeatedKeyEventTime.IsNull() || !aEvent.CanSkipInRemoteProcess() ||
       (aEvent.mMessage != eKeyDown && aEvent.mMessage != eKeyPress)) {
     mRepeatedKeyEventTime = TimeStamp();
     mSkipKeyPress = false;
     return false;
   }
@@ -3998,18 +4006,19 @@ bool BrowserChild::UpdateSessionStore(ui
   nsTArray<nsString> keys, values;
   bool isFullStorage = false;
   if (store->IsStorageUpdated()) {
     isFullStorage = store->GetAndClearStorageChanges(origins, keys, values);
   }
 
   Unused << SendSessionStoreUpdate(
       docShellCaps, privatedMode, positions, positionDescendants, inputs,
-      idVals, xPathVals, origins, keys, values, isFullStorage, aFlushId,
-      aIsFinal, mSessionStoreListener->GetEpoch());
+      idVals, xPathVals, origins, keys, values, isFullStorage,
+      store->GetAndClearSHistoryChanged(), aFlushId, aIsFinal,
+      mSessionStoreListener->GetEpoch());
   return true;
 }
 
 #ifdef XP_WIN
 RefPtr<PBrowserChild::IsWindowSupportingProtectedMediaPromise>
 BrowserChild::DoesWindowSupportProtectedMedia() {
   MOZ_ASSERT(
       NS_IsMainThread(),
--- a/dom/ipc/BrowserChild.h
+++ b/dom/ipc/BrowserChild.h
@@ -376,16 +376,18 @@ class BrowserChild final : public nsMess
                                                 aApzResponse);
   }
 
   mozilla::ipc::IPCResult RecvFlushTabState(const uint32_t& aFlushId,
                                             const bool& aIsFinal);
 
   mozilla::ipc::IPCResult RecvUpdateEpoch(const uint32_t& aEpoch);
 
+  mozilla::ipc::IPCResult RecvUpdateSHistory(const bool& aImmediately);
+
   mozilla::ipc::IPCResult RecvNativeSynthesisResponse(
       const uint64_t& aObserverId, const nsCString& aResponse);
 
   mozilla::ipc::IPCResult RecvPluginEvent(const WidgetPluginEvent& aEvent);
 
   mozilla::ipc::IPCResult RecvCompositionEvent(
       const mozilla::WidgetCompositionEvent& aEvent);
 
--- a/dom/ipc/BrowserParent.cpp
+++ b/dom/ipc/BrowserParent.cpp
@@ -2920,17 +2920,18 @@ void BrowserParent::ReconstructWebProgre
 mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
     const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
     nsTArray<nsCString>&& aPositions, nsTArray<int32_t>&& aPositionDescendants,
     const nsTArray<InputFormData>& aInputs,
     const nsTArray<CollectedInputDataValue>& aIdVals,
     const nsTArray<CollectedInputDataValue>& aXPathVals,
     nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
     nsTArray<nsString>&& aValues, const bool aIsFullStorage,
-    const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch) {
+    const bool aNeedCollectSHistory, const uint32_t& aFlushId,
+    const bool& aIsFinal, const uint32_t& aEpoch) {
   UpdateSessionStoreData data;
   if (aDocShellCaps.isSome()) {
     data.mDocShellCaps.Construct() = aDocShellCaps.value();
   }
   if (aPrivatedMode.isSome()) {
     data.mIsPrivate.Construct() = aPrivatedMode.value();
   }
   if (aPositions.Length() != 0) {
@@ -2977,18 +2978,18 @@ mozilla::ipc::IPCResult BrowserParent::R
   NS_ENSURE_TRUE(funcs, IPC_OK());
   nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
   AutoJSAPI jsapi;
   MOZ_ALWAYS_TRUE(jsapi.Init(wrapped->GetJSObjectGlobal()));
   JS::Rooted<JS::Value> dataVal(jsapi.cx());
   bool ok = ToJSValue(jsapi.cx(), data, &dataVal);
   NS_ENSURE_TRUE(ok, IPC_OK());
 
-  nsresult rv = funcs->UpdateSessionStore(mFrameElement, aFlushId, aIsFinal,
-                                          aEpoch, dataVal);
+  nsresult rv = funcs->UpdateSessionStore(
+      mFrameElement, aFlushId, aIsFinal, aEpoch, dataVal, aNeedCollectSHistory);
   NS_ENSURE_SUCCESS(rv, IPC_OK());
 
   return IPC_OK();
 }
 
 bool BrowserParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent) {
   nsCOMPtr<nsIWidget> textInputHandlingWidget = GetTextInputHandlingWidget();
   if (!textInputHandlingWidget) {
--- a/dom/ipc/BrowserParent.h
+++ b/dom/ipc/BrowserParent.h
@@ -335,17 +335,18 @@ class BrowserParent final : public PBrow
       const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
       nsTArray<nsCString>&& aPositions,
       nsTArray<int32_t>&& aPositionDescendants,
       const nsTArray<InputFormData>& aInputs,
       const nsTArray<CollectedInputDataValue>& aIdVals,
       const nsTArray<CollectedInputDataValue>& aXPathVals,
       nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
       nsTArray<nsString>&& aValues, const bool aIsFullStorage,
-      const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch);
+      const bool aNeedCollectSHistory, const uint32_t& aFlushId,
+      const bool& aIsFinal, const uint32_t& aEpoch);
 
   mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(
       PBrowserParent* aOpener, const nsString& aURL, const nsString& aName,
       bool aForceNoReferrer, const nsString& aFeatures,
       BrowserFrameOpenWindowResolver&& aResolve);
 
   mozilla::ipc::IPCResult RecvSyncMessage(
       const nsString& aMessage, const ClonedMessageData& aData,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -611,22 +611,24 @@ parent:
     async NavigationFinished();
 
     async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode,
                              nsCString[] aPositions, int32_t[] aPositionDescendants,
                              InputFormData[] aInputs, CollectedInputDataValue[] aIdVals,
                              CollectedInputDataValue[] aXPathVals,
                              nsCString[] aOrigins, nsString[] aKeys,
                              nsString[] aValues, bool aIsFullStorage,
-                             uint32_t aFlushId, bool aIsFinal, uint32_t aEpoch);
+                             bool aNeedCollectSHistory, uint32_t aFlushId,
+                             bool aIsFinal, uint32_t aEpoch);
 
 child:
     async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
     async FlushTabState(uint32_t aFlushId, bool aIsFinal);
     async UpdateEpoch(uint32_t aEpoch);
+    async UpdateSHistory(bool aImmediately);
 
 parent:
 
     /**
      * Child informs the parent that the graphics objects are ready for
      * compositing.  This is sent when all pending changes have been
      * sent to the compositor and are ready to be shown on the next composite.
      * @see PCompositor
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -108,16 +108,21 @@ interface FrameLoader {
   boolean requestTabStateFlush(unsigned long aFlushId);
 
   /**
    * Force Epoch update in native sessionStoreListeners.
    */
   void requestEpochUpdate(unsigned long aEpoch);
 
   /**
+   * Request a session history update in native sessionStoreListeners.
+   */
+  void requestSHistoryUpdate(boolean aImmediately);
+
+  /**
    * Print the current document.
    *
    * @param aOuterWindowID the ID of the outer window to print
    * @param aPrintSettings optional print settings to use; printSilent can be
    *                       set to prevent prompting.
    * @param aProgressListener optional print progress listener.
    */
   [Throws]
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -280,22 +280,16 @@ nsresult EditorBase::Init(Document& aDoc
     mComposition->OnTextNodeRemoved();
   }
 
   // Show the caret.
   DebugOnly<nsresult> rvIgnored = selectionController->SetCaretReadOnly(false);
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rvIgnored),
       "nsISelectionController::SetCaretReadOnly(false) failed, but ignored");
-  rvIgnored = selectionController->SetDisplaySelection(
-      nsISelectionController::SELECTION_ON);
-  NS_WARNING_ASSERTION(
-      NS_SUCCEEDED(rvIgnored),
-      "nsISelectionController::SetDisplaySelection(nsISelectionController::"
-      "SELECTION_ON) failed, but ignored");
   // Show all the selection reflected to user.
   rvIgnored =
       selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                        "nsISelectionController::SetSelectionFlags("
                        "nsISelectionDisplay::DISPLAY_ALL) failed, but ignored");
 
   MOZ_ASSERT(IsInitialized());
@@ -304,17 +298,17 @@ nsresult EditorBase::Init(Document& aDoc
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_FAILURE;
   }
 
   SelectionRefPtr()->AddSelectionListener(this);
 
   // Make sure that the editor will be destroyed properly
   mDidPreDestroy = false;
-  // Make sure that the ediotr will be created properly
+  // Make sure that the editor will be created properly
   mDidPostCreate = false;
 
   return NS_OK;
 }
 
 nsresult EditorBase::PostCreate() {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -2500,17 +2494,17 @@ nsresult EditorBase::CommitComposition()
 nsresult EditorBase::GetPreferredIMEState(IMEState* aState) {
   if (NS_WARN_IF(!aState)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   aState->mEnabled = IMEState::ENABLED;
   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     aState->mEnabled = IMEState::DISABLED;
     return NS_OK;
   }
 
   Element* rootElement = GetRoot();
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_FAILURE;
   }
@@ -5034,17 +5028,17 @@ nsresult EditorBase::HandleKeyPressEvent
 
   if (NS_WARN_IF(!aKeyboardEvent)) {
     return NS_ERROR_UNEXPECTED;
   }
   MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
              "HandleKeyPressEvent gets non-keypress event");
 
   // if we are readonly or disabled, then do nothing.
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     // consume backspace for disabled and readonly textfields, to prevent
     // back in history, which could be confusing to users
     if (aKeyboardEvent->mKeyCode == NS_VK_BACK) {
       aKeyboardEvent->PreventDefault();
     }
     return NS_OK;
   }
 
@@ -5129,31 +5123,23 @@ nsresult EditorBase::InitializeSelection
   // SetCaretEnabled(true), since that would override mIgnoreUserModify to true.
   //
   // Also, make sure to always ignore it for designMode, since that effectively
   // overrides everything and we allow to edit stuff with
   // contenteditable="false" subtrees in such a document.
   caret->SetIgnoreUserModify(targetNode->OwnerDoc()->HasFlag(NODE_IS_EDITABLE));
 
   // Init selection
-  rvIgnored = selectionController->SetDisplaySelection(
-      nsISelectionController::SELECTION_ON);
-  NS_WARNING_ASSERTION(
-      NS_SUCCEEDED(rvIgnored),
-      "nsISelectionController::SetDisplaySelection() failed, but ignored");
   rvIgnored =
       selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rvIgnored),
       "nsISelectionController::SetSelectionFlags() failed, but ignored");
-  rvIgnored = selectionController->RepaintSelection(
-      nsISelectionController::SELECTION_NORMAL);
-  NS_WARNING_ASSERTION(
-      NS_SUCCEEDED(rvIgnored),
-      "nsISelectionController::RepaintSelection() failed, but ignored");
+
+  selectionController->SelectionWillTakeFocus();
 
   // If the computed selection root isn't root content, we should set it
   // as selection ancestor limit.  However, if that is root element, it means
   // there is not limitation of the selection, then, we must set nullptr.
   // NOTE: If we set a root element to the ancestor limit, some selection
   // methods don't work fine.
   if (selectionRootContent->GetParent()) {
     InitializeSelectionAncestorLimit(*selectionRootContent);
@@ -5187,36 +5173,16 @@ nsresult EditorBase::InitializeSelection
           NS_SUCCEEDED(rvIgnored),
           "CompositionTransaction::SetIMESelection() failed, but ignored");
     }
   }
 
   return NS_OK;
 }
 
-class RepaintSelectionRunner final : public Runnable {
- public:
-  explicit RepaintSelectionRunner(nsISelectionController* aSelectionController)
-      : Runnable("RepaintSelectionRunner"),
-        mSelectionController(aSelectionController) {}
-
-  NS_IMETHOD Run() override {
-    DebugOnly<nsresult> rvIgnored = mSelectionController->RepaintSelection(
-        nsISelectionController::SELECTION_NORMAL);
-    NS_WARNING_ASSERTION(
-        NS_SUCCEEDED(rvIgnored),
-        "nsISelectionController::RepaintSelection(nsISelectionController::"
-        "SELECTION_NORMAL) failed, but ignored");
-    return NS_OK;
-  }
-
- private:
-  nsCOMPtr<nsISelectionController> mSelectionController;
-};
-
 nsresult EditorBase::FinalizeSelection() {
   nsCOMPtr<nsISelectionController> selectionController =
       GetSelectionController();
   if (NS_WARN_IF(!selectionController)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
@@ -5238,68 +5204,21 @@ nsresult EditorBase::FinalizeSelection()
         "nsISelectionController::SetCaretEnabled(false) failed, but ignored");
   }
 
   nsFocusManager* focusManager = nsFocusManager::GetFocusManager();
   if (NS_WARN_IF(!focusManager)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   focusManager->UpdateCaretForCaretBrowsingMode();
-
-  if (!HasIndependentSelection()) {
-    // If this editor doesn't have an independent selection, i.e., it must
-    // mean that it is an HTML editor, the selection controller is shared with
-    // presShell.  So, even this editor loses focus, other part of the document
-    // may still have focus.
-    RefPtr<Document> doc = GetDocument();
-    ErrorResult ret;
-    if (!doc || !doc->HasFocus(ret)) {
-      // If the document already lost focus, mark the selection as disabled.
-      DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
-          nsISelectionController::SELECTION_DISABLED);
-      NS_WARNING_ASSERTION(
-          NS_SUCCEEDED(rvIgnored),
-          "nsISelectionController::SetDisplaySelection(nsISelectionController::"
-          "SELECTION_DISABLED) failed, but ignored");
-    } else {
-      // Otherwise, mark selection as normal because outside of a
-      // contenteditable element should be selected with normal selection
-      // color after here.
-      DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
-          nsISelectionController::SELECTION_ON);
-      NS_WARNING_ASSERTION(
-          NS_SUCCEEDED(rvIgnored),
-          "nsISelectionController::SetDisplaySelection(nsISelectionController::"
-          "SELECTION_ON) failed, but ignored");
+  if (nsCOMPtr<nsINode> node = do_QueryInterface(GetDOMEventTarget())) {
+    if (node->OwnerDoc()->GetUnretargetedFocusedContent() != node) {
+      selectionController->SelectionWillLoseFocus();
     }
-  } else if (IsFormWidget() || IsPasswordEditor() || IsReadonly() ||
-             IsDisabled() || IsInputFiltered()) {
-    // In <input> or <textarea>, the independent selection should be hidden
-    // while this editor doesn't have focus.
-    DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
-        nsISelectionController::SELECTION_HIDDEN);
-    NS_WARNING_ASSERTION(
-        NS_SUCCEEDED(rvIgnored),
-        "nsISelectionController::SetDisplaySelection(nsISelectionController::"
-        "SELECTION_HIDDEN) failed, but ignored");
-  } else {
-    // Otherwise, although we're not sure how this case happens, the
-    // independent selection should be marked as disabled.
-    DebugOnly<nsresult> rvIgnored = selectionController->SetDisplaySelection(
-        nsISelectionController::SELECTION_DISABLED);
-    NS_WARNING_ASSERTION(
-        NS_SUCCEEDED(rvIgnored),
-        "nsISelectionController::SetDisplaySelection(nsISelectionController::"
-        "SELECTION_DISABLED) failed, but ignored");
-  }
-
-  // FinalizeSelection might be called from ContentRemoved even if selection
-  // isn't updated.  So we need to call RepaintSelection after updated it.
-  nsContentUtils::AddScriptRunner(
-      new RepaintSelectionRunner(selectionController));
+  }
   return NS_OK;
 }
 
 void EditorBase::ReinitializeSelection(Element& aElement) {
   if (NS_WARN_IF(Destroyed())) {
     return;
   }
 
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -486,20 +486,16 @@ class EditorBase : public nsIEditor,
   bool IsLeftToRight() const {
     return (mFlags & nsIEditor::eEditorLeftToRight) != 0;
   }
 
   bool IsReadonly() const {
     return (mFlags & nsIEditor::eEditorReadonlyMask) != 0;
   }
 
-  bool IsDisabled() const {
-    return (mFlags & nsIEditor::eEditorDisabledMask) != 0;
-  }
-
   bool IsInputFiltered() const {
     return (mFlags & nsIEditor::eEditorFilterInputMask) != 0;
   }
 
   bool IsMailEditor() const {
     return (mFlags & nsIEditor::eEditorMailMask) != 0;
   }
 
@@ -2560,18 +2556,17 @@ class EditorBase : public nsIEditor,
    * Return true if spellchecking should be enabled for this editor.
    */
   bool GetDesiredSpellCheckState();
 
   bool CanEnableSpellCheck() {
     // Check for password/readonly/disabled, which are not spellchecked
     // regardless of DOM. Also, check to see if spell check should be skipped
     // or not.
-    return !IsPasswordEditor() && !IsReadonly() && !IsDisabled() &&
-           !ShouldSkipSpellCheck();
+    return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck();
   }
 
   /**
    * InitializeSelectionAncestorLimit() is called by InitializeSelection().
    * When this is called, each implementation has to call
    * Selection::SetAncestorLimiter() with aAnotherLimit.
    *
    * @param aAncestorLimit      New ancestor limit of Selection.  This always
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -665,17 +665,17 @@ nsresult EditorEventListener::KeyPress(W
 }
 
 nsresult EditorEventListener::MouseClick(WidgetMouseEvent* aMouseClickEvent) {
   if (NS_WARN_IF(!aMouseClickEvent) || DetachedFromEditor()) {
     return NS_OK;
   }
   // nothing to do if editor isn't editable or clicked on out of the editor.
   RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
-  if (textEditor->IsReadonly() || textEditor->IsDisabled() ||
+  if (textEditor->IsReadonly() ||
       !textEditor->IsAcceptableInputEvent(aMouseClickEvent)) {
     return NS_OK;
   }
 
   // Notifies clicking on editor to IMEStateManager even when the event was
   // consumed.
   if (EditorHasFocus()) {
     RefPtr<nsPresContext> presContext = GetPresContext();
@@ -835,18 +835,18 @@ nsresult EditorEventListener::DragOverOr
   if (NS_WARN_IF(!dropParentContent)) {
     return NS_ERROR_FAILURE;
   }
   if (DetachedFromEditor()) {
     RefuseToDropAndHideCaret(aDragEvent);
     return NS_OK;
   }
 
-  bool notEditable = !dropParentContent->IsEditable() ||
-                     mEditorBase->IsReadonly() || mEditorBase->IsDisabled();
+  bool notEditable =
+      !dropParentContent->IsEditable() || mEditorBase->IsReadonly();
 
   // First of all, hide caret if we won't insert the drop data into the editor
   // obviously.
   if (mCaret && (IsFileControlTextBox() || notEditable)) {
     mCaret->SetVisible(false);
   }
 
   // If we're a native anonymous <input> element in <input type="file">,
@@ -972,17 +972,17 @@ bool EditorEventListener::DragEventHasSu
          (!mEditorBase->IsPlaintextEditor() &&
           (dataTransfer->HasType(NS_LITERAL_STRING(kHTMLMime)) ||
            dataTransfer->HasType(NS_LITERAL_STRING(kFileMime))));
 }
 
 bool EditorEventListener::CanInsertAtDropPosition(DragEvent* aDragEvent) {
   MOZ_ASSERT(
       !DetachedFromEditorOrDefaultPrevented(aDragEvent->WidgetEventPtr()));
-  MOZ_ASSERT(!mEditorBase->IsReadonly() && !mEditorBase->IsDisabled());
+  MOZ_ASSERT(!mEditorBase->IsReadonly());
   MOZ_ASSERT(DragEventHasSupportingData(aDragEvent));
 
   // If there is no source node, this is probably an external drag and the
   // drop is allowed. The later checks rely on checking if the drag target
   // is the same as the drag source.
   nsCOMPtr<nsINode> sourceNode =
       aDragEvent->GetDataTransfer()->GetMozSourceNode();
   if (!sourceNode) {
@@ -1081,18 +1081,18 @@ nsresult EditorEventListener::HandleChan
   if (DetachedFromEditor()) {
     return NS_OK;
   }
   RefPtr<TextEditor> textEditor = mEditorBase->AsTextEditor();
   if (!textEditor->IsAcceptableInputEvent(aCompositionChangeEvent)) {
     return NS_OK;
   }
 
-  // if we are readonly or disabled, then do nothing.
-  if (textEditor->IsReadonly() || textEditor->IsDisabled()) {
+  // if we are readonly, then do nothing.
+  if (textEditor->IsReadonly()) {
     return NS_OK;
   }
 
   nsresult rv = textEditor->OnCompositionChange(*aCompositionChangeEvent);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "TextEditor::OnCompositionChange() failed");
   return rv;
 }
@@ -1112,21 +1112,17 @@ void EditorEventListener::HandleEndCompo
   textEditor->OnCompositionEnd(*aCompositionEndEvent);
 }
 
 nsresult EditorEventListener::Focus(InternalFocusEvent* aFocusEvent) {
   if (NS_WARN_IF(!aFocusEvent) || DetachedFromEditor()) {
     return NS_OK;
   }
 
-  // Don't turn on selection and caret when the editor is disabled.
   RefPtr<EditorBase> editorBase(mEditorBase);
-  if (editorBase->IsDisabled()) {
-    return NS_OK;
-  }
 
   // Spell check a textarea the first time that it is focused.
   SpellCheckIfNeeded();
   if (!editorBase) {
     // In e10s, this can cause us to flush notifications, which can destroy
     // the node we're about to focus.
     return NS_OK;
   }
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -10,16 +10,19 @@
 #include "mozilla/dom/Element.h"
 #include "nsGkAtoms.h"
 
 class nsAtom;
 
 namespace mozilla {
 
 class HTMLEditUtils final {
+  using Element = dom::Element;
+  using Selection = dom::Selection;
+
  public:
   static bool IsInlineStyle(nsINode* aNode);
   /**
    * IsRemovableInlineStyleElement() returns true if aElement is an inline
    * element and can be removed or split to in order to modifying inline
    * styles.
    */
   static bool IsRemovableInlineStyleElement(dom::Element& aElement);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -657,17 +657,17 @@ nsresult HTMLEditor::MaybeCollapseSelect
                        "HTMLEditor::CollapseSelectionTo() failed");
   return rv;
 }
 
 nsresult HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
   // NOTE: When you change this method, you should also change:
   //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     // When we're not editable, the events are handled on EditorBase, so, we can
     // bypass TextEditor.
     nsresult rv = EditorBase::HandleKeyPressEvent(aKeyboardEvent);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "EditorBase::HandleKeyPressEvent() failed");
     return rv;
   }
 
@@ -1670,17 +1670,17 @@ nsresult HTMLEditor::InsertElementAtSele
     return EditorBase::ToGenericNSResult(rv);
   }
 
   DebugOnly<nsresult> rvIgnored = CommitComposition();
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                        "EditorBase::CommitComposition() failed, but ignored");
 
   // XXX Oh, this should be done before dispatching `beforeinput` event.
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   EditActionResult result = CanHandleHTMLEditSubAction();
   if (result.Failed() || result.Canceled()) {
     NS_WARNING_ASSERTION(result.Succeeded(),
                          "HTMLEditor::CanHandleHTMLEditSubAction() failed");
     return EditorBase::ToGenericNSResult(result.Rv());
@@ -5344,17 +5344,17 @@ bool HTMLEditor::IsAcceptableInputEvent(
   // because content editable element needs selection in itself for editing.
   // However, when we're not focused, it's not guaranteed.
   return IsActiveInDOMWindow();
 }
 
 nsresult HTMLEditor::GetPreferredIMEState(IMEState* aState) {
   // HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
   aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     aState->mEnabled = IMEState::DISABLED;
   } else {
     aState->mEnabled = IMEState::ENABLED;
   }
   return NS_OK;
 }
 
 already_AddRefed<Element> HTMLEditor::GetInputEventTargetElement() {
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -335,18 +335,18 @@ nsresult HTMLEditor::DoInsertHTMLWithCon
     }
     // collapse selection to beginning of deleted table content
     IgnoredErrorResult ignoredError;
     SelectionRefPtr()->CollapseToStart(ignoredError);
     NS_WARNING_ASSERTION(!ignoredError.Failed(),
                          "Selection::Collapse() failed, but ignored");
   }
 
-  // XXX Why don't we test these first?
-  if (IsReadonly() || IsDisabled()) {
+  // XXX Why don't we test this first?
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   EditActionResult result = CanHandleHTMLEditSubAction();
   if (result.Failed() || result.Canceled()) {
     NS_WARNING_ASSERTION(result.Succeeded(),
                          "HTMLEditor::CanHandleHTMLEditSubAction() failed");
     return result.Rv();
@@ -1974,17 +1974,17 @@ bool HTMLEditor::CanPasteTransferable(ns
 }
 
 nsresult HTMLEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
                                               bool aDispatchPasteEvent,
                                               nsIPrincipal* aPrincipal) {
   MOZ_ASSERT(aClipboardType == nsIClipboard::kGlobalClipboard ||
              aClipboardType == nsIClipboard::kSelectionClipboard);
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   AutoEditActionDataSetter editActionData(*this, EditAction::ePasteAsQuotation,
                                           aPrincipal);
   editActionData.InitializeDataTransferWithClipboard(
       SettingDataTransfer::eWithFormat, aClipboardType);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -2157,17 +2157,17 @@ nsresult HTMLEditor::PasteAsPlaintextQuo
                        "HTMLEditor::InsertAsPlaintextQuotation() failed");
   return rv;
 }
 
 nsresult HTMLEditor::InsertWithQuotationsAsSubAction(
     const nsAString& aQuotedText) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   EditActionResult result = CanHandleHTMLEditSubAction();
   if (result.Failed() || result.Canceled()) {
     NS_WARNING_ASSERTION(result.Succeeded(),
                          "HTMLEditor::CanHandleHTMLEditSubAction() failed");
     return result.Rv();
@@ -2400,17 +2400,17 @@ nsresult HTMLEditor::InsertAsPlaintextQu
                                                 bool aAddCites,
                                                 nsINode** aNodeInserted) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (aNodeInserted) {
     *aNodeInserted = nullptr;
   }
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   EditActionResult result = CanHandleHTMLEditSubAction();
   if (result.Failed() || result.Canceled()) {
     NS_WARNING_ASSERTION(result.Succeeded(),
                          "HTMLEditor::CanHandleHTMLEditSubAction() failed");
     return result.Rv();
@@ -2651,17 +2651,17 @@ NS_IMETHODIMP HTMLEditor::InsertAsCitedQ
 }
 
 nsresult HTMLEditor::InsertAsCitedQuotationInternal(
     const nsAString& aQuotedText, const nsAString& aCitation, bool aInsertHTML,
     nsINode** aNodeInserted) {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(!IsPlaintextEditor());
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   EditActionResult result = CanHandleHTMLEditSubAction();
   if (result.Failed() || result.Canceled()) {
     NS_WARNING_ASSERTION(result.Succeeded(),
                          "HTMLEditor::CanHandleHTMLEditSubAction() failed");
     return result.Rv();
--- a/editor/libeditor/TextEditSubActionHandler.cpp
+++ b/editor/libeditor/TextEditSubActionHandler.cpp
@@ -35,19 +35,19 @@
 #include "nsUnicharUtils.h"
 #include "nsIHTMLCollection.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
-#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
-  if (IsReadonly() || IsDisabled()) {                                          \
-    return EditActionCanceled(NS_OK);                                          \
+#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY \
+  if (IsReadonly()) {                                              \
+    return EditActionCanceled(NS_OK);                              \
   }
 
 nsresult TextEditor::InitEditorContentAndSelection() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   nsresult rv = MaybeCreatePaddingBRElementForEmptyEditor();
   if (NS_FAILED(rv)) {
     NS_WARNING(
@@ -184,17 +184,17 @@ nsresult TextEditor::OnEndHandlingTopLev
 
 EditActionResult TextEditor::InsertLineFeedCharacterAtSelection() {
   MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(!AsHTMLEditor());
   MOZ_ASSERT(!IsSingleLineEditor());
 
   UndefineCaretBidiLevel();
 
-  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
+  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
 
   if (mMaxTextLength >= 0) {
     nsAutoString insertionString(NS_LITERAL_STRING("\n"));
     EditActionResult result =
         TruncateInsertionStringForMaxLength(insertionString);
     if (result.Failed()) {
       NS_WARNING("TextEditor::TruncateInsertionStringForMaxLength() failed");
       return result;
@@ -482,17 +482,17 @@ EditActionResult TextEditor::HandleInser
     if (NS_FAILED(rv)) {
       NS_WARNING(
           "TextEditor::DeleteSelectionAsSubAction(eNone, eStrip) failed");
       return EditActionHandled(rv);
     }
   }
 
   // XXX Why don't we cancel here?  Shouldn't we do this first?
-  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
+  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
 
   MaybeDoAutoPasswordMasking();
 
   nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
   if (NS_FAILED(rv)) {
     NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
     return EditActionHandled(rv);
   }
@@ -625,17 +625,17 @@ EditActionResult TextEditor::SetTextWith
   MOZ_ASSERT(!IsUndoRedoEnabled());
   MOZ_ASSERT(GetEditAction() != EditAction::eReplaceText);
   MOZ_ASSERT(mMaxTextLength < 0);
   MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == kNotFound);
 
   UndefineCaretBidiLevel();
 
   // XXX If we're setting value, shouldn't we keep setting the new value here?
-  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
+  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
 
   MaybeDoAutoPasswordMasking();
 
   nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
   if (NS_FAILED(rv)) {
     NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
     return EditActionResult(rv);
   }
@@ -743,17 +743,17 @@ EditActionResult TextEditor::SetTextWith
 
 EditActionResult TextEditor::HandleDeleteSelection(
     nsIEditor::EDirection aDirectionAndAmount,
     nsIEditor::EStripWrappers aStripWrappers) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   UndefineCaretBidiLevel();
 
-  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
+  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY
 
   // if there is only padding <br> element for empty editor, cancel the
   // operation.
   if (mPaddingBRElementForEmptyEditor) {
     return EditActionCanceled();
   }
   EditActionResult result =
       HandleDeleteSelectionInternal(aDirectionAndAmount, aStripWrappers);
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -311,17 +311,17 @@ bool TextEditor::UpdateMetaCharset(Docum
 nsresult TextEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
   // NOTE: When you change this method, you should also change:
   //   * editor/libeditor/tests/test_texteditor_keyevent_handling.html
   //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
   //
   // And also when you add new key handling, you need to change the subclass's
   // HandleKeyPressEvent()'s switch statement.
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     // When we're not editable, the events handled on EditorBase.
     return EditorBase::HandleKeyPressEvent(aKeyboardEvent);
   }
 
   if (NS_WARN_IF(!aKeyboardEvent)) {
     return NS_ERROR_UNEXPECTED;
   }
   MOZ_ASSERT(aKeyboardEvent->mMessage == eKeyPress,
@@ -1489,17 +1489,17 @@ NS_IMETHODIMP TextEditor::GetTextLength(
     }
   }
 
   *aCount = totalLength;
   return NS_OK;
 }
 
 nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
-  if (aCount == 0 || IsReadonly() || IsDisabled()) {
+  if (aCount == 0 || IsReadonly()) {
     return NS_OK;
   }
 
   // If we don't have transaction in the undo stack, we shouldn't notify
   // anybody of trying to undo since it's not useful notification but we
   // need to pay some runtime cost.
   if (!CanUndo()) {
     return NS_OK;
@@ -1570,17 +1570,17 @@ nsresult TextEditor::UndoAsAction(uint32
     }
   }
 
   NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   return EditorBase::ToGenericNSResult(rv);
 }
 
 nsresult TextEditor::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
-  if (aCount == 0 || IsReadonly() || IsDisabled()) {
+  if (aCount == 0 || IsReadonly()) {
     return NS_OK;
   }
 
   // If we don't have transaction in the redo stack, we shouldn't notify
   // anybody of trying to redo since it's not useful notification but we
   // need to pay some runtime cost.
   if (!CanRedo()) {
     return NS_OK;
@@ -2011,17 +2011,17 @@ nsresult TextEditor::PasteAsQuotationAsA
                        "TextEditor::InsertWithQuotationsAsSubAction() failed");
   return EditorBase::ToGenericNSResult(rv);
 }
 
 nsresult TextEditor::InsertWithQuotationsAsSubAction(
     const nsAString& aQuotedText) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
-  if (IsReadonly() || IsDisabled()) {
+  if (IsReadonly()) {
     return NS_OK;
   }
 
   // Let the citer quote it for us:
   nsString quotedStuff;
   nsresult rv = InternetCiter::GetCiteString(aQuotedText, quotedStuff);
   if (NS_FAILED(rv)) {
     NS_WARNING("InternetCiter::GetCiteString() failed");
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -53,43 +53,41 @@ interface nsIEditor  : nsISupports
   // only plain text entry is allowed via events
   const long eEditorPlaintextMask       = 0x0001;
   // enter key and CR-LF handled specially
   const long eEditorSingleLineMask      = 0x0002;
   // text is not entered into content, only a representative character
   const long eEditorPasswordMask        = 0x0004;
   // editing events are disabled.  Editor may still accept focus.
   const long eEditorReadonlyMask        = 0x0008;
-  // all events are disabled (like scrolling).  Editor will not accept focus.
-  const long eEditorDisabledMask        = 0x0010;
   // text input is limited to certain character types, use mFilter
-  const long eEditorFilterInputMask     = 0x0020;
+  const long eEditorFilterInputMask     = 0x0010;
   // use mail-compose editing rules
-  const long eEditorMailMask            = 0x0040;
+  const long eEditorMailMask            = 0x0020;
   // allow the editor to set font: monospace on the root node
-  const long eEditorEnableWrapHackMask  = 0x0080;
+  const long eEditorEnableWrapHackMask  = 0x0040;
   // bit for widgets (form elements)
-  const long eEditorWidgetMask          = 0x0100;
+  const long eEditorWidgetMask          = 0x0080;
   // this HTML editor should not create css styles
-  const long eEditorNoCSSMask           = 0x0200;
+  const long eEditorNoCSSMask           = 0x0100;
   // whether HTML document specific actions are executed or not.
   // e.g., if this flag is set, the editor doesn't handle Tab key.
   // besides, anchors of HTML are not clickable.
-  const long eEditorAllowInteraction    = 0x0400;
+  const long eEditorAllowInteraction    = 0x0200;
   // when this is set, the characters in password editor are always masked.
   // see bug 530367 for the detail.
-  const long eEditorDontEchoPassword    = 0x0800;
+  const long eEditorDontEchoPassword    = 0x0400;
   // when this flag is set, the internal direction of the editor is RTL.
   // if neither of the direction flags are set, the direction is determined
   // from the text control's content node.
-  const long eEditorRightToLeft         = 0x1000;
+  const long eEditorRightToLeft         = 0x0800;
   // when this flag is set, the internal direction of the editor is LTR.
-  const long eEditorLeftToRight         = 0x2000;
+  const long eEditorLeftToRight         = 0x1000;
   // when this flag is set, the editor's text content is not spell checked.
-  const long eEditorSkipSpellCheck      = 0x4000;
+  const long eEditorSkipSpellCheck      = 0x2000;
 
   /*
    * The valid values for newlines handling.
    * Can't change the values unless we remove
    * use of the pref.
    */
   const long eNewlinesPasteIntact                = 0;
   const long eNewlinesPasteToFirst               = 1;
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -90,19 +90,19 @@ skip-if(Android) needs-focus == spellche
 skip-if(Android) needs-focus == spellcheck-non-latin-chinese-traditional.html spellcheck-non-latin-chinese-traditional-ref.html
 skip-if(Android) needs-focus == spellcheck-non-latin-hebrew.html spellcheck-non-latin-hebrew-ref.html
 skip-if(Android) needs-focus == spellcheck-non-latin-japanese.html spellcheck-non-latin-japanese-ref.html
 skip-if(Android) needs-focus == spellcheck-non-latin-korean.html spellcheck-non-latin-korean-ref.html
 == unneeded_scroll.html unneeded_scroll-ref.html
 == caret_on_presshell_reinit.html caret_on_presshell_reinit-ref.html
 fuzzy-if(browserIsRemote,0-255,0-3) asserts-if(browserIsRemote,0-3) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # bug 959132 for assertions
 fuzzy-if(asyncPan&&!layersGPUAccelerated,0-102,0-2824) == 642800.html 642800-ref.html
-== selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
-!= selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
-!= selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
+needs-focus == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
+needs-focus != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
+needs-focus != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
 == 672709.html 672709-ref.html
 == 338427-1.html 338427-1-ref.html
 needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html
 needs-focus == 338427-2.html 338427-2-ref.html
 needs-focus == 338427-3.html 338427-3-ref.html
 needs-focus == 462758-grabbers-resizers.html 462758-grabbers-resizers-ref.html
 == readwrite-non-editable.html readwrite-non-editable-ref.html
 == readwrite-editable.html readwrite-editable-ref.html
--- a/editor/reftests/selection_visibility_after_reframe-2.html
+++ b/editor/reftests/selection_visibility_after_reframe-2.html
@@ -1,11 +1,12 @@
 <!DOCTYPE html>
 <html>
   <body>
     <input value="foo">
     <script>
       var i = document.querySelector("input");
+      i.focus();
       i.selectionStart = 1;
       i.selectionEnd = 2;
     </script>
   </body>
 </html>
--- a/editor/reftests/selection_visibility_after_reframe-3.html
+++ b/editor/reftests/selection_visibility_after_reframe-3.html
@@ -1,14 +1,15 @@
 <!DOCTYPE html>
 <html>
   <body>
     <input value="foo">
     <script>
       var i = document.querySelector("input");
+      i.focus();
       i.selectionStart = 1;
       i.selectionEnd = 2;
       i.style.display = "none";
       document.body.clientHeight;
       i.style.display = "";
     </script>
   </body>
 </html>
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -62,18 +62,17 @@ class APZSampler {
       const wr::DocumentId& aRenderRootId,
       const wr::WrPipelineIdEpochs* aEpochsBeingRendered);
 
   void SetSampleTime(const TimeStamp& aSampleTime);
   void SampleForWebRender(wr::TransactionWrapper& aTxn,
                           wr::RenderRoot aRenderRoot,
                           const wr::WrPipelineIdEpochs* aEpochsBeingRendered);
 
-  bool SampleAnimations(const LayerMetricsWrapper& aLayer,
-                        const TimeStamp& aSampleTime);
+  bool AdvanceAnimations(const TimeStamp& aSampleTime);
 
   /**
    * Compute the updated shadow transform for a scroll thumb layer that
    * reflects async scrolling of the associated scroll frame.
    *
    * Refer to APZCTreeManager::ComputeTransformForScrollThumb for the
    * description of parameters. The only difference is that this function takes
    * |aContent| instead of |aApzc| and |aMetrics|; aContent is the
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -106,17 +106,17 @@ struct APZCTreeManager::TreeBuildingStat
   nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
 
   // This map is populated as we place APZCs into the new tree. Its purpose is
   // to facilitate re-using the same APZC for different layers that scroll
   // together (and thus have the same ScrollableLayerGuid). The presShellId
   // doesn't matter for this purpose, and we move the map to the APZCTreeManager
   // after we're done building, so it's useful to have the presshell-ignoring
   // map for that.
-  std::unordered_map<ScrollableLayerGuid, RefPtr<AsyncPanZoomController>,
+  std::unordered_map<ScrollableLayerGuid, ApzcMapData,
                      ScrollableLayerGuid::HashIgnoringPresShellFn,
                      ScrollableLayerGuid::EqualIgnoringPresShellFn>
       mApzcMap;
 
   // This is populated with all the HitTestingTreeNodes that are scroll thumbs
   // and have a scrollthumb animation id (which indicates that they need to be
   // sampled for WebRender on the sampler thread).
   std::vector<HitTestingTreeNode*> mScrollThumbs;
@@ -607,16 +607,22 @@ APZCTreeManager::UpdateHitTestingTreeImp
 
   // We do not support tree structures where the root node has siblings.
   MOZ_ASSERT(!(mRootNode && mRootNode->GetPrevSibling()));
 
   {  // scope lock and update our mApzcMap before we destroy all the unused
     // APZC instances
     MutexAutoLock lock(mMapLock);
     mApzcMap = std::move(state.mApzcMap);
+
+    for (auto& mapping : mApzcMap) {
+      AsyncPanZoomController* parent = mapping.second.apzc->GetParent();
+      mapping.second.parent = parent ? Some(parent->GetGuid()) : Nothing();
+    }
+
     mScrollThumbInfo.clear();
     // For non-webrender, state.mScrollThumbs will be empty so this will be a
     // no-op.
     for (HitTestingTreeNode* thumb : state.mScrollThumbs) {
       MOZ_ASSERT(thumb->IsScrollThumbNode());
       ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0,
                                      thumb->GetScrollTargetId());
       auto it = state.mScrollTargets.find(targetGuid);
@@ -708,17 +714,17 @@ void APZCTreeManager::SampleForWebRender
     const wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
   AssertOnSamplerThread();
   MutexAutoLock lock(mMapLock);
 
   nsTArray<wr::WrTransformProperty> transforms;
 
   // Sample async transforms on scrollable layers.
   for (const auto& mapping : mApzcMap) {
-    AsyncPanZoomController* apzc = mapping.second;
+    AsyncPanZoomController* apzc = mapping.second.apzc;
     if (apzc->GetRenderRoot() != aRenderRoot) {
       // If this APZC belongs to a different render root, skip over it
       continue;
     }
 
     const AsyncTransformComponents asyncTransformComponents =
         apzc->GetZoomAnimationId()
             ? AsyncTransformComponents{AsyncTransformComponent::eLayout}
@@ -783,18 +789,16 @@ void APZCTreeManager::SampleForWebRender
     // move down (or to the right), and that corresponds to a reduction in
     // the scroll offset. Since we are effectively giving WR the async
     // scroll delta here, we want to negate the translation.
     LayoutDevicePoint asyncScrollDelta = -layerTranslation / resolution;
     aTxn.UpdateScrollPosition(wr::AsPipelineId(apzc->GetGuid().mLayersId),
                               apzc->GetGuid().mScrollId,
                               wr::ToLayoutPoint(asyncScrollDelta));
 
-    apzc->ReportCheckerboard(aSampleTime);
-
 #if defined(MOZ_WIDGET_ANDROID)
     // Send the root frame metrics to java through the UIController
     RefPtr<UiCompositorControllerParent> uiController =
         UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayersId);
     if (uiController &&
         apzc->UpdateRootFrameMetricsIfChanged(mLastRootMetrics)) {
       uiController->NotifyUpdateScreenMetrics(mLastRootMetrics);
     }
@@ -806,17 +810,17 @@ void APZCTreeManager::SampleForWebRender
     auto it = mApzcMap.find(info.mTargetGuid);
     if (it == mApzcMap.end()) {
       // It could be that |info| is a scrollthumb for content which didn't
       // have an APZC, for example if the content isn't layerized. Regardless,
       // we can't async-scroll it so we don't need to worry about putting it
       // in mScrollThumbInfo.
       continue;
     }
-    AsyncPanZoomController* scrollTargetApzc = it->second;
+    AsyncPanZoomController* scrollTargetApzc = it->second.apzc;
     MOZ_ASSERT(scrollTargetApzc);
     if (scrollTargetApzc->GetRenderRoot() != aRenderRoot) {
       // If this APZC belongs to a different render root, skip over it
       continue;
     }
     LayerToParentLayerMatrix4x4 transform =
         scrollTargetApzc->CallWithLastContentPaintMetrics(
             [&](const FrameMetrics& aMetrics) {
@@ -861,38 +865,119 @@ void APZCTreeManager::SampleForWebRender
   }
 
   aTxn.AppendTransformProperties(transforms);
 
   // Advance animations. It's important that this happens after
   // sampling all async transforms, because AdvanceAnimations() updates
   // the effective scroll offset to the value it should have for the *next*
   // composite after this one (if the APZ frame delay is enabled).
-  bool activeAnimations = false;
-  for (const auto& mapping : mApzcMap) {
-    AsyncPanZoomController* apzc = mapping.second;
-    if (apzc->GetRenderRoot() != aRenderRoot) {
-      // If this APZC belongs to a different render root, skip over it
-      continue;
-    }
-    activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
-  }
+  bool activeAnimations =
+      AdvanceAnimationsInternal(lock, Some(aRenderRoot), aSampleTime);
   if (activeAnimations) {
     RefPtr<CompositorController> controller;
     CompositorBridgeParent::CallWithIndirectShadowTree(
         mRootLayersId, [&](LayerTreeState& aState) -> void {
           controller = aState.GetCompositorController();
         });
     if (controller) {
       controller->ScheduleRenderOnCompositorThread(
           wr::RenderRootSet(aRenderRoot));
     }
   }
 }
 
+bool APZCTreeManager::AdvanceAnimations(Maybe<wr::RenderRoot> aRenderRoot,
+                                        const TimeStamp& aSampleTime) {
+  MutexAutoLock lock(mMapLock);
+  return AdvanceAnimationsInternal(lock, std::move(aRenderRoot), aSampleTime);
+}
+
+ParentLayerRect APZCTreeManager::ComputeClippedCompositionBounds(
+    const MutexAutoLock& aProofOfMapLock, ClippedCompositionBoundsMap& aDestMap,
+    ScrollableLayerGuid aGuid) {
+  auto insertResult = aDestMap.insert(std::make_pair(aGuid, ParentLayerRect()));
+  if (!insertResult.second) {
+    // We already computed it for this one, early-exit. This might happen
+    // because on a later iteration of mApzcMap we might encounter an ancestor
+    // of an APZC that we processed on an earlier iteration. In this case we
+    // would have computed the ancestor's clipped composition bounds when
+    // recursing up on the earlier iteration.
+    return insertResult.first->second;
+  }
+
+  ParentLayerRect bounds = mApzcMap[aGuid].apzc->GetCompositionBounds();
+  const auto& mapEntry = mApzcMap.find(aGuid);
+  MOZ_ASSERT(mapEntry != mApzcMap.end());
+  if (mapEntry->second.parent.isNothing()) {
+    // Recursion base case, where the APZC with guid `aGuid` has no parent.
+    // In this case, we don't need to clip `bounds` any further and can just
+    // early exit.
+    insertResult.first->second = bounds;
+    return bounds;
+  }
+
+  ScrollableLayerGuid parentGuid = mapEntry->second.parent.value();
+  auto parentBoundsEntry = aDestMap.find(parentGuid);
+  // If aDestMap doesn't contain the parent entry yet, we recurse to compute
+  // that one first.
+  ParentLayerRect parentClippedBounds =
+      (parentBoundsEntry == aDestMap.end())
+          ? ComputeClippedCompositionBounds(aProofOfMapLock, aDestMap,
+                                            parentGuid)
+          : parentBoundsEntry->second;
+
+  // The parent layer's async transform applies to the current layer to take
+  // `bounds` into the same coordinate space as `parentClippedBounds`. However,
+  // we're going to do the inverse operation and unapply this transform to
+  // `parentClippedBounds` to bring it into the same coordinate space as
+  // `bounds`.
+  AsyncTransform appliesToLayer =
+      mApzcMap[parentGuid].apzc->GetCurrentAsyncTransform(
+          AsyncPanZoomController::eForCompositing);
+
+  // Do the unapplication
+  LayerRect parentClippedBoundsInParentLayerSpace =
+      (parentClippedBounds - appliesToLayer.mTranslation) /
+      appliesToLayer.mScale;
+
+  // And then clip `bounds` by the parent's comp bounds in the current space.
+  bounds = bounds.Intersect(
+      ViewAs<ParentLayerPixel>(parentClippedBoundsInParentLayerSpace,
+                               PixelCastJustification::MovingDownToChildren));
+
+  // Done!
+  insertResult.first->second = bounds;
+  return bounds;
+}
+
+bool APZCTreeManager::AdvanceAnimationsInternal(
+    const MutexAutoLock& aProofOfMapLock, Maybe<wr::RenderRoot> aRenderRoot,
+    const TimeStamp& aSampleTime) {
+  ClippedCompositionBoundsMap clippedCompBounds;
+  bool activeAnimations = false;
+  for (const auto& mapping : mApzcMap) {
+    AsyncPanZoomController* apzc = mapping.second.apzc;
+    if (aRenderRoot && apzc->GetRenderRoot() != *aRenderRoot) {
+      // If this APZC belongs to a different render root, skip over it
+      continue;
+    }
+
+    // Note that this call is recursive, but it early-exits if called again
+    // with the same guid. So this loop is still amortized O(n) with respect to
+    // the number of APZCs.
+    ParentLayerRect clippedBounds = ComputeClippedCompositionBounds(
+        aProofOfMapLock, clippedCompBounds, mapping.first);
+
+    apzc->ReportCheckerboard(aSampleTime, clippedBounds);
+    activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
+  }
+  return activeAnimations;
+}
+
 // Compute the clip region to be used for a layer with an APZC. This function
 // is only called for layers which actually have scrollable metrics and an APZC.
 template <class ScrollNode>
 Maybe<ParentLayerIntRegion> APZCTreeManager::ComputeClipRegion(
     const ScrollNode& aLayer) {
   Maybe<ParentLayerIntRegion> clipRegion;
   if (aLayer.GetClipRect()) {
     clipRegion.emplace(*aLayer.GetClipRect());
@@ -1137,20 +1222,21 @@ HitTestingTreeNode* APZCTreeManager::Pre
   // it has an APZC instance to manage its scrolling.
 
   // aState.mApzcMap allows reusing the exact same APZC instance for different
   // layers with the same FrameMetrics data. This is needed because in some
   // cases content that is supposed to scroll together is split into multiple
   // layers because of e.g. non-scrolling content interleaved in z-index order.
   ScrollableLayerGuid guid(aLayersId, aMetrics.GetPresShellId(),
                            aMetrics.GetScrollId());
-  auto insertResult = aState.mApzcMap.insert(
-      std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
+  auto insertResult = aState.mApzcMap.insert(std::make_pair(
+      guid,
+      ApzcMapData{static_cast<AsyncPanZoomController*>(nullptr), Nothing()}));
   if (!insertResult.second) {
-    apzc = insertResult.first->second;
+    apzc = insertResult.first->second.apzc;
     PrintAPZCInfo(aLayer, apzc);
   }
   APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64
              "\n",
              apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId);
 
   // If we haven't encountered a layer already with the same metrics, then we
   // need to do the full reuse-or-make-an-APZC algorithm, which is contained
@@ -1292,17 +1378,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
         // APZCs for that subtree allow double-tap to zoom.
         apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
       }
       // Otherwise, this is the root of a layers id, but we didn't have a saved
       // zoom constraints. Leave it empty for now.
     }
 
     // Add a guid -> APZC mapping for the newly created APZC.
-    insertResult.first->second = apzc;
+    insertResult.first->second.apzc = apzc;
   } else {
     // We already built an APZC earlier in this tree walk, but we have another
     // layer now that will also be using that APZC. The hit-test region on the
     // APZC needs to be updated to deal with the new layer's hit region.
 
     node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId);
     AttachNodeToTree(node, aParent, aNextSibling);
 
@@ -2711,17 +2797,17 @@ static bool GuidComparatorIgnoringPresSh
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC(
     const LayersId& aLayersId,
     const ScrollableLayerGuid::ViewID& aScrollId) const {
   MutexAutoLock lock(mMapLock);
   ScrollableLayerGuid guid(aLayersId, 0, aScrollId);
   auto it = mApzcMap.find(guid);
   RefPtr<AsyncPanZoomController> apzc =
-      (it != mApzcMap.end() ? it->second : nullptr);
+      (it != mApzcMap.end() ? it->second.apzc : nullptr);
   return apzc.forget();
 }
 
 already_AddRefed<HitTestingTreeNode> APZCTreeManager::GetTargetNode(
     const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const {
   mTreeLock.AssertCurrentThreadIn();
   RefPtr<HitTestingTreeNode> target =
       DepthFirstSearchPostOrder<ReverseIterator>(
@@ -3562,27 +3648,30 @@ bool APZCTreeManager::GetAPZTestData(Lay
     auto it = mTestData.find(aLayersId);
     if (it == mTestData.end()) {
       return false;
     }
     *aOutData = *(it->second);
   }
 
   {  // add some additional "current state" into the returned APZTestData
-    RecursiveMutexAutoLock treeLock(
-        mTreeLock);                   // for IsCurrentlyCheckerboarding
-    MutexAutoLock mapLock(mMapLock);  // for mApzcMap
+    MutexAutoLock mapLock(mMapLock);
+
+    ClippedCompositionBoundsMap clippedCompBounds;
     for (const auto& mapping : mApzcMap) {
       if (mapping.first.mLayersId != aLayersId) {
         continue;
       }
-      AsyncPanZoomController* apzc = mapping.second;
+
+      ParentLayerRect clippedBounds = ComputeClippedCompositionBounds(
+          mapLock, clippedCompBounds, mapping.first);
+      AsyncPanZoomController* apzc = mapping.second.apzc;
       std::string viewId = std::to_string(mapping.first.mScrollId);
       std::string apzcState;
-      if (apzc->IsCurrentlyCheckerboarding()) {
+      if (apzc->GetCheckerboardMagnitude(clippedBounds)) {
         apzcState += "checkerboarding,";
       }
       aOutData->RecordAdditionalData(viewId, apzcState);
     }
   }
   return true;
 }
 
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -200,16 +200,24 @@ class APZCTreeManager : public IAPZCTree
    * from APZC instances in that render root.
    */
   void SampleForWebRender(wr::TransactionWrapper& aTxn,
                           const TimeStamp& aSampleTime,
                           wr::RenderRoot aRenderRoot,
                           const wr::WrPipelineIdEpochs* aEpochsBeingRendered);
 
   /**
+   * Walk through all the APZCs and do the sampling steps needed when
+   * advancing to the next frame. The APZCs walked can be restricted to a
+   * specific render root by providing that as the first argument.
+   */
+  bool AdvanceAnimations(Maybe<wr::RenderRoot> aRenderRoot,
+                         const TimeStamp& aSampleTime);
+
+  /**
    * Refer to the documentation of APZInputBridge::ReceiveInputEvent() and
    * APZEventResult.
    */
   APZEventResult ReceiveInputEvent(InputData& aEvent) override;
 
   /**
    * Set the keyboard shortcuts to use for translating keyboard events.
    */
@@ -738,16 +746,33 @@ class APZCTreeManager : public IAPZCTree
   // Requires the caller to hold mTreeLock.
   LayerToParentLayerMatrix4x4 ComputeTransformForNode(
       const HitTestingTreeNode* aNode) const;
 
   // Look up the GeckoContentController for the given layers id.
   static already_AddRefed<GeckoContentController> GetContentController(
       LayersId aLayersId);
 
+  bool AdvanceAnimationsInternal(const MutexAutoLock& aProofOfMapLock,
+                                 Maybe<wr::RenderRoot> aRenderRoot,
+                                 const TimeStamp& aSampleTime);
+
+  using ClippedCompositionBoundsMap =
+      std::unordered_map<ScrollableLayerGuid, ParentLayerRect,
+                         ScrollableLayerGuid::HashIgnoringPresShellFn,
+                         ScrollableLayerGuid::EqualIgnoringPresShellFn>;
+  // This is a recursive function that populates `aDestMap` with the clipped
+  // composition bounds for the APZC corresponding to `aGuid` and returns those
+  // bounds as a convenience. It recurses to also populate `aDestMap` with that
+  // APZC's ancestors. In order to do this it needs to access mApzcMap
+  // and therefore requires the caller to hold the map lock.
+  ParentLayerRect ComputeClippedCompositionBounds(
+      const MutexAutoLock& aProofOfMapLock,
+      ClippedCompositionBoundsMap& aDestMap, ScrollableLayerGuid aGuid);
+
  protected:
   /* The input queue where input events are held until we know enough to
    * figure out where they're going. Protected so gtests can access it.
    */
   RefPtr<InputQueue> mInputQueue;
 
  private:
   /* Layers id for the root CompositorBridgeParent that owns this
@@ -792,22 +817,34 @@ class APZCTreeManager : public IAPZCTree
    * node.
    */
   bool mUsingAsyncZoomContainer;
 
   /** A lock that protects mApzcMap, mScrollThumbInfo, mRootScrollbarInfo, and
    * mFixedPositionInfo.
    */
   mutable mozilla::Mutex mMapLock;
+
   /**
-   * A map for quick access to get APZC instances by guid, without having to
+   * Helper structure to store a bunch of things in mApzcMap so that they can
+   * be used from the sampler thread.
+   */
+  struct ApzcMapData {
+    // A pointer to the APZC itself
+    RefPtr<AsyncPanZoomController> apzc;
+    // The parent APZC's guid, or Nothing() if there is no parent
+    Maybe<ScrollableLayerGuid> parent;
+  };
+
+  /**
+   * A map for quick access to get some APZC data by guid, without having to
    * acquire the tree lock. mMapLock must be acquired while accessing or
    * modifying mApzcMap.
    */
-  std::unordered_map<ScrollableLayerGuid, RefPtr<AsyncPanZoomController>,
+  std::unordered_map<ScrollableLayerGuid, ApzcMapData,
                      ScrollableLayerGuid::HashIgnoringPresShellFn,
                      ScrollableLayerGuid::EqualIgnoringPresShellFn>
       mApzcMap;
   /**
    * A helper structure to store all the information needed to compute the
    * async transform for a scrollthumb on the sampler thread.
    */
   struct ScrollThumbInfo {
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -94,36 +94,20 @@ void APZSampler::SampleForWebRender(
     // WebRenderBridgeParent hasn't yet provided us with a sample time.
     // If we're that early there probably aren't any APZ animations happening
     // anyway, so using Timestamp::Now() should be fine.
     sampleTime = mSampleTime.IsNull() ? TimeStamp::Now() : mSampleTime;
   }
   mApz->SampleForWebRender(aTxn, sampleTime, aRenderRoot, aEpochsBeingRendered);
 }
 
-bool APZSampler::SampleAnimations(const LayerMetricsWrapper& aLayer,
-                                  const TimeStamp& aSampleTime) {
+bool APZSampler::AdvanceAnimations(const TimeStamp& aSampleTime) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
-
-  // TODO: eventually we can drop the aLayer argument and just walk the APZ
-  // tree directly in mApz.
-
-  bool activeAnimations = false;
-
-  ForEachNodePostOrder<ForwardIterator>(
-      aLayer,
-      [&activeAnimations, &aSampleTime](LayerMetricsWrapper aLayerMetrics) {
-        if (AsyncPanZoomController* apzc = aLayerMetrics.GetApzc()) {
-          apzc->ReportCheckerboard(aSampleTime);
-          activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
-        }
-      });
-
-  return activeAnimations;
+  return mApz->AdvanceAnimations(Nothing(), aSampleTime);
 }
 
 LayerToParentLayerMatrix4x4 APZSampler::ComputeTransformForScrollThumb(
     const LayerToParentLayerMatrix4x4& aCurrentTransform,
     const LayerMetricsWrapper& aContent, const ScrollbarData& aThumbData,
     bool aScrollbarIsDescendant,
     AsyncTransformComponentMatrix* aOutClipTransform) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -4322,114 +4322,73 @@ CSSRect AsyncPanZoomController::GetVisib
   AutoApplyAsyncTestAttributes testAttributeApplier(this, aProofOfLock);
   CSSPoint currentScrollOffset =
       GetEffectiveScrollOffset(AsyncPanZoomController::eForCompositing);
   CSSRect visible = CSSRect(currentScrollOffset,
                             Metrics().CalculateCompositedSizeInCssPixels());
   return visible;
 }
 
-ParentLayerRect AsyncPanZoomController::RecursivelyClipCompBounds(
-    const ParentLayerRect& aChildCompBounds) const {
-  // The childCompBounds is in the ParentLayer space of a child layer, which
-  // is the Layer space of this layer, so we can cast it.
-  LayerRect compBoundsInLayerSpace = ViewAs<LayerPixel>(
-      aChildCompBounds, PixelCastJustification::MovingDownToChildren);
-
-  // Apply the async transform from this layer and then clip with this
-  // layer's composition bounds.
-  AsyncTransform appliesToLayer =
-      GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing);
-  ParentLayerRect compBoundsInParentSpace =
-      (compBoundsInLayerSpace * appliesToLayer.mScale) +
-      appliesToLayer.mTranslation;
-
-  {  // hold lock while reading Metrics()
-    RecursiveMutexAutoLock lock(mRecursiveMutex);
-    compBoundsInParentSpace =
-        compBoundsInParentSpace.Intersect(Metrics().GetCompositionBounds());
-  }
-
-  // Recurse up the tree
-  if (mParent) {
-    // Make sure we're not holding our lock when we do this, to be extra safe.
-    mRecursiveMutex.AssertNotCurrentThreadIn();
-    compBoundsInParentSpace =
-        mParent->RecursivelyClipCompBounds(compBoundsInParentSpace);
-  }
-
-  // Undo async transformation from above to produce return value in the same
-  // coordinate space as the input parameter.
-  compBoundsInLayerSpace =
-      (compBoundsInParentSpace - appliesToLayer.mTranslation) /
-      appliesToLayer.mScale;
-  return ViewAs<ParentLayerPixel>(compBoundsInLayerSpace,
-                                  PixelCastJustification::MovingDownToChildren);
-}
-
-CSSRect AsyncPanZoomController::GetRecursivelyVisibleRect() const {
-  CSSRect visible;
-  ParentLayerRect compBounds;
-  CSSToParentLayerScale2D zoom;
-
-  {  // scope mutex
-    RecursiveMutexAutoLock lock(mRecursiveMutex);
-    visible = GetVisibleRect(lock);  // relative to scrolled frame origin
-    compBounds = Metrics().GetCompositionBounds();
-    zoom = Metrics().GetZoom();
-  }
-
-  if (mParent) {
-    // compBounds and clippedCompBounds are relative to the layer tree origin
-    ParentLayerRect clippedCompBounds =
-        mParent->RecursivelyClipCompBounds(compBounds);
-
-    // the "*RelativeToItself*" variables are relative to the comp bounds origin
-    ParentLayerRect visiblePartOfCompBoundsRelativeToItself =
-        clippedCompBounds - compBounds.TopLeft();
-
-    CSSRect visiblePartOfCompBoundsRelativeToItselfInCssSpace =
-        (visiblePartOfCompBoundsRelativeToItself / zoom);
-
-    // this one is relative to the scrolled frame origin, same as `visible`
-    CSSRect visiblePartOfCompBoundsInCssSpace =
-        visiblePartOfCompBoundsRelativeToItselfInCssSpace + visible.TopLeft();
-
-    visible = visible.Intersect(visiblePartOfCompBoundsInCssSpace);
-  }
-
-  return visible;
-}
-
-uint32_t AsyncPanZoomController::GetCheckerboardMagnitude() const {
+uint32_t AsyncPanZoomController::GetCheckerboardMagnitude(
+    const ParentLayerRect& aClippedCompositionBounds) const {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() +
                     mLastContentPaintMetrics.GetScrollOffset();
-  CSSRect visible = GetVisibleRect(lock);
+  painted.Inflate(CSSMargin::FromAppUnits(
+      nsMargin(1, 1, 1, 1)));  // fuzz for rounding error
+
+  CSSRect visible = GetVisibleRect(lock);  // relative to scrolled frame origin
+  if (visible.IsEmpty() || painted.Contains(visible)) {
+    // early-exit if we're definitely not checkerboarding
+    return 0;
+  }
+
+  // aClippedCompositionBounds and Metrics().GetCompositionBounds() are both
+  // relative to the layer tree origin.
+  // The "*RelativeToItself*" variables are relative to the comp bounds origin
+  ParentLayerRect visiblePartOfCompBoundsRelativeToItself =
+      aClippedCompositionBounds - Metrics().GetCompositionBounds().TopLeft();
+
+  CSSRect visiblePartOfCompBoundsRelativeToItselfInCssSpace =
+      (visiblePartOfCompBoundsRelativeToItself / Metrics().GetZoom());
+
+  // This one is relative to the scrolled frame origin, same as `visible`
+  CSSRect visiblePartOfCompBoundsInCssSpace =
+      visiblePartOfCompBoundsRelativeToItselfInCssSpace + visible.TopLeft();
+
+  visible = visible.Intersect(visiblePartOfCompBoundsInCssSpace);
 
   CSSIntRegion checkerboard;
   // Round so as to minimize checkerboarding; if we're only showing fractional
   // pixels of checkerboarding it's not really worth counting
   checkerboard.Sub(RoundedIn(visible), RoundedOut(painted));
-  return checkerboard.Area();
-}
-
-void AsyncPanZoomController::ReportCheckerboard(const TimeStamp& aSampleTime) {
+  uint32_t area = checkerboard.Area();
+  if (area) {
+    APZC_LOG_FM(Metrics(),
+                "%p is currently checkerboarding (painted %s visible %s)", this,
+                Stringify(painted).c_str(), Stringify(visible).c_str());
+  }
+  return area;
+}
+
+void AsyncPanZoomController::ReportCheckerboard(
+    const TimeStamp& aSampleTime,
+    const ParentLayerRect& aClippedCompositionBounds) {
   if (mLastCheckerboardReport == aSampleTime) {
     // This function will get called multiple times for each APZC on a single
     // composite (once for each layer it is attached to). Only report the
     // checkerboard once per composite though.
     return;
   }
   mLastCheckerboardReport = aSampleTime;
 
   bool recordTrace = StaticPrefs::apz_record_checkerboarding();
   bool forTelemetry = Telemetry::CanRecordExtended();
-  uint32_t magnitude = GetCheckerboardMagnitude();
+  uint32_t magnitude = GetCheckerboardMagnitude(aClippedCompositionBounds);
 
   // IsInTransformingState() acquires the APZC lock and thus needs to
   // be called before acquiring mCheckerboardEventLock.
   bool inTransformingState = IsInTransformingState();
 
   MutexAutoLock lock(mCheckerboardEventLock);
   if (!mCheckerboardEvent && (recordTrace || forTelemetry)) {
     mCheckerboardEvent = MakeUnique<CheckerboardEvent>(recordTrace);
@@ -4469,36 +4428,16 @@ void AsyncPanZoomController::UpdateCheck
 
 void AsyncPanZoomController::FlushActiveCheckerboardReport() {
   MutexAutoLock lock(mCheckerboardEventLock);
   // Pretend like we got a frame with 0 pixels checkerboarded. This will
   // terminate the checkerboard event and flush it out
   UpdateCheckerboardEvent(lock, 0);
 }
 
-bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
-  CSSRect painted;
-  {  // scope lock
-    RecursiveMutexAutoLock lock(mRecursiveMutex);
-    painted = mLastContentPaintMetrics.GetDisplayPort() +
-              mLastContentPaintMetrics.GetScrollOffset();
-  }
-
-  painted.Inflate(CSSMargin::FromAppUnits(
-      nsMargin(1, 1, 1, 1)));  // fuzz for rounding error
-  CSSRect visible = GetRecursivelyVisibleRect();
-  if (visible.IsEmpty() || painted.Contains(visible)) {
-    return false;
-  }
-  APZC_LOG_FM(Metrics(),
-              "%p is currently checkerboarding (painted %s visible %s)", this,
-              Stringify(painted).c_str(), Stringify(visible).c_str());
-  return true;
-}
-
 void AsyncPanZoomController::NotifyLayersUpdated(
     const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
     bool aThisLayerTreeUpdated) {
   AssertOnUpdaterThread();
 
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   bool isDefault = mScrollMetadata.IsDefault();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -304,43 +304,41 @@ class AsyncPanZoomController {
    * we asked gecko to paint. In cases where that last request has not yet been
    * processed, this is needed to transform input events properly into a space
    * gecko will understand.
    */
   Matrix4x4 GetTransformToLastDispatchedPaint() const;
 
   /**
    * Returns the number of CSS pixels of checkerboard according to the metrics
-   * in this APZC.
+   * in this APZC. The argument provided by the caller is the composition bounds
+   * of this APZC, additionally clipped by the composition bounds of any
+   * ancestor APZCs, accounting for all the async transforms.
    */
-  uint32_t GetCheckerboardMagnitude() const;
+  uint32_t GetCheckerboardMagnitude(
+      const ParentLayerRect& aClippedCompositionBounds) const;
 
   /**
    * Report the number of CSSPixel-milliseconds of checkerboard to telemetry.
+   * See GetCheckerboardMagnitude for documentation of the
+   * aClippedCompositionBounds argument that needs to be provided by the caller.
    */
-  void ReportCheckerboard(const TimeStamp& aSampleTime);
+  void ReportCheckerboard(const TimeStamp& aSampleTime,
+                          const ParentLayerRect& aClippedCompositionBounds);
 
   /**
    * Flush any active checkerboard report that's in progress. This basically
    * pretends like any in-progress checkerboard event has terminated, and pushes
    * out the report to the checkerboard reporting service and telemetry. If the
    * checkerboard event has not really finished, it will start a new event
    * on the next composite.
    */
   void FlushActiveCheckerboardReport();
 
   /**
-   * Returns whether or not the APZC is currently in a state of checkerboarding.
-   * This is a simple computation based on the last-painted content and whether
-   * the async transform has pushed it so far that it doesn't fully contain the
-   * composition bounds.
-   */
-  bool IsCurrentlyCheckerboarding() const;
-
-  /**
    * Recalculates the displayport. Ideally, this should paint an area bigger
    * than the composite-to dimensions so that when you scroll down, you don't
    * checkerboard immediately. This includes a bunch of logic, including
    * algorithms to bias painting in the direction of the velocity.
    */
   static const ScreenMargin CalculatePendingDisplayPort(
       const FrameMetrics& aFrameMetrics, const ParentLayerPoint& aVelocity);
 
@@ -1209,35 +1207,16 @@ class AsyncPanZoomController {
   CSSPoint GetEffectiveScrollOffset(AsyncTransformConsumer aMode) const;
   CSSToParentLayerScale2D GetEffectiveZoom(AsyncTransformConsumer aMode) const;
 
   /**
    * Returns the visible portion of the content scrolled by this APZC, in
    * CSS pixels. The caller must have acquired the mRecursiveMutex lock.
    */
   CSSRect GetVisibleRect(const RecursiveMutexAutoLock& aProofOfLock) const;
-  /**
-   * This returns the composition bounds of this APZC, but accounting for
-   * ancestor state. It's possible to have a scrollable frame with giant
-   * composition bounds nested inside a scrollable frame with smaller
-   * composition bounds, or even scrolled out of view entirely by an
-   * ancestor scrollable frame. This function accounts for those possibilities,
-   * by walking up to the root of the tree and clipping the composition bounds
-   * needed by the state of ancestor scrollframes. The returned value is
-   * in the same coordinate space as the composition bounds, and is guaranteed
-   * to be contained by the composition bounds.
-   */
-  ParentLayerRect RecursivelyClipCompBounds(
-      const ParentLayerRect& aChildCompBounds) const;
-  /**
-   * Similar to GetVisibleRect, but this accounts for ancestor APZC's
-   * composition bounds as well. Conceptually this is the intersection of
-   * GetVisibleRect() with RecursivelyClipCompBounds().
-   */
-  CSSRect GetRecursivelyVisibleRect() const;
 
  private:
   friend class AutoApplyAsyncTestAttributes;
 
   /**
    * Applies |mTestAsyncScrollOffset| and |mTestAsyncZoom| to this
    * AsyncPanZoomController. Calls |SampleCompositedAsyncTransform| to ensure
    * that the GetCurrentAsync* functions consider the test offset and zoom in
--- a/gfx/layers/apz/src/CheckerboardEvent.cpp
+++ b/gfx/layers/apz/src/CheckerboardEvent.cpp
@@ -3,16 +3,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 "CheckerboardEvent.h"
 
 #include <algorithm>  // for std::sort
 
+static mozilla::LazyLogModule sApzCheckLog("apz.checkerboard");
+
 namespace mozilla {
 namespace layers {
 
 // Relatively arbitrary limit to prevent a perma-checkerboard event from
 // eating up gobs of memory. Ideally we shouldn't have perma-checkerboarding
 // but better to guard against it.
 #define LOG_LENGTH_LIMIT (50 * 1024)
 
@@ -115,16 +117,17 @@ bool CheckerboardEvent::RecordFrameInfo(
     }
     MOZ_ASSERT(!mCheckerboardingActive);
   }
   mLastSampleTime = sampleTime;
   return eventEnding;
 }
 
 void CheckerboardEvent::StartEvent() {
+  MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Starting checkerboard event"));
   MOZ_ASSERT(!mCheckerboardingActive);
   mCheckerboardingActive = true;
   mStartTime = TimeStamp::Now();
 
   if (!mRecordTrace) {
     return;
   }
   MonitorAutoLock lock(mRendertraceLock);
@@ -135,16 +138,17 @@ void CheckerboardEvent::StartEvent() {
   std::sort(history.begin(), history.end());
   for (const PropertyValue& p : history) {
     LogInfo(p.mProperty, p.mTimeStamp, p.mRect, p.mExtraInfo, lock);
   }
   mRendertraceInfo << " -- checkerboarding starts below --" << std::endl;
 }
 
 void CheckerboardEvent::StopEvent() {
+  MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Stopping checkerboard event"));
   mCheckerboardingActive = false;
   mEndTime = TimeStamp::Now();
 
   if (!mRecordTrace) {
     return;
   }
   MonitorAutoLock lock(mRendertraceLock);
   if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -1485,18 +1485,17 @@ bool AsyncCompositionManager::TransformS
         MoveScrollbarForLayerMargin(root, mRootScrollableId,
                                     GetFixedLayerMargins());
       }
 #endif
     }
 
     bool apzAnimating = false;
     if (RefPtr<APZSampler> apz = mCompositorBridge->GetAPZSampler()) {
-      apzAnimating =
-          apz->SampleAnimations(LayerMetricsWrapper(root), nextFrame);
+      apzAnimating = apz->AdvanceAnimations(nextFrame);
     }
     wantNextFrame |= apzAnimating;
   }
 
   HostLayer* rootComposite = root->AsHostLayer();
 
   gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
   trans *= gfx::Matrix4x4::From2D(mWorldTransform);
--- a/js/src/builtin/.eslintrc.js
+++ b/js/src/builtin/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   "plugins": [
     "spidermonkey-js"
   ],
 
   "overrides": [{
--- a/js/src/shell/.eslintrc.js
+++ b/js/src/shell/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   "plugins": [
     "spidermonkey-js"
   ],
 
   "overrides": [{
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1276,16 +1276,18 @@ void PresShell::Destroy() {
 
   ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
 
   if (mCaret) {
     mCaret->Terminate();
     mCaret = nullptr;
   }
 
+  mFocusedFrameSelection = nullptr;
+
   if (mSelection) {
     RefPtr<nsFrameSelection> frameSelection = mSelection;
     frameSelection->DisconnectFromPresShell();
   }
 
   // release our pref style sheet, if we have one still
   //
   // TODO(emilio): Should we move the preference sheet tracking to the Document?
@@ -1537,16 +1539,77 @@ void PresShell::AddAuthorSheet(StyleShee
     StyleSet()->InsertStyleSheetBefore(*aSheet, *firstAuthorSheet);
   } else {
     StyleSet()->AppendStyleSheet(*aSheet);
   }
 
   mDocument->ApplicableStylesChanged();
 }
 
+void PresShell::SelectionWillTakeFocus() {
+  if (mSelection) {
+    FrameSelectionWillTakeFocus(*mSelection);
+  }
+}
+
+void PresShell::SelectionWillLoseFocus() {
+  // Do nothing, the main selection is the default focused selection.
+}
+
+void PresShell::FrameSelectionWillLoseFocus(nsFrameSelection& aFrameSelection) {
+  if (mFocusedFrameSelection != &aFrameSelection) {
+    return;
+  }
+
+  // Do nothing, the main selection is the default focused selection.
+  if (&aFrameSelection == mSelection) {
+    return;
+  }
+
+  RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
+  MOZ_ASSERT(!mFocusedFrameSelection);
+
+  if (old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
+    old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
+    old->RepaintSelection(SelectionType::eNormal);
+  }
+
+  if (mSelection) {
+    FrameSelectionWillTakeFocus(*mSelection);
+  }
+}
+
+void PresShell::FrameSelectionWillTakeFocus(nsFrameSelection& aFrameSelection) {
+  if (mFocusedFrameSelection == &aFrameSelection) {
+#ifdef XP_MACOSX
+    // FIXME: Mac needs to update the global selection cache, even if the
+    // document's focused selection doesn't change, and this is currently done
+    // from RepaintSelection. Maybe we should move part of the global selection
+    // handling here, or something of that sort, unclear.
+    aFrameSelection.RepaintSelection(SelectionType::eNormal);
+#endif
+    return;
+  }
+
+  RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
+  mFocusedFrameSelection = &aFrameSelection;
+
+  if (old &&
+      old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
+    old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
+    old->RepaintSelection(SelectionType::eNormal);
+  }
+
+  if (aFrameSelection.GetDisplaySelection() !=
+      nsISelectionController::SELECTION_ON) {
+    aFrameSelection.SetDisplaySelection(nsISelectionController::SELECTION_ON);
+    aFrameSelection.RepaintSelection(SelectionType::eNormal);
+  }
+}
+
 NS_IMETHODIMP
 PresShell::SetDisplaySelection(int16_t aToggle) {
   RefPtr<nsFrameSelection> frameSelection = mSelection;
   frameSelection->SetDisplaySelection(aToggle);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1578,51 +1641,26 @@ Selection* PresShell::GetSelection(RawSe
     return nullptr;
   }
 
   RefPtr<nsFrameSelection> frameSelection = mSelection;
   return frameSelection->GetSelection(ToSelectionType(aRawSelectionType));
 }
 
 Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
-  if (!mSelection) return nullptr;
+  if (!mSelection) {
+    return nullptr;
+  }
 
   RefPtr<nsFrameSelection> frameSelection = mSelection;
   return frameSelection->GetSelection(aSelectionType);
 }
 
-already_AddRefed<nsISelectionController>
-PresShell::GetSelectionControllerForFocusedContent(
-    nsIContent** aFocusedContent) {
-  if (aFocusedContent) {
-    *aFocusedContent = nullptr;
-  }
-
-  if (mDocument) {
-    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
-    nsCOMPtr<nsIContent> focusedContent = nsFocusManager::GetFocusedDescendant(
-        mDocument->GetWindow(), nsFocusManager::eOnlyCurrentWindow,
-        getter_AddRefs(focusedWindow));
-    if (focusedContent) {
-      nsIFrame* frame = focusedContent->GetPrimaryFrame();
-      if (frame) {
-        nsCOMPtr<nsISelectionController> selectionController;
-        frame->GetSelectionController(mPresContext,
-                                      getter_AddRefs(selectionController));
-        if (selectionController) {
-          if (aFocusedContent) {
-            focusedContent.forget(aFocusedContent);
-          }
-          return selectionController.forget();
-        }
-      }
-    }
-  }
-  nsCOMPtr<nsISelectionController> self(this);
-  return self.forget();
+nsFrameSelection* PresShell::GetLastFocusedFrameSelection() {
+  return mFocusedFrameSelection ? mFocusedFrameSelection : mSelection;
 }
 
 NS_IMETHODIMP
 PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
                                    SelectionRegion aRegion, int16_t aFlags) {
   if (!mSelection) return NS_ERROR_NULL_POINTER;
 
   RefPtr<nsFrameSelection> frameSelection = mSelection;
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -553,16 +553,22 @@ class PresShell final : public nsStubDoc
   nsresult PostReflowCallback(nsIReflowCallback* aCallback);
   void CancelReflowCallback(nsIReflowCallback* aCallback);
 
   void ScheduleBeforeFirstPaint();
   void UnsuppressAndInvalidate();
 
   void ClearFrameRefs(nsIFrame* aFrame);
 
+  // Clears the selection of the older focused frame selection if any.
+  void FrameSelectionWillTakeFocus(nsFrameSelection&);
+
+  // Clears and repaint mFocusedFrameSelection if it matches the argument.
+  void FrameSelectionWillLoseFocus(nsFrameSelection&);
+
   /**
    * Get a reference rendering context. This is a context that should not
    * be rendered to, but is suitable for measuring text and performing
    * other non-rendering operations. Guaranteed to return non-null.
    */
   already_AddRefed<gfxContext> CreateReferenceRenderingContext();
 
   /**
@@ -618,32 +624,20 @@ class PresShell final : public nsStubDoc
    * Restore the caret to the original caret that this pres shell was created
    * with.
    */
   void RestoreCaret();
 
   dom::Selection* GetCurrentSelection(SelectionType aSelectionType);
 
   /**
-   * Gets a selection controller for the focused content in the DOM window
-   * for mDocument.
-   *
-   * @param aFocusedContent     If there is focused content in the DOM window,
-   *                            the focused content will be returned.  This may
-   *                            be nullptr if it's not necessary.
-   * @return                    A selection controller for focused content.
-   *                            E.g., if an <input> element has focus, returns
-   *                            the independent selection controller of it.
-   *                            If the DOM window does not have focused content
-   *                            (similar to Document.activeElement), returns
-   *                            nullptr.
+   * Gets the last selection that took focus in this document. This is basically
+   * the frame selection that's visible to the user.
    */
-  already_AddRefed<nsISelectionController>
-  GetSelectionControllerForFocusedContent(
-      nsIContent** aFocusedContent = nullptr);
+  nsFrameSelection* GetLastFocusedFrameSelection();
 
   /**
    * Interface to dispatch events via the presshell
    * @note The caller must have a strong reference to the PresShell.
    */
   MOZ_CAN_RUN_SCRIPT
   nsresult HandleEventWithTarget(WidgetEvent* aEvent, nsIFrame* aFrame,
                                  nsIContent* aContent,
@@ -1267,16 +1261,18 @@ class PresShell final : public nsStubDoc
   dom::Selection* GetSelection(RawSelectionType aRawSelectionType) override;
 
   NS_IMETHOD SetDisplaySelection(int16_t aToggle) override;
   NS_IMETHOD GetDisplaySelection(int16_t* aToggle) override;
   NS_IMETHOD ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
                                      SelectionRegion aRegion,
                                      int16_t aFlags) override;
   NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
+  void SelectionWillTakeFocus() override;
+  void SelectionWillLoseFocus() override;
 
   /**
    * Set a "resolution" for the document, which if not 1.0 will
    * allocate more or fewer pixels for rescalable content by a factor
    * of |resolution| in both dimensions.  Return NS_OK iff the
    * resolution bounds are sane, and the resolution of this was
    * actually updated.
    *
@@ -2827,16 +2823,20 @@ class PresShell final : public nsStubDoc
   // call their can-run- script methods without local RefPtr variables.
   RefPtr<Document> const mDocument;
   RefPtr<nsPresContext> const mPresContext;
   // The document's style set owns it but we maintain a ref, may be null.
   RefPtr<StyleSheet> mPrefStyleSheet;
   UniquePtr<nsCSSFrameConstructor> mFrameConstructor;
   nsViewManager* mViewManager;  // [WEAK] docViewer owns it so I don't have to
   RefPtr<nsFrameSelection> mSelection;
+  // The frame selection that last took focus on this shell, which we need to
+  // hide if we focus another selection. May or may not be the same as
+  // `mSelection`.
+  RefPtr<nsFrameSelection> mFocusedFrameSelection;
   RefPtr<nsCaret> mCaret;
   RefPtr<nsCaret> mOriginalCaret;
   RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
   // Pointer into mFrameConstructor - this is purely so that GetRootFrame() can
   // be inlined:
   nsFrameManager* mFrameManager;
   WeakPtr<nsDocShell> mForwardingContainer;
 
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2624,17 +2624,17 @@ NS_IMETHODIMP nsDocumentViewer::GetConte
   aOutValue.Truncate();
 
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_INITIALIZED);
 
   // Now we have the selection.  Make sure it's nonzero:
   RefPtr<Selection> sel;
   if (selectionOnly) {
-    nsCopySupport::GetSelectionForCopy(mDocument, getter_AddRefs(sel));
+    sel = nsCopySupport::GetSelectionForCopy(mDocument);
     NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
 
     if (sel->IsCollapsed()) {
       return NS_OK;
     }
   }
 
   // call the copy code
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1048,52 +1048,31 @@ nsresult nsTextControlFrame::AttributeCh
 
   if (nsGkAtoms::maxlength == aAttribute) {
     if (textEditor) {
       textEditor->SetMaxTextLength(textControlElement->UsedMaxLength());
     }
     return NS_OK;
   }
 
-  if (nsGkAtoms::readonly == aAttribute) {
-    if (AttributeExists(nsGkAtoms::readonly)) {  // set readonly
+  if (nsGkAtoms::readonly == aAttribute || nsGkAtoms::disabled == aAttribute) {
+    if (AttributeExists(aAttribute)) {
       if (nsContentUtils::IsFocusedContent(mContent)) {
         selCon->SetCaretEnabled(false);
       }
       textEditor->AddFlags(nsIEditor::eEditorReadonlyMask);
-    } else {  // unset readonly
-      if (!textEditor->IsDisabled() &&
-          nsContentUtils::IsFocusedContent(mContent)) {
-        selCon->SetCaretEnabled(true);
+    } else {
+      if (!AttributeExists(aAttribute == nsGkAtoms::readonly
+                               ? nsGkAtoms::disabled
+                               : nsGkAtoms::readonly)) {
+        if (nsContentUtils::IsFocusedContent(mContent)) {
+          selCon->SetCaretEnabled(true);
+        }
+        textEditor->RemoveFlags(nsIEditor::eEditorReadonlyMask);
       }
-      textEditor->RemoveFlags(nsIEditor::eEditorReadonlyMask);
-    }
-    return NS_OK;
-  }
-
-  if (nsGkAtoms::disabled == aAttribute) {
-    int16_t displaySelection = nsISelectionController::SELECTION_OFF;
-    const bool focused = nsContentUtils::IsFocusedContent(mContent);
-    const bool hasAttr = AttributeExists(nsGkAtoms::disabled);
-    bool disable;
-    if (hasAttr) {  // set disabled
-      disable = true;
-    } else {  // unset disabled
-      disable = false;
-      displaySelection = focused ? nsISelectionController::SELECTION_ON
-                                 : nsISelectionController::SELECTION_HIDDEN;
-    }
-    selCon->SetDisplaySelection(displaySelection);
-    if (focused) {
-      selCon->SetCaretEnabled(!hasAttr);
-    }
-    if (disable) {
-      textEditor->AddFlags(nsIEditor::eEditorDisabledMask);
-    } else {
-      textEditor->RemoveFlags(nsIEditor::eEditorDisabledMask);
     }
     return NS_OK;
   }
 
   if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) {
     UpdateValueDisplay(true);
     return NS_OK;
   }
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -1291,16 +1291,18 @@ nsresult nsFrameSelection::TakeFocus(nsI
   if (!aNewFocus) return NS_ERROR_NULL_POINTER;
 
   NS_ENSURE_STATE(mPresShell);
 
   if (!IsValidSelectionPoint(aNewFocus)) {
     return NS_ERROR_FAILURE;
   }
 
+  mPresShell->FrameSelectionWillTakeFocus(*this);
+
   // Clear all table selection data
   mTableSelection.mMode = TableSelectionMode::None;
   mTableSelection.mDragSelectingCells = false;
   mTableSelection.mStartSelectedCell = nullptr;
   mTableSelection.mEndSelectedCell = nullptr;
   mTableSelection.mAppendStartSelectedCell = nullptr;
   mCaret.mHint = aHint;
 
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -200,21 +200,16 @@ enum class TableSelectionMode : uint32_t
   Column,   /* A column is being selected. */
   Table,    /* A table (including cells and captions) is being selected. */
   AllCells, /* All the cells in a table are being selected. */
 };
 
 }  // namespace mozilla
 class nsIScrollableFrame;
 
-/**
- * Methods which are marked with *unsafe* should be handled with special care.
- * They may cause nsFrameSelection to be deleted, if strong pointer isn't used,
- * or they may cause other objects to be deleted.
- */
 class nsFrameSelection final {
  public:
   typedef mozilla::CaretAssociationHint CaretAssociateHint;
 
   /*interfaces for addref and release and queryinterface*/
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsFrameSelection)
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsFrameSelection)
@@ -255,18 +250,19 @@ class nsFrameSelection final {
    * @param aPresContext is the context to use when figuring out what frame
    * contains the point.
    *
    * @param aFrame is the parent of all frames to use when searching for the
    * closest frame to the point.
    *
    * @param aPoint is relative to aFrame
    */
-  /*unsafe*/
-  void HandleDrag(nsIFrame* aFrame, const nsPoint& aPoint);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void HandleDrag(nsIFrame* aFrame,
+                                              const nsPoint& aPoint);
 
   /**
    * HandleTableSelection will set selection to a table, cell, etc
    * depending on information contained in aFlags
    *
    * @param aParentContent is the paretent of either a table or cell that user
    * clicked or dragged the mouse in
    *
@@ -282,20 +278,21 @@ class nsFrameSelection final {
    *   * TableSelectionMode::Table
    *       We should select a table (content points to the table)
    *   * TableSelectionMode::AllCells
    *       We should select all cells (content points to any cell in table)
    *
    * @param aMouseEvent passed in so we can get where event occurred
    * and what keys are pressed
    */
-  /*unsafe*/
-  nsresult HandleTableSelection(nsINode* aParentContent, int32_t aContentOffset,
-                                mozilla::TableSelectionMode aTarget,
-                                mozilla::WidgetMouseEvent* aMouseEvent);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
+  HandleTableSelection(nsINode* aParentContent, int32_t aContentOffset,
+                       mozilla::TableSelectionMode aTarget,
+                       mozilla::WidgetMouseEvent* aMouseEvent);
 
   /**
    * Add cell to the selection with `SelectionType::eNormal`.
    *
    * @param  aCell  [in] HTML td element.
    */
   nsresult SelectCellElement(nsIContent* aCell);
 
@@ -412,20 +409,20 @@ class nsFrameSelection final {
    *
    * @param aFlags the scroll flags.  Valid bits include:
    *   * SCROLL_SYNCHRONOUS: when set, scrolls the selection into view
    *     before returning. If not set, posts a request which is processed
    *     at some point after the method returns.
    *   * SCROLL_FIRST_ANCESTOR_ONLY: if set, only the first ancestor will be
    *     scrolled into view.
    */
-  /*unsafe*/
-  nsresult ScrollSelectionIntoView(mozilla::SelectionType aSelectionType,
-                                   SelectionRegion aRegion,
-                                   int16_t aFlags) const;
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
+  ScrollSelectionIntoView(mozilla::SelectionType aSelectionType,
+                          SelectionRegion aRegion, int16_t aFlags) const;
 
   /**
    * RepaintSelection repaints the selected frames that are inside the
    * selection specified by aSelectionType.
    *
    * @param aSelectionType The selection type what you want to repaint.
    */
   nsresult RepaintSelection(mozilla::SelectionType aSelectionType);
@@ -495,80 +492,84 @@ class nsFrameSelection final {
   /**
    * PhysicalMove will generally be called from the nsiselectioncontroller
    * implementations. the effect being the selection will move one unit
    * 'aAmount' in the given aDirection.
    * @param aDirection  the direction to move the selection
    * @param aAmount     amount of movement (char/line; word/page; eol/doc)
    * @param aExtend     continue selection
    */
-  MOZ_CAN_RUN_SCRIPT_BOUNDARY
-  nsresult PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult PhysicalMove(int16_t aDirection,
+                                                    int16_t aAmount,
+                                                    bool aExtend);
 
   /**
    * CharacterMove will generally be called from the nsiselectioncontroller
    * implementations. the effect being the selection will move one character
    * left or right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
-  /*unsafe*/
-  nsresult CharacterMove(bool aForward, bool aExtend);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult CharacterMove(bool aForward,
+                                                     bool aExtend);
 
   /**
    * CharacterExtendForDelete extends the selection forward (logically) to
    * the next character cell, so that the selected cell can be deleted.
    */
-  /*unsafe*/
-  nsresult CharacterExtendForDelete();
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult CharacterExtendForDelete();
 
   /**
    * CharacterExtendForBackspace extends the selection backward (logically) to
    * the previous character cell, so that the selected cell can be deleted.
    */
-  /*unsafe*/
-  nsresult CharacterExtendForBackspace();
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult CharacterExtendForBackspace();
 
   /**
    * WordMove will generally be called from the nsiselectioncontroller
    * implementations. the effect being the selection will move one word left or
    * right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
-  /*unsafe*/
-  nsresult WordMove(bool aForward, bool aExtend);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult WordMove(bool aForward, bool aExtend);
 
   /**
    * WordExtendForDelete extends the selection backward or forward (logically)
    * to the next word boundary, so that the selected word can be deleted.
    * @param aForward select forward in document.
    */
-  /*unsafe*/
-  nsresult WordExtendForDelete(bool aForward);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult WordExtendForDelete(bool aForward);
 
   /**
    * LineMove will generally be called from the nsiselectioncontroller
    * implementations. the effect being the selection will move one line up or
    * down.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
-  /*unsafe*/
-  nsresult LineMove(bool aForward, bool aExtend);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult LineMove(bool aForward, bool aExtend);
 
   /**
    * IntraLineMove will generally be called from the nsiselectioncontroller
    * implementations. the effect being the selection will move to beginning or
    * end of line
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
-  /*unsafe*/
-  nsresult IntraLineMove(bool aForward, bool aExtend);
+  // TODO: replace with `MOZ_CAN_RUN_SCRIPT`.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult IntraLineMove(bool aForward,
+                                                     bool aExtend);
 
   /**
    * Select All will generally be called from the nsiselectioncontroller
    * implementations. it will select the whole doc
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult SelectAll();
 
   /** Sets/Gets The display selection enum.
@@ -759,20 +760,20 @@ class nsFrameSelection final {
   friend class mozilla::dom::Selection;
   friend class mozilla::SelectionChangeEventDispatcher;
   friend struct mozilla::AutoPrepareFocusRange;
 
   /*HELPER METHODS*/
   // Whether MoveCaret should use logical or visual movement,
   // or follow the bidi.edit.caret_movement_style preference.
   enum CaretMovementStyle { eLogical, eVisual, eUsePrefStyle };
-  MOZ_CAN_RUN_SCRIPT_BOUNDARY
-  nsresult MoveCaret(nsDirection aDirection, bool aContinueSelection,
-                     nsSelectionAmount aAmount,
-                     CaretMovementStyle aMovementStyle);
+  MOZ_CAN_RUN_SCRIPT nsresult MoveCaret(nsDirection aDirection,
+                                        bool aContinueSelection,
+                                        nsSelectionAmount aAmount,
+                                        CaretMovementStyle aMovementStyle);
 
   nsresult FetchDesiredPos(
       nsPoint& aDesiredPos);  // the position requested by the Key Handling for
                               // up down
   void InvalidateDesiredPos();  // do not listen to mDesiredPos.mValue you must
                                 // get another.
   void SetDesiredPos(nsPoint aPos);  // set the mDesiredPos.mValue
 
@@ -811,23 +812,21 @@ class nsFrameSelection final {
     // (according to GetFirstCellNodeInRange).
     nsRange* GetFirstCellRange(const mozilla::dom::Selection& aNormalSelection);
 
     // Get our next range, if its first selected node is a cell.  If this does
     // not return null, then the first node in the returned range is a cell
     // (according to GetFirstCellNodeInRange).
     nsRange* GetNextCellRange(const mozilla::dom::Selection& aNormalSelection);
 
-    // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead.
-    MOZ_CAN_RUN_SCRIPT_BOUNDARY
-    nsresult HandleSelection(nsINode* aParentContent, int32_t aContentOffset,
-                             mozilla::TableSelectionMode aTarget,
-                             mozilla::WidgetMouseEvent* aMouseEvent,
-                             bool aDragState,
-                             mozilla::dom::Selection& aNormalSelection);
+    MOZ_CAN_RUN_SCRIPT nsresult
+    HandleSelection(nsINode* aParentContent, int32_t aContentOffset,
+                    mozilla::TableSelectionMode aTarget,
+                    mozilla::WidgetMouseEvent* aMouseEvent, bool aDragState,
+                    mozilla::dom::Selection& aNormalSelection);
 
     // TODO: annotate this with `MOZ_CAN_RUN_SCRIPT` instead.
     MOZ_CAN_RUN_SCRIPT_BOUNDARY
     nsresult SelectBlockOfCells(nsIContent* aStartCell, nsIContent* aEndCell,
                                 mozilla::dom::Selection& aNormalSelection);
 
     nsresult SelectRowOrColumn(nsIContent* aCellContent,
                                mozilla::dom::Selection& aNormalSelection);
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -707,25 +707,26 @@ struct nsCSSRendering {
       case mozilla::StyleBlend::Luminosity:
         return CompositionOp::OP_LUMINOSITY;
       default:
         MOZ_ASSERT(false);
         return CompositionOp::OP_OVER;
     }
   }
 
-  static CompositionOp GetGFXCompositeMode(uint8_t aCompositeMode) {
+  static CompositionOp GetGFXCompositeMode(
+      mozilla::StyleMaskComposite aCompositeMode) {
     switch (aCompositeMode) {
-      case NS_STYLE_MASK_COMPOSITE_ADD:
+      case mozilla::StyleMaskComposite::Add:
         return CompositionOp::OP_OVER;
-      case NS_STYLE_MASK_COMPOSITE_SUBTRACT:
+      case mozilla::StyleMaskComposite::Subtract:
         return CompositionOp::OP_OUT;
-      case NS_STYLE_MASK_COMPOSITE_INTERSECT:
+      case mozilla::StyleMaskComposite::Intersect:
         return CompositionOp::OP_IN;
-      case NS_STYLE_MASK_COMPOSITE_EXCLUDE:
+      case mozilla::StyleMaskComposite::Exclude:
         return CompositionOp::OP_XOR;
       default:
         MOZ_ASSERT(false);
         return CompositionOp::OP_OVER;
     }
   }
 
  protected:
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -159,16 +159,17 @@ rusty-enums = [
     "mozilla::StyleGeometryBox",
     "mozilla::SystemColor",
     "mozilla::StyleMaskMode",
     "mozilla::StyleScrollBehavior",
     "mozilla::StyleColorInterpolation",
     "mozilla::StyleVectorEffect",
     "mozilla::StyleBackfaceVisibility",
     "mozilla::StyleBlend",
+    "mozilla::StyleMaskComposite",
 ]
 whitelist-vars = [
     "NS_AUTHOR_SPECIFIED_.*",
     "NS_THEME_.*",
     "NS_ATTRVALUE_.*",
     "NODE_.*",
     "ELEMENT_.*",
     "NS_FONT_.*",
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1321,18 +1321,18 @@ already_AddRefed<nsROCSSPrimitiveValue> 
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetOsxFontSmoothing() {
   if (nsContentUtils::ShouldResistFingerprinting(
           mPresShell->GetPresContext()->GetDocShell())) {
     return nullptr;
   }
 
   nsAutoString result;
-  mComputedStyle->GetComputedPropertyValue(
-      eCSSProperty__moz_osx_font_smoothing, result);
+  mComputedStyle->GetComputedPropertyValue(eCSSProperty__moz_osx_font_smoothing,
+                                           result);
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetString(result);
   return val.forget();
 }
 
 already_AddRefed<CSSValue> nsComputedDOMStyle::DoGetImageLayerPosition(
     const nsStyleImageLayers& aLayers) {
   if (aLayers.mPositionXCount != aLayers.mPositionYCount) {
@@ -2471,17 +2471,17 @@ already_AddRefed<CSSValue> nsComputedDOM
   const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0];
 
   // Mask is now a shorthand, but it used to be a longhand, so that we
   // need to support computed style for the cases where it used to be
   // a longhand.
   if (svg->mMask.mImageCount > 1 ||
       firstLayer.mClip != StyleGeometryBox::BorderBox ||
       firstLayer.mOrigin != StyleGeometryBox::BorderBox ||
-      firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD ||
+      firstLayer.mComposite != StyleMaskComposite::Add ||
       firstLayer.mMaskMode != StyleMaskMode::MatchSource ||
       firstLayer.mPosition != Position::FromPercentage(0.0f) ||
       !firstLayer.mRepeat.IsInitialValue() ||
       !firstLayer.mSize.IsInitialValue() ||
       !(firstLayer.mImage.IsNone() || firstLayer.mImage.IsUrl())) {
     return nullptr;
   }
 
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -779,21 +779,22 @@ enum class StyleBlend : uint8_t {
   Exclusion,
   Hue,
   Saturation,
   Color,
   Luminosity,
 };
 
 // composite
-#define NS_STYLE_MASK_COMPOSITE_ADD 0
-#define NS_STYLE_MASK_COMPOSITE_SUBTRACT 1
-#define NS_STYLE_MASK_COMPOSITE_INTERSECT 2
-#define NS_STYLE_MASK_COMPOSITE_EXCLUDE 3
-
+enum class StyleMaskComposite : uint8_t {
+  Add = 0,
+  Subtract,
+  Intersect,
+  Exclude
+};
 // See nsStyleText::mControlCharacterVisibility
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN 0
 #define NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE 1
 
 // counter system
 #define NS_STYLE_COUNTER_SYSTEM_CYCLIC 0
 #define NS_STYLE_COUNTER_SYSTEM_NUMERIC 1
 #define NS_STYLE_COUNTER_SYSTEM_ALPHABETIC 2
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1964,17 +1964,17 @@ static bool SizeDependsOnPositioningArea
 nsStyleImageLayers::Layer::Layer()
     : mImage(StyleImage::None()),
       mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(),
                                               LengthPercentageOrAuto::Auto())),
 
       mClip(StyleGeometryBox::BorderBox),
       mAttachment(StyleImageLayerAttachment::Scroll),
       mBlendMode(StyleBlend::Normal),
-      mComposite(NS_STYLE_MASK_COMPOSITE_ADD),
+      mComposite(StyleMaskComposite::Add),
       mMaskMode(StyleMaskMode::MatchSource) {}
 
 nsStyleImageLayers::Layer::~Layer() = default;
 
 void nsStyleImageLayers::Layer::Initialize(
     nsStyleImageLayers::LayerType aType) {
   mRepeat.SetInitialValues();
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -223,18 +223,18 @@ struct nsStyleImageLayers {
 
     // This property is used for background layer only.
     // For a mask layer, it should always be the initial value, which is
     // StyleBlend::Normal.
     mozilla::StyleBlend mBlendMode;
 
     // This property is used for mask layer only.
     // For a background layer, it should always be the initial value, which is
-    // NS_STYLE_COMPOSITE_MODE_ADD.
-    uint8_t mComposite;  // NS_STYLE_MASK_COMPOSITE_*
+    // StyleMaskComposite::Add.
+    mozilla::StyleMaskComposite mComposite;
 
     // mask-only property. This property is used for mask layer only. For a
     // background layer, it should always be the initial value, which is
     // StyleMaskMode::MatchSource.
     mozilla::StyleMaskMode mMaskMode;
 
     Repeat mRepeat;
 
--- a/mobile/android/.eslintrc.js
+++ b/mobile/android/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   overrides: [
     {
       files: [
         // Bug 1425048 - mainly going away, see bug 1583370.
         "components/extensions/**",
--- a/mobile/android/examples/messaging_example/app/src/main/assets/messaging/.eslintrc.js
+++ b/mobile/android/examples/messaging_example/app/src/main/assets/messaging/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: {
     webextensions: true,
   },
 };
--- a/mobile/android/examples/port_messaging_example/app/src/main/assets/messaging/.eslintrc.js
+++ b/mobile/android/examples/port_messaging_example/app/src/main/assets/messaging/.eslintrc.js
@@ -1,7 +1,11 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   env: {
     webextensions: true,
   },
 };
--- a/mobile/android/modules/geckoview/.eslintrc.js
+++ b/mobile/android/modules/geckoview/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   globals: {
     debug: false,
     warn: false,
   },
   rules: {
--- a/netwerk/base/nsProxyInfo.h
+++ b/netwerk/base/nsProxyInfo.h
@@ -4,16 +4,17 @@
  * 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 nsProxyInfo_h__
 #define nsProxyInfo_h__
 
 #include "nsIProxyInfo.h"
 #include "nsString.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 
 // Use to support QI nsIProxyInfo to nsProxyInfo
 #define NS_PROXYINFO_IID                             \
   { /* ed42f751-825e-4cc2-abeb-3670711a8b85 */       \
     0xed42f751, 0x825e, 0x4cc2, {                    \
       0xab, 0xeb, 0x36, 0x70, 0x71, 0x1a, 0x8b, 0x85 \
     }                                                \
@@ -81,17 +82,19 @@ class nsProxyInfo final : public nsIProx
   const char* mType;  // pointer to statically allocated value
   nsCString mHost;
   nsCString mUsername;
   nsCString mPassword;
   nsCString mProxyAuthorizationHeader;
   nsCString mConnectionIsolationKey;
   int32_t mPort;
   uint32_t mFlags;
-  uint32_t mResolveFlags;
+  // We need to read on multiple threads, but don't need to sync on anything
+  // else
+  Atomic<uint32_t, Relaxed> mResolveFlags;
   uint32_t mTimeout;
   nsProxyInfo* mNext;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyInfo, NS_PROXYINFO_IID)
 
 }  // namespace net
 }  // namespace mozilla
--- a/security/.eslintrc.js
+++ b/security/.eslintrc.js
@@ -1,8 +1,12 @@
+/* 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/. */
+
 "use strict";
 
 module.exports = {
   rules: {
     // Enforce return statements in callbacks of array methods.
     "array-callback-return": "error",
 
     // Verify calls of super() in constructors.
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -146,24 +146,16 @@ static const char kGOOGLE_PIN_GTSCA1O1Fi
 /* GOOGLE_PIN_GeoTrustGlobal2 */
 static const char kGOOGLE_PIN_GeoTrustGlobal2Fingerprint[] =
   "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
 
 /* GOOGLE_PIN_GoDaddySecure */
 static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
   "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
 
-/* GOOGLE_PIN_GoogleG2 */
-static const char kGOOGLE_PIN_GoogleG2Fingerprint[] =
-  "7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=";
-
-/* GOOGLE_PIN_GoogleG3 */
-static const char kGOOGLE_PIN_GoogleG3Fingerprint[] =
-  "f8NnEFZxQ4ExFOhSN7EiFWtiudZQVD2oY60uauV/n78=";
-
 /* GOOGLE_PIN_RapidSSL */
 static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
   "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
 
 /* GOOGLE_PIN_SecureCertificateServices */
 static const char kGOOGLE_PIN_SecureCertificateServicesFingerprint[] =
   "RpHL/ehKa2BS3b4VK7DCFq4lqG5XR4E9vA8UfzOFcL4=";
 
@@ -202,16 +194,32 @@ static const char kGOOGLE_PIN_VeriSignCl
 /* GOOGLE_PIN_VeriSignClass3_G2 */
 static const char kGOOGLE_PIN_VeriSignClass3_G2Fingerprint[] =
   "AjyBzOjnxk+pQtPBUEhwfTXZu1uH9PVExb8bxWQ68vo=";
 
 /* GOOGLE_PIN_VeriSignClass4_G3 */
 static const char kGOOGLE_PIN_VeriSignClass4_G3Fingerprint[] =
   "VnuCEf0g09KD7gzXzgZyy52ZvFtIeljJ1U7Gf3fUqPU=";
 
+/* GTS Root R1 */
+static const char kGTS_Root_R1Fingerprint[] =
+  "hxqRlPTu1bMS/0DITB1SSu0vd4u/8l8TjPgfaAp63Gc=";
+
+/* GTS Root R2 */
+static const char kGTS_Root_R2Fingerprint[] =
+  "Vfd95BwDeSQo+NUYxVEEIlvkOlWY2SalKK1lPhzOx78=";
+
+/* GTS Root R3 */
+static const char kGTS_Root_R3Fingerprint[] =
+  "QXnt2YHvdHR3tJYmQIr0Paosp6t/nggsEGD4QJZ3Q0g=";
+
+/* GTS Root R4 */
+static const char kGTS_Root_R4Fingerprint[] =
+  "mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=";
+
 /* GeoTrust Global CA */
 static const char kGeoTrust_Global_CAFingerprint[] =
   "h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=";
 
 /* GeoTrust Primary Certification Authority */
 static const char kGeoTrust_Primary_Certification_AuthorityFingerprint[] =
   "SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=";
 
@@ -454,21 +462,23 @@ static const char* const kPinset_test_Da
   kTestSPKIFingerprint,
 };
 static const StaticFingerprints kPinset_test = {
   sizeof(kPinset_test_Data) / sizeof(const char*),
   kPinset_test_Data
 };
 
 static const char* const kPinset_google_Data[] = {
-  kGOOGLE_PIN_GoogleG2Fingerprint,
   kGoogleBackup2048Fingerprint,
+  kGTS_Root_R3Fingerprint,
+  kGTS_Root_R2Fingerprint,
   kGOOGLE_PIN_GTSCA1O1Fingerprint,
-  kGOOGLE_PIN_GoogleG3Fingerprint,
+  kGTS_Root_R1Fingerprint,
   kGlobalSign_Root_CA___R2Fingerprint,
+  kGTS_Root_R4Fingerprint,
 };
 static const StaticFingerprints kPinset_google = {
   sizeof(kPinset_google_Data) / sizeof(const char*),
   kPinset_google_Data
 };
 
 static const char* const kPinset_tor_Data[] = {
   kTor3Fingerprint,
@@ -1134,9 +1144,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 490;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1592832947452000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1593092155751000);
--- a/security/manager/ssl/nsSTSPreloadList.inc
+++ b/security/manager/ssl/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.                */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1595252139069000);
+const PRTime gPreloadListExpirationTime = INT64_C(1595511350605000);
 %%
 0--1.de, 1
 0-1.party, 1
 0-24.com, 1
 0-24.net, 1
 000321365.com, 1
 0007552.com, 0
 000a1.com, 1
@@ -209,19 +209,19 @@ 0372z6.com, 1
 0373z6.com, 1
 0375z6.com, 1
 0376z6.com, 1
 0377z6.com, 1
 038456.com, 1
 038663.com, 1
 038899.com, 1
 0391315.com, 1
-0393gg.com, 1
-0393hh.com, 1
-0393ii.com, 1
+0393gg.com, 0
+0393hh.com, 0
+0393ii.com, 0
 0399z6.com, 1
 039ks.com, 1
 03d88.com, 1
 03d88.net, 1
 03region.ga, 1
 040552.com, 0
 041552.com, 0
 042552.com, 0
@@ -1465,17 +1465,16 @@ 1620339.com, 1
 1620340.com, 1
 1620341.com, 1
 1620342.com, 1
 1620343.com, 1
 1620349.com, 1
 1620350.com, 1
 162223.com, 1
 162229.com, 1
-162231.com, 1
 162361.com, 1
 162632.com, 1
 162be.com, 1
 162bf.com, 1
 162bg.com, 1
 162bj.com, 1
 162ca.com, 1
 162cb.com, 1
@@ -1888,16 +1887,17 @@ 1simplesolution.com, 1
 1st-bounce.co.uk, 1
 1st-community.de, 1
 1st2bounce.com, 1
 1stcarpetcleaning.co.uk, 1
 1stchoicelandscapingwa.com, 1
 1stclassbouncycastles.co.uk, 1
 1stforfun.co.uk, 1
 1stpeninsulabouncers.co.uk, 1
+1ststop.co.uk, 1
 1ticks.com, 1
 1u0m.com, 1
 1v1.xyz, 1
 1v9.im, 1
 1v9.io, 1
 1volcano.ru, 1
 1voz.org, 1
 1vpns.com, 1
@@ -2303,17 +2303,16 @@ 2600edinburgh.org, 1
 2600hq.com, 1
 260887.com, 1
 262569.com, 1
 263.info, 1
 2666z6.com, 1
 266k66.com, 1
 266z6.com, 1
 267221.com, 1
-267661.com, 1
 269196.com, 1
 26bbc.com, 1
 26ce.com, 1
 26ck.com, 1
 26ddc.com, 1
 26gt.com, 1
 26he.com, 1
 26ja.com, 1
@@ -2828,17 +2827,16 @@ 360gpscorp.com, 1
 360hosting.com.au, 1
 360live.fr, 1
 360rail.nl, 1
 360videoshare.com, 1
 360vrs.com, 1
 360woodworking.com, 1
 361116.com, 1
 361171.com, 1
-361183.com, 1
 361365.cc, 0
 3615jacky.fr, 1
 362590.com, 1
 363331.com, 1
 364553.com, 1
 365.asia, 1
 365.systems, 1
 3650607.com, 0
@@ -2965,17 +2963,16 @@ 365yapan.com, 1
 365ypw.com, 1
 365yuwen.com, 1
 365zg.com, 1
 365zg.org, 1
 365zzz.vip, 0
 3666ks.com, 1
 366k66.com, 1
 366z6.com, 1
-367556.com, 1
 369ak.com, 1
 369az.com, 1
 369be.com, 1
 369bk.com, 1
 369bn.com, 1
 369bp.com, 1
 369bq.com, 1
 369br.com, 1
@@ -3981,17 +3978,16 @@ 5197y.co, 1
 5197yy.co, 1
 5197z.co, 1
 5197zz.co, 1
 51acg.eu.org, 1
 51aifuli.com, 1
 51cls.tw, 1
 51club8.com, 1
 51guaq.com, 1
-51kly.net, 1
 51tiaojiu.com, 1
 52002a.com, 1
 52002b.com, 1
 52002c.com, 1
 52002d.com, 1
 52002e.com, 1
 52002f.com, 1
 52002g.com, 1
@@ -4012,32 +4008,27 @@ 52002u.com, 1
 52002v.com, 1
 52002w.com, 1
 52002x.com, 1
 52002y.com, 1
 5201365.com, 0
 52051.com, 1
 52051a.com, 1
 5205365.com, 0
-52062b.com, 1
-52062c.com, 1
 52062d.com, 1
-52062e.com, 1
 52062f.com, 1
+52062g.com, 1
 52062h.com, 1
 52062i.com, 1
 52062j.com, 1
-52062l.com, 1
-52062m.com, 1
-52062p.com, 1
+52062n.com, 1
+52062o.com, 1
 52062q.com, 1
-52062r.com, 1
 52062s.com, 1
 52062t.com, 1
-52062u.com, 1
 52062v.com, 1
 52062w.com, 1
 52062x.com, 1
 52062y.com, 1
 5206365.com, 0
 5209365.com, 0
 520xpjxpj.com, 0
 5214889.com, 1
@@ -4242,17 +4233,16 @@ 5981r.com, 1
 5981s.com, 1
 5981t.com, 1
 5981u.com, 1
 5981v.com, 1
 5981w.com, 1
 5981x.com, 1
 5981y.com, 0
 5981z.com, 1
-598380.com, 1
 598598598.net, 1
 59859h.vip, 1
 59859j.vip, 1
 59859k.vip, 1
 59859l.vip, 1
 59859y.vip, 1
 59859z.vip, 1
 5986fc.com, 1
@@ -4961,17 +4951,16 @@ 678365app.com, 1
 678365cc.com, 1
 678365t.com, 1
 678678365.com, 1
 67877777.com, 1
 678z6.com, 1
 679422.com, 1
 679660.com, 1
 67y7.com, 1
-680226.com, 1
 680422.com, 1
 6810app.com, 1
 681vv.com, 1
 68277.me, 1
 6830521.com, 1
 6848.com, 1
 68522.com, 1
 68522c.com, 1
@@ -5141,19 +5130,17 @@ 71365365.com, 1
 7139365.com, 1
 713kb.com, 1
 714133.com, 1
 714533.com, 1
 714633.com, 1
 715433.com, 1
 716176.com, 1
 716227.com, 1
-716331.com, 1
 7177bet.com, 1
-718113.com, 1
 718227.com, 1
 7183.org, 0
 718337.com, 1
 718433.com, 1
 718552.com, 1
 718772.com, 1
 719433.com, 1
 721167.com, 1
@@ -5324,17 +5311,17 @@ 74th.jp, 1
 7552001.com, 0
 7552002.com, 0
 7552005.com, 0
 7552006.com, 0
 7552008.com, 1
 7552009.com, 1
 7552010.com, 0
 7552011.com, 0
-7552012.com, 1
+7552012.com, 0
 7552013.com, 1
 755204.com, 1
 755243.com, 1
 755245.com, 1
 755246.com, 1
 755249.com, 1
 755274.com, 1
 755284.com, 0
@@ -5514,17 +5501,17 @@ 8028d.com, 1
 8028d88.com, 1
 8029d.com, 1
 8029d88.com, 1
 803001.com, 1
 8030d88.com, 1
 8032d88.com, 1
 8033d88.com, 1
 8035d88.com, 1
-80365365.com, 0
+80365365.com, 1
 8036d88.com, 1
 8037d88.com, 1
 8038d88.com, 1
 8039d.com, 1
 804322.com, 1
 8050d.com, 1
 8051d.com, 1
 8053d.com, 1
@@ -5578,17 +5565,17 @@ 8121d88.com, 1
 812221.com, 1
 8128d.com, 1
 8129d.com, 1
 8130d88.com, 1
 8132365.com, 1
 8133d.com, 1
 8133d88.com, 1
 8135d88.com, 1
-81365365.com, 0
+81365365.com, 1
 81365b.com, 1
 81365c.com, 1
 81365d.com, 1
 81365e.com, 1
 81365f.com, 1
 81365g.com, 1
 81365h.com, 1
 81365i.com, 1
@@ -6038,17 +6025,16 @@ 861365t.com, 1
 861365u.com, 1
 861365v.com, 1
 861365vip.com, 1
 861365w.com, 1
 861365x.com, 1
 861365y.com, 1
 861365z.com, 1
 861kb.com, 1
-86286286.com, 1
 86499.com, 1
 8649955.com, 1
 8649966.com, 1
 8649977.com, 1
 866300.vip, 1
 866304.com, 1
 866305.vip, 1
 866308.vip, 1
@@ -6147,17 +6133,16 @@ 8832ks.com, 1
 8833445.com, 1
 8835365.com, 1
 8835ks.com, 1
 88365.net, 1
 88365t.com, 1
 8836ks.com, 1
 8838ks.com, 1
 8839ks.com, 1
-883z6.com, 1
 8850d.com, 1
 8850d88.com, 1
 8850ks.com, 1
 8851d88.com, 1
 8851ks.com, 1
 88522am.com, 1
 885287.com, 1
 8852d88.com, 1
@@ -6403,17 +6388,16 @@ 8xx.io, 1
 8xx888.com, 1
 8xxxxxxx.com, 1
 8y.network, 1
 8yabo.com, 1
 8yun.cf, 1
 8yun.ga, 0
 9-11commission.gov, 1
 9005424.com, 1
-900823.com, 1
 9009019.com, 1
 900aaaa.com, 1
 900bbbb.com, 1
 900cccc.com, 1
 900dddd.com, 1
 900eeee.com, 1
 900gggg.com, 1
 900iiii.com, 1
@@ -6639,19 +6623,17 @@ 918nu.com, 1
 918ny.com, 1
 918og.com, 1
 918pn.com, 1
 918pt.com, 1
 918qa.com, 1
 918qg.com, 1
 918qi.com, 1
 918qs.com, 1
-918qz.com, 1
 918rh.com, 1
-918rt.com, 1
 918rw.com, 1
 918sa.com, 1
 918sj.com, 1
 918sn.com, 1
 918ta.com, 1
 918tj.com, 1
 918tr.com, 1
 918tw.com, 1
@@ -6898,17 +6880,16 @@ 9499vvvv.com, 0
 9499wwww.com, 0
 9499xxx.com, 0
 9499xxxx.com, 0
 9499yl.com, 1
 9499yyyy.com, 0
 9499zzzz.com, 0
 94imk.com, 1
 9528365.com, 1
-955z6.com, 1
 956jj.com, 1
 95am8.com, 1
 95kb88.com, 1
 96002.com, 1
 96002e.com, 1
 961705.com, 1
 9617818.com, 1
 9617818.net, 1
@@ -6917,17 +6898,16 @@ 96658.com, 1
 96658.la, 1
 9666ks.com, 1
 96678.com, 1
 96685.com, 1
 966k66.com, 1
 966kb.com, 1
 966ks.com, 1
 966ty.com, 1
-966z6.com, 1
 967606.com, 1
 9679693.com, 1
 967you.com, 1
 9681909.com, 1
 9696178.com, 1
 9696178.net, 1
 96kb88.com, 1
 96z66.com, 1
@@ -7017,17 +6997,16 @@ 9728z.co, 1
 9728zz.co, 1
 97735.com, 1
 97736.com, 1
 97737.com, 1
 97738.com, 1
 97739.com, 1
 977hghg.com, 1
 977kb.com, 1
-977z6.com, 1
 9788876.com, 1
 9796k8.com, 1
 9796k8.net, 1
 9800.cc, 1
 981ccc.com, 1
 9822.am, 1
 9822am.com, 1
 9822cn.com, 1
@@ -7042,41 +7021,37 @@ 9859365.com, 1
 985ccc.com, 1
 985kb.com, 1
 986ccc.com, 1
 9877bet.com, 1
 988316.com, 0
 988am8.com, 1
 988am8.net, 1
 988wh.com, 1
-988z6.com, 1
 989z6.com, 1
 98agks.com, 1
 98d88.com, 1
 98e.site, 1
 98kb88.com, 1
 98ks.app, 1
 98laba.com, 0
 98laba.net, 0
 98lc98.net, 1
 9906753.net, 1
 99123j.com, 1
 9918883.com, 1
-991z6.com, 1
 992z6.com, 1
 99321365.com, 1
 9933445.com, 1
 99365t.com, 1
 993ccc.com, 1
-993z6.com, 1
 99456j.com, 1
 9950p.com, 1
 99599.fi, 1
 99599.net, 1
-995z6.com, 1
 9968101.com, 1
 9968110.com, 1
 9968121.com, 1
 9968159.com, 1
 9968161.com, 1
 9968202.com, 1
 9968232.com, 1
 9968235.com, 0
@@ -7089,27 +7064,25 @@ 9968368.com, 1
 9968383.com, 1
 9968454.com, 1
 9968565.com, 1
 9968606.com, 1
 9968676.com, 1
 9968678.com, 1
 9968787.com, 1
 9968808.com, 1
-9968909.com, 0
+9968909.com, 1
 9968989.com, 1
 9968aaa.com, 1
 9968good.com, 1
 9968live.com, 1
 9968love.com, 1
-9968rr.com, 1
 9968ss.com, 1
 9968xl.com, 1
 9968xpj.com, 1
-996z6.com, 1
 9977432.com, 1
 99789j.com, 1
 997z6.com, 1
 998081.com, 1
 9988551.com, 1
 9988959.com, 1
 9988ty.com, 1
 998k66.com, 1
@@ -7420,17 +7393,16 @@ a6729.com, 1
 a6957.co, 1
 a7035.com, 1
 a77018.com, 1
 a7m2.me, 1
 a81365.com, 1
 a82365.com, 1
 a88fc.com, 1
 a899365.com, 1
-a8q.org, 1
 a9297.co, 1
 a9728.co, 1
 aa-tour.ru, 1
 aa00228.com, 1
 aa43d.cn, 1
 aa4888.com, 1
 aa5197.co, 1
 aa6688.net, 0
@@ -7720,17 +7692,16 @@ academica.nl, 1
 academichealthscience.net, 1
 academie-de-police.ch, 0
 academie-essentiel.ch, 1
 academie-musique-nice.com, 0
 academkin.com, 1
 academus.io, 1
 academy-awards.ml, 1
 academytv.com.au, 1
-acadianapatios.com, 1
 acandroid.top, 1
 acaonegocios.com.br, 1
 acapadena.co, 1
 acaptureservices.com, 1
 acara-yoga.de, 1
 acareer.in, 1
 acarreosvillavicencio.com, 1
 acat.io, 1
@@ -7867,17 +7838,16 @@ acpica.org, 1
 acpinformatique.fr, 1
 acprail.com.vn, 1
 acquaparrucchieri.it, 1
 acquisition.gov, 1
 acquistareviagragenericoitalia.net, 0
 acrepairgeorgetown.com, 1
 acrepairhutto.com, 1
 acrepairroundrocktx.com, 1
-acrevalue.com, 1
 acriticismlab.org, 1
 acrobatic.tk, 1
 acronis.com, 1
 acronis.org, 1
 acronym24.com, 1
 across.ml, 1
 acrossgw.com, 1
 acrosstheblvd.com, 1
@@ -8016,16 +7986,17 @@ adapt-elektronik.com, 1
 adapt.de, 1
 adaptablesecurity.org, 1
 adapti.de, 1
 adaptiv.ltd, 1
 adaptiveicons.com, 1
 adaptivemechanics.edu.au, 1
 adarixconsultores.com, 1
 adarshcloud.in, 1
+adarshthapa.in, 1
 adasbench.com, 1
 adata.kz, 1
 adativos.com.br, 1
 adawolfa.cz, 1
 adayinthelifeof.nl, 1
 adblock.ovh, 1
 adblockextreme.com, 1
 adblockextreme.net, 1
@@ -8177,17 +8148,16 @@ adrianseo.ro, 0
 adriarae.xyz, 1
 adriatic.hr, 1
 adriatrans.ga, 1
 adrienjacquierbret.com, 1
 adrienkohlbecker.com, 1
 adriennesmiles.com, 1
 adrinet.tk, 1
 adrup.com, 1
-adsamcik.com, 1
 adsbouncycastles.co.uk, 1
 adsbtc.org, 1
 adsense-arbitrage.com, 1
 adsib.gob.bo, 1
 adsl2meg.fr, 1
 adspu.org, 1
 adsviews.gq, 1
 adswoo.com, 1
@@ -8261,23 +8231,23 @@ advertis.biz, 1
 advertisemant.com, 1
 adveszker.hu, 1
 advisercentre.com.au, 1
 advocate-europe.eu, 1
 advocator.ca, 1
 advocoeurdehaan.nl, 1
 advogatech.com.br, 1
 advokat-malinovskii.ml, 1
-advokat-romanov.com, 1
 advokat-vvp.com.ua, 1
 advokaty-onlajn.gq, 1
 advokaty-yuristy.tk, 1
 advst.uk, 1
 advtran.com, 1
 adware.pl, 0
+adwokatkosterka.pl, 1
 adwokatzdunek.pl, 1
 adws.io, 1
 adxperience.com, 1
 adzalanwp.com, 1
 adzie.xyz, 1
 adzuna.at, 1
 adzuna.ca, 1
 adzuna.co.nz, 1
@@ -8342,26 +8312,24 @@ aergia.eu, 1
 aerisnetwork.com, 1
 aerlux.md, 1
 aero-pioneer.com, 1
 aero.parts, 1
 aeroacademia.com.mx, 1
 aeroalbrook.com, 1
 aerobasegroup.com, 1
 aerobotz.com, 1
-aeronautix.com, 1
 aeronote.net, 1
 aeropole.de, 1
 aeropole.eu, 1
 aerorecords.net, 1
 aerosimexperience.com, 1
 aerospace-schools.com, 1
 aerotechcoatings.com, 1
 aertel.ie, 1
-aes-freundeskreis.de, 1
 aestheticsplus.xyz, 1
 aesthetikpiercing.de, 1
 aesthetx.com, 1
 aestore.by, 1
 aesvalanalys.com, 1
 aesym.de, 1
 aeternus.tech, 1
 aetherc0r3.eu, 1
@@ -8371,17 +8339,16 @@ aevar.io, 1
 aevpn.org, 1
 aextron.com, 1
 aextron.de, 1
 aextron.org, 1
 af.link, 1
 afadvantage.gov, 1
 afashion.com.au, 1
 afavre.io, 1
-afbeelding.im, 1
 afbeeldinguploaden.nl, 1
 afbrtn.com, 1
 afbrunswick.com, 1
 afbtoto.com, 1
 afc-capital.mx, 1
 afcmrs.org, 1
 afcmrsfeedback.org, 1
 afcmrstest.org, 1
@@ -8994,17 +8961,16 @@ agrikulturchic.com, 1
 agrios.de, 1
 agripartner.fr, 1
 agriquads.nl, 1
 agro-forestry.net, 1
 agrobaza.com.ua, 1
 agroconsultoraplus.com, 1
 agrodronechile.cl, 1
 agrolab.dk, 1
-agroline.by, 1
 agromotorsburzaco.com, 1
 agroplas.cf, 1
 agropotter.com.ua, 1
 agroxxi.ru, 0
 agroyard.com.ua, 1
 agsb.ch, 0
 agscinemas.com, 1
 agscinemasapp.com, 1
@@ -9461,16 +9427,17 @@ albayan.ae, 1
 albbounce.co.uk, 1
 albergolafiorita.com, 1
 albersdruck.de, 1
 albert-yu.com, 1
 albertathome.org, 1
 albertcuyp-markt.amsterdam, 1
 albertify.xyz, 1
 albertinum-goettingen.de, 1
+alberts-blatt.de, 1
 albilaga.id, 1
 albinma.com, 1
 albion2.org, 1
 albionfaeries.org.uk, 1
 alboweb.nl, 1
 albrocar.com, 1
 albstaedter-kids-cup.de, 1
 albuic.tk, 1
@@ -9548,17 +9515,17 @@ alexeykopytko.com, 1
 alexfabian.myftp.org, 1
 alexgaynor.net, 1
 alexgebhard.com, 1
 alexglover.co.uk, 1
 alexhalderman.com, 1
 alexhd.de, 1
 alexio.ml, 1
 alexisabarca.com, 1
-alexisathlani.com, 1
+alexisathlani.com, 0
 alexischaussy.xyz, 1
 alexiskoustoulidis.com, 1
 alexismeza.com, 1
 alexismeza.com.mx, 1
 alexismeza.dk, 1
 alexismeza.es, 1
 alexismeza.nl, 1
 alexjett.com, 1
@@ -9677,17 +9644,16 @@ aljaspod.hu, 1
 aljaspod.net, 1
 aljaspod.org, 1
 aljweb.com, 1
 alkacoin.net, 1
 alkami.com, 1
 alkamitech.com, 1
 alkel.info, 1
 alkemi-si.fr, 1
-alko-centr.ru, 1
 alko-stop.ml, 1
 alkopedia.tk, 1
 alkor.tk, 1
 alkusin.net, 1
 all-connect.net, 0
 all-fashion-schools.com, 1
 all-markup-news.com, 1
 all-things.tk, 1
@@ -10070,16 +10036,17 @@ amardham.org, 1
 amaresq.com, 1
 amarreconbrujeria.com, 1
 amarresconvudu.com, 1
 amarresdeamorconelbrujoguillermo.com, 1
 amarresimposibles.com, 1
 amarresperuanos.com, 1
 amarresydominio.com, 1
 amartinz.at, 1
+amaruddinmufid.com, 1
 amasea.yachts, 1
 amateri.com, 1
 amateurpornhours.com, 1
 amateurradionotes.com, 1
 amateurvoicetalent.com, 1
 amati.solutions, 1
 amato.tk, 1
 amatsuka.com, 1
@@ -10245,17 +10212,16 @@ ampleitsolutions.com.au, 1
 ampleroads.com, 1
 ampproject.com, 1
 ampproject.org, 1
 amputated.tk, 1
 amrcaustin.com, 1
 amrcla.com, 1
 amrff.com, 0
 amruta.org, 1
-ams-web-qa.azurewebsites.net, 1
 ams.co.rs, 1
 amsel305nc.ddnss.de, 1
 amsfoodhk.com, 1
 amstelveentje.nl, 1
 amsterdamian.com, 1
 amt-taxfrance.com, 1
 amtcd88.com, 1
 amtentertainments.co.uk, 1
@@ -11594,17 +11560,16 @@ asaabforever.com, 1
 asabacortoscaseros.tk, 1
 asadatec.de, 1
 asafaweb.com, 1
 asafilm.co, 1
 asafomba.com, 1
 asakoh.co.jp, 1
 asana.com, 1
 asananutrition.co.uk, 1
-asandu.eu, 1
 asanger.biz, 1
 asaphomeinspect.com, 1
 asart.bg, 1
 asato-jewelry.com, 1
 asbestosthedarkarts.com, 1
 asbito.de, 1
 ascamso.com, 1
 ascar.us, 1
@@ -12312,17 +12277,16 @@ avalon-studios.de, 1
 avalonbelltown.com, 1
 avalyuan.com, 1
 avamax.cz, 1
 avamax.eu, 1
 avancen.com, 1
 avanet.com, 1
 avangvpn.ga, 1
 avanovum.de, 1
-avanpost.co, 1
 avantitualatin.com, 1
 avarcom.tk, 1
 avarty.com, 1
 avatardiffusion.com, 1
 avcd.cz, 1
 avdagic.net, 1
 avdh.top, 1
 ave.zone, 1
@@ -12424,17 +12388,16 @@ awaremi-tai.com, 1
 awaresec.com, 1
 awaresec.no, 1
 awarify.io, 1
 awarify.me, 1
 awaro.net, 1
 awaygroundguide.com, 1
 awbouncycastlehire.com, 1
 awei.pub, 1
-awen.me, 1
 awesome-coconut-software.fr, 1
 awesomebouncycastles.co.uk, 1
 awesomelifedeals.today, 1
 awesomenamegenerator.com, 1
 awesomesit.es, 1
 awf0.xyz, 1
 awic.ca, 1
 awinninghabit.com, 1
@@ -12653,20 +12616,20 @@ b4ckbone.de, 1
 b4lint.hu, 1
 b4r7.de, 1
 b4z.eu, 1
 b5189.com, 1
 b5189.net, 1
 b5197.co, 1
 b5289.com, 1
 b538.com, 1
-b5706.com, 1
+b5706.com, 0
 b5707.com, 0
 b5708.com, 0
-b5709.com, 1
+b5709.com, 0
 b57bb.com, 0
 b57cc.com, 0
 b58365.com, 1
 b58app.com, 1
 b58appb58app.com, 1
 b58appb58appb58app.com, 1
 b5901.com, 1
 b5902.com, 1
@@ -12762,21 +12725,21 @@ b70995.com, 1
 b72.com, 1
 b72.net, 1
 b7306.com, 1
 b73app.com, 1
 b73bb.com, 1
 b73dd.com, 1
 b73ee.com, 1
 b73ff.com, 1
-b7501.com, 1
+b7501.com, 0
 b7502.com, 0
-b7503.com, 1
+b7503.com, 0
 b7506.com, 0
-b7507.com, 1
+b7507.com, 0
 b7508.com, 0
 b7509.com, 1
 b767.net, 1
 b77018.com, 1
 b789.co, 1
 b81365.com, 1
 b82365.com, 1
 b83.tv, 1
@@ -13168,16 +13131,17 @@ balinese.dating, 1
 balist.es, 1
 balivillassanur.com, 1
 baliyano.com, 1
 balkancrystals.com, 1
 balkanpharmstore.com, 1
 balkenbushmechanical.com, 1
 balkonien.org, 1
 ball-bizarr.de, 1
+ball3d.es, 1
 ballarin.cc, 1
 ballast.tk, 1
 ballbusting-cbt.com, 1
 ballejaune.com, 1
 balletcenterofhouston.com, 1
 ballettstudio-ost.de, 1
 ballinw.com, 1
 ballisticdetailing.com, 1
@@ -13321,17 +13285,16 @@ baoxue9.com, 1
 bap-consult.at, 1
 bapha.be, 1
 baptiste-peugnez.fr, 1
 baptisteplanckaert.tk, 1
 bar-harcourt.com, 1
 barabrume.fr, 1
 barakayu.com, 1
 baranmovie.tk, 1
-barans2239.com, 1
 baranyavar.hu, 1
 barao.tk, 1
 baraxolka.ru, 1
 barbaderespeito.com.br, 1
 barbara-fuchs-gruene-fuerth.de, 1
 barbarabowersrealty.com, 1
 barbarafabbri.com, 1
 barbarafeldman.com, 1
@@ -13437,17 +13400,17 @@ basementdoctor.com, 0
 basementdoctornorthwest.com, 1
 basementdoctorwestvirginia.com, 1
 basementdoctorwv.com, 1
 basementfinishingohio.com, 1
 basementwaterproofingdesmoines.com, 1
 basementwaterproofingsaintlouis.com, 1
 basementwaterproofingwi.com, 1
 baserverz.ga, 1
-baseweb.design, 1
+baseweb.design, 0
 bashc.at, 1
 bashing-battlecats.com, 1
 bashkirlife.tk, 1
 bashstreetband.co.uk, 1
 basicapparel.de, 1
 basicattentiontoken.org, 1
 basicports.com, 1
 basicports.eu, 1
@@ -13561,16 +13524,17 @@ baytv.it, 1
 baywatch.io, 1
 bayz.de, 1
 baza-gai.com.ua, 1
 bazaarbhaav.com, 1
 bazaarcompass.com, 1
 bazaclub.ru, 1
 bazari.com.pl, 1
 bazdell.com, 1
+bazinga-events.nl, 1
 bazos.at, 1
 bazos.cz, 1
 bazos.pl, 1
 bazos.sk, 1
 bazziergraphik.com, 1
 bb00228.com, 1
 bb057.com, 1
 bb087.com, 1
@@ -13756,16 +13720,17 @@ bebest.gov, 1
 bebetrotteur.com, 1
 beboldpr.com, 1
 bebout.domains, 1
 bebout.pw, 1
 becausecapitalism.org, 1
 beccaanne.photography, 1
 beccajoshwedding.com, 1
 bech32.net, 1
+beckerantiques.com, 1
 beckijayes.family, 1
 becklove.cn, 1
 becleverwithyourcash.com, 1
 become-lucky.com, 1
 becomeabricklayer.com.au, 1
 becquerelgroup.com, 1
 becs.ch, 0
 becubed.co, 1
@@ -13842,17 +13807,16 @@ beeswax-orgone.com, 1
 beetgroup.id, 1
 beethoveninlove.com, 1
 beetman.net, 1
 beeutifulparties.co.uk, 1
 beexfit.com, 0
 beezkneezcastles.co.uk, 1
 beeznest.com, 1
 befoodsafe.gov, 1
-beforesunrise.de, 1
 beforeyoueatoc.com, 1
 beframed.ch, 0
 befreewifi.info, 1
 befundonline.de, 1
 bega-dc.gov, 1
 begabungsfoerderung.info, 1
 begbie.com, 1
 beggintime.com, 1
@@ -14088,17 +14052,16 @@ bergstoneware.com, 1
 bergunabanget.com, 1
 berichtsheft-vorlage.de, 1
 berikod.ru, 1
 beris.us, 1
 beritanow.tk, 1
 beritatopbanten.com, 1
 berkat-luqs.ddns.net, 1
 berksabstract.com, 1
-berksarl.org, 1
 berksnetworking.com, 1
 berksteensmatter.org, 1
 berlin-cuisine.com, 1
 berlin-flirt.de, 1
 berlin.dating, 1
 berluga.com, 1
 bermeitinger.eu, 1
 bermos.net, 1
@@ -15258,16 +15221,17 @@ bizpare.com, 1
 bizpay.su, 1
 bizstarter.cz, 1
 biztera.com, 1
 biztok.eu, 1
 biztouch.work, 1
 bizzdesign.cloud, 1
 bizzdesign.com, 1
 bizzi.tv, 1
+bizzit.se, 1
 bjarnerest.de, 1
 bjfuli.com, 1
 bjl5689.com, 1
 bjl5689.net, 1
 bjl688.cc, 1
 bjmgeek.science, 1
 bjmun.cn, 1
 bjoe2k4.de, 1
@@ -15300,16 +15264,33 @@ bl4ckb0x.de, 1
 bl4ckb0x.eu, 1
 bl4ckb0x.info, 1
 bl4ckb0x.net, 1
 bl4ckb0x.org, 1
 blaargh.com, 1
 blaauwgeers.pro, 1
 blaauwgeers.travel, 1
 blabber.im, 1
+blablacar.co.uk, 1
+blablacar.com.tr, 1
+blablacar.com.ua, 1
+blablacar.de, 1
+blablacar.es, 1
+blablacar.fr, 1
+blablacar.hr, 1
+blablacar.hu, 1
+blablacar.in, 1
+blablacar.it, 1
+blablacar.mx, 1
+blablacar.nl, 1
+blablacar.pl, 1
+blablacar.pt, 1
+blablacar.ro, 1
+blablacar.rs, 1
+blablacar.ru, 1
 black-gay-porn.biz, 1
 black-holes.org, 1
 black-hornis.com, 1
 black-khat.com, 1
 black-magic-love-spells.com, 1
 black-mail.nl, 1
 black-pool.net, 1
 black-raven.fr, 1
@@ -15913,17 +15894,16 @@ bookreport.ga, 1
 bookshopofindia.com, 1
 booksinthefridge.at, 1
 booksjar.com, 1
 bookslibrarybooks.gq, 1
 booktoan.com, 1
 booktracker-org.appspot.com, 1
 bookwave.art, 1
 bookwormex.com, 1
-bookzaga.com, 1
 bool.be, 1
 boomersurf.com, 1
 boomfestival.org, 1
 boomkins.net, 1
 boomshelf.com, 1
 boomshelf.org, 1
 boomsual.com, 1
 boomvm.pw, 1
@@ -16158,16 +16138,17 @@ boutoncoupdepoing.fr, 1
 bouw.live, 1
 bouwbedrijfvandortbv.nl, 1
 bouzouada.com, 1
 bouzouks.net, 1
 bovenwebdesign.nl, 1
 bovworkplacepensions.com, 1
 bowdens.me, 1
 bowedwallcrackrepair.com, 1
+boweryandvine.com, 1
 bowlcake.fr, 1
 bowling.com, 1
 bowntycdn.net, 1
 bowtie.com.hk, 1
 boxcritters.wiki, 1
 boxcryptor.com, 0
 boxdevigneron.fr, 1
 boxdropcc.com, 1
@@ -16279,17 +16260,16 @@ brandbil.dk, 1
 brandbuilderwebsites.com, 1
 brandcodestyle.com, 0
 brandfolder.com, 1
 brandingclic.com, 1
 brandingclick.com, 1
 brandingcoapps.com, 1
 brando753.xyz, 1
 brandondivorcelawyer.com, 1
-brandonforce.com, 1
 brandongomez.me, 1
 brandonhaynesmd.com, 1
 brandonhubbard.com, 1
 brandonlin.me, 1
 brandonlui.com, 1
 brandonlui.ml, 1
 brandonsample.com, 1
 brandontaylor-black.com, 1
@@ -16729,18 +16709,18 @@ btc-doge.ga, 1
 btcarmory.com, 1
 btcbenthuizen.nl, 1
 btcbolsa.com, 1
 btcontract.com, 1
 btcp.space, 1
 btcpop.co, 1
 btdays.com, 1
 btddd.com, 1
-bte365app.com, 1
-bteapp.com, 1
+bte365app.com, 0
+bteapp.com, 0
 bthub.site, 1
 bthub.xyz, 1
 btine.tk, 1
 btio.pw, 0
 btku.org, 1
 btnissanparts.com, 1
 btopc.jp, 1
 btorrent.xyz, 1
@@ -17013,17 +16993,16 @@ bumble.com, 1
 bumilangkawi.com, 1
 bunadarbankinn.is, 1
 bunbun.be, 0
 bund-von-theramore.de, 1
 bundespolizei-forum.de, 1
 bundesverband-krisenintervention.de, 1
 bundesverbandkrisenintervention.de, 1
 bundito.com, 1
-bungabuket.com, 1
 bungee.pw, 1
 bungus.space, 1
 bunkyo-life.com, 1
 bunny-rabbits.com, 1
 bunny.parts, 1
 bunnycarenotes.com, 1
 bunnydiamond.de, 1
 bunnymud.com, 1
@@ -17601,17 +17580,16 @@ callumsilcock.com, 1
 callumsilcock.me, 1
 calluro.hr, 1
 calminteractive.fr, 1
 calmtech.com, 1
 calomel.org, 1
 calories.org, 1
 calotte-academy.com, 1
 calposa.ml, 1
-calprut.com, 1
 calrotaract.org, 1
 calucon.de, 1
 calverleyparish.church, 1
 calvin.my, 1
 calvinallen.net, 0
 calypso-tour.net, 1
 calypsohost.net, 1
 calyxengineers.com, 1
@@ -17840,17 +17818,16 @@ capper.de, 1
 capriccio.to, 1
 caprice-holdings.co.uk, 1
 caprichosdevicky.com, 1
 caps.is, 1
 capsogusto.com, 1
 capssouthafrica.co.za, 1
 capstansecurity.co.uk, 1
 capstansecurity.com, 1
-capstoneinsights.com, 1
 capsulesubs.fr, 1
 captain-dandelion.com, 1
 captainark.net, 1
 captainfit.in, 1
 captainsfarm.in, 1
 captalize.com, 1
 captivationscience.com, 1
 captivationtheory.com, 1
@@ -18019,17 +17996,16 @@ carlosmfalves.eu, 1
 carlospiga.fr, 1
 carlosvelezmarketing.com, 1
 carlot-j.com, 1
 carlovanwyk.com, 1
 carls-fallout-4-guide.com, 1
 carltontownfc.tk, 1
 carmatworld.co.uk, 1
 carmelglenane.com, 1
-carmelon-digital.com, 1
 carmelrise.co.uk, 1
 carmeni.tk, 1
 carmenluz.fr, 1
 carmineforsheriff.com, 1
 carnaticalifornia.com, 1
 carnet-du-voyageur.com, 1
 carnildo.com, 1
 carnivorousplants.co.uk, 1
@@ -18075,16 +18051,17 @@ carroattrezzimilanodaluiso.it, 1
 carroceriascarluis.com, 1
 carrolcountyohioelections.gov, 1
 carrolltontx.gov, 1
 carrouselcompany.fr, 1
 cars4salecy.com, 1
 carseatchecks.ca, 1
 carshippingcarriers.com, 1
 carsinsuranceis.com, 1
+carson-aviation-adventures.com, 1
 carson-matthews.co.uk, 1
 carsoug.com, 1
 carspneu.cz, 1
 cartale.ru, 1
 cartaodigi.com, 1
 cartegrise.xyz, 1
 cartelloni.roma.it, 1
 carterdan.net, 1
@@ -18477,30 +18454,30 @@ celadas.tk, 1
 celcelulares.com, 1
 celcomhomefibre.com.my, 1
 cele.bi, 1
 celebmasta.com, 1
 celebrasianconference.com, 1
 celebrityhealthcritic.com, 1
 celebrityphotos.blog, 1
 celebritypics.club, 1
+celebritypics.co, 1
 celebrityscope.net, 1
 celebritytopnews.tk, 1
 celebxx.com, 1
 celec.gob.ec, 0
 celectro-pro.com, 1
 celestebonito.pt, 1
 celestialdental.com, 1
 celestialenergies.com.au, 1
 celestialisms.com, 1
 celiac.com, 1
 celiendev.ch, 0
 celine-patisserie.fr, 1
 cell-lookup.com, 1
-cellartracker.com, 1
 cellebrite.com, 1
 celliberate.co.uk, 1
 cellohealth.com, 1
 celltek-server.de, 0
 celltesequ.com, 1
 celltick.com, 1
 celluliteorangeskin.com, 1
 celluliteremovaldiet.com, 1
@@ -18601,16 +18578,17 @@ ceramiche.roma.it, 1
 ceramixcoating.nl, 1
 ceramiya.com, 1
 cerastar.com, 1
 cerber.us, 1
 cerberis.com, 1
 cerberusinformatica.it, 1
 cerebelo.info, 1
 cerebrosano.gov, 1
+cerecup.com, 1
 ceredowv.gov, 1
 cerena-silver.ru, 1
 ceres-corp.org, 1
 cerivo.co.uk, 1
 cermak.photos, 1
 cernac.cz, 1
 cernakova.eu, 1
 cerpus-course.com, 1
@@ -18955,17 +18933,16 @@ checkmyessay.com, 1
 checkmyhttps.net, 1
 checkmypsoriasis.com, 1
 checkout.google.com, 1
 checkpoint-tshirt.com, 1
 checkra.in, 1
 checkras.tk, 1
 checkrente.nl, 1
 checkspf.net, 1
-checktech.group, 1
 checktype.com, 1
 checkui.com, 1
 checkwebsiteonline.com, 1
 checkyourmath.com, 1
 checkyourprivilege.org, 1
 checkyourreps.org, 1
 checookies.com, 1
 checos.co.uk, 1
@@ -19131,17 +19108,16 @@ chinaspaceflight.com, 1
 chinawhale.com, 1
 chinefrancophonie.fr, 1
 chinesemedicine.be, 1
 chineserecipes.xyz, 1
 ching.tv, 1
 chinookdigital.ca, 1
 chinookwebdesign.ca, 1
 chint.ai, 1
-chinternet.xyz, 1
 chinwag.im, 1
 chinwag.org, 1
 chip.pl, 1
 chipcore.com, 0
 chipdig.com, 1
 chipollinko.com.ua, 1
 chippy.ch, 0
 chips-scheduler.de, 1
@@ -19453,16 +19429,17 @@ cinq-elements.fr, 1
 cinq-elements.net, 1
 cinsects.de, 1
 cintactimber.com, 1
 cinteo.com, 1
 cio-ciso-interchange.org, 1
 cio-cisointerchange.org, 1
 cio.go.jp, 0
 cio.gov, 0
+cioconference.co.nz, 1
 cioscloud.com, 1
 cipartyhire.co.uk, 1
 cipher.team, 1
 cipherboy.com, 1
 cipherli.st, 0
 ciphersuite.info, 1
 ciphrex.com, 1
 cipri.com, 1
@@ -19600,17 +19577,16 @@ ckna.ca, 1
 ckostecki.de, 1
 ckp.ie, 1
 ckp.io, 1
 ckrubble.co.za, 1
 cktennis.com, 1
 ckventura.sk, 1
 cl.search.yahoo.com, 0
 cl0ud.space, 1
-claaruba.com, 1
 clacetandil.com.ar, 1
 clad.cf, 1
 claibornecountytn.gov, 1
 claimconnect.com, 1
 claimconnect.us, 1
 claimflights.at, 1
 claimflights.co.uk, 0
 claimflights.com, 0
@@ -20269,17 +20245,16 @@ cokebar.info, 1
 coker.com.au, 1
 cokomi.com, 1
 col-head.com, 1
 col.la, 1
 cola-host.tk, 1
 colaborativa.tv, 1
 colabug.com, 1
 coladv.com, 1
-colantonio.homelinux.net, 1
 colarelli.ch, 1
 colchonesmoon.com, 1
 colchonminicuna.com, 0
 colcomm.com, 1
 colcompany.com, 1
 coldaddy.com, 1
 coldawn.com, 0
 coldcardwallet.com, 1
@@ -20321,17 +20296,16 @@ collada.org, 1
 collage.me, 1
 collapsed.de, 1
 collard.tk, 1
 collare.com.mx, 1
 collare.mx, 1
 collectdocs.com, 1
 collectfood.com, 1
 collectiblebeans.com, 1
-collectivesupply.com, 1
 collectorknives.net, 1
 collectorsystems.com, 1
 colleencornez.com, 1
 colleenfaulknernovels.com, 1
 collegegirlhd.com, 1
 collegenavigator.gov, 1
 collegephysicsanswers.com, 1
 collegeprospectsofcentralindiana.com, 1
@@ -20347,17 +20321,16 @@ collinmbarrett.com, 1
 collins.kg, 1
 colloquy.mobi, 1
 colmena.biz, 1
 colo-tech.com, 1
 colocation-rennes.com, 1
 cololi.moe, 1
 colombiaemprendedora.org, 1
 colombian.dating, 1
-colombianas.webcam, 1
 colonialfurniturestripping.com, 1
 colonize.africa, 1
 coloppe.com, 1
 color01.net, 1
 coloradobluebook.gov, 1
 coloradolottery.com, 1
 coloradoroofingservice.com, 1
 coloraid.net, 1
@@ -20383,16 +20356,17 @@ colson-occasions.be, 0
 coltellisurvival.com, 1
 coltonrb.com, 1
 columbiacountyor.gov, 1
 columbiascaffolding.com, 1
 columbushydroxide.com, 1
 columbushydroxide.net, 1
 columbushydroxide.org, 1
 columbusks.gov, 1
+columbuswines.com, 1
 com-in.de, 1
 com-news.io, 1
 com.cc, 1
 comalia.com, 1
 comandofilmes.club, 1
 comarkinstruments.net, 1
 combattrecellulite.com, 1
 combigo.com, 1
@@ -20503,17 +20477,16 @@ comp.kiev.ua, 1
 comp2go.com.au, 1
 compactchess.cc, 1
 compagnia-buffo.de, 0
 compagniedesateliers.com, 1
 compagniemartin.com, 0
 compalliance.com, 1
 companion-web.net, 1
 comparatif-moto.fr, 1
-compareinsurance.com.au, 1
 comparelegalforms.com, 1
 comparemymobile.com, 1
 comparesoft.com, 1
 comparetheproject.com, 1
 comparewatch.com, 1
 comparexcloudcenter.com, 1
 compartirtrenmesaave.com, 1
 compassbest.com, 1
@@ -20615,17 +20588,16 @@ conceptatelier.de, 1
 concertengine.com, 1
 concerto.amsterdam, 1
 concerts-metal.ch, 0
 concertsenboite.fr, 1
 concertsto.com, 1
 concetrabajos.cl, 1
 conciencia.fit, 1
 concierge.diet, 1
-concilio.com, 1
 conciliumnotaire.ca, 1
 conclave.global, 1
 concordiagaming.com, 1
 concordsoftwareleasing.com, 1
 concretehermit.com, 0
 concreterepairatlanta.com, 1
 concreterepairconcreteraising.com, 1
 concursopublico.com.br, 1
@@ -20732,18 +20704,16 @@ conradsautotransmissionrepair.com, 1
 conrail.blue, 1
 conrazon.me, 1
 consagracionamariasantisima.org, 1
 consciente.ch, 1
 consciente.ngo, 1
 consciente.ong, 1
 consciouschoices.net, 1
 consegnafioridomicilio.net, 1
-consegne.it, 1
-consejosdenutricion.com, 1
 consensoprivacy.it, 1
 consertodecelulares.com.br, 1
 conservativenewsandviews.com, 1
 consideredgifts.com, 1
 consideryourways.net, 1
 consilium-vitae.ch, 1
 consiliumvitae.ch, 1
 consill.com, 1
@@ -20947,17 +20917,17 @@ corecdn.org, 1
 corecodec.com, 1
 coredns.rocks, 1
 coreless-stretchfilm.com, 1
 corelia.net, 1
 corepartners.com.ua, 1
 coresolutions.ca, 1
 coresos.com, 1
 coreum.ca, 1
-coreup.de, 1
+coreup.de, 0
 corevetconnect.co.uk, 1
 coreyjmahler.com, 1
 corgei.com, 1
 corgi.party, 1
 coribi.com, 1
 corinastefan.ro, 1
 corinnanese.de, 1
 corintech.net, 1
@@ -20991,16 +20961,17 @@ coropiacere.org, 1
 corp.goog, 1
 corpfin.net, 1
 corpio.nl, 1
 corpkitnw.com, 1
 corpoflow.nl, 1
 corporateclash.net, 1
 corporatecomputingsolutions.com, 1
 corporateinfluencers.com, 1
+corporatesubscriptions.com.au, 0
 corporativoarval.info, 1
 corpsepaint.life, 1
 corpulant.coffee, 1
 corpulantcoffee.com, 1
 corpulent.coffee, 1
 corpulentcoffee.com, 1
 corpuschristisouthriver.org, 1
 corpusslayer.com, 1
@@ -21552,17 +21523,16 @@ crypto-clix.xyz, 1
 crypto.cat, 1
 crypto.graphics, 1
 crypto.is, 0
 cryptobin.co, 1
 cryptocaseproject.com, 1
 cryptoclix.website, 1
 cryptocon.org, 1
 cryptocurrencyfare.com, 1
-cryptodigitalgroup.com, 1
 cryptoearnblog.xyz, 1
 cryptofan.org, 1
 cryptofomo.capital, 1
 cryptofomocapital.com, 1
 cryptofox.nl, 1
 cryptography.ch, 1
 cryptography.io, 1
 cryptoguidemap.com, 1
@@ -21761,17 +21731,16 @@ cubos.io, 1
 cubsbestteaminbaseball.com, 1
 cubua.com, 1
 cubyhome.com, 1
 cucaracha.tk, 1
 cuchichi.es, 1
 cuckmysock.com, 1
 cuckoo.ee, 1
 cuddlecat.io, 1
-cuddlecomfort.com, 1
 cuddlingyaks.com, 1
 cudoo.de, 1
 cuegee.com, 1
 cuentamecomopaso.es, 1
 cuentasmutualamr.org.ar, 1
 cuetoems.com, 1
 cuff-links.nl, 1
 cuibonobo.com, 1
@@ -22046,16 +22015,17 @@ cycledownunder.com, 1
 cyclehackluxembourgcity.lu, 1
 cycleluxembourg.lu, 1
 cyclinggoodso.com, 1
 cyclisjumper.gallery, 1
 cyclonebikes.com.ua, 1
 cyclonedesign.ca, 1
 cyclop-editorial.fr, 1
 cyclowiz.com, 1
+cydetec.com, 1
 cyelint.com, 1
 cyfly.org, 1
 cygnaltech.com, 1
 cygnan.com, 1
 cygnatus.com, 1
 cygnius.net, 1
 cykelbanor.se, 1
 cyl6.com, 1
@@ -22133,17 +22103,16 @@ d2ph.com, 1
 d2s.uk, 1
 d30365.com, 1
 d36533.com, 1
 d36594.com, 1
 d3a.xyz, 1
 d3dev.cf, 1
 d3lab.net, 1
 d3x.pw, 1
-d3xt3r01.tk, 1
 d42.no, 1
 d4b.in.ua, 1
 d4done.com, 1
 d4fx.de, 1
 d4h.live, 1
 d4rkdeagle.tk, 1
 d4wson.com, 1
 d4x.de, 1
@@ -23218,17 +23187,16 @@ debie-usedcars.be, 0
 debierhandel.nl, 1
 debigare.com, 1
 debitterballetjes.tk, 1
 debora-singkreis.de, 1
 deborahmarinelli.eu, 1
 debraydesign.com.au, 1
 debron-ot.nl, 1
 debrusoft.ch, 1
-debt.com, 1
 debtrecycling.com.au, 1
 debuemon.com, 1
 debuis.nl, 1
 debzsh.tk, 1
 dec6.gc.ca, 1
 decal-times.com, 1
 decalquai.ch, 0
 decarrouseloss.nl, 1
@@ -23280,17 +23248,17 @@ decs.es, 1
 dede.ml, 1
 dedelta.net, 1
 dedg3.com, 1
 dedge.org, 1
 dedicatedtowomenobgyn.com, 1
 dedirten.com, 1
 dedmoroz.ga, 1
 dedmorozrzn.ru, 0
-dedoho.pw, 0
+dedoho.pw, 1
 dedoles.at, 1
 dedoles.com, 1
 dedoles.cz, 1
 dedoles.de, 1
 dedoles.hu, 1
 dedoles.pl, 1
 dedoles.ro, 1
 dedoles.sk, 1
@@ -23342,17 +23310,16 @@ defendbearbutte.org, 1
 defender-pro.com, 1
 defendersz.com, 1
 defendinnovation.org, 1
 defendtheweb.net, 1
 defensivefirearmsinstruction.org, 1
 defero.io, 1
 defesa.gov.br, 1
 defesaaereanaval.com.br, 1
-deffo.com.au, 1
 defi-metier.org, 1
 defiant.com, 1
 defiantrust.com, 1
 defibrillateur.co, 1
 defifa.ga, 1
 defimetier.fr, 1
 define-atheism.com, 1
 define-atheist.com, 1
@@ -23478,17 +23445,16 @@ deltaservers.blog.br, 1
 deltaservers.com.br, 1
 deltasigmachi.org, 1
 deltav.ml, 0
 deltava.org, 1
 deltna.com, 1
 deluxe-dubai.com, 1
 delvinoadegas.com.br, 1
 delycate.com, 1
-demadryn.com, 1
 demand.io, 1
 demarle.ch, 0
 demastglazenwasserij.nl, 1
 demedx.at, 1
 dementiacaring.com.au, 1
 dementiapraecox.de, 1
 dementieva-pennetta.tk, 1
 demeyere-usedcars.be, 0
@@ -23549,17 +23515,17 @@ denissealatinsoul.com, 1
 denistruffaut.fr, 0
 deniszczuk.pl, 1
 deniz.uk, 1
 denizdesign.co.uk, 1
 denkeandersblog.de, 1
 denkmalagentur.ch, 1
 denkmalsetzung.at, 1
 denkubator.de, 1
-dennhat.com, 1
+dennhat.com, 0
 denninger.jp, 1
 dennisang.com, 1
 dennisdoes.net, 0
 dennisforbes.ca, 1
 dennishzg.com, 1
 denniskoot.nl, 1
 dennismurphy.biz, 1
 dennisvandenbos.nl, 1
@@ -24534,17 +24500,16 @@ dist-it.com, 1
 dist.torproject.org, 0
 distancelove.ml, 1
 disti.com, 1
 distiduffer.org, 1
 distillery.com, 1
 distinctdesign2009.com, 1
 distinctivephotography.com.au, 1
 distinguishedprisoner.com, 1
-distortmotion.com, 1
 distracteddriving.gov, 1
 distraction.gov, 1
 distratus.com, 1
 distribuidoradecierres.com, 1
 distribuidoraplus.com, 1
 distribuidorveterinario.es, 1
 distributednya.com, 1
 distributore.it, 1
@@ -24788,19 +24753,17 @@ dobrisan.ro, 1
 doc-baza.ru, 1
 doc.ai, 1
 doc.python.org, 1
 doc.to, 0
 doc8643.com, 1
 docabo.ch, 1
 docassure.de, 1
 docbox.ch, 1
-docdoc.ru, 1
 docemeldoces.com, 1
-doceo.com, 1
 dochimera.com, 1
 dochub.com, 1
 dockerbook.com, 0
 dockerm.com, 1
 dockerup.net, 1
 dockflow.com, 1
 dockstarter.com, 1
 dockysearch.com, 1
@@ -24808,16 +24771,17 @@ doclassworks.com, 1
 docline.gov, 1
 docloh.de, 1
 docloudu.info, 1
 docmed360.com, 1
 docplexus.com, 1
 docs.google.com, 1
 docs.python.org, 1
 docs.tw, 1
+docsoc.org.uk, 1
 doctabaila.com, 1
 doctafit.com, 1
 docteurcardin.com, 0
 doctor-locks.co.uk, 1
 doctor.dating, 1
 doctor360.com.au, 1
 doctorbini.com, 1
 doctorcalefon.com, 1
@@ -24848,17 +24812,16 @@ doesinfotech.com, 1
 doesmycodehavebugs.today, 1
 doesnotscale.com, 0
 doetwat.nl, 1
 dofuspvp.com, 0
 dofux.org, 1
 dogadayiz.net, 1
 dogan.ch, 0
 dogandoganay.com, 1
-dogcat.vn, 1
 dogcontrol.ca, 1
 dogcratereview.info, 1
 doge.town, 1
 dogear.ch, 1
 dogforum.de, 1
 dogfriendly.co.uk, 1
 dogft.com, 1
 doggedbyirs.com, 1
@@ -25142,17 +25105,16 @@ dot42.no, 1
 dotacni-parazit.cz, 1
 dotbigbang.com, 1
 dotbox.org, 1
 dotcircle.co, 1
 dotcomtest02-single.azurewebsites.net, 1
 dotesports.com, 1
 dotgov.gov, 1
 dothebangthingsalon.com, 1
-dothydesign.com, 1
 dotjesper.com, 1
 dotjesper.dk, 1
 dotjesper.net, 1
 dotjs.party, 1
 dotkniseandroida.cz, 1
 dotkod.com, 1
 dotkod.pl, 1
 dotneko.net, 1
@@ -25259,16 +25221,17 @@ dposit.org, 1
 dprb.biz, 1
 dpress24.it, 1
 dps.srl, 1
 dpsg-hohenlinden.de, 1
 dpsg-roden.de, 0
 dpucarriersma.gov, 1
 dpwsweeps.co.uk, 1
 dr-becarelli-philippe.chirurgiens-dentistes.fr, 1
+dr-bodendorf.de, 1
 dr-ermilov.com, 1
 dr-knirr.de, 1
 dr-marlen-nystroem.de, 1
 dr-moldovan.de, 1
 dr-nystroem.de, 1
 dr-peter-jahn.de, 1
 dr-schlamminger.de, 1
 dr-schmutzer.de, 1
@@ -25650,17 +25613,16 @@ drvr.xyz, 1
 drwang.group, 1
 drweissbrot.net, 1
 drybjed.net, 1
 drycleancoalition.org, 1
 drycreekphoto.com, 1
 drydrydry.com, 1
 dryerventcleaningarlington.com, 1
 dryerventcleaningcarrollton.com, 1
-dryjersey.com, 1
 drymx.cn, 1
 drywallresponse.gov, 1
 drywtea.com, 1
 ds.lol, 1
 ds138.cc, 1
 ds168.cc, 1
 ds28s.com, 1
 ds388.cc, 1
@@ -25731,16 +25693,17 @@ dtmbx.org, 1
 dtmf.io, 1
 dtmlnp.com, 1
 dtngny.com, 1
 dtnx.email, 1
 dtnx.eu, 1
 dtnx.net, 1
 dtnx.org, 1
 dtnxny.com, 1
+dtoweb.be, 1
 dtp-mstdn.jp, 0
 dtuaarsfest.dk, 1
 dtune.me, 1
 dtx.sk, 1
 du-alex.ru, 1
 dualascent.com, 1
 dualias.xyz, 0
 duama.top, 1
@@ -25761,17 +25724,16 @@ dubrovnikfoodtours.com, 1
 dubrovskiy.net, 1
 dubrovskiy.pro, 1
 dubstep.fr, 1
 dubtrack.fm, 1
 dubyou.tw, 1
 ducadu.com, 1
 ducalendars.com, 1
 duch.cloud, 1
-duchateaugyn.be, 1
 duchyoffeann.com, 1
 ducius.net, 1
 duckasylum.com, 0
 duckbase.com, 1
 duckblade.com, 1
 duckcorp.org, 1
 duckduck.horse, 1
 duckduckstart.com, 1
@@ -25888,16 +25850,17 @@ dustygroove.com, 1
 dustyro.se, 1
 dustyspokesbnb.ca, 1
 dustywilson.com, 1
 dutabisnis.com, 1
 dutabisniz.com, 0
 dutch.desi, 1
 dutch1.nl, 1
 dutchassistancedogs.nl, 1
+dutchessuganda.com, 1
 dutchfoodie.nl, 1
 dutchforkrunners.com, 1
 dutchplayers.com, 1
 dutchrank.nl, 1
 dutchsailors.com, 1
 dutchwanderers.nl, 1
 dutchweballiance.nl, 1
 dutkoteam.com, 1
@@ -25951,17 +25914,16 @@ dxzsj.cn, 1
 dy1d.com, 1
 dybuster.at, 1
 dybuster.ch,