Merge autoland to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Thu, 15 Aug 2019 12:43:29 +0300
changeset 488057 144fbfb409b72b5849ace2a1e3c199c259f7c1d3
parent 487956 7d9a2196d3132f3716652666a945cb1831dfda36 (current diff)
parent 488056 26094588b48e14bb39fb838196e8d489b1a4c3ee (diff)
child 488153 0db07ff50ab5c9b655ba7e23788f68a2b9d66e71
push id36434
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:44:30 +0000
treeherdermozilla-central@144fbfb409b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
ipc/chromium/src/base/file_util.cc
ipc/chromium/src/base/file_util.h
ipc/chromium/src/base/file_util_mac.mm
ipc/chromium/src/base/file_util_posix.cc
ipc/chromium/src/base/file_util_win.cc
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1563,19 +1563,16 @@ pref("privacy.trackingprotection.fingerp
 
 // Enable cryptomining blocking by default for all channels, only on desktop.
 pref("privacy.trackingprotection.cryptomining.enabled", true);
 
 pref("browser.contentblocking.database.enabled", true);
 
 pref("dom.storage_access.enabled", true);
 
-pref("dom.storage_access.auto_grants", true);
-pref("dom.storage_access.max_concurrent_auto_grants", 5);
-
 pref("browser.contentblocking.control-center.ui.showBlockedLabels", true);
 pref("browser.contentblocking.control-center.ui.showAllowedLabels", false);
 
 pref("browser.contentblocking.cryptomining.preferences.ui.enabled", true);
 pref("browser.contentblocking.fingerprinting.preferences.ui.enabled", true);
 
 // Possible values for browser.contentblocking.features.strict pref:
 //   Tracking Protection:
--- a/browser/base/content/test/performance/browser_startup_content_mainthreadio.js
+++ b/browser/base/content/test/performance/browser_startup_content_mainthreadio.js
@@ -286,17 +286,17 @@ add_task(async function() {
     processes[process] = processes[process].filter(
       entry => !("condition" in entry) || entry.condition
     );
     processes[process].forEach(entry => {
       entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
     });
   }
 
-  let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
+  let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
   let shouldPass = true;
   for (let procName in processes) {
     let whitelist = processes[procName];
     info(
       `whitelisted paths for ${procName} process:\n` +
         whitelist
           .map(e => {
             let operations = Object.keys(e)
@@ -340,25 +340,32 @@ add_task(async function() {
       let filename = marker.filename.toLowerCase();
 
       if (!filename) {
         // We are still missing the filename on some mainthreadio markers,
         // these markers are currently useless for the purpose of this test.
         continue;
       }
 
-      if (!WIN) {
-        if (filename == "/dev/urandom") {
-          continue;
-        }
+      if (!WIN && filename == "/dev/urandom") {
+        continue;
+      }
 
-        // Ignore I/O due to IPC. This doesn't really touch the disk.
-        if (filename.startsWith(tmpPath + "/org.chromium.")) {
-          continue;
-        }
+      // /dev/shm is always tmpfs (a memory filesystem); this isn't
+      // really I/O any more than mmap/munmap are.
+      if (LINUX && filename.startsWith("/dev/shm/")) {
+        continue;
+      }
+
+      // Shared memory uses temporary files on MacOS <= 10.11 to avoid
+      // a kernel security bug that will never be patched (see
+      // https://crbug.com/project-zero/1671 for details).  This can
+      // be removed when we no longer support those OS versions.
+      if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
+        continue;
       }
 
       let expected = false;
       for (let entry of whitelist) {
         if (pathMatches(entry.path, filename)) {
           entry[marker.operation] = (entry[marker.operation] || 0) - 1;
           entry._used = true;
           expected = true;
--- a/browser/base/content/test/performance/browser_startup_mainthreadio.js
+++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js
@@ -879,17 +879,17 @@ add_task(async function() {
     startupPhases[phase] = startupPhases[phase].filter(
       entry => !("condition" in entry) || entry.condition
     );
     startupPhases[phase].forEach(entry => {
       entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
     });
   }
 
-  let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
+  let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
   let shouldPass = true;
   for (let phase in phases) {
     let whitelist = startupPhases[phase];
     info(
       `whitelisted paths ${phase}:\n` +
         whitelist
           .map(e => {
             let operations = Object.keys(e)
@@ -914,25 +914,32 @@ add_task(async function() {
       let filename = marker.filename.toLowerCase();
 
       if (!filename) {
         // We are still missing the filename on some mainthreadio markers,
         // these markers are currently useless for the purpose of this test.
         continue;
       }
 
-      if (!WIN) {
-        if (filename == "/dev/urandom") {
-          continue;
-        }
+      if (!WIN && filename == "/dev/urandom") {
+        continue;
+      }
 
-        // Ignore I/O due to IPC. This doesn't really touch the disk.
-        if (filename.startsWith(tmpPath + "/org.chromium.")) {
-          continue;
-        }
+      // /dev/shm is always tmpfs (a memory filesystem); this isn't
+      // really I/O any more than mmap/munmap are.
+      if (LINUX && filename.startsWith("/dev/shm/")) {
+        continue;
+      }
+
+      // Shared memory uses temporary files on MacOS <= 10.11 to avoid
+      // a kernel security bug that will never be patched (see
+      // https://crbug.com/project-zero/1671 for details).  This can
+      // be removed when we no longer support those OS versions.
+      if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
+        continue;
       }
 
       let expected = false;
       for (let entry of whitelist) {
         if (pathMatches(entry.path, filename)) {
           entry[marker.operation] = (entry[marker.operation] || 0) - 1;
           entry._used = true;
           expected = true;
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -30,16 +30,20 @@ class UrlbarView {
    */
   constructor(input) {
     this.input = input;
     this.panel = input.panel;
     this.controller = input.controller;
     this.document = this.panel.ownerDocument;
     this.window = this.document.defaultView;
 
+    if (this.input.megabar) {
+      this.panel.classList.add("megabar");
+    }
+
     this._mainContainer = this.panel.querySelector(".urlbarView-body-inner");
     this._rows = this.panel.querySelector("#urlbarView-results");
 
     this._rows.addEventListener("mousedown", this);
     this._rows.addEventListener("mouseup", this);
 
     // For the horizontal fade-out effect, set the overflow attribute on result
     // rows when they overflow.
@@ -418,76 +422,86 @@ class UrlbarView {
   _openPanel() {
     if (this.isOpen) {
       return;
     }
     this.controller.userSelectionBehavior = "none";
 
     this.panel.removeAttribute("actionoverride");
 
-    // Make the panel span the width of the window.
+    let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
+
     let px = number => number.toFixed(2) + "px";
-    let documentRect = this._getBoundsWithoutFlushing(
-      this.document.documentElement
-    );
-    let width = documentRect.right - documentRect.left;
-    this.panel.setAttribute("width", width);
+    let width;
+    if (this.input.megabar) {
+      // Make the panel span the width of the textbox.
+      width = inputRect.width;
+    } else {
+      // Make the panel span the width of the window.
+      let documentRect = this._getBoundsWithoutFlushing(
+        this.document.documentElement
+      );
+      width = documentRect.right - documentRect.left;
+
+      // Keep the popup items' site icons aligned with the input's identity
+      // icon if it's not too far from the edge of the window.  We define
+      // "too far" as "more than 30% of the window's width AND more than
+      // 250px".
+      let boundToCheck = this.window.RTL_UI ? "right" : "left";
+      let startOffset = Math.abs(
+        inputRect[boundToCheck] - documentRect[boundToCheck]
+      );
+      let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
+
+      if (alignSiteIcons) {
+        // Calculate the end margin if we have a start margin.
+        let boundToCheckEnd = this.window.RTL_UI ? "left" : "right";
+        let endOffset = Math.abs(
+          inputRect[boundToCheckEnd] - documentRect[boundToCheckEnd]
+        );
+        if (endOffset > startOffset * 2) {
+          // Provide more space when aligning would result in an unbalanced
+          // margin. This allows the location bar to be moved to the start
+          // of the navigation toolbar to reclaim space for results.
+          endOffset = startOffset;
+        }
+
+        // We need to align with the tracking protection icon if the
+        // 'pageproxystate' is valid since the tracking protection icon would be
+        // at the first position instead of the identity icon in this case.
+        let alignIcon;
+        if (this.input.getAttribute("pageproxystate") === "valid") {
+          alignIcon = this.document.getElementById(
+            "tracking-protection-icon-box"
+          );
+        } else {
+          alignIcon = this.document.getElementById("identity-icon");
+        }
+        let alignRect = this._getBoundsWithoutFlushing(alignIcon);
+        let start = this.window.RTL_UI
+          ? documentRect.right - alignRect.right
+          : alignRect.left;
+
+        this.panel.style.setProperty("--item-padding-start", px(start));
+        this.panel.style.setProperty("--item-padding-end", px(endOffset));
+      } else {
+        this.panel.style.removeProperty("--item-padding-start");
+        this.panel.style.removeProperty("--item-padding-end");
+      }
+    }
+
+    this.panel.style.width = px(width);
     this._mainContainer.style.maxWidth = px(width);
 
-    // Keep the popup items' site icons aligned with the input's identity
-    // icon if it's not too far from the edge of the window.  We define
-    // "too far" as "more than 30% of the window's width AND more than
-    // 250px".
-    let boundToCheck = this.window.RTL_UI ? "right" : "left";
-    let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
-    let startOffset = Math.abs(
-      inputRect[boundToCheck] - documentRect[boundToCheck]
-    );
-    let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
-    if (alignSiteIcons) {
-      // Calculate the end margin if we have a start margin.
-      let boundToCheckEnd = this.window.RTL_UI ? "left" : "right";
-      let endOffset = Math.abs(
-        inputRect[boundToCheckEnd] - documentRect[boundToCheckEnd]
-      );
-      if (endOffset > startOffset * 2) {
-        // Provide more space when aligning would result in an unbalanced
-        // margin. This allows the location bar to be moved to the start
-        // of the navigation toolbar to reclaim space for results.
-        endOffset = startOffset;
-      }
-
-      // We need to align with the tracking protection icon if the
-      // 'pageproxystate' is valid since the tracking protection icon would be
-      // at the first position instead of the identity icon in this case.
-      let alignIcon;
-      if (this.input.getAttribute("pageproxystate") === "valid") {
-        alignIcon = this.document.getElementById(
-          "tracking-protection-icon-box"
-        );
-      } else {
-        alignIcon = this.document.getElementById("identity-icon");
-      }
-      let alignRect = this._getBoundsWithoutFlushing(alignIcon);
-      let start = this.window.RTL_UI
-        ? documentRect.right - alignRect.right
-        : alignRect.left;
-
-      this.panel.style.setProperty("--item-padding-start", px(start));
-      this.panel.style.setProperty("--item-padding-end", px(endOffset));
-    } else {
-      this.panel.style.removeProperty("--item-padding-start");
-      this.panel.style.removeProperty("--item-padding-end");
-    }
-
-    // Align the panel with the input's parent toolbar.
-    let toolbarRect = this._getBoundsWithoutFlushing(
-      this.input.textbox.closest("toolbar")
-    );
-    this.panel.style.top = px(toolbarRect.bottom);
+    // Align the panel with the input or the input's parent toolbar, depending
+    // on megabar status.
+    let alignmentRect = this.input.megabar
+      ? this._getBoundsWithoutFlushing(this.input.textbox)
+      : this._getBoundsWithoutFlushing(this.input.textbox.closest("toolbar"));
+    this.panel.style.top = px(alignmentRect.bottom);
 
     this.panel.removeAttribute("hidden");
     this.input.inputField.setAttribute("aria-expanded", "true");
     this.input.dropmarker.setAttribute("open", "true");
 
     this.window.addEventListener("mousedown", this);
     this.panel.addEventListener("mousedown", this);
     this.input.textbox.addEventListener("mousedown", this);
--- a/browser/locales/en-US/chrome/browser/brandings.dtd
+++ b/browser/locales/en-US/chrome/browser/brandings.dtd
@@ -1,11 +1,9 @@
 <!-- LOCALIZATION NOTE:
 	The following feature names must be treated as a brand, and kept in English.
 	They cannot be:
 	- Declined to adapt to grammatical case.
 	- Transliterated.
 	- Translated. -->
 
-<!ENTITY sendShortName "Send">
 <!ENTITY sendFullName "Firefox Send">
-<!ENTITY monitorShortName "Monitor">
 <!ENTITY monitorFullName "Firefox Monitor">
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -107,20 +107,16 @@ convenience of Safari and Chrome users o
 <!ENTITY fxa.menu.firefoxAccount "&syncBrand.fxAccount.label;">
 <!ENTITY fxa.menu.signin.label "Sign in to &brandProductName;">
 <!ENTITY fxa.menu.turnOnSync.label "Turn on &syncBrand.shortName.label;">
 <!ENTITY fxa.menu.account.label "Account">
 <!ENTITY fxa.menu.firefoxServices.label "&brandProductName; Services">
 
 <!-- LOCALIZATION NOTE (fxa.service) Used to define the different FxA services
      for the Firefox Account toolbar menu screen.  -->
-<!ENTITY fxa.service.send.description "Upload and share files with end-to-end encryption plus a link that automatically expires.">
-<!ENTITY fxa.service.send.launch "Launch &sendShortName;">
-<!ENTITY fxa.service.monitor.description "Check if your email has appeared in a data breach and get alerted if it appears in a new breach.">
-<!ENTITY fxa.service.monitor.launch "Launch &monitorShortName;">
 <!ENTITY fxa.service.sendTab.description "Send a tab instantly to any device you’re signed in on.">
 
 <!-- LOCALIZATION NOTE (fullscreenWarning.beforeDomain.label,
      fullscreenWarning.afterDomain.label): these two strings are used
      respectively before and after the domain requiring fullscreen.
      Localizers can use one of them, or both, to better adapt this
      sentence to their language. -->
 <!ENTITY fullscreenWarning.beforeDomain.label "">
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -2,16 +2,17 @@
 /* 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 = [
   "BrowserUsageTelemetry",
+  "getUniqueDomainsVisitedInPast24Hours",
   "URICountListener",
   "URLBAR_SELECTED_RESULT_TYPES",
   "URLBAR_SELECTED_RESULT_METHODS",
   "MINIMUM_TAB_COUNT_INTERVAL_MS",
 ];
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
@@ -885,8 +886,13 @@ let BrowserUsageTelemetry = {
       if (tabCount === undefined) {
         tabCount = getTabCount();
       }
       Services.telemetry.getHistogramById("TAB_COUNT").add(tabCount);
       this._lastRecordTabCount = currentTime;
     }
   },
 };
+
+// Used by nsIBrowserUsage
+function getUniqueDomainsVisitedInPast24Hours() {
+  return URICountListener.uniqueDomainsVisitedInPast24Hours;
+}
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -73,21 +73,16 @@ ChromeUtils.defineModuleGetter(
 );
 ChromeUtils.defineModuleGetter(
   this,
   "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
-  "URICountListener",
-  "resource:///modules/BrowserUsageTelemetry.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
   "PermissionUITelemetry",
   "resource:///modules/PermissionUITelemetry.jsm"
 );
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "IDNService",
   "@mozilla.org/network/idn-service;1",
@@ -1149,27 +1144,16 @@ MIDIPermissionPrompt.prototype = {
     ];
   },
 };
 
 PermissionUI.MIDIPermissionPrompt = MIDIPermissionPrompt;
 
 function StorageAccessPermissionPrompt(request) {
   this.request = request;
-
-  XPCOMUtils.defineLazyPreferenceGetter(
-    this,
-    "_autoGrants",
-    "dom.storage_access.auto_grants"
-  );
-  XPCOMUtils.defineLazyPreferenceGetter(
-    this,
-    "_maxConcurrentAutoGrants",
-    "dom.storage_access.max_concurrent_auto_grants"
-  );
 }
 
 StorageAccessPermissionPrompt.prototype = {
   __proto__: PermissionPromptForRequestPrototype,
 
   get usePermissionManager() {
     return false;
   },
@@ -1243,113 +1227,52 @@ StorageAccessPermissionPrompt.prototype 
       "<>",
       "{}",
     ]);
   },
 
   get promptActions() {
     let self = this;
 
-    let storageAccessHistogram = Services.telemetry.getHistogramById(
-      "STORAGE_ACCESS_API_UI"
-    );
-
     return [
       {
         label: gBrowserBundle.GetStringFromName(
           "storageAccess.DontAllow.label"
         ),
         accessKey: gBrowserBundle.GetStringFromName(
           "storageAccess.DontAllow.accesskey"
         ),
         action: Ci.nsIPermissionManager.DENY_ACTION,
         callback(state) {
-          storageAccessHistogram.add("Deny");
           self.cancel();
         },
       },
       {
         label: gBrowserBundle.GetStringFromName("storageAccess.Allow.label"),
         accessKey: gBrowserBundle.GetStringFromName(
           "storageAccess.Allow.accesskey"
         ),
         action: Ci.nsIPermissionManager.ALLOW_ACTION,
         callback(state) {
-          storageAccessHistogram.add("Allow");
           self.allow({ "storage-access": "allow" });
         },
       },
       {
         label: gBrowserBundle.GetStringFromName(
           "storageAccess.AllowOnAnySite.label"
         ),
         accessKey: gBrowserBundle.GetStringFromName(
           "storageAccess.AllowOnAnySite.accesskey"
         ),
         action: Ci.nsIPermissionManager.ALLOW_ACTION,
         callback(state) {
-          storageAccessHistogram.add("AllowOnAnySite");
           self.allow({ "storage-access": "allow-on-any-site" });
         },
       },
     ];
   },
 
   get topLevelPrincipal() {
     return this.request.topLevelPrincipal;
   },
-
-  get maxConcurrentAutomaticGrants() {
-    // one percent of the number of top-levels origins visited in the current
-    // session (but not to exceed 24 hours), or the value of the
-    // dom.storage_access.max_concurrent_auto_grants preference, whichever is
-    // higher.
-    return Math.max(
-      Math.max(
-        Math.floor(URICountListener.uniqueDomainsVisitedInPast24Hours / 100),
-        this._maxConcurrentAutoGrants
-      ),
-      0
-    );
-  },
-
-  getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) {
-    let prefix = `3rdPartyStorage^${thirdPartyOrigin}`;
-    let perms = Services.perms.getAllWithTypePrefix(prefix);
-    let origins = new Set();
-    while (perms.length) {
-      let perm = perms.shift();
-      // Let's make sure that we're not looking at a permission for
-      // https://exampletracker.company when we mean to look for the
-      // permisison for https://exampletracker.com!
-      if (perm.type != prefix && !perm.type.startsWith(`${prefix}^`)) {
-        continue;
-      }
-      origins.add(perm.principal.origin);
-    }
-    return origins.size;
-  },
-
-  onBeforeShow() {
-    let storageAccessHistogram = Services.telemetry.getHistogramById(
-      "STORAGE_ACCESS_API_UI"
-    );
-
-    storageAccessHistogram.add("Request");
-
-    let thirdPartyOrigin = this.request.principal.origin;
-    if (
-      this._autoGrants &&
-      this.getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) <
-        this.maxConcurrentAutomaticGrants
-    ) {
-      // Automatically accept the prompt
-      this.allow({ "storage-access": "allow-auto-grant" });
-
-      storageAccessHistogram.add("AllowAutomatically");
-
-      return false;
-    }
-    return true;
-  },
 };
 
 PermissionUI.StorageAccessPermissionPrompt = StorageAccessPermissionPrompt;
--- a/browser/themes/shared/places/tree-icons.css
+++ b/browser/themes/shared/places/tree-icons.css
@@ -80,15 +80,20 @@ treechildren::-moz-tree-cell-text(title,
   color: ThreeDShadow;
   margin: 0 5px;
 }
 
 treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
   color: HighlightText;
 }
 
+/* Remove tiny spacing in separators appearing after the twisty column */
+treechildren::-moz-tree-twisty(title, separator) {
+  padding: 0;
+}
+
 treechildren::-moz-tree-image(cutting) {
   opacity: 0.5;
 }
 
 treechildren::-moz-tree-cell-text(cutting) {
   opacity: 0.7;
 }
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -24,21 +24,28 @@
 :root[lwt-popup-brighttext] {
   --urlbar-popup-url-color: @lwtPopupBrighttextLinkColor@;
   --urlbar-popup-action-color: #30e60b;
 }
 
 #urlbar-results {
   position: fixed;
   z-index: 1;
-  left: 0;
-  right: 0;
   background: var(--autocomplete-popup-background);
   color: var(--autocomplete-popup-color);
   text-shadow: none;
+}
+
+#urlbar-results.megabar {
+  margin-inline-start: 5px;
+}
+
+#urlbar-results:not(.megabar) {
+  left: 0;
+  right: 0;
   border-block: 1px solid var(--chrome-content-separator-color);
 }
 
 #urlbar-contextual-tip {
   font-size: 13px;
   align-items: center;
   display: flex;
 
@@ -246,20 +253,23 @@
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-tags > .urlbarView-tag {
   background-color: var(--autocomplete-popup-highlight-color);
   color: var(--autocomplete-popup-highlight-background);
 }
 
 /* Search one-offs. */
 #urlbar-results > .search-one-offs {
   -moz-box-orient: horizontal;
+  padding-top: 16px;
+  padding-bottom: 16px;
+}
+
+#urlbar-results:not(.megabar) > .search-one-offs {
   padding-inline-start: var(--item-padding-start, 5px);
   padding-inline-end: var(--item-padding-end, 5px);
-  padding-top: 16px;
-  padding-bottom: 16px;
 }
 
 #urlbar-results .search-panel-one-offs {
   padding-left: 12px;
   padding-right: 12px;
 }
 
 #urlbar-results .search-panel-header {
--- a/build.gradle
+++ b/build.gradle
@@ -204,38 +204,31 @@ task machBuildFaster(type: MachExec) {
 }
 
 def createMachStagePackageTask(name) {
     return task(name, type: MachExec) {
         onlyIf rootProject.ext.geckoBinariesOnlyIf
 
         dependsOn rootProject.machBuildFaster
 
-        // We'd prefer to take these from the :omnijar project directly, but
-        // it's awkward to reach across projects at evaluation time, so we
-        // duplicate the list here.
-        inputs.dir "${topsrcdir}/mobile/android/chrome"
-        inputs.dir "${topsrcdir}/mobile/android/components"
-        inputs.dir "${topsrcdir}/mobile/android/locales"
-        inputs.dir "${topsrcdir}/mobile/android/modules"
-        inputs.dir "${topsrcdir}/mobile/android/themes"
-        inputs.dir "${topsrcdir}/toolkit"
-
         workingDir "${topobjdir}"
 
         // We'd prefer this to be a `mach` invocation, but `mach build
         // mobile/android/installer/stage-package` doesn't work as expected.
         commandLine mozconfig.substs.GMAKE
         args '-C'
         args "${topobjdir}/mobile/android/installer"
         args 'stage-package'
 
         outputs.file "${topobjdir}/dist/fennec/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
         outputs.file "${topobjdir}/dist/fennec/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
 
+        // Force running `stage-package`.
+        outputs.upToDateWhen { false }
+
         // `path` is like `:machStagePackage`.
         standardOutput = new TaggedLogOutputStream("${path}>", logger)
         errorOutput = standardOutput
     }
 }
 
 createMachStagePackageTask("machStagePackageForFennec").with {
     outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
--- a/devtools/client/debugger/panel.js
+++ b/devtools/client/debugger/panel.js
@@ -155,19 +155,24 @@ DebuggerPanel.prototype = {
     return this._selectors.getIsPaused(this._getState(), thread);
   },
 
   selectSourceURL(url, line, column) {
     const cx = this._selectors.getContext(this._getState());
     return this._actions.selectSourceURL(cx, url, { line, column });
   },
 
-  selectSource(sourceId, line, column) {
+  async selectSource(sourceId, line, column) {
     const cx = this._selectors.getContext(this._getState());
-    return this._actions.selectSource(cx, sourceId, { line, column });
+    const location = { sourceId, line, column };
+
+    await this._actions.selectSource(cx, sourceId, location);
+    if (this._selectors.hasLogpoint(this._getState(), location)) {
+      this._actions.openConditionalPanel(location, true);
+    }
   },
 
   canLoadSource(sourceId) {
     return this._selectors.canLoadSource(this._getState(), sourceId);
   },
 
   getSourceByActorId(sourceId) {
     return this._selectors.getSourceByActorId(this._getState(), sourceId);
--- a/devtools/client/debugger/src/client/firefox/commands.js
+++ b/devtools/client/debugger/src/client/firefox/commands.js
@@ -179,17 +179,18 @@ function removeXHRBreakpoint(path: strin
   return threadFront.removeXHRBreakpoint(path, method);
 }
 
 // Get the string key to use for a breakpoint location.
 // See also duplicate code in breakpoint-actor-map.js :(
 function locationKey(location: BreakpointLocation) {
   const { sourceUrl, line, column } = location;
   const sourceId = location.sourceId || "";
-  return `${(sourceUrl: any)}:${(sourceId: any)}:${line}:${(column: any)}`;
+  // $FlowIgnore
+  return `${sourceUrl}:${sourceId}:${line}:${column}`;
 }
 
 function detachWorkers() {
   for (const thread of listWorkerThreadFronts()) {
     thread.detach();
   }
 }
 
--- a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
@@ -7,17 +7,17 @@ import React, { PureComponent } from "re
 import ReactDOM from "react-dom";
 import { connect } from "../../utils/connect";
 import classNames from "classnames";
 import "./ConditionalPanel.css";
 import { toEditorLine } from "../../utils/editor";
 import actions from "../../actions";
 
 import {
-  getBreakpointForLocation,
+  getBreakpoint,
   getConditionalPanelLocation,
   getLogPointStatus,
   getContext,
 } from "../../selectors";
 
 import type { SourceLocation, Context } from "../../types";
 
 function addNewLine(doc: Object) {
@@ -226,17 +226,17 @@ export class ConditionalPanel extends Pu
     return null;
   }
 }
 
 const mapStateToProps = state => {
   const location = getConditionalPanelLocation(state);
   return {
     cx: getContext(state),
-    breakpoint: getBreakpointForLocation(state, location),
+    breakpoint: getBreakpoint(state, location),
     location,
     log: getLogPointStatus(state),
   };
 };
 
 const {
   setBreakpointOptions,
   openConditionalPanel,
--- a/devtools/client/debugger/src/components/Editor/Preview/Popup.css
+++ b/devtools/client/debugger/src/components/Editor/Preview/Popup.css
@@ -47,16 +47,20 @@
 }
 
 .popover .preview-popup .header .link {
   align-self: flex-end;
   color: var(--theme-highlight-blue);
   text-decoration: underline;
 }
 
+.popover .preview-popup .object-node {
+  padding-inline-start: 0px;
+}
+
 .preview-token:hover {
   cursor: default;
 }
 
 .preview-token,
 .debug-expression.preview-token {
   background-color: var(--theme-highlight-yellow);
 }
--- a/devtools/client/debugger/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/src/components/Editor/Preview/Popup.js
@@ -79,20 +79,24 @@ export class Popup extends Component<Pro
     }
   }
 
   calculateMaxHeight = () => {
     const { editorRef } = this.props;
     if (!editorRef) {
       return "auto";
     }
-    return (
-      editorRef.getBoundingClientRect().height +
-      editorRef.getBoundingClientRect().top
-    );
+
+    const { height, top } = editorRef.getBoundingClientRect();
+    const maxHeight = height + top;
+    if (maxHeight < 250) {
+      return maxHeight;
+    }
+
+    return 250;
   };
 
   renderFunctionPreview() {
     const {
       cx,
       selectSourceURL,
       preview: { result },
     } = this.props;
--- a/devtools/client/debugger/src/components/Editor/SearchBar.js
+++ b/devtools/client/debugger/src/components/Editor/SearchBar.js
@@ -136,40 +136,40 @@ class SearchBar extends Component<Props,
   clearSearch = () => {
     const { editor: ed, query } = this.props;
     if (ed) {
       const ctx = { ed, cm: ed.codeMirror };
       removeOverlay(ctx, query);
     }
   };
 
-  closeSearch = (e: SyntheticEvent<HTMLElement>) => {
-    const { cx, closeFileSearch, editor, searchOn } = this.props;
+  closeSearch = (e: SyntheticKeyboardEvent<HTMLElement>) => {
+    const { cx, closeFileSearch, editor, searchOn, query } = this.props;
+    this.clearSearch();
     if (editor && searchOn) {
-      this.clearSearch();
       closeFileSearch(cx, editor);
       e.stopPropagation();
       e.preventDefault();
     }
-    this.setState({ query: "", inputFocused: false });
+    this.setState({ query, inputFocused: false });
   };
 
   toggleSearch = (e: SyntheticKeyboardEvent<HTMLElement>) => {
     e.stopPropagation();
     e.preventDefault();
     const { editor, searchOn, setActiveSearch } = this.props;
 
     // Set inputFocused to false, so that search query is highlighted whenever search shortcut is used, even if the input already has focus.
     this.setState({ inputFocused: false });
 
     if (!searchOn) {
       setActiveSearch("file");
     }
 
-    if (searchOn && editor) {
+    if (this.props.searchOn && editor) {
       const query = editor.codeMirror.getSelection() || this.state.query;
 
       if (query !== "") {
         this.setState({ query, inputFocused: true });
         this.doSearch(query);
       } else {
         this.setState({ query: "", inputFocused: true });
       }
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -330,22 +330,29 @@ class Editor extends PureComponent<Props
     event.preventDefault();
 
     const {
       cx,
       selectedSourceWithContent,
       breakpointActions,
       editorActions,
       isPaused,
+      conditionalPanelLocation,
+      closeConditionalPanel,
     } = this.props;
     const { editor } = this.state;
     if (!selectedSourceWithContent || !editor) {
       return;
     }
 
+    // only allow one conditionalPanel location.
+    if (conditionalPanelLocation) {
+      closeConditionalPanel();
+    }
+
     const target: Element = (event.target: any);
     const { id: sourceId } = selectedSourceWithContent.source;
     const line = lineAtHeight(editor, sourceId, event);
 
     if (typeof line != "number") {
       return;
     }
 
--- a/devtools/client/debugger/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/src/reducers/breakpoints.js
@@ -173,18 +173,22 @@ export function getBreakpointsList(state
 }
 
 export function getBreakpointCount(state: OuterState): number {
   return getBreakpointsList(state).length;
 }
 
 export function getBreakpoint(
   state: OuterState,
-  location: SourceLocation
+  location: ?SourceLocation
 ): ?Breakpoint {
+  if (!location) {
+    return undefined;
+  }
+
   const breakpoints = getBreakpointsMap(state);
   return breakpoints[makeBreakpointId(location)];
 }
 
 export function getBreakpointsDisabled(state: OuterState): boolean {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.every(breakpoint => breakpoint.disabled);
 }
@@ -203,27 +207,35 @@ export function getBreakpointsForSource(
   return breakpoints.filter(bp => {
     const location = isGeneratedSource ? bp.generatedLocation : bp.location;
     return location.sourceId === sourceId && (!line || line == location.line);
   });
 }
 
 export function getBreakpointForLocation(
   state: OuterState,
-  location: SourceLocation | null
+  location: ?SourceLocation
 ): ?Breakpoint {
-  if (!location || !location.sourceId) {
+  if (!location) {
     return undefined;
   }
 
   const isGeneratedSource = isGeneratedId(location.sourceId);
   return getBreakpointsList(state).find(bp => {
     const loc = isGeneratedSource ? bp.generatedLocation : bp.location;
     return isMatchingLocation(loc, location);
   });
 }
 
 export function getHiddenBreakpoint(state: OuterState): ?Breakpoint {
   const breakpoints = getBreakpointsList(state);
   return breakpoints.find(bp => bp.options.hidden);
 }
 
+export function hasLogpoint(
+  state: OuterState,
+  location: ?SourceLocation
+): ?string {
+  const breakpoint = getBreakpoint(state, location);
+  return breakpoint && breakpoint.options.logValue;
+}
+
 export default update;
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -103,16 +103,18 @@ skip-if = os == "win"
 skip-if = os == "win"
 [browser_dbg-react-jsx.js]
 [browser_dbg-returnvalues.js]
 [browser_dbg-reload.js]
 [browser_dbg-reloading.js]
 skip-if = true
 [browser_dbg-pause-points.js]
 [browser_dbg-scopes-mutations.js]
+[browser_dbg-search-file-retains-query.js]
+skip-if = os == "win" # Bug 1393121
 [browser_dbg-search-file.js]
 skip-if = os == "win" # Bug 1393121
 [browser_dbg-search-file-paused.js]
 skip-if = os == "win" # Bug 1393121
 [browser_dbg-quick-open.js]
 skip-if = os == "win"
 [browser_dbg-search-project.js]
 [browser_dbg-blackbox-original.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file-retains-query.js
@@ -0,0 +1,45 @@
+/* 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/>. */
+
+// Tests the search bar retains previous query on re-opening.
+
+function waitForSearchState(dbg) {
+  return waitForState(dbg, () => getCM(dbg).state.search);
+}
+
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html", "simple1.js");
+  const {
+    selectors: { getActiveSearch, getFileSearchQuery },
+  } = dbg;
+  const source = findSource(dbg, "simple1.js");
+
+  await selectSource(dbg, source.url);
+
+  // Open search bar
+  pressKey(dbg, "fileSearch");
+  await waitFor(() => getActiveSearch() === "file");
+  is(getActiveSearch(), "file");
+
+  // Type a search query
+  type(dbg, "con");
+  await waitForSearchState(dbg);
+  is(getFileSearchQuery(), "con");
+  is(getCM(dbg).state.search.query, "con");
+
+  // Close the search bar
+  pressKey(dbg, "Escape");
+  await waitFor(() => getActiveSearch() === null);
+  is(getActiveSearch(), null);
+
+  // Re-open search bar
+  pressKey(dbg, "fileSearch");
+  await waitFor(() => getActiveSearch() === "file");
+  is(getActiveSearch(), "file");
+
+  // Test for the retained query
+  is(getCM(dbg).state.search.query, "con");
+  await waitForDispatch(dbg, "UPDATE_FILE_SEARCH_QUERY");
+  is(getFileSearchQuery(), "con");
+});
--- a/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js
@@ -34,19 +34,20 @@ add_task(async function() {
   is(dbg.selectors.getActiveSearch(), null);
 
   pressKey(dbg, "fileSearch");
 
   const el = getFocusedEl(dbg);
 
   type(dbg, "con");
   await waitForSearchState(dbg);
+  await waitForDispatch(dbg, "UPDATE_SEARCH_RESULTS");
 
   const state = cm.state.search;
-
+  
   pressKey(dbg, "Enter");
   is(state.posFrom.line, 3);
 
   pressKey(dbg, "Enter");
   is(state.posFrom.line, 4);
 
   pressKey(dbg, "ShiftEnter");
   is(state.posFrom.line, 3);
@@ -70,9 +71,10 @@ add_task(async function() {
   await selectSource(dbg, "simple2");
   ok(findElement(dbg, "searchField"), "Search field is still visible");
 
   // search is always focused regardless of when or how it was opened
   pressKey(dbg, "fileSearch");
   await clickElement(dbg, "codeMirror");
   pressKey(dbg, "fileSearch");
   is(dbg.win.document.activeElement.tagName, "INPUT", "Search field focused");
+  
 });
--- a/devtools/client/shared/components/Frame.js
+++ b/devtools/client/shared/components/Frame.js
@@ -199,21 +199,21 @@ class Frame extends Component {
             },
             functionDisplayName
           ),
           " "
         );
       }
     }
 
-    // If the message comes from a logPoint or conditional breakpoint,
+    // If the message comes from a logPoint,
     // prefix the source location accordingly
-    if (frame.origin) {
+    if (frame.options) {
       let locationPrefix;
-      if (frame.origin === "logPoint") {
+      if (frame.options.logPoint) {
         locationPrefix = "Logpoint @ ";
       }
 
       if (locationPrefix) {
         sourceElements.push(
           dom.span(
             {
               key: "locationPrefix",
--- a/devtools/client/shared/components/test/mochitest/test_frame_01.html
+++ b/devtools/client/shared/components/test/mochitest/test_frame_01.html
@@ -328,20 +328,20 @@ window.onload = async function () {
       tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:23",
       source: "https://bugzilla.mozilla.org/original.js",
     });
 
     // Check when a message comes from a logPoint,
     // a prefix should render before source
     await checkFrameComponent({
       frame: {
-        origin: "logPoint",
         source: "http://myfile.com/mahscripts.js",
         line: 55,
         column: 10,
+        options: { logPoint: true },
       }
     }, {
       locationPrefix: "Logpoint @ ",
       file: "mahscripts.js",
       line: 55,
       column: 10,
       shouldLink: true,
       tooltip: "View source in Debugger → http://myfile.com/mahscripts.js:55:10",
--- a/devtools/client/shared/view-source.js
+++ b/devtools/client/shared/view-source.js
@@ -47,17 +47,17 @@ exports.viewSourceInStyleEditor = async 
  * the source was able to be displayed in the Debugger, as the built-in Firefox
  * View Source is the fallback.
  *
  * @param {Toolbox} toolbox
  * @param {string} sourceURL
  * @param {number} sourceLine
  * @param {number} sourceColumn
  * @param {string} sourceID
- * @param {string} [reason=unknown]
+ * @param {(string|object)} [reason=unknown]
  *
  * @return {Promise<boolean>}
  */
 exports.viewSourceInDebugger = async function(
   toolbox,
   sourceURL,
   sourceLine,
   sourceColumn,
--- a/devtools/client/webconsole/test/browser/browser.ini
+++ b/devtools/client/webconsole/test/browser/browser.ini
@@ -86,16 +86,19 @@ support-files =
   test-insecure-passwords-web-console-warning.html
   test-inspect-cross-domain-objects-frame.html
   test-inspect-cross-domain-objects-top.html
   test_jsterm_screenshot_command.html
   test-local-session-storage.html
   test-location-debugger-link-console-log.js
   test-location-debugger-link-errors.js
   test-location-debugger-link.html
+  test-location-debugger-link-logpoint-1.js
+  test-location-debugger-link-logpoint-2.js
+  test-location-debugger-link-logpoint.html
   test-location-styleeditor-link-1.css
   test-location-styleeditor-link-2.css
   test-location-styleeditor-link-minified.css
   test-location-styleeditor-link.html
   test-message-categories-canvas-css.html
   test-message-categories-canvas-css.js
   test-message-categories-css-loader.css
   test-message-categories-css-loader.css^headers^
@@ -354,16 +357,17 @@ skip-if = fission
 [browser_webconsole_input_field_focus_on_panel_select.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_insecure_passwords_about_blank_web_console_warning.js]
 [browser_webconsole_insecure_passwords_web_console_warning.js]
 [browser_webconsole_inspect_cross_domain_object.js]
 skip-if = fission
 [browser_webconsole_keyboard_accessibility.js]
 [browser_webconsole_location_debugger_link.js]
+[browser_webconsole_location_logpoint_debugger_link.js]
 [browser_webconsole_location_scratchpad_link.js]
 [browser_webconsole_location_styleeditor_link.js]
 [browser_webconsole_logErrorInPage.js]
 [browser_webconsole_loglimit.js]
 [browser_webconsole_logWarningInPage.js]
 [browser_webconsole_longstring_getter.js]
 [browser_webconsole_longstring.js]
 [browser_webconsole_message_categories.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_location_logpoint_debugger_link.js
@@ -0,0 +1,187 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test clicking locations of logpoint logs and errors will open corresponding
+// conditional panels in the debugger.
+
+"use strict";
+
+const TEST_URI =
+  "http://example.com/browser/devtools/client/webconsole/" +
+  "test/browser/test-location-debugger-link-logpoint.html";
+
+add_task(async function() {
+  // On e10s, the exception thrown in test-location-debugger-link-errors.js
+  // is triggered in child process and is ignored by test harness
+  if (!Services.appinfo.browserTabsRemoteAutostart) {
+    expectUncaughtException();
+  }
+
+  // Eliminate interference from "saved" breakpoints
+  // when running the test multiple times
+  await clearDebuggerPreferences();
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  info("Open the Debugger panel");
+  await openDebugger();
+
+  const toolbox = gDevTools.getToolbox(hud.target);
+  const dbg = createDebuggerContext(toolbox);
+  await selectSource(dbg, "test-location-debugger-link-logpoint-1.js");
+
+  info("Add a logpoint with an invalid expression");
+  await setLogPoint(dbg, 9, "undefinedVariable");
+
+  info("Add a logpoint with a valid expression");
+  await setLogPoint(dbg, 10, "`a is ${a}`");
+
+  await assertEditorLogpoint(dbg, 9, { hasLog: true });
+  await assertEditorLogpoint(dbg, 10, { hasLog: true });
+
+  info("Close the file in the debugger");
+  await closeTab(dbg, "test-location-debugger-link-logpoint-1.js");
+
+  info("Selecting the console");
+  await toolbox.selectTool("webconsole");
+
+  info("Call the function");
+  await invokeInTab("add");
+
+  info("Wait for two messages");
+  await waitFor(() => findMessages(hud, "").length === 2);
+
+  await testOpenInDebugger(
+    hud,
+    toolbox,
+    "[Logpoint threw]: undefinedVariable is not defined",
+    true,
+    false,
+    false,
+    "undefinedVariable"
+  );
+
+  info("Selecting the console again");
+  await toolbox.selectTool("webconsole");
+  await testOpenInDebugger(
+    hud,
+    toolbox,
+    "a is 1",
+    true,
+    false,
+    false,
+    "`a is ${a}`"
+  );
+
+  // Test clicking location of a removed logpoint, or a newly added breakpoint
+  // at an old logpoint's location will only highlight its line
+  info("Remove the logpoints");
+  const source = await findSource(
+    dbg,
+    "test-location-debugger-link-logpoint-1.js"
+  );
+  await removeBreakpoint(dbg, source.id, 9);
+  await removeBreakpoint(dbg, source.id, 10);
+  await addBreakpoint(dbg, "test-location-debugger-link-logpoint-1.js", 10);
+
+  info("Selecting the console");
+  await toolbox.selectTool("webconsole");
+  await testOpenInDebugger(
+    hud,
+    toolbox,
+    "[Logpoint threw]: undefinedVariable is not defined",
+    true,
+    9,
+    12
+  );
+
+  info("Selecting the console again");
+  await toolbox.selectTool("webconsole");
+  await testOpenInDebugger(hud, toolbox, "a is 1", true, 10, 12);
+});
+
+// Test clicking locations of logpoints from different files
+add_task(async function() {
+  if (!Services.appinfo.browserTabsRemoteAutostart) {
+    expectUncaughtException();
+  }
+
+  await clearDebuggerPreferences();
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  info("Open the Debugger panel");
+  await openDebugger();
+
+  const toolbox = gDevTools.getToolbox(hud.target);
+  const dbg = createDebuggerContext(toolbox);
+
+  info("Add a logpoint to the first file");
+  await selectSource(dbg, "test-location-debugger-link-logpoint-1.js");
+  await setLogPoint(dbg, 10, "`a is ${a}`");
+
+  info("Add a logpoint to the second file");
+  await selectSource(dbg, "test-location-debugger-link-logpoint-2.js");
+  await setLogPoint(dbg, 10, "`c is ${c}`");
+
+  info("Selecting the console");
+  await toolbox.selectTool("webconsole");
+
+  info("Call the function from the first file");
+  await invokeInTab("add");
+
+  info("Wait for the first message");
+  await waitFor(() => findMessages(hud, "").length === 1);
+  await testOpenInDebugger(
+    hud,
+    toolbox,
+    "a is 1",
+    true,
+    false,
+    false,
+    "`a is ${a}`"
+  );
+
+  info("Selecting the console again");
+  await toolbox.selectTool("webconsole");
+
+  info("Call the function from the second file");
+  await invokeInTab("subtract");
+
+  info("Wait for the second message");
+  await waitFor(() => findMessages(hud, "").length === 2);
+  await testOpenInDebugger(
+    hud,
+    toolbox,
+    "c is 1",
+    true,
+    false,
+    false,
+    "`c is ${c}`"
+  );
+});
+
+async function setLogPoint(dbg, index, expression) {
+  rightClickElement(dbg, "gutter", index);
+  selectContextMenuItem(
+    dbg,
+    `${selectors.addLogItem},${selectors.editLogItem}`
+  );
+  const onBreakpointSet = waitForDispatch(dbg, "SET_BREAKPOINT");
+  await typeInPanel(dbg, expression);
+  await onBreakpointSet;
+}
+
+function getLineEl(dbg, line) {
+  const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
+  return lines[line - 1];
+}
+
+function assertEditorLogpoint(dbg, line, { hasLog = false } = {}) {
+  const hasLogClass = getLineEl(dbg, line).classList.contains("has-log");
+
+  ok(
+    hasLogClass === hasLog,
+    `Breakpoint log ${hasLog ? "exists" : "does not exist"} on line ${line}`
+  );
+}
--- a/devtools/client/webconsole/test/browser/head.js
+++ b/devtools/client/webconsole/test/browser/head.js
@@ -376,69 +376,75 @@ function waitForNodeMutation(node, obser
       resolve(mutations);
       observer.disconnect();
     });
     observer.observe(node, observeConfig);
   });
 }
 
 /**
- * Search for a given message.  When found, simulate a click on the
+ * Search for a given message. When found, simulate a click on the
  * message's location, checking to make sure that the debugger opens
- * the corresponding URL.
+ * the corresponding URL. If the message was generated by a logpoint,
+ * check if the corresponding logpoint editing panel is opened.
  *
  * @param {Object} hud
  *        The webconsole
  * @param {Object} toolbox
  *        The toolbox
  * @param {String} text
- *        The text to search for.  This should be contained in the
- *        message.  The searching is done with @see findMessage.
+ *        The text to search for. This should be contained in the
+ *        message. The searching is done with @see findMessage.
  * @param {boolean} expectUrl
  *        Whether the URL in the opened source should match the link, or whether
  *        it is expected to be null.
  * @param {boolean} expectLine
  *        It indicates if there is the need to check the line.
  * @param {boolean} expectColumn
  *        It indicates if there is the need to check the column.
+ * @param {String} logPointExpr
+ *        The logpoint expression
  */
 async function testOpenInDebugger(
   hud,
   toolbox,
   text,
   expectUrl = true,
   expectLine = true,
-  expectColumn = true
+  expectColumn = true,
+  logPointExpr = undefined
 ) {
   info(`Finding message for open-in-debugger test; text is "${text}"`);
   const messageNode = await waitFor(() => findMessage(hud, text));
   const frameLinkNode = messageNode.querySelector(
     ".message-location .frame-link"
   );
   ok(frameLinkNode, "The message does have a location link");
   await checkClickOnNode(
     hud,
     toolbox,
     frameLinkNode,
     expectUrl,
     expectLine,
-    expectColumn
+    expectColumn,
+    logPointExpr
   );
 }
 
 /**
  * Helper function for testOpenInDebugger.
  */
 async function checkClickOnNode(
   hud,
   toolbox,
   frameLinkNode,
   expectUrl,
   expectLine,
-  expectColumn
+  expectColumn,
+  logPointExpr
 ) {
   info("checking click on node location");
 
   const onSourceInDebuggerOpened = once(hud.ui, "source-in-debugger-opened");
 
   EventUtils.sendMouseEvent(
     { type: "click" },
     frameLinkNode.querySelector(".frame-link-filename")
@@ -480,16 +486,32 @@ async function checkClickOnNode(
     ok(column, `source column found ("${column}")`);
 
     is(
       dbg._selectors.getSelectedLocation(dbg._getState()).column,
       column,
       "expected source column"
     );
   }
+
+  if (logPointExpr !== undefined && logPointExpr !== "") {
+    const inputEl = dbg.panelWin.document.activeElement;
+    is(
+      inputEl.tagName,
+      "TEXTAREA",
+      "The textarea of logpoint panel is focused"
+    );
+
+    const inputValue = inputEl.parentElement.parentElement.innerText.trim();
+    is(
+      inputValue,
+      logPointExpr,
+      "The input in the open logpoint panel matches the logpoint expression"
+    );
+  }
 }
 
 /**
  * Returns true if the give node is currently focused.
  */
 function hasFocus(node) {
   return (
     node.ownerDocument.activeElement == node && node.ownerDocument.hasFocus()
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/test-location-debugger-link-logpoint-1.js
@@ -0,0 +1,15 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function add() {
+  const a = 1;
+  const b = 2;
+
+  return a + b;
+}
+
+add();
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/test-location-debugger-link-logpoint-2.js
@@ -0,0 +1,15 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function subtract() {
+  const c = 1;
+  const d = 2;
+
+  return c - d;
+}
+
+subtract();
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/test-location-debugger-link-logpoint.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <title>
+      Web Console test for opening logpoint message links in Debugger
+    </title>
+    <!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <script
+      type="text/javascript"
+      src="test-location-debugger-link-logpoint-1.js"
+    ></script>
+    <script
+      type="text/javascript"
+      src="test-location-debugger-link-logpoint-2.js"
+    ></script>
+  </head>
+  <body>
+    <p>Web Console test for opening logpoint message links in Debugger.</p>
+    <button onclick="add()">Add</button>
+    <button onclick="subtract()">Subtract</button>
+  </body>
+</html>
--- a/devtools/client/webconsole/utils/messages.js
+++ b/devtools/client/webconsole/utils/messages.js
@@ -242,17 +242,17 @@ function transformConsoleAPICallPacket(p
         source: message.filename,
         sourceId: message.sourceId,
         line: message.lineNumber,
         column: message.columnNumber,
       }
     : null;
 
   if (type === "logPointError" || type === "logPoint") {
-    frame.origin = "logPoint";
+    frame.options = { logPoint: true };
   }
 
   return new ConsoleMessage({
     source: MESSAGE_SOURCE.CONSOLE_API,
     type,
     level,
     parameters,
     messageText,
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -9965,16 +9965,31 @@ exports.CSS_PROPERTIES = {
       "line-through",
       "none",
       "overline",
       "revert",
       "underline",
       "unset"
     ]
   },
+  "text-decoration-skip-ink": {
+    "isInherited": true,
+    "subproperties": [
+      "text-decoration-skip-ink"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "inherit",
+      "initial",
+      "none",
+      "revert",
+      "unset"
+    ]
+  },
   "text-decoration-style": {
     "isInherited": false,
     "subproperties": [
       "text-decoration-style"
     ],
     "supports": [],
     "values": [
       "-moz-none",
@@ -9984,16 +9999,31 @@ exports.CSS_PROPERTIES = {
       "inherit",
       "initial",
       "revert",
       "solid",
       "unset",
       "wavy"
     ]
   },
+  "text-decoration-thickness": {
+    "isInherited": false,
+    "subproperties": [
+      "text-decoration-thickness"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "from-font",
+      "inherit",
+      "initial",
+      "revert",
+      "unset"
+    ]
+  },
   "text-emphasis": {
     "isInherited": true,
     "subproperties": [
       "text-emphasis-style",
       "text-emphasis-color"
     ],
     "supports": [
       "color"
@@ -10197,16 +10227,31 @@ exports.CSS_PROPERTIES = {
       "initial",
       "lowercase",
       "none",
       "revert",
       "unset",
       "uppercase"
     ]
   },
+  "text-underline-offset": {
+    "isInherited": true,
+    "subproperties": [
+      "text-underline-offset"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "from-font",
+      "inherit",
+      "initial",
+      "revert",
+      "unset"
+    ]
+  },
   "top": {
     "isInherited": false,
     "subproperties": [
       "top"
     ],
     "supports": [],
     "values": [
       "auto",
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11344,16 +11344,20 @@ nsresult nsDocShell::UpdateURLAndHistory
           aDocument->NodePrincipal(),  // triggeringPrincipal
           nullptr, nullptr, aDocument->GetCsp(), true,
           getter_AddRefs(newSHEntry));
       NS_ENSURE_SUCCESS(rv, rv);
       mOSHE = newSHEntry;
     }
     newSHEntry->SetURI(aNewURI);
     newSHEntry->SetOriginalURI(aNewURI);
+    // Setting the resultPrincipalURI to nullptr is fine here: it will cause
+    // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
+    // in our case.  We could also set it to aNewURI, with the same result.
+    newSHEntry->SetResultPrincipalURI(nullptr);
     newSHEntry->SetLoadReplace(false);
   }
 
   // Step 2.4 and 3: Modify new/original session history entry and clear its
   // POST data, if there is any.
   newSHEntry->SetStateData(aData);
   newSHEntry->SetPostData(nullptr);
 
new file mode 100644
--- /dev/null
+++ b/docshell/test/mochitest/bug1422334_redirect.html
@@ -0,0 +1,3 @@
+<html>
+  <body>You should never see this</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/mochitest/bug1422334_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Moved Temporarily
+Location: ../navigation/blank.html?x=y
--- a/docshell/test/mochitest/mochitest.ini
+++ b/docshell/test/mochitest/mochitest.ini
@@ -50,16 +50,21 @@ support-files =
   historyframes.html
   start_historyframe.html
   url1_historyframe.html
   url2_historyframe.html
 
 [test_anchor_scroll_after_document_open.html]
 [test_bfcache_plus_hash.html]
 [test_bug123696.html]
+[test_bug1422334.html]
+support-files =
+  bug1422334_redirect.html
+  bug1422334_redirect.html^headers^
+  !/docshell/test/navigation/blank.html
 [test_bug384014.html]
 [test_bug385434.html]
 [test_bug387979.html]
 [test_bug402210.html]
 [test_bug404548.html]
 [test_bug413310.html]
 skip-if = true
 # Disabled for too many intermittent failures (bug 719186)
new file mode 100644
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1422334.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Ensure that reload after replaceState after 3xx redirect does the right thing.</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+  <script>
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(function() {
+      var ifr = document.querySelector("iframe");
+      var win = ifr.contentWindow;
+      is(win.location.href,
+         location.href.replace("mochitest/test_bug1422334.html",
+                               "navigation/blank.html?x=y"),
+         "Should have the right location on initial load");
+
+      win.history.replaceState(null, '', win.location.pathname);
+      is(win.location.href,
+         location.href.replace("mochitest/test_bug1422334.html",
+                               "navigation/blank.html"),
+         "Should have the right location after replaceState call");
+
+      ifr.onload = function() {
+        is(win.location.href,
+           location.href.replace("mochitest/test_bug1422334.html",
+                                 "navigation/blank.html"),
+           "Should have the right location after reload");
+        SimpleTest.finish();
+      }
+      win.location.reload();
+    })
+  </script>
+</head>
+<body>
+<p id="display"><iframe src="bug1422334_redirect.html"></iframe></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/ComputedStyleInlines.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/LayerAnimationInfo.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ServoBindings.h"  // Servo_GetProperties_Overriding_Animation
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TypeTraits.h"  // For std::forward<>
 #include "nsContentUtils.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h"
 #include "nsDisplayItemTypes.h"
 #include "nsAtom.h"
@@ -68,17 +69,17 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(E
 bool EffectCompositor::AllowCompositorAnimationsOnFrame(
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aWarning /* out */) {
   if (aFrame->RefusedAsyncAnimation()) {
     return false;
   }
 
   if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
-    if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
+    if (StaticPrefs::layers_offmainthreadcomposition_log_animations()) {
       nsCString message;
       message.AppendLiteral(
           "Performance warning: Async animations are "
           "disabled");
       AnimationUtils::LogAsyncAnimationFailure(message);
     }
     return false;
   }
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/LayerAnimationInfo.h"
 #include "mozilla/LookAndFeel.h"  // For LookAndFeel::GetInt
 #include "mozilla/KeyframeUtils.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/TypeTraits.h"
 #include "Layers.h"              // For Layer
 #include "nsComputedDOMStyle.h"  // nsComputedDOMStyle::GetComputedStyle
 #include "nsContentUtils.h"
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h"             // For nsCSSProps::PropHasFlags
 #include "nsCSSPseudoElements.h"    // For PseudoStyleType
 #include "nsDOMMutationObserver.h"  // For nsAutoAnimationMutationBatch
@@ -44,17 +45,17 @@ void AnimationProperty::SetPerformanceWa
     const AnimationPerformanceWarning& aWarning, const Element* aElement) {
   if (mPerformanceWarning && *mPerformanceWarning == aWarning) {
     return;
   }
 
   mPerformanceWarning = Some(aWarning);
 
   nsAutoString localizedString;
-  if (nsLayoutUtils::IsAnimationLoggingEnabled() &&
+  if (StaticPrefs::layers_offmainthreadcomposition_log_animations() &&
       mPerformanceWarning->ToLocalizedString(localizedString)) {
     nsAutoCString logMessage = NS_ConvertUTF16toUTF8(localizedString);
     AnimationUtils::LogAsyncAnimationFailure(logMessage, aElement);
   }
 }
 
 bool PropertyValuePair::operator==(const PropertyValuePair& aOther) const {
   if (mProperty != aOther.mProperty) {
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -117,16 +117,17 @@
 #include "mozilla/dom/CDATASection.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "mozilla/dom/PostMessageEvent.h"
 #include "nsDOMString.h"
 #include "nsNodeUtils.h"
 #include "nsLayoutUtils.h"  // for GetFrameForPoint
 #include "nsIFrame.h"
 #include "nsIBrowserChild.h"
+#include "nsImportModule.h"
 
 #include "nsRange.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/NodeIterator.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/TreeWalker.h"
 
@@ -137,33 +138,35 @@
 #include "nsAboutProtocolUtils.h"
 #include "nsCanvasFrame.h"
 #include "nsContentCID.h"
 #include "nsContentSecurityUtils.h"
 #include "nsError.h"
 #include "nsPresContext.h"
 #include "nsThreadUtils.h"
 #include "nsNodeInfoManager.h"
+#include "nsIBrowserUsage.h"
 #include "nsIEditingSession.h"
 #include "nsIFileChannel.h"
 #include "nsIMultiPartChannel.h"
 #include "nsIRefreshURI.h"
 #include "nsIWebNavigation.h"
 #include "nsIScriptError.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIRequestContext.h"
 #include "nsStyleSheetService.h"
 
 #include "nsNetUtil.h"  // for NS_NewURI
 #include "nsIInputStreamChannel.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
 
 #include "nsIScriptSecurityManager.h"
-#include "nsIPermissionManager.h"
+#include "nsIPermission.h"
+#include "nsPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "ExpandedPrincipal.h"
 #include "mozilla/NullPrincipal.h"
 
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsFocusManager.h"
@@ -15433,67 +15436,118 @@ already_AddRefed<mozilla::dom::Promise> 
       // Note: If this has returned true, the top-level document is guaranteed
       // to not be on the Content Blocking allow list.
       DebugOnly<bool> isOnAllowList = false;
       MOZ_ASSERT_IF(
           NS_SUCCEEDED(AntiTrackingCommon::IsOnContentBlockingAllowList(
               parent->GetDocumentURI(), false, isOnAllowList)),
           !isOnAllowList);
 
-      auto performFinalChecks = [inner]()
+      RefPtr<Document> self(this);
+
+      auto performFinalChecks = [inner, self]()
           -> RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise> {
         RefPtr<AntiTrackingCommon::StorageAccessFinalCheckPromise::Private> p =
             new AntiTrackingCommon::StorageAccessFinalCheckPromise::Private(
                 __func__);
         RefPtr<StorageAccessPermissionRequest> sapr =
             StorageAccessPermissionRequest::Create(
                 inner,
                 // Allow
-                [p] { p->Resolve(AntiTrackingCommon::eAllow, __func__); },
-                // Allow auto grant
                 [p] {
-                  p->Resolve(AntiTrackingCommon::eAllowAutoGrant, __func__);
+                  Telemetry::AccumulateCategorical(
+                      Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
+                  p->Resolve(AntiTrackingCommon::eAllow, __func__);
                 },
                 // Allow on any site
                 [p] {
+                  Telemetry::AccumulateCategorical(
+                      Telemetry::LABELS_STORAGE_ACCESS_API_UI::AllowOnAnySite);
                   p->Resolve(AntiTrackingCommon::eAllowOnAnySite, __func__);
                 },
                 // Block
-                [p] { p->Reject(false, __func__); });
+                [p] {
+                  Telemetry::AccumulateCategorical(
+                      Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
+                  p->Reject(false, __func__);
+                });
 
         typedef ContentPermissionRequestBase::PromptResult PromptResult;
         PromptResult pr = sapr->CheckPromptPrefs();
         bool onAnySite = false;
         if (pr == PromptResult::Pending) {
           // Also check our custom pref for the "Allow on any site" case
           if (Preferences::GetBool("dom.storage_access.prompt.testing",
                                    false) &&
               Preferences::GetBool(
                   "dom.storage_access.prompt.testing.allowonanysite", false)) {
             pr = PromptResult::Granted;
             onAnySite = true;
           }
         }
 
-        if (pr != PromptResult::Pending) {
-          MOZ_ASSERT_IF(pr != PromptResult::Granted,
-                        pr == PromptResult::Denied);
-          if (pr == PromptResult::Granted) {
-            return AntiTrackingCommon::StorageAccessFinalCheckPromise::
-                CreateAndResolve(onAnySite ? AntiTrackingCommon::eAllowOnAnySite
-                                           : AntiTrackingCommon::eAllow,
-                                 __func__);
-          }
-          return AntiTrackingCommon::StorageAccessFinalCheckPromise::
-              CreateAndReject(false, __func__);
+        if (pr == PromptResult::Pending) {
+          // We're about to show a prompt, record the request attempt
+          Telemetry::AccumulateCategorical(
+              Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
         }
 
-        sapr->RequestDelayedTask(
-            inner->EventTargetFor(TaskCategory::Other),
-            ContentPermissionRequestBase::DelayedTaskType::Request);
+        self->AutomaticStorageAccessCanBeGranted()->Then(
+            GetCurrentThreadSerialEventTarget(), __func__,
+            [p, pr, sapr, inner, onAnySite](
+                const AutomaticStorageAccessGrantPromise::ResolveOrRejectValue&
+                    aValue) -> void {
+              // Make a copy because we can't modified copy-captured lambda
+              // variables.
+              PromptResult pr2 = pr;
+
+              bool storageAccessCanBeGrantedAutomatically =
+                  aValue.IsResolve() && aValue.ResolveValue();
+
+              bool autoGrant = false;
+              if (pr2 == PromptResult::Pending &&
+                  storageAccessCanBeGrantedAutomatically) {
+                pr2 = PromptResult::Granted;
+                autoGrant = true;
+
+                Telemetry::AccumulateCategorical(
+                    Telemetry::LABELS_STORAGE_ACCESS_API_UI::
+                        AllowAutomatically);
+              }
+
+              if (pr2 != PromptResult::Pending) {
+                MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
+                              pr2 == PromptResult::Denied);
+                if (pr2 == PromptResult::Granted) {
+                  AntiTrackingCommon::StorageAccessPromptChoices choice =
+                      AntiTrackingCommon::eAllow;
+                  if (onAnySite) {
+                    choice = AntiTrackingCommon::eAllowOnAnySite;
+                  } else if (autoGrant) {
+                    choice = AntiTrackingCommon::eAllowAutoGrant;
+                  }
+                  if (!autoGrant) {
+                    p->Resolve(choice, __func__);
+                  } else {
+                    sapr->MaybeDelayAutomaticGrants()->Then(
+                        GetCurrentThreadSerialEventTarget(), __func__,
+                        [p, choice] { p->Resolve(choice, __func__); },
+                        [p] { p->Reject(false, __func__); });
+                  }
+                  return;
+                }
+                p->Reject(false, __func__);
+                return;
+              }
+
+              sapr->RequestDelayedTask(
+                  inner->EventTargetFor(TaskCategory::Other),
+                  ContentPermissionRequestBase::DelayedTaskType::Request);
+            });
+
         return p.forget();
       };
       AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(
           NodePrincipal(), inner, AntiTrackingCommon::eStorageAccessAPI,
           performFinalChecks)
           ->Then(
               GetCurrentThreadSerialEventTarget(), __func__,
               [outer, promise] {
@@ -15513,16 +15567,132 @@ already_AddRefed<mozilla::dom::Promise> 
     }
   }
 
   outer->SetHasStorageAccess(true);
   promise->MaybeResolveWithUndefined();
   return promise.forget();
 }
 
+RefPtr<Document::AutomaticStorageAccessGrantPromise>
+Document::AutomaticStorageAccessCanBeGranted() {
+  if (XRE_IsContentProcess()) {
+    // In the content process, we need to ask the parent process to compute
+    // this.  The reason is that nsIPermissionManager::GetAllWithTypePrefix()
+    // isn't accessible in the content process.
+    ContentChild* cc = ContentChild::GetSingleton();
+    MOZ_ASSERT(cc);
+
+    return cc
+        ->SendAutomaticStorageAccessCanBeGranted(
+            IPC::Principal(NodePrincipal()))
+        ->Then(
+            GetCurrentThreadSerialEventTarget(), __func__,
+            [](const ContentChild::AutomaticStorageAccessCanBeGrantedPromise::
+                   ResolveOrRejectValue& aValue) {
+              if (aValue.IsResolve()) {
+                return AutomaticStorageAccessGrantPromise::CreateAndResolve(
+                    aValue.ResolveValue(), __func__);
+              }
+
+              return AutomaticStorageAccessGrantPromise::CreateAndReject(
+                  false, __func__);
+            });
+  }
+
+  if (XRE_IsParentProcess()) {
+    // In the parent process, we can directly compute this.
+    return AutomaticStorageAccessGrantPromise::CreateAndResolve(
+        AutomaticStorageAccessCanBeGranted(NodePrincipal()), __func__);
+  }
+
+  return AutomaticStorageAccessGrantPromise::CreateAndReject(false, __func__);
+}
+
+bool Document::AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal) {
+  nsAutoCString prefix;
+  AntiTrackingCommon::CreateStoragePermissionKey(aPrincipal, prefix);
+
+  nsPermissionManager* permManager = nsPermissionManager::GetInstance();
+  if (NS_WARN_IF(!permManager)) {
+    return false;
+  }
+
+  typedef nsTArray<RefPtr<nsIPermission>> Permissions;
+  Permissions perms;
+  nsresult rv = permManager->GetAllWithTypePrefix(prefix, perms);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  nsAutoCString prefix2(prefix);
+  prefix2.Append('^');
+  typedef nsTArray<nsCString> Origins;
+  Origins origins;
+
+  for (const auto& perm : perms) {
+    nsAutoCString type;
+    rv = perm->GetType(type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+    // Let's make sure that we're not looking at a permission for
+    // https://exampletracker.company when we mean to look for the
+    // permission for https://exampletracker.com!
+    if (type != prefix && StringHead(type, prefix2.Length()) != prefix2) {
+      continue;
+    }
+
+    nsCOMPtr<nsIPrincipal> principal;
+    rv = perm->GetPrincipal(getter_AddRefs(principal));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    nsAutoCString origin;
+    rv = principal->GetOrigin(origin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+
+    ToLowerCase(origin);
+
+    if (origins.IndexOf(origin) == Origins::NoIndex) {
+      origins.AppendElement(origin);
+    }
+  }
+
+  nsCOMPtr<nsIBrowserUsage> bu =
+      do_ImportModule("resource:///modules/BrowserUsageTelemetry.jsm");
+  if (NS_WARN_IF(!bu)) {
+    return false;
+  }
+
+  uint32_t uniqueDomainsVisitedInPast24Hours = 0;
+  rv = bu->GetUniqueDomainsVisitedInPast24Hours(
+      &uniqueDomainsVisitedInPast24Hours);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  // one percent of the number of top-levels origins visited in the current
+  // session (but not to exceed 24 hours), or the value of the
+  // dom.storage_access.max_concurrent_auto_grants preference, whichever is
+  // higher.
+  size_t maxConcurrentAutomaticGrants = std::max(
+      std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours / 100)),
+               StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
+      0);
+
+  size_t originsThirdPartyHasAccessTo = origins.Length();
+
+  return StaticPrefs::dom_storage_access_auto_grants() &&
+         originsThirdPartyHasAccessTo < maxConcurrentAutomaticGrants;
+}
+
 void Document::RecordNavigationTiming(ReadyState aReadyState) {
   if (!XRE_IsContentProcess()) {
     return;
   }
   if (!IsTopLevelContentDocument()) {
     return;
   }
   // If we dont have the timing yet (mostly because the doc is still loading),
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef mozilla_dom_Document_h___
 #define mozilla_dom_Document_h___
 
 #include "mozilla/EventStates.h"  // for EventStates
 #include "mozilla/FlushType.h"    // for enum
+#include "mozilla/MozPromise.h"   // for MozPromise
 #include "mozilla/Pair.h"         // for Pair
 #include "mozilla/Saturate.h"     // for SaturateUint32
 #include "nsAutoPtr.h"            // for member
 #include "nsCOMArray.h"           // for member
 #include "nsCompatibility.h"      // for member
 #include "nsCOMPtr.h"             // for member
 #include "nsICookieSettings.h"
 #include "nsGkAtoms.h"  // for static class members
@@ -4124,16 +4125,18 @@ class Document : public nsINode,
   void SetInRDMPane(bool aInRDMPane) { mInRDMPane = aInRDMPane; }
 
   // Returns true if we use overlay scrollbars on the system wide or on the
   // given document.
   static bool UseOverlayScrollbars(const Document* aDocument);
 
   static bool HasRecentlyStartedForegroundLoads();
 
+  static bool AutomaticStorageAccessCanBeGranted(nsIPrincipal* aPrincipal);
+
  protected:
   void DoUpdateSVGUseElementShadowTrees();
 
   already_AddRefed<nsIPrincipal> MaybeDowngradePrincipal(
       nsIPrincipal* aPrincipal);
 
   void EnsureOnloadBlocker();
 
@@ -4392,16 +4395,20 @@ class Document : public nsINode,
 
   // Helpers for GetElementsByName.
   static bool MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
                                  nsAtom* aAtom, void* aData);
   static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
 
   void MaybeResolveReadyForIdle();
 
+  typedef MozPromise<bool, bool, true> AutomaticStorageAccessGrantPromise;
+  MOZ_MUST_USE RefPtr<AutomaticStorageAccessGrantPromise>
+  AutomaticStorageAccessCanBeGranted();
+
   // This should *ONLY* be used in GetCookie/SetCookie.
   already_AddRefed<nsIChannel> CreateDummyChannelForCookies(
       nsIURI* aContentURI);
 
   void AddToplevelLoadingDocument(Document* aDoc);
   void RemoveToplevelLoadingDocument(Document* aDoc);
   static AutoTArray<Document*, 8>* sLoadingForegroundTopLevelContentDocument;
 
--- a/dom/base/StorageAccessPermissionRequest.cpp
+++ b/dom/base/StorageAccessPermissionRequest.cpp
@@ -16,97 +16,113 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(Stora
                                    ContentPermissionRequestBase)
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(StorageAccessPermissionRequest,
                                                ContentPermissionRequestBase)
 
 StorageAccessPermissionRequest::StorageAccessPermissionRequest(
     nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
     AllowCallback&& aAllowCallback,
-    AllowAutoGrantCallback&& aAllowAutoGrantCallback,
     AllowAnySiteCallback&& aAllowAnySiteCallback,
     CancelCallback&& aCancelCallback)
     : ContentPermissionRequestBase(aNodePrincipal, aWindow,
                                    NS_LITERAL_CSTRING("dom.storage_access"),
                                    NS_LITERAL_CSTRING("storage-access")),
       mAllowCallback(std::move(aAllowCallback)),
-      mAllowAutoGrantCallback(std::move(aAllowAutoGrantCallback)),
       mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
       mCancelCallback(std::move(aCancelCallback)),
       mCallbackCalled(false) {
   mPermissionRequests.AppendElement(
       PermissionRequest(mType, nsTArray<nsString>()));
 }
 
 StorageAccessPermissionRequest::~StorageAccessPermissionRequest() { Cancel(); }
 
 NS_IMETHODIMP
 StorageAccessPermissionRequest::Cancel() {
   if (!mCallbackCalled) {
     mCallbackCalled = true;
-    mTimer = nullptr;
     mCancelCallback();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices) {
   nsTArray<PermissionChoice> choices;
   nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  // There is no support to allow grants automatically from the prompting code
+  // path.
+
   if (!mCallbackCalled) {
     mCallbackCalled = true;
     if (choices.Length() == 1 &&
         choices[0].choice().EqualsLiteral("allow-on-any-site")) {
       mAllowAnySiteCallback();
     } else if (choices.Length() == 1 &&
-               choices[0].choice().EqualsLiteral("allow-auto-grant")) {
-      unsigned simulatedDelay = CalculateSimulatedDelay();
-      if (simulatedDelay) {
-        MOZ_ASSERT(!mTimer);
-        RefPtr<StorageAccessPermissionRequest> self = this;
-        rv = NS_NewTimerWithFuncCallback(
-            getter_AddRefs(mTimer), CallAutoGrantCallback, this, simulatedDelay,
-            nsITimer::TYPE_ONE_SHOT, "DelayedAllowAutoGrantCallback");
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-        NS_ADDREF(this);
-      } else {
-        mAllowAutoGrantCallback();
-      }
-    } else {
+               choices[0].choice().EqualsLiteral("allow")) {
       mAllowCallback();
     }
   }
   return NS_OK;
 }
 
+RefPtr<StorageAccessPermissionRequest::AutoGrantDelayPromise>
+StorageAccessPermissionRequest::MaybeDelayAutomaticGrants() {
+  RefPtr<AutoGrantDelayPromise::Private> p =
+      new AutoGrantDelayPromise::Private(__func__);
+
+  unsigned simulatedDelay = CalculateSimulatedDelay();
+  if (simulatedDelay) {
+    nsCOMPtr<nsITimer> timer;
+    RefPtr<AutoGrantDelayPromise::Private> promise = p;
+    nsresult rv = NS_NewTimerWithFuncCallback(
+        getter_AddRefs(timer),
+        [](nsITimer* aTimer, void* aClosure) -> void {
+          auto* promise =
+              static_cast<AutoGrantDelayPromise::Private*>(aClosure);
+          promise->Resolve(true, __func__);
+          NS_RELEASE(aTimer);
+          NS_RELEASE(promise);
+        },
+        promise, simulatedDelay, nsITimer::TYPE_ONE_SHOT,
+        "DelayedAllowAutoGrantCallback");
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      p->Reject(false, __func__);
+    } else {
+      // Leak the references here! We'll release them inside the callback.
+      Unused << timer.forget();
+      Unused << promise.forget();
+    }
+  } else {
+    p->Resolve(false, __func__);
+  }
+  return p;
+}
+
 already_AddRefed<StorageAccessPermissionRequest>
 StorageAccessPermissionRequest::Create(
     nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
-    AllowAutoGrantCallback&& aAllowAutoGrantCallback,
     AllowAnySiteCallback&& aAllowAnySiteCallback,
     CancelCallback&& aCancelCallback) {
   if (!aWindow) {
     return nullptr;
   }
   nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
   if (!win->GetPrincipal()) {
     return nullptr;
   }
   RefPtr<StorageAccessPermissionRequest> request =
       new StorageAccessPermissionRequest(
           aWindow, win->GetPrincipal(), std::move(aAllowCallback),
-          std::move(aAllowAutoGrantCallback), std::move(aAllowAnySiteCallback),
-          std::move(aCancelCallback));
+          std::move(aAllowAnySiteCallback), std::move(aCancelCallback));
   return request.forget();
 }
 
 unsigned StorageAccessPermissionRequest::CalculateSimulatedDelay() {
   if (!StaticPrefs::dom_storage_access_auto_grants_delayed()) {
     return 0;
   }
 
@@ -116,18 +132,10 @@ unsigned StorageAccessPermissionRequest:
 
   const unsigned kMin = 5000;
   const unsigned kMax = 6000;
   const unsigned random = std::abs(std::rand());
 
   return kMin + random % (kMax - kMin);
 }
 
-void StorageAccessPermissionRequest::CallAutoGrantCallback(nsITimer* aTimer,
-                                                           void* aClosure) {
-  auto self = static_cast<StorageAccessPermissionRequest*>(aClosure);
-  self->mAllowAutoGrantCallback();
-  self->mTimer = nullptr;
-  NS_RELEASE(self);
-}
-
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/StorageAccessPermissionRequest.h
+++ b/dom/base/StorageAccessPermissionRequest.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef StorageAccessPermissionRequest_h_
 #define StorageAccessPermissionRequest_h_
 
 #include "nsContentPermissionHelper.h"
+#include "mozilla/MozPromise.h"
 
 #include <functional>
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
@@ -23,44 +24,40 @@ class StorageAccessPermissionRequest fin
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StorageAccessPermissionRequest,
                                            ContentPermissionRequestBase)
 
   // nsIContentPermissionRequest
   NS_IMETHOD Cancel(void) override;
   NS_IMETHOD Allow(JS::HandleValue choices) override;
 
   typedef std::function<void()> AllowCallback;
-  typedef std::function<void()> AllowAutoGrantCallback;
   typedef std::function<void()> AllowAnySiteCallback;
   typedef std::function<void()> CancelCallback;
 
   static already_AddRefed<StorageAccessPermissionRequest> Create(
       nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
-      AllowAutoGrantCallback&& aAllowAutoGrantCallback,
       AllowAnySiteCallback&& aAllowAnySiteCallback,
       CancelCallback&& aCancelCallback);
 
+  typedef MozPromise<bool, bool, true> AutoGrantDelayPromise;
+  RefPtr<AutoGrantDelayPromise> MaybeDelayAutomaticGrants();
+
  private:
-  StorageAccessPermissionRequest(
-      nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
-      AllowCallback&& aAllowCallback,
-      AllowAutoGrantCallback&& aAllowAutoGrantCallback,
-      AllowAnySiteCallback&& aAllowAnySiteCallback,
-      CancelCallback&& aCancelCallback);
+  StorageAccessPermissionRequest(nsPIDOMWindowInner* aWindow,
+                                 nsIPrincipal* aNodePrincipal,
+                                 AllowCallback&& aAllowCallback,
+                                 AllowAnySiteCallback&& aAllowAnySiteCallback,
+                                 CancelCallback&& aCancelCallback);
   ~StorageAccessPermissionRequest();
 
   unsigned CalculateSimulatedDelay();
 
-  static void CallAutoGrantCallback(nsITimer* aTimer, void* aClosure);
-
   AllowCallback mAllowCallback;
-  AllowAutoGrantCallback mAllowAutoGrantCallback;
   AllowAnySiteCallback mAllowAnySiteCallback;
   CancelCallback mCancelCallback;
-  nsCOMPtr<nsITimer> mTimer;
   nsTArray<PermissionRequest> mPermissionRequests;
   bool mCallbackCalled;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // StorageAccessPermissionRequest_h_
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -492,16 +492,17 @@ LOCAL_INCLUDES += [
     '/dom/ipc',
     '/dom/storage',
     '/dom/svg',
     '/dom/u2f',
     '/dom/xbl',
     '/dom/xml',
     '/dom/xslt/xpath',
     '/dom/xul',
+    '/extensions/permissions',
     '/gfx/2d',
     '/image',
     '/js/xpconnect/loader',
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
     '/layout/base',
     '/layout/forms',
     '/layout/generic',
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1569648.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<base href="a:">
+<script>
+  document.createElement("canvas").getContext("2d").filter = "url()";
+</script>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -50,8 +50,9 @@ load 1334647-1.html
 skip-if(geckoview&&webrender) load 1349067.html # Bug 1563214 for GV+WR
 pref(gfx.offscreencanvas.enabled,true) load 1348976-1.html
 load 1357092.html
 load 1441613.html
 pref(gfx.offscreencanvas.enabled,true) load 1443671.html
 pref(gfx.offscreencanvas.enabled,true) load 1546390.html
 load 1549853.html
 load 1551745.html
+load 1569648.html
--- a/dom/chrome-webidl/ChannelWrapper.webidl
+++ b/dom/chrome-webidl/ChannelWrapper.webidl
@@ -33,16 +33,38 @@ enum MozContentPolicyType {
   "csp_report",
   "imageset",
   "web_manifest",
   "speculative",
   "other"
 };
 
 /**
+ * String versions of CLASSIFIED_* tracking flags from nsHttpChannel.idl
+ */
+enum MozUrlClassificationFlags {
+  "fingerprinting",
+  "fingerprinting_content",
+  "cryptomining",
+  "cryptomining_content",
+  "tracking",
+  "tracking_ad",
+  "tracking_analytics",
+  "tracking_social",
+  "tracking_content",
+  "socialtracking",
+  "socialtracking_facebook",
+  "socialtracking_linkedin",
+  "socialtracking_twitter",
+  "any_basic_tracking",
+  "any_strict_tracking",
+  "any_social_tracking"
+};
+
+/**
  * A thin wrapper around nsIChannel and nsIHttpChannel that allows JS
  * callers to access them without XPConnect overhead.
  */
 [ChromeOnly, Exposed=Window]
 interface ChannelWrapper : EventTarget {
   /**
    * Returns the wrapper instance for the given channel. The same wrapper is
    * always returned for a given channel.
@@ -377,16 +399,30 @@ interface ChannelWrapper : EventTarget {
    *
    * Note: The content type header is handled specially by this function. See
    * getResponseHeaders() for details.
    */
   [Throws]
   void setResponseHeader(ByteString header,
                          ByteString value,
                          optional boolean merge = false);
+
+  /**
+   * Provides the tracking classification data when it is available.
+   */
+  [Cached, Frozen, GetterThrows, Pure]
+  readonly attribute MozUrlClassification? urlClassification;
+};
+
+/**
+ * Wrapper for first and third party tracking classification data.
+ */
+dictionary MozUrlClassification {
+  required sequence<MozUrlClassificationFlags> firstParty;
+  required sequence<MozUrlClassificationFlags> thirdParty;
 };
 
 /**
  * Information about the proxy server handing a request. This is approximately
  * equivalent to nsIProxyInfo.
  */
 dictionary MozProxyInfo {
   /**
--- a/dom/docs/Fission.rst
+++ b/dom/docs/Fission.rst
@@ -11,63 +11,404 @@ We don't have an all-encompassing design
 IPC Diagram
 ===========
 
 .. image:: Fission-IPC-Diagram.svg
 
 JS Window Actor
 ===============
 
-What are actors?
-----------------
+What are JS Window Actors?
+--------------------------
 
-In the Fission world, Actors will be the replacement for framescripts. Framescripts were how we structured code to be aware of the parent (UI) and child (content) separation, including establishing the communication channel between the two (via the message manager).
+In the Fission world, JS Window Actors will be the replacement for framescripts. Framescripts were how we structured code to be aware of the parent (UI) and child (content) separation, including establishing the communication channel between the two (via the Frame Message Manager).
 
-However, the framescripts had no way to establish further process separation downwards (that is, for out-of-process iframes). Actors will be the replacement.
+However, the framescripts had no way to establish further process separation downwards (that is, for out-of-process iframes). JS Window Actors will be the replacement.
 
 How are they structured?
 ------------------------
 
-Currently, in the post-e10s Firefox codebase, we have code living in the parent process (UI) that is in plain JS (.js files) or in JS modules (.jsm). In the child process (hosting the content), we use framescripts (.js) and also JS modules. The framescripts are instantiated once per top-level frame (or, in simpler terms, once per tab). This code has access to all of the DOM from the web content, including iframes on it.
+A review of the current Message Manager mechanism
+`````````````````````````````````````````````````
+
+.. note::
+   There are actually several types of Message Managers: Frame Message Managers, Window Message Managers, Group Message Managers and Process Message Managers. For the purposes of this documentation, it's simplest to refer to all of these mechanisms altogether as the "Message Manager mechanism". Most of the examples in this document will be operating on the assumption that the Message Manager is a Frame Message Manager, which is the most commonly used one.
+
+Currently, in the post `Electrolysis Project`_ Firefox codebase, we have code living in the parent process (UI) that is in plain JS (.js files) or in JS modules (.jsm files). In the child process (hosting the content), we use framescripts (.js) and also JS modules. The framescripts are instantiated once per top-level frame (or, in simpler terms, once per tab). This code has access to all of the DOM from the web content, including all iframes within it.
 
-The two processes communicate between them through the message manager (mm) using the sendAsyncMessage API, and any code in the parent can communicate with any code in the child (and vice versa), by just listening to the messsages of interest.
+The two processes communicate via the Frame Message Manager (mm) using the ``sendAsyncMessage`` / ``receiveMessage`` API, and any code in the parent can communicate with any code in the child (and vice versa), by just listening to the messages of interest.
+
+The Frame Message Manager communication mechanism follows a publish / subscribe pattern similar to how Events work in Firefox:
+
+1. Something exposes a mechanism for subscribing to notifications (``addMessageListener`` for the Frame Message Manager, ``addEventListener`` for Events).
+2. The subscriber is responsible for unsubscribing when there's no longer interest in the notifications (``removeMessageListener`` for the Frame Message Manager, ``removeEventListener`` for Events).
+3. Any number of subscribers can be attached at any one time.
 
 .. figure:: Fission-framescripts.png
    :width: 320px
    :height: 200px
 
-For fission, the actors replacing FrameScript will be structured in pairs. A pair of actors will be instantiated lazily: one in the parent and one in the child process, and a direct channel of communication between the two will be established.
-These actors are "managed" by the WindowGlobal actors, and are implemented as JS classes instantiated when requested for any particular window.
+How JS Window Actors differ from the Frame Message Manager
+``````````````````````````````````````````````````````````
+
+For Fission, the JS Window Actors replacing framescripts will be structured in pairs. A pair of JS Window Actors will be instantiated lazily: one in the parent and one in the child process, and a direct channel of communication between the two will be established. The JS Window Actor in the parent must extend the global ``JSWindowActorParent`` class, and the JS Window Actor in the child must extend the global ``JSWindowActorChild`` class.
+
+The JS Window Actor mechanism is similar to how `IPC Actors`_ work in the native layer of Firefox:
+
+#. Every Actor has one counterpart in another process that they can communicate directly with.
+#. Every Actor inherits a common communications API from a parent class.
+#. Every Actor has a name that ends in either ``Parent`` or ``Child``.
+#. There is no built-in mechanism for subscribing to messages. When one JS Window Actor sends a message, the counterpart JS Window Actor on the other side will receive it without needing to explicitly listen for it.
+
+Other notable differences between JSWindowActor's and Message Manager / framescripts:
+
+#. Each JSWindowActor pair is associated with a particular frame. For example, given the following DOM hierarchy::
+
+     <browser src="https://www.example.com">
+       <iframe src="https://www.a.com" />
+       <iframe src="https://www.b.com" />
+
+   A ``JSWindowActorParent / ``JSWindowActorChild`` pair instantiated for either of the ``iframe``'s would only be sending messages to and from that ``iframe``.
+
+#. There's only one pair per actor type, per frame.
+
+   For example, suppose we have a ``ContextMenu`` actor. The parent process can have up to N instances of the ``ContextMenuParent`` actor, where N is the number of frames that are currently loaded. For any individual frame though, there's only ever one `ContextMenuChild` associated with that frame.
+
+#. We can no longer assume full, synchronous access to the frame tree, even in content processes.
+
+   This is a natural consequence of splitting frames to run out-of-process.
+
+#. ``JSWindowActorChild``'s live as long as the ``BrowsingContext`` they're associated with.
+
+  If in the previously mentioned DOM hierarchy, one of the ``<iframe>``'s unload, any associated JSWindowActor pairs will be torn down.
+
+.. hint::
+   JS Window Actors are "managed" by the WindowGlobal IPC Actors, and are implemented as JS classes (subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``) instantiated when requested for any particular window. Like the Frame Message Manager, they are ultimately using IPC Actors to communicate under the hood.
 
 .. figure:: Fission-actors-diagram.png
    :width: 233px
    :height: 240px
 
-Communicating between actors
-----------------------------
+Cross-process communication with JS Window Actors
+-------------------------------------------------
+
+.. note::
+    Like the Message Manager, JSWindowActors are implemented for both in-process and out-of-process frame communication. This means that porting to JSWindowActors can be done immediately without waiting for out-of-process iframes to be enabled.
+
+The ``JSWindowActorParent`` and ``JSWindowActorChild`` base classes expose two methods for sending messages:
+
+``sendAsyncMessage``
+````````````````````
 
-The actor will only communicate between each other. To communicate, the actor supports ``sendAsyncMessage`` and ``sendQuery`` which acts like send async message, but also returns a promise, and it will be present for both in-process and out-of-process windows.
+This has a similar signature as the ``sendAsyncMessage`` method for Message Managers::
+
+    sendAsyncMessage("SomeMessage", { key: "value" }, { transferredObject });
+
+Like messages sent via the Message Manager, anything that can be serialized using the structured clone algorithm can be sent down through the second argument. Additionally, ``nsIPrincipal``'s can be sent without manually serializing and deserializing them.
+
+The third argument sends `Transferables`_ to the receiver, for example an ``ArrayBuffer``.
+
+.. note::
+    Cross Process Object Wrappers (CPOWs) cannot be sent over JSWindowActors.
 
 .. note::
+    Notably absent is ``sendSyncMessage`` or ``sendRPCMessage``. Sync IPC is not supported on JSWindowActors, and code which needs to send sync messages should be modified to use async messages, or must send them over the per-process message manager.
 
-    Sync IPC is not supported on JSWindowActors, and code which needs to send sync messages should be modified, or must send them over the per-process message manager.
+``sendQuery``
+`````````````
+
+``sendQuery`` improves upon ``sendAsyncMessage`` by returning a ``Promise``. The receiver of the message must then return a ``Promise`` that can eventually resolve into a value - at which time the ``sendQuery`` ``Promise`` resolves with that value.
+
+The ``sendQuery`` method arguments follow the same conventions as ``sendAsyncMessage``, with the second argument being a structured clone, and the third being for `Transferables`_.
+
+``receiveMessage``
+``````````````````
+
+This is identical to the Message Manager implementation of ``receiveMessage``. The method receives a single argument, which is the de-serialized arguments that were sent via either ``sendAsyncMessage`` or ``sendQuery``. Note that upon handling a ``sendQuery`` message, the ``receiveMessage`` handler must return a ``Promise`` for that message.
+
+.. hint::
+    Using ``sendQuery``, and the ``receiveMessage`` is able to return a value right away? Try using ``Promise.resolve(value);`` to return ``value``, or you could also make your ``receiveMessage`` method an async function, presuming none of the other messages it handles need to get a non-Promise return value.
+
+Other JSWindowActor methods that can be overridden
+--------------------------------------------------
+
+``constructor()``
+
+If there's something you need to do as soon as the JSWindowActor is instantiated, the ``constructor`` function is a great place to do that.
+
+``observe(subject, topic, data)``
+`````````````````````````````````
+
+If you register your Actor to listen for ``nsIObserver`` notifications, implement an ``observe`` method with the above signature to handle the notification.
+
+``handleEvent(event)``
+``````````````````````
+
+If you register your Actor to listen for content events, implement a ``handleEvent`` method with the above signature to handle the event.
+
+``willDestroy``
+```````````````
+
+This method will be called when we know that the JSWindowActor pair is going to be destroyed because the associated BrowsingContext is going away. You should override this method if you have any cleanup you need to do before going away.
+
+You can also use ``willDestroy`` as a last opportunity to send messages to the other side, as the communications channel at this point is still running.
+
+.. note::
+    This method cannot be async.
+
+``didDestroy``
+``````````````
+
+This is another point to clean-up an Actor before it is destroyed, but at this point, no communication is possible with the other side.
 
-Things are exposed on a JS Window Actor
----------------------------------------
+.. note::
+    This method cannot be async.
+
+
+Other things exposed on a JSWindowActorParent
+---------------------------------------------
+
+``CanonicalBrowsingContext``
+````````````````````````````
+
+TODO
+
+``WindowGlobalParent``
+``````````````````````
+
+TODO
+
+Other things exposed on a JSWindowActorChild
+--------------------------------------------
+
+``BrowsingContext``
+```````````````````
+
+TODO
 
-See `JSWindowActor.webidl <https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSWindowActor.webidl>`_ for more detail.
+``WindowGlobalChild``
+`````````````````````
+
+TODO
+
+
+Helpful getters
+```````````````
+
+A number of helpful getters exist on a ``JSWindowActorChild``, including:
+
+``this.document``
+^^^^^^^^^^^^^^^^^
 
+The currently loaded document in the frame associated with this ``JSWindowActorChild``.
+
+``this.contentWindow``
+^^^^^^^^^^^^^^^^^^^^^^
+
+The outer window for the frame associated with this ``JSWindowActorChild``.
+
+``this.docShell``
+^^^^^^^^^^^^^^^^^
+
+The ``nsIDocShell`` for the frame associated with this ``JSWindowActorChild``.
+
+See `JSWindowActor.webidl`_ for more detail on exactly what is exposed on both ``JSWindowActorParent`` and ``JSWindowActorChild`` implementations.
 
 How to port from message manager and framescripts to JS Window Actors
 ---------------------------------------------------------------------
 
-TBD
+.. _fission.message-manager-actors:
+
+Message Manager Actors
+``````````````````````
+
+While the JSWindowActor mechanism was being designed and developed, large sections of our framescripts were converted to an "actor style" pattern to make eventual porting to JSWindowActors easier. These Actors use the Message Manager under the hood, but made it much easier to shrink our framescripts, and also allowed us to gain significant memory savings by having the actors be lazily instantiated.
+
+You can find the list of Message Manager Actors (or "Legacy Actors") in `BrowserGlue.jsm <https://searchfox.org/mozilla-central/source/browser/components/BrowserGlue.jsm>`_ and `ActorManagerParent.jsm <https://searchfox.org/mozilla-central/source/toolkit/modules/ActorManagerParent.jsm>`_, in the ``LEGACY_ACTORS`` lists.
+
+.. note::
+  The split in Message Manager Actors defined between ``BrowserGlue`` and ``ActorManagerParent`` is mainly to keep Firefox Desktop specific Actors separate from Actors that can (in theory) be instantiated for non-Desktop browsers (like Fennec and GeckoView-based browsers). Firefox Desktop-specific Actors should be registered in ``BrowserGlue``. Shared "toolkit" Actors should go into ``ActorManagerParent``.
+
+"Porting" these Actors often means doing what is necessary in order to move their registration entries from ``LEGACY_ACTORS`` to the ``ACTORS`` list.
+
+Figuring out the lifetime of a new Actor pair
+`````````````````````````````````````````````
+
+In the old model, framescript were loaded and executed as soon as possible by the top-level frame. In the JSWindowActor model, the Actors are much lazier, and only instantiate when:
+
+1. They're instantiated explicitly by calling ``getActor`` on a ``WindowGlobal``, and passing in the name of the Actor.
+2. A message is sent to them.
+3. A pre-defined ``nsIObserver`` observer notification fires
+4. A pre-defined content Event fires
+
+Making the Actors lazy like this saves on processing time to get a frame ready to load web pages, as well as the overhead of loading the Actor into memory.
+
+When porting a framescript to JSWindowActors, often the first question to ask is: what's the entrypoint? At what point should the Actors instantiate and become active?
+
+For example, when porting the content area context menu for Firefox, it was noted that the ``contextmenu`` event firing in content was a natural event to wait for to instantiate the Actor pair. Once the ``ContextMenuChild`` instantiated, the ``handleEvent`` method was used to inspect the event and prepare a message to be sent to the ``ContextMenuParent``. This example can be found by looking at the patch for the `Context Menu Fission Port`_.
+
+.. _fission.registering-a-new-jswindowactor:
+
+Registering a new JSWindowActor
+```````````````````````````````
+
+``ChromeUtils`` exposes an API for registering window actors, but both ``BrowserGlue`` and ``ActorManagerParent`` are the main entry points where the registration occurs. If you want to register an actor, you should put them in one of the ``ACTORS`` lists in one of those two files. See :ref:`fission.message-manager-actors` for details.
+
+The ``ACTORS`` lists expect a key-value pair, where the key is the name of the actor pair (example: ``ContextMenu``), and the value is an ``Object`` of registration parameters.
+
+The full list of registration parameters can be found in the `JSWindowActor.webidl`_ file as ``WindowActorOptions``, ``WindowActorSidedOptions`` and ``WindowActorChildOptions``.
+
+Here's an example JSWindowActor registration pulled from ``BrowserGlue.jsm``:
+
+.. code-block:: javascript
+
+   Plugin: {
+      parent: {
+        moduleURI: "resource:///actors/PluginParent.jsm",
+      },
+      child: {
+        moduleURI: "resource:///actors/PluginChild.jsm",
+        events: {
+          PluginBindingAttached: { capture: true, wantUntrusted: true },
+          PluginCrashed: { capture: true },
+          PluginOutdated: { capture: true },
+          PluginInstantiated: { capture: true },
+          PluginRemoved: { capture: true },
+          HiddenPlugin: { capture: true },
+        },
+
+        observers: ["decoder-doctor-notification"],
+      },
+
+      allFrames: true,
+    },
+
+This example is for the JSWindowActor implementation of click-to-play for Flash.
+
+Let's examine the first chunk:
+
+.. code-block:: javascript
+
+      parent: {
+        moduleURI: "resource:///actors/PluginParent.jsm",
+      },
+
+Here, we're declaring that the ``PluginParent`` subclassing ``JSWindowActorParent`` will be defined and exported inside the ``PluginParent.jsm`` file. That's all we have to say for the parent (main process) side of things.
+
+.. note::
+    It's not sufficient to just add a new .jsm file to the actors subdirectories. You also need to update the ``moz.build`` files in the same directory to get the ``resource://`` linkages set up correctly.
+
+Let's look at the second chunk:
+
+.. code-block:: javascript
 
-Example
--------
+      child: {
+        moduleURI: "resource:///actors/PluginChild.jsm",
+        events: {
+          PluginBindingAttached: { capture: true, wantUntrusted: true },
+          PluginCrashed: { capture: true },
+          PluginOutdated: { capture: true },
+          PluginInstantiated: { capture: true },
+          PluginRemoved: { capture: true },
+          HiddenPlugin: { capture: true },
+        },
+
+        observers: ["decoder-doctor-notification"],
+      },
+
+      allFrames: true,
+    },
+
+We're similarly declaring where the ``PluginChild`` subclassing ``JSWindowActorChild`` can be found.
+
+Next, we declare the content events, if fired in a BrowsingContext, will cause the JSWindowActor pair to instantiate if it doesn't already exist, and then have ``handleEvent`` called on the ``PluginChild`` instance. For each event name, an Object of event listener options can be passed. You can use the same event listener options as accepted by ``addEventListener``.
+
+Next, we declare that ``PluginChild`` should observe the ``decoder-doctor-notification`` ``nsIObserver`` notification. When that observer notification fires, the ``PluginChild`` will be instantiated for all ``BrowsingContext``'s, and the ``observe`` method on the ``PluginChild`` implementation will be called.
+
+Finally, we say that the ``PluginChild`` actor should apply to ``allFrames``. This means that the ``PluginChild`` is allowed to be loaded in any subframe. If ``allFrames`` is set to false, the actor will only ever load in the top-level frame.
+
+Using ContentDOMReference instead of CPOWs
+``````````````````````````````````````````
+
+Despite being outlawed as a way of synchronously accessing the properties of objects in other processes, CPOWs ended up being useful as a way of passing handles for DOM elements between processes.
+
+CPOW messages, however, cannot be sent over the JSWindowActor communications pipe, so this handy mechanism will no longer work.
+
+Instead, a new module called `ContentDOMReference.jsm`_ has been created which supplies the same capability. See that file for documentation.
+
+How to start porting parent-process browser code to use JSWindowActors
+``````````````````````````````````````````````````````````````````````
+
+The :ref:`fission.message-manager-actors` work made it much easier to migrate away from framescripts towards something that is similar to ``JSWindowActors``. It did not, however, substantially change how the parent process interacted with those framescripts.
+
+So when porting code to work with ``JSWindowActors``, we find that this is often where the time goes - refactoring the parent process browser code to accomodate the new ``JSWindowActor`` model.
+
+Usually, the first thing to do is to find a reasonable name for your actor pair, and get them registered (see :ref:`fission.registering-a-new-jswindowactor`), even if the actors implementations themselves are nothing but unmodified subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``.
+
+Next, it's often helpful to find and note all of the places where ``sendAsyncMessage`` is being used to send messages through the old message manager interface for the component you're porting, and where any messages listeners are defined.
+
+Let's look at a hypothetical example. Suppose we're porting part of the Page Info dialog, which scans each frame for useful information to display in the dialog. Given a chunk of code like this:
+
+.. code-block:: javascript
+
+    // This is some hypothetical Page Info dialog code.
+
+    let mm = browser.messageManager;
+    mm.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
+
+    // ... and then later on
+
+    mm.addMessageListener("PageInfo:info", async function onmessage(message) {
+      // ...
+    });
+
+If a ``PageInfo`` pair of ``JSWindowActor``'s is registered, it might be tempting to simply replace the first part with:
+
+.. code-block:: javascript
+
+    let actor = browser.browsingContext.currentWindowGlobal.getActor("PageInfo");
+    actor.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
+
+However, if any of the frames on the page are running in their own process, they're not going to receive that ``PageInfo:getInfoFromAllFrames`` message. Instead, in this case, we should walk the ``BrowsingContext`` tree, and instantiate a ``PageInfo`` actor for each global, and send one message each to get information for each frame. Perhaps something like this:
+
+.. code-block:: javascript
+
+    let contextsToVisit = [browser.browsingContext];
+    while (contextsToVisit.length) {
+      let currentContext = contextsToVisit.pop();
+      let global = currentContext.currentWindowGlobal;
+
+      if (!global) {
+        continue;
+      }
+
+      let actor = global.getActor("PageInfo");
+      actor.sendAsyncMessage("PageInfo:getInfoForFrame", { someArgument: 123 });
+
+      contextsToVisit.push(...currentContext.getChildren());
+    }
+
+The original ``"PageInfo:info"`` message listener will need to be updated, too. Any responses from the ``PageInfoChild`` actor will end up being passed to the ``receiveMessage`` method of the ``PageInfoParent`` actor. It will be necessary to pass that information along to the interested party (in this case, the dialog code which is showing the table of interesting Page Info).
+
+It might be necessary to refactor or rearchitect the original senders and consumers of message manager messages in order to accommodate the ``JSWindowActor`` model. Sometimes it's also helpful to have a singleton management object that manages all ``JSWindowActorParent`` instances and does something with their results. See ``PermitUnloader`` inside the implementation of `BrowserElementParent.jsm`_ for example.
+
+Where to store state
+````````````````````
+
+It's not a good idea to store any state within a ``JSWindowActorChild`` that you want to last beyond the lifetime of its ``BrowsingContext``. An out-of-process ``<iframe>`` can be closed at any time, and if it's the only one for a particular content process, that content process will soon be shut down, and any state you may have stored there will go away.
+
+Your best bet for storing state is in the parent process.
+
+.. hint::
+    If each individual frame needs state, consider using a ``WeakMap`` in the parent process, mapping ``CanonicalBrowsingContext``'s with that state. That way, if the associates frames ever go away, you don't have to do any cleaning up yourself.
+
+If you have state that you want multiple ``JSWindowActorParent``'s to have access to, consider having a "manager" of those ``JSWindowActorParent``'s inside of the same .jsm file to hold that state. See ``PermitUnloader`` inside the implementation of `BrowserElementParent.jsm`_ for example.
+
+Minimal Example Actors
+----------------------
 
 **Define an Actor**
 
 .. code-block:: javascript
 
   // resource://testing-common/TestParent.jsm
   var EXPORTED_SYMBOLS = ["TestParent"];
   class TestParent extends JSWindowActorParent {
@@ -84,41 +425,25 @@ Example
   class TestChild extends JSWindowActorChild {
     constructor() {
       super();
     }
     ...
   }
 
 
-**Registering an Actor**
-
-.. code-block:: javascript
-
-    ChromeUtils.registerWindowActor("Test", {
-      parent: {
-        moduleURI: "resource://testing-common/TestParent.jsm",
-      },
-      child: {
-        moduleURI: "resource://testing-common/TestChild.jsm",
-
-        events: {
-          "mozshowdropdown": {},
-        },
-
-        observers: [
-          "test-js-window-actor-child-observer",
-        ],
-      },
-
-      allFrames: true,
-    });
-
-
 **Get a JS window actor for a specific window**
 
 .. code-block:: javascript
 
   // get parent side actor
   let parentActor = this.browser.browsingContext.currentWindowGlobal.getActor("Test");
 
   // get child side actor
   let childActor = content.window.getWindowGlobalChild().getActor("Test");
+
+.. _Electrolysis Project: https://wiki.mozilla.org/Electrolysis
+.. _IPC Actors: https://developer.mozilla.org/en-US/docs/Mozilla/IPDL/Tutorial
+.. _Transferables: https://developer.mozilla.org/en-US/docs/Web/API/Transferable
+.. _Context Menu Fission Port: https://hg.mozilla.org/mozilla-central/rev/adc60720b7b8
+.. _ContentDOMReference.jsm: https://searchfox.org/mozilla-central/source/toolkit/modules/ContentDOMReference.jsm
+.. _JSWindowActor.webidl: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSWindowActor.webidl
+.. _BrowserElementParent.jsm: https://searchfox.org/mozilla-central/rev/ec806131cb7bcd1c26c254d25cd5ab8a61b2aeb6/toolkit/actors/BrowserElementParent.jsm
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -26,17 +26,19 @@
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/UIEvent.h"
 #include "mozilla/dom/UIEventBinding.h"
 #include "mozilla/dom/WheelEventBinding.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPrefs_mousewheel.h"
 #include "mozilla/StaticPrefs_ui.h"
+#include "mozilla/StaticPrefs_zoom.h"
 
 #include "ContentEventHandler.h"
 #include "IMEContentObserver.h"
 #include "WheelHandlingHelper.h"
 #include "RemoteDragStartData.h"
 
 #include "nsCommandParams.h"
 #include "nsCOMPtr.h"
@@ -245,17 +247,16 @@ EventStateManager::EventStateManager()
       mInTouchDrag(false),
       m_haveShutdown(false) {
   if (sESMInstanceCount == 0) {
     gUserInteractionTimerCallback = new UITimerCallback();
     if (gUserInteractionTimerCallback) NS_ADDREF(gUserInteractionTimerCallback);
     UpdateUserActivityTimer();
   }
   ++sESMInstanceCount;
-  WheelTransaction::InitializeStatics();
 }
 
 nsresult EventStateManager::UpdateUserActivityTimer() {
   if (!gUserInteractionTimerCallback) return NS_OK;
 
   if (!gUserInteractionTimer) {
     gUserInteractionTimer =
         NS_NewTimer(SystemGroup::EventTargetFor(TaskCategory::Other)).take();
@@ -2122,18 +2123,18 @@ nsresult EventStateManager::GetContentVi
 
 nsresult EventStateManager::ChangeTextSize(int32_t change) {
   nsCOMPtr<nsIContentViewer> cv;
   nsresult rv = GetContentViewer(getter_AddRefs(cv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (cv) {
     float textzoom;
-    float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
-    float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
+    float zoomMin = ((float)StaticPrefs::zoom_minPercent()) / 100;
+    float zoomMax = ((float)StaticPrefs::zoom_maxPercent()) / 100;
     cv->GetTextZoom(&textzoom);
     textzoom += ((float)change) / 10;
     if (textzoom < zoomMin)
       textzoom = zoomMin;
     else if (textzoom > zoomMax)
       textzoom = zoomMax;
     cv->SetTextZoom(textzoom);
   }
@@ -2143,18 +2144,18 @@ nsresult EventStateManager::ChangeTextSi
 
 nsresult EventStateManager::ChangeFullZoom(int32_t change) {
   nsCOMPtr<nsIContentViewer> cv;
   nsresult rv = GetContentViewer(getter_AddRefs(cv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (cv) {
     float fullzoom;
-    float zoomMin = ((float)Preferences::GetInt("zoom.minPercent", 50)) / 100;
-    float zoomMax = ((float)Preferences::GetInt("zoom.maxPercent", 300)) / 100;
+    float zoomMin = ((float)StaticPrefs::zoom_minPercent()) / 100;
+    float zoomMax = ((float)StaticPrefs::zoom_maxPercent()) / 100;
     cv->GetFullZoom(&fullzoom);
     fullzoom += ((float)change) / 10;
     if (fullzoom < zoomMin)
       fullzoom = zoomMin;
     else if (fullzoom > zoomMax)
       fullzoom = zoomMax;
     cv->SetFullZoom(fullzoom);
   }
@@ -5835,17 +5836,18 @@ void EventStateManager::ClearGlobalActiv
 void EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
     nsIFrame* aTargetFrame, EventStateManager* aESM, WidgetWheelEvent* aEvent) {
   MOZ_ASSERT(aESM);
   MOZ_ASSERT(aEvent);
 
   // Reset if the previous wheel event is too old.
   if (!mLastTime.IsNull()) {
     TimeDuration duration = TimeStamp::Now() - mLastTime;
-    if (duration.ToMilliseconds() > WheelTransaction::GetTimeoutTime()) {
+    if (duration.ToMilliseconds() >
+        StaticPrefs::mousewheel_transaction_timeout()) {
       Reset();
     }
   }
   // If we have accumulated delta,  we may need to reset it.
   if (IsInTransaction()) {
     // If wheel event type is changed, reset the values.
     if (mHandlingDeltaMode != aEvent->mDeltaMode ||
         mIsNoLineOrPageDeltaDevice != aEvent->mIsNoLineOrPageDelta) {
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -8,16 +8,18 @@
 
 #include <utility>  // for std::swap
 
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_mousewheel.h"
+#include "mozilla/StaticPrefs_test.h"
 #include "mozilla/dom/WheelEventBinding.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "mozilla/dom/Document.h"
 #include "DocumentInlines.h"  // for Document and HTMLBodyElement
 #include "nsIScrollableFrame.h"
@@ -218,29 +220,30 @@ bool WheelTransaction::WillHandleDefault
 }
 
 /* static */
 void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
   if (!sTargetFrame) {
     return;
   }
 
-  if (OutOfTime(sTime, GetTimeoutTime())) {
+  if (OutOfTime(sTime, StaticPrefs::mousewheel_transaction_timeout())) {
     // Even if the scroll event which is handled after timeout, but onTimeout
     // was not fired by timer, then the scroll event will scroll old frame,
     // therefore, we should call OnTimeout here and ensure to finish the old
     // transaction.
     OnTimeout(nullptr, nullptr);
     return;
   }
 
   switch (aEvent->mMessage) {
     case eWheel:
       if (sMouseMoved != 0 &&
-          OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) {
+          OutOfTime(sMouseMoved,
+                    StaticPrefs::mousewheel_transaction_ignoremovedelay())) {
         // Terminate the current mousewheel transaction if the mouse moved more
         // than ignoremovedelay milliseconds ago
         EndTransaction();
       }
       return;
     case eMouseMove:
     case eDragOver: {
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
@@ -255,17 +258,19 @@ void WheelTransaction::OnEvent(WidgetEve
           EndTransaction();
           return;
         }
 
         // If the cursor is moving inside the frame, and it is less than
         // ignoremovedelay milliseconds since the last scroll operation, ignore
         // the mouse move; otherwise, record the current mouse move time to be
         // checked later
-        if (!sMouseMoved && OutOfTime(sTime, GetIgnoreMoveDelayTime())) {
+        if (!sMouseMoved &&
+            OutOfTime(sTime,
+                      StaticPrefs::mousewheel_transaction_ignoremovedelay())) {
           sMouseMoved = PR_IntervalToMilliseconds(PR_IntervalNow());
         }
       }
       return;
     }
     case eKeyPress:
     case eKeyUp:
     case eKeyDown:
@@ -285,17 +290,17 @@ void WheelTransaction::OnEvent(WidgetEve
 
 /* static */
 void WheelTransaction::Shutdown() { NS_IF_RELEASE(sTimer); }
 
 /* static */
 void WheelTransaction::OnFailToScrollTarget() {
   MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
 
-  if (Prefs::sTestMouseScroll) {
+  if (StaticPrefs::test_mousescroll()) {
     // This event is used for automated tests, see bug 442774.
     nsContentUtils::DispatchTrustedEvent(
         sTargetFrame->GetContent()->OwnerDoc(), sTargetFrame->GetContent(),
         NS_LITERAL_STRING("MozMouseScrollFailed"), CanBubble::eYes,
         Cancelable::eYes);
   }
   // The target frame might be destroyed in the event handler, at that time,
   // we need to finish the current transaction
@@ -312,17 +317,17 @@ void WheelTransaction::OnTimeout(nsITime
     return;
   }
   // Store the sTargetFrame, the variable becomes null in EndTransaction.
   nsIFrame* frame = sTargetFrame;
   // We need to finish current transaction before DOM event firing. Because
   // the next DOM event might create strange situation for us.
   MayEndTransaction();
 
-  if (Prefs::sTestMouseScroll) {
+  if (StaticPrefs::test_mousescroll()) {
     // This event is used for automated tests, see bug 442774.
     nsContentUtils::DispatchTrustedEvent(
         frame->GetContent()->OwnerDoc(), frame->GetContent(),
         NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"), CanBubble::eYes,
         Cancelable::eYes);
   }
 }
 
@@ -331,18 +336,18 @@ void WheelTransaction::SetTimeout() {
   if (!sTimer) {
     sTimer = NS_NewTimer().take();
     if (!sTimer) {
       return;
     }
   }
   sTimer->Cancel();
   DebugOnly<nsresult> rv = sTimer->InitWithNamedFuncCallback(
-      OnTimeout, nullptr, GetTimeoutTime(), nsITimer::TYPE_ONE_SHOT,
-      "WheelTransaction::SetTimeout");
+      OnTimeout, nullptr, StaticPrefs::mousewheel_transaction_timeout(),
+      nsITimer::TYPE_ONE_SHOT, "WheelTransaction::SetTimeout");
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "nsITimer::InitWithFuncCallback failed");
 }
 
 /* static */
 LayoutDeviceIntPoint WheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent) {
   NS_ASSERTION(aEvent, "aEvent is null");
   NS_ASSERTION(aEvent->mWidget, "aEvent-mWidget is null");
@@ -359,19 +364,19 @@ DeltaValues WheelTransaction::Accelerate
     return result;
   }
 
   if (aAllowScrollSpeedOverride) {
     result = OverrideSystemScrollSpeed(aEvent);
   }
 
   // Accelerate by the sScrollSeriesCounter
-  int32_t start = GetAccelerationStart();
+  int32_t start = StaticPrefs::mousewheel_acceleration_start();
   if (start >= 0 && sScrollSeriesCounter >= start) {
-    int32_t factor = GetAccelerationFactor();
+    int32_t factor = StaticPrefs::mousewheel_acceleration_factor();
     if (factor > 0) {
       result.deltaX = ComputeAcceleratedWheelDelta(result.deltaX, factor);
       result.deltaY = ComputeAcceleratedWheelDelta(result.deltaY, factor);
     }
   }
 
   return result;
 }
@@ -514,43 +519,16 @@ void ScrollbarsForWheel::DeactivateAllTe
         scrollbarMediator->ScrollbarActivityStopped();
       }
       *scrollTarget = nullptr;
     }
   }
 }
 
 /******************************************************************/
-/* mozilla::WheelTransaction::Prefs                               */
-/******************************************************************/
-
-int32_t WheelTransaction::Prefs::sMouseWheelAccelerationStart = -1;
-int32_t WheelTransaction::Prefs::sMouseWheelAccelerationFactor = -1;
-uint32_t WheelTransaction::Prefs::sMouseWheelTransactionTimeout = 1500;
-uint32_t WheelTransaction::Prefs::sMouseWheelTransactionIgnoreMoveDelay = 100;
-bool WheelTransaction::Prefs::sTestMouseScroll = false;
-
-/* static */
-void WheelTransaction::Prefs::InitializeStatics() {
-  static bool sIsInitialized = false;
-  if (!sIsInitialized) {
-    Preferences::AddIntVarCache(&sMouseWheelAccelerationStart,
-                                "mousewheel.acceleration.start", -1);
-    Preferences::AddIntVarCache(&sMouseWheelAccelerationFactor,
-                                "mousewheel.acceleration.factor", -1);
-    Preferences::AddUintVarCache(&sMouseWheelTransactionTimeout,
-                                 "mousewheel.transaction.timeout", 1500);
-    Preferences::AddUintVarCache(&sMouseWheelTransactionIgnoreMoveDelay,
-                                 "mousewheel.transaction.ignoremovedelay", 100);
-    Preferences::AddBoolVarCache(&sTestMouseScroll, "test.mousescroll", false);
-    sIsInitialized = true;
-  }
-}
-
-/******************************************************************/
 /* mozilla::WheelDeltaHorizontalizer                              */
 /******************************************************************/
 
 void WheelDeltaHorizontalizer::Horizontalize() {
   MOZ_ASSERT(!mWheelEvent.mDeltaValuesHorizontalizedForDefaultHandler,
              "Wheel delta values in one wheel scroll event are being adjusted "
              "a second time");
 
--- a/dom/events/WheelHandlingHelper.h
+++ b/dom/events/WheelHandlingHelper.h
@@ -130,67 +130,44 @@ class WheelTransaction {
                                       AutoWeakFrame& aTargetWeakFrame);
   static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
                                       nsIFrame* aTargetFrame) {
     AutoWeakFrame targetWeakFrame(aTargetFrame);
     return WillHandleDefaultAction(aWheelEvent, targetWeakFrame);
   }
   static void OnEvent(WidgetEvent* aEvent);
   static void Shutdown();
-  static uint32_t GetTimeoutTime() {
-    return Prefs::sMouseWheelTransactionTimeout;
-  }
 
   static void OwnScrollbars(bool aOwn);
 
   static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent,
                                           bool aAllowScrollSpeedOverride);
-  static void InitializeStatics() { Prefs::InitializeStatics(); }
 
  protected:
   static void BeginTransaction(nsIFrame* aTargetFrame,
                                const WidgetWheelEvent* aEvent);
   // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
   // frame might be destroyed in the event handler.
   static bool UpdateTransaction(const WidgetWheelEvent* aEvent);
   static void MayEndTransaction();
 
   static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
   static void OnFailToScrollTarget();
   static void OnTimeout(nsITimer* aTimer, void* aClosure);
   static void SetTimeout();
-  static uint32_t GetIgnoreMoveDelayTime() {
-    return Prefs::sMouseWheelTransactionIgnoreMoveDelay;
-  }
-  static int32_t GetAccelerationStart() {
-    return Prefs::sMouseWheelAccelerationStart;
-  }
-  static int32_t GetAccelerationFactor() {
-    return Prefs::sMouseWheelAccelerationFactor;
-  }
   static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
   static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
   static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
 
   static AutoWeakFrame sTargetFrame;
   static uint32_t sTime;        // in milliseconds
   static uint32_t sMouseMoved;  // in milliseconds
   static nsITimer* sTimer;
   static int32_t sScrollSeriesCounter;
   static bool sOwnScrollbars;
-
-  class Prefs {
-   public:
-    static void InitializeStatics();
-    static int32_t sMouseWheelAccelerationStart;
-    static int32_t sMouseWheelAccelerationFactor;
-    static uint32_t sMouseWheelTransactionTimeout;
-    static uint32_t sMouseWheelTransactionIgnoreMoveDelay;
-    static bool sTestMouseScroll;
-  };
 };
 
 // For some kinds of scrollings, the delta values of WidgetWheelEvent are
 // possbile to be adjusted. For example, the user has configured the pref to let
 // [vertical wheel + Shift key] to perform horizontal scrolling instead of
 // vertical scrolling.
 // The values in this enumeration list all kinds of scrollings whose delta
 // values are possible to be adjusted.
--- a/dom/imptests/testharnessreport.js
+++ b/dom/imptests/testharnessreport.js
@@ -273,17 +273,17 @@ var W3CTest = {
       "todo": false
     });
   },
 
   /**
    * Timeout the current test. Intended to be used from harness code, not
    * from tests.
    */
-  "timeout": function() {
+  "timeout": async function() {
     this.logFailure("Timeout", "Test runner timed us out.");
     timeout();
   }
 };
 (function() {
   try {
     var path = W3CTest.getPath();
     if (path) {
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -7,16 +7,17 @@
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM: Core & HTML")
 
 XPIDL_SOURCES += [
     'domstubs.idl',
     'nsIBrowser.idl',
     'nsIBrowserChild.idl',
     'nsIBrowserDOMWindow.idl',
+    'nsIBrowserUsage.idl',
     'nsIContentPermissionPrompt.idl',
     'nsIContentPrefService2.idl',
     'nsIContentProcess.idl',
     'nsIDOMChromeWindow.idl',
     'nsIDOMGlobalPropertyInitializer.idl',
     'nsIDOMWindow.idl',
     'nsIDOMWindowUtils.idl',
     'nsIFocusManager.idl',
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIBrowserUsage.idl
@@ -0,0 +1,16 @@
+/* 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 "domstubs.idl"
+
+[scriptable, uuid(2703b5ed-a41f-42be-8764-b795eb67ed25)]
+interface nsIBrowserUsage : nsISupports
+{
+  /**
+   * Returns the number of unique domains (eTLD+1) visited in the past
+   * 24 hours by the user.
+   */
+  uint32_t getUniqueDomainsVisitedInPast24Hours();
+};
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5701,16 +5701,23 @@ mozilla::ipc::IPCResult ContentParent::R
     // moving our deserialized argument.
     nsCOMPtr<nsIHangDetails> hangDetails =
         new nsHangDetails(HangDetails(aDetails));
     obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
   }
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult ContentParent::RecvAutomaticStorageAccessCanBeGranted(
+    const Principal& aPrincipal,
+    AutomaticStorageAccessCanBeGrantedResolver&& aResolver) {
+  aResolver(Document::AutomaticStorageAccessCanBeGranted(aPrincipal));
+  return IPC_OK();
+}
+
 mozilla::ipc::IPCResult
 ContentParent::RecvFirstPartyStorageAccessGrantedForOrigin(
     const Principal& aParentPrincipal, const Principal& aTrackingPrincipal,
     const nsCString& aTrackingOrigin, const nsCString& aGrantedOrigin,
     const int& aAllowMode,
     FirstPartyStorageAccessGrantedForOriginResolver&& aResolver) {
   AntiTrackingCommon::
       SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1149,16 +1149,20 @@ class ContentParent final : public PCont
       const DiscardedData& aDiscardedData);
   mozilla::ipc::IPCResult RecvRecordOrigin(const uint32_t& aMetricId,
                                            const nsCString& aOrigin);
   mozilla::ipc::IPCResult RecvReportContentBlockingLog(
       const IPCStream& aIPCStream);
 
   mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aHangDetails);
 
+  mozilla::ipc::IPCResult RecvAutomaticStorageAccessCanBeGranted(
+      const Principal& aPrincipal,
+      AutomaticStorageAccessCanBeGrantedResolver&& aResolver);
+
   mozilla::ipc::IPCResult RecvFirstPartyStorageAccessGrantedForOrigin(
       const Principal& aParentPrincipal, const Principal& aTrackingPrincipal,
       const nsCString& aTrackingOrigin, const nsCString& aGrantedOrigin,
       const int& aAllowMode,
       FirstPartyStorageAccessGrantedForOriginResolver&& aResolver);
 
   mozilla::ipc::IPCResult RecvStoreUserInteractionAsPermission(
       const Principal& aPrincipal);
--- a/dom/ipc/MemMapSnapshot.cpp
+++ b/dom/ipc/MemMapSnapshot.cpp
@@ -1,151 +1,46 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MemMapSnapshot.h"
 
-#include "base/eintr_wrapper.h"
-#include "base/file_util.h"
-#include "mozilla/Assertions.h"
+#include "mozilla/AutoMemMap.h"
 #include "mozilla/ResultExtensions.h"
-#include "mozilla/ScopeExit.h"
-#include "mozilla/ipc/FileDescriptorUtils.h"
-#include "nsIFile.h"
-
-#ifdef XP_WIN
-#  include "mozilla/RandomNum.h"
-#  include "mozilla/WindowsVersion.h"
-#  include "nsDebug.h"
-#  include "nsString.h"
-#  include <windows.h>
-#endif
-
-#ifdef XP_UNIX
-#  include <sys/stat.h>
-#endif
 
 namespace mozilla {
 
-using loader::AutoMemMap;
-
 namespace ipc {
 
 Result<Ok, nsresult> MemMapSnapshot::Init(size_t aSize) {
   MOZ_ASSERT(!mInitialized);
 
-  MOZ_TRY(Create(aSize));
+  if (NS_WARN_IF(!mMem.CreateFreezeable(aSize))) {
+    return Err(NS_ERROR_FAILURE);
+  }
+  if (NS_WARN_IF(!mMem.Map(aSize))) {
+    return Err(NS_ERROR_FAILURE);
+  }
 
   mInitialized = true;
   return Ok();
 }
 
-Result<Ok, nsresult> MemMapSnapshot::Finalize(AutoMemMap& aMem) {
+Result<Ok, nsresult> MemMapSnapshot::Finalize(loader::AutoMemMap& aMem) {
   MOZ_ASSERT(mInitialized);
 
-  MOZ_TRY(Freeze(aMem));
+  if (NS_WARN_IF(!mMem.Freeze())) {
+    return Err(NS_ERROR_FAILURE);
+  }
+  // TakeHandle resets mMem, so call max_size first.
+  size_t size = mMem.max_size();
+  FileDescriptor memHandle(mMem.TakeHandle());
+  MOZ_TRY(aMem.initWithHandle(memHandle, size));
 
   mInitialized = false;
   return Ok();
 }
 
-#if defined(XP_WIN)
-
-Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
-  SECURITY_ATTRIBUTES sa;
-  SECURITY_DESCRIPTOR sd;
-  ACL dacl;
-
-  sa.nLength = sizeof(sa);
-  sa.lpSecurityDescriptor = &sd;
-  sa.bInheritHandle = FALSE;
-
-  if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
-      NS_WARN_IF(
-          !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
-      NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  nsAutoStringN<sizeof("MozSharedMem_") + 16 * 4> name;
-  if (!IsWin8Point1OrLater()) {
-    name.AssignLiteral("MozSharedMem_");
-    for (size_t i = 0; i < 4; ++i) {
-      Maybe<uint64_t> randomNum = RandomUint64();
-      if (NS_WARN_IF(randomNum.isNothing())) {
-        return Err(NS_ERROR_UNEXPECTED);
-      }
-      name.AppendPrintf("%016llx", *randomNum);
-    }
-  }
-
-  HANDLE handle =
-      CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
-                        DWORD(aSize), name.IsEmpty() ? nullptr : name.get());
-
-  if (!handle) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  mFile.emplace(handle);
-  return mMem.initWithHandle(mFile.ref(), aSize, PR_PROT_READWRITE);
-}
-
-Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
-  auto orig = mFile.ref().ClonePlatformHandle();
-  mFile.reset();
-
-  HANDLE handle;
-  if (!::DuplicateHandle(
-          GetCurrentProcess(), orig.release(), GetCurrentProcess(), &handle,
-          GENERIC_READ | FILE_MAP_READ, false, DUPLICATE_CLOSE_SOURCE)) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  return aMem.initWithHandle(FileDescriptor(handle), mMem.size());
-}
-
-#elif defined(XP_UNIX)
-
-Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
-  FilePath path;
-  ScopedCloseFile fd(file_util::CreateAndOpenTemporaryShmemFile(&path));
-  if (!fd) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  if (HANDLE_EINTR(ftruncate(fileno(fd), aSize)) != 0) {
-    return Err(NS_ERROR_FAILURE);
-  }
-
-  MOZ_TRY(mMem.init(FILEToFileDescriptor(fd), PR_PROT_READWRITE));
-
-  mPath.Assign(path.value().data(), path.value().length());
-  return Ok();
-}
-
-Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
-  // Delete the shm file after we're done here, whether we succeed or not. The
-  // open file descriptor will keep it alive until all remaining references
-  // are closed, at which point it will be automatically freed.
-  auto cleanup = MakeScopeExit([&]() { PR_Delete(mPath.get()); });
-
-  // Make the shm file readonly. This doesn't make a difference in practice,
-  // since we open and share a read-only file descriptor, and then delete the
-  // file. But it doesn't hurt, either.
-  chmod(mPath.get(), 0400);
-
-  nsCOMPtr<nsIFile> file;
-  MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false,
-                                getter_AddRefs(file)));
-
-  return aMem.init(file);
-}
-
-#else
-#  error "Unsupported build configuration"
-#endif
-
 }  // namespace ipc
 }  // namespace mozilla
--- a/dom/ipc/MemMapSnapshot.h
+++ b/dom/ipc/MemMapSnapshot.h
@@ -2,26 +2,27 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dom_ipc_MemMapSnapshot_h
 #define dom_ipc_MemMapSnapshot_h
 
-#include "mozilla/AutoMemMap.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Result.h"
-#ifdef XP_WIN
-#  include "mozilla/ipc/FileDescriptor.h"
-#endif
+#include "base/shared_memory.h"
 
 namespace mozilla {
+namespace loader {
+class AutoMemMap;
+}
+
 namespace ipc {
 
 /**
  * A helper class for creating a read-only snapshot of memory-mapped data.
  *
  * The Init() method initializes a read-write memory mapped region of the given
  * size, which can be initialized with arbitrary data. The Finalize() method
  * remaps that region as read-only (and backs it with a read-only file
@@ -30,33 +31,23 @@ namespace ipc {
  * The file descriptor for the resulting AutoMemMap can be shared among
  * processes, to safely access a shared, read-only copy of the data snapshot.
  */
 class MOZ_RAII MemMapSnapshot {
  public:
   Result<Ok, nsresult> Init(size_t aSize);
   Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
 
-  template <typename T = void>
+  template <typename T>
   RangedPtr<T> Get() {
     MOZ_ASSERT(mInitialized);
-    return mMem.get<T>();
+    return {static_cast<T*>(mMem.memory()), mMem.max_size() / sizeof(T)};
   }
 
  private:
-  Result<Ok, nsresult> Create(size_t aSize);
-  Result<Ok, nsresult> Freeze(loader::AutoMemMap& aMem);
-
-  loader::AutoMemMap mMem;
-
+  base::SharedMemory mMem;
   bool mInitialized = false;
-
-#ifdef XP_WIN
-  Maybe<FileDescriptor> mFile;
-#else
-  nsCString mPath;
-#endif
 };
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // dom_ipc_MemMapSnapshot_h
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1374,16 +1374,23 @@ parent:
 
     async MaybeReloadPlugins();
 
     async BHRThreadHang(HangDetails aHangDetails);
 
     async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
 
     /*
+     * Determines whether storage access can be granted automatically by the
+     * storage access API without showing a user prompt.
+     */
+    async AutomaticStorageAccessCanBeGranted(Principal aPrincipal)
+          returns (bool success);
+
+    /*
      * A 3rd party tracking origin (aTrackingOrigin) has received the permission
      * granted to have access to aGrantedOrigin when loaded by aParentPrincipal.
      */
     async FirstPartyStorageAccessGrantedForOrigin(Principal aParentPrincipal,
                                                   Principal aTrackingPrincipal,
                                                   nsCString aTrackingOrigin,
                                                   nsCString aGrantedOrigin,
                                                   int aAllowMode)
--- a/dom/ipc/SharedStringMap.cpp
+++ b/dom/ipc/SharedStringMap.cpp
@@ -16,35 +16,39 @@ using namespace mozilla::loader;
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 namespace ipc {
 
+static constexpr uint32_t kSharedStringMapMagic = 0x9e3779b9;
+
 static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) {
   auto mod = aOffset % aAlign;
   return mod ? aAlign - mod : 0;
 }
 
 SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
                                  size_t aMapSize) {
   auto result = mMap.initWithHandle(aMapFile, aMapSize);
   MOZ_RELEASE_ASSERT(result.isOk());
+  MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
   // We return literal nsStrings and nsCStrings pointing to the mapped data,
   // which means that we may still have references to the mapped data even
   // after this instance is destroyed. That means that we need to keep the
   // mapping alive until process shutdown, in order to be safe.
   mMap.setPersistent();
 }
 
 SharedStringMap::SharedStringMap(SharedStringMapBuilder&& aBuilder) {
   auto result = aBuilder.Finalize(mMap);
   MOZ_RELEASE_ASSERT(result.isOk());
+  MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
   mMap.setPersistent();
 }
 
 mozilla::ipc::FileDescriptor SharedStringMap::CloneFileDescriptor() const {
   return mMap.cloneHandle();
 }
 
 bool SharedStringMap::Has(const nsCString& aKey) {
@@ -87,17 +91,17 @@ Result<Ok, nsresult> SharedStringMapBuil
   MOZ_ASSERT(mEntries.Count() == mKeyTable.Count());
 
   nsTArray<nsCString> keys(mEntries.Count());
   for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
     keys.AppendElement(iter.Key());
   }
   keys.Sort();
 
-  Header header = {uint32_t(keys.Length())};
+  Header header = {kSharedStringMapMagic, uint32_t(keys.Length())};
 
   size_t offset = sizeof(header);
   offset += GetAlignmentOffset(offset, alignof(Header));
 
   offset += keys.Length() * sizeof(SharedStringMap::Entry);
 
   header.mKeyStringsOffset = offset;
   header.mKeyStringsSize = mKeyTable.Size();
--- a/dom/ipc/SharedStringMap.h
+++ b/dom/ipc/SharedStringMap.h
@@ -54,16 +54,17 @@ class SharedStringMap {
    *
    * - Optional alignment padding for char16_t[]
    *
    * - StringTable<nsString>:
    *   A region of flat, null-terminated UTF-16 strings. Entry value strings are
    *   encoded as character (*not* byte) offsets into this region.
    */
   struct Header {
+    uint32_t mMagic;
     // The number of entries in this map.
     uint32_t mEntryCount;
 
     // The raw byte offset of the beginning of the key string table, from the
     // start of the shared memory region, and its size in bytes.
     size_t mKeyStringsOffset;
     size_t mKeyStringsSize;
 
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -11,17 +11,17 @@ namespace gmp {
 
 async refcounted protocol PChromiumCDM
 {
   manager PGMPContent;
 child:
 
   // cdm::ContentDecryptionModule9+10
   async Init(bool aAllowDistinctiveIdentifier,
-             bool aAllowPersistentState) returns (bool unused);
+             bool aAllowPersistentState) returns (bool aSuccess);
 
   async GetStatusForPolicy(uint32_t aPromiseId,
                            nsCString aMinHdcpVersion);
 
   async SetServerCertificate(uint32_t aPromiseId,
                              uint8_t[] aServerCert);
 
   async CreateSessionAndGenerateRequest(uint32_t aPromiseId,
--- a/dom/media/mediasource/test/mediasource.js
+++ b/dom/media/mediasource/test/mediasource.js
@@ -178,22 +178,22 @@ function fetchAndLoadAsync(sb, prefix, c
     for (const chunk of chunks) {
       rv = rv.then(loadSegmentAsync.bind(null, sb, buffers[chunk]));
     }
     return rv;
   });
 }
 
 // Register timeout function to dump debugging logs.
-SimpleTest.registerTimeoutFunction(function() {
+SimpleTest.registerTimeoutFunction(async function() {
   for (const v of document.getElementsByTagName("video")) {
-    v.mozDumpDebugInfo();
+    console.log(await SpecialPowers.wrap(v).mozRequestDebugInfo());
   }
   for (const a of document.getElementsByTagName("audio")) {
-    a.mozDumpDebugInfo();
+    console.log(await SpecialPowers.wrap(a).mozRequestDebugInfo());
   }
 });
 
 async function waitUntilTime(target, targetTime) {
   await new Promise(resolve => {
     target.addEventListener("waiting", function onwaiting() {
       info("Got a waiting event at " + target.currentTime);
       if (target.currentTime >= targetTime) {
--- a/dom/media/mediasource/test/test_Eviction_mp4.html
+++ b/dom/media/mediasource/test/test_Eviction_mp4.html
@@ -47,17 +47,17 @@ runWithMSE(async (ms, el) => {
   dump("dump: seeked to " + seekTime);
   is(el.currentTime, seekTime, "correctly seeked to " + seekTime);
   try {
     audiosb.appendBuffer(audioBuffer);
     await once(audiosb, "update");
     ok(true, "appendBuffer succeeded");
   } catch (ex) {
     ok(false, "Shouldn't throw another time when data can be evicted");
-    el.mozDumpDebugInfo();
+    dump(JSON.stringify(await SpecialPowers.wrap(el).mozRequestDebugInfo()));
   }
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/mediasource/test/test_ExperimentalAsync.html
+++ b/dom/media/mediasource/test/test_ExperimentalAsync.html
@@ -56,19 +56,19 @@ runWithMSE(async function(ms, el) {
     async function(ex1) { // onCaughtExceptionCallback
       is(ex1.name, "QuotaExceededError", "QuotaExceededError thrown");
       is(audiosb.buffered.end(0), el.duration, "Duration is end of buffered range");
       const seekTime = audiosb.buffered.end(0) / 2;
       el.currentTime = seekTime;
       await once(el, "seeked");
       dump("dump: seeked to " + seekTime);
       is(el.currentTime, seekTime, "correctly seeked to " + seekTime);
-      await audiosb.appendBufferAsync(audioBuffer).catch(function(ex2) {
+      await audiosb.appendBufferAsync(audioBuffer).catch(async function(ex2) {
         ok(false, "Shouldn't throw another time when data can be evicted");
-        el.mozDumpDebugInfo();
+        dump(JSON.stringify(await SpecialPowers.wrap(el).mozRequestDebugInfo()));
         SimpleTest.finish();
       });
       // Test that an error in remove return a rejected promise
       await audiosb.removeAsync(5, 0).catch(async function(ex3) {
         ok(true, "remove promise got rejected with end <= start");
         is(ex3.name, "TypeError");
         await audiosb.removeAsync(ms.duration + 1, Infinity).catch(async function(ex4) {
           ok(true, "remove promise got rejected with start > duration");
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -472,30 +472,32 @@ class RemoteAudioDecoder : public Remote
       return;
     }
 
     RenderOrReleaseOutput autoRelease(mJavaDecoder, aSample);
 
     BufferInfo::LocalRef info = aSample->Info();
     MOZ_ASSERT(info);
 
-    int32_t flags;
+    int32_t flags = 0;
     bool ok = NS_SUCCEEDED(info->Flags(&flags));
+    bool isEOS = !!(flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM);
 
     int32_t offset;
     ok &= NS_SUCCEEDED(info->Offset(&offset));
 
     int64_t presentationTimeUs;
     ok &= NS_SUCCEEDED(info->PresentationTimeUs(&presentationTimeUs));
 
     int32_t size;
     ok &= NS_SUCCEEDED(info->Size(&size));
 
     if (!ok ||
-        IsSampleTimeSmallerThanFirstDemuxedSampleTime(presentationTimeUs)) {
+        (IsSampleTimeSmallerThanFirstDemuxedSampleTime(presentationTimeUs) &&
+         !isEOS)) {
       Error(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
       return;
     }
 
     if (size > 0) {
 #ifdef MOZ_SAMPLE_TYPE_S16
       const int32_t numSamples = size / 2;
 #else
@@ -513,17 +515,17 @@ class RemoteAudioDecoder : public Remote
 
       RefPtr<AudioData> data =
           new AudioData(0, TimeUnit::FromMicroseconds(presentationTimeUs),
                         std::move(audio), mOutputChannels, mOutputSampleRate);
 
       UpdateOutputStatus(std::move(data));
     }
 
-    if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
+    if (isEOS) {
       DrainComplete();
     }
   }
 
   void ProcessOutputFormatChange(int32_t aChannels, int32_t aSampleRate) {
     if (!mTaskQueue->IsCurrentThreadIn()) {
       nsresult rv = mTaskQueue->Dispatch(NewRunnableMethod<int32_t, int32_t>(
           "RemoteAudioDecoder::ProcessOutputFormatChange", this,
--- a/dom/media/platforms/apple/AppleDecoderModule.cpp
+++ b/dom/media/platforms/apple/AppleDecoderModule.cpp
@@ -2,17 +2,16 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AppleATDecoder.h"
 #include "AppleDecoderModule.h"
 #include "AppleVTDecoder.h"
-#include "mozilla/gfx/MacIOSurface.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Logging.h"
 #include "mozilla/gfx/gfxVars.h"
 
 namespace mozilla {
 
 bool AppleDecoderModule::sInitialized = false;
 bool AppleDecoderModule::sCanUseHardwareVideoDecoder = true;
@@ -22,21 +21,17 @@ AppleDecoderModule::AppleDecoderModule()
 AppleDecoderModule::~AppleDecoderModule() {}
 
 /* static */
 void AppleDecoderModule::Init() {
   if (sInitialized) {
     return;
   }
 
-  // Ensure IOSurface framework is loaded.
-  MacIOSurfaceLib::LoadLibrary();
-
-  sCanUseHardwareVideoDecoder =
-      MacIOSurfaceLib::isInit() && gfx::gfxVars::CanUseHardwareVideoDecoding();
+  sCanUseHardwareVideoDecoder = gfx::gfxVars::CanUseHardwareVideoDecoding();
 
   sInitialized = true;
 }
 
 nsresult AppleDecoderModule::Startup() {
   if (!sInitialized) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -1,16 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AppleVTDecoder.h"
 
+#include <CoreVideo/CVPixelBufferIOSurface.h>
+#include <IOSurface/IOSurface.h>
+
 #include "AppleDecoderModule.h"
 #include "AppleUtils.h"
 #include "MacIOSurfaceImage.h"
 #include "MediaData.h"
 #include "mozilla/ArrayUtils.h"
 #include "H264.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
@@ -386,17 +389,17 @@ void AppleVTDecoder::OutputFrame(CVPixel
     data = VideoData::CreateAndCopyData(
         info, mImageContainer, aFrameRef.byte_offset,
         aFrameRef.composition_timestamp, aFrameRef.duration, buffer,
         aFrameRef.is_sync_point, aFrameRef.decode_timestamp, visible);
     // Unlock the returned image data.
     CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
   } else {
 #ifndef MOZ_WIDGET_UIKIT
-    IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
+    IOSurfacePtr surface = (IOSurfacePtr)CVPixelBufferGetIOSurface(aImage);
     MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
 
     RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
     macSurface->SetYUVColorSpace(mColorSpace);
 
     RefPtr<layers::Image> image = new layers::MacIOSurfaceImage(macSurface);
 
     data = VideoData::CreateFromImage(
@@ -552,17 +555,17 @@ CFDictionaryRef AppleVTDecoder::CreateOu
   // Output format type:
   SInt32 PixelFormatTypeValue =
       mColorRange == gfx::ColorRange::FULL
           ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
           : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
   AutoCFRelease<CFNumberRef> PixelFormatTypeNumber = CFNumberCreate(
       kCFAllocatorDefault, kCFNumberSInt32Type, &PixelFormatTypeValue);
   // Construct IOSurface Properties
-  const void* IOSurfaceKeys[] = {MacIOSurfaceLib::kPropIsGlobal};
+  const void* IOSurfaceKeys[] = {kIOSurfaceIsGlobal};
   const void* IOSurfaceValues[] = {kCFBooleanTrue};
   static_assert(ArrayLength(IOSurfaceKeys) == ArrayLength(IOSurfaceValues),
                 "Non matching keys/values array size");
 
   // Contruct output configuration.
   AutoCFRelease<CFDictionaryRef> IOSurfaceProperties = CFDictionaryCreate(
       kCFAllocatorDefault, IOSurfaceKeys, IOSurfaceValues,
       ArrayLength(IOSurfaceKeys), &kCFTypeDictionaryKeyCallBacks,
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -1852,36 +1852,35 @@ function mediaTestCleanup(callback) {
       A[i] = null;
     }
     SpecialPowers.exactGC(callback);
 }
 
 async function dumpDebugInfoForToken(token) {
   for (let v of document.getElementsByTagName("video")) {
     if (token === v.token) {
-      return v.mozDumpDebugInfo();
+      info(JSON.stringify(await SpecialPowers.wrap(v).mozRequestDebugInfo()));
+      return;
     }
   }
   for (let a of document.getElementsByTagName("audio")) {
     if (token === a.token) {
-      return a.mozDumpDebugInfo();
+      info(JSON.stringify(await SpecialPowers.wrap(a).mozRequestDebugInfo()));
+      return;
     }
   }
-  return Promise.resolve();
-}
-
-function dumpDebugInfo() {
-  for (var v of document.getElementsByTagName("video")) {
-    v.mozDumpDebugInfo();
-  }
-  for (var a of document.getElementsByTagName("audio")) {
-    a.mozDumpDebugInfo();
-  }
 }
 
 // Could be undefined in a page opened by the parent test page
 // like file_access_controls.html.
 if ("SimpleTest" in window) {
   SimpleTest.requestFlakyTimeout("untriaged");
 
   // Register timeout function to dump debugging logs.
-  SimpleTest.registerTimeoutFunction(dumpDebugInfo);
+  SimpleTest.registerTimeoutFunction(async function() {
+    for (const v of document.getElementsByTagName("video")) {
+      SimpleTest.info(JSON.stringify(await SpecialPowers.wrap(v).mozRequestDebugInfo()));
+    }
+    for (const a of document.getElementsByTagName("audio")) {
+      SimpleTest.info(JSON.stringify(await SpecialPowers.wrap(a).mozRequestDebugInfo()));
+    }
+  });
 }
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -1,288 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MacIOSurface.h"
+#include <IOSurface/IOSurface.h>
 #include <OpenGL/gl.h>
+#include <OpenGL/CGLIOSurface.h>
 #include <QuartzCore/QuartzCore.h>
-#include <dlfcn.h>
 #include "GLConsts.h"
 #include "GLContextCGL.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/RefPtr.h"
 
 using namespace mozilla;
-// IOSurface signatures
-#define IOSURFACE_FRAMEWORK_PATH \
-  "/System/Library/Frameworks/IOSurface.framework/IOSurface"
-#define OPENGL_FRAMEWORK_PATH \
-  "/System/Library/Frameworks/OpenGL.framework/OpenGL"
-#define COREGRAPHICS_FRAMEWORK_PATH                                      \
-  "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
-  "CoreGraphics.framework/CoreGraphics"
-#define COREVIDEO_FRAMEWORK_PATH                                         \
-  "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/" \
-  "CoreVideo.framework/CoreVideo"
-
-#define GET_CONST(const_name) \
-  ((CFStringRef*)dlsym(sIOSurfaceFramework, const_name))
-#define GET_IOSYM(dest, sym_name) \
-  (typeof(dest)) dlsym(sIOSurfaceFramework, sym_name)
-#define GET_CGLSYM(dest, sym_name) \
-  (typeof(dest)) dlsym(sOpenGLFramework, sym_name)
-#define GET_CGSYM(dest, sym_name) \
-  (typeof(dest)) dlsym(sCoreGraphicsFramework, sym_name)
-#define GET_CVSYM(dest, sym_name) \
-  (typeof(dest)) dlsym(sCoreVideoFramework, sym_name)
-
-MacIOSurfaceLib::LibraryUnloader MacIOSurfaceLib::sLibraryUnloader;
-bool MacIOSurfaceLib::isLoaded = false;
-void* MacIOSurfaceLib::sIOSurfaceFramework;
-void* MacIOSurfaceLib::sOpenGLFramework;
-void* MacIOSurfaceLib::sCoreGraphicsFramework;
-void* MacIOSurfaceLib::sCoreVideoFramework;
-IOSurfaceCreateFunc MacIOSurfaceLib::sCreate;
-IOSurfaceGetIDFunc MacIOSurfaceLib::sGetID;
-IOSurfaceLookupFunc MacIOSurfaceLib::sLookup;
-IOSurfaceGetBaseAddressFunc MacIOSurfaceLib::sGetBaseAddress;
-IOSurfaceGetBaseAddressOfPlaneFunc MacIOSurfaceLib::sGetBaseAddressOfPlane;
-IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sWidth;
-IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sHeight;
-IOSurfaceSizeTFunc MacIOSurfaceLib::sPlaneCount;
-IOSurfaceSizePlaneTFunc MacIOSurfaceLib::sBytesPerRow;
-IOSurfaceGetPropertyMaximumFunc MacIOSurfaceLib::sGetPropertyMaximum;
-IOSurfaceVoidFunc MacIOSurfaceLib::sIncrementUseCount;
-IOSurfaceVoidFunc MacIOSurfaceLib::sDecrementUseCount;
-IOSurfaceLockFunc MacIOSurfaceLib::sLock;
-IOSurfaceUnlockFunc MacIOSurfaceLib::sUnlock;
-CGLTexImageIOSurface2DFunc MacIOSurfaceLib::sTexImage;
-IOSurfaceContextCreateFunc MacIOSurfaceLib::sIOSurfaceContextCreate;
-IOSurfaceContextCreateImageFunc MacIOSurfaceLib::sIOSurfaceContextCreateImage;
-IOSurfaceContextGetSurfaceFunc MacIOSurfaceLib::sIOSurfaceContextGetSurface;
-CVPixelBufferGetIOSurfaceFunc MacIOSurfaceLib::sCVPixelBufferGetIOSurface;
-unsigned int (*MacIOSurfaceLib::sCGContextGetTypePtr)(CGContextRef) = nullptr;
-IOSurfacePixelFormatFunc MacIOSurfaceLib::sPixelFormat;
-
-CFStringRef MacIOSurfaceLib::kPropWidth;
-CFStringRef MacIOSurfaceLib::kPropHeight;
-CFStringRef MacIOSurfaceLib::kPropBytesPerElem;
-CFStringRef MacIOSurfaceLib::kPropBytesPerRow;
-CFStringRef MacIOSurfaceLib::kPropIsGlobal;
-
-bool MacIOSurfaceLib::isInit() {
-  // Guard against trying to reload the library
-  // if it is not available.
-  if (!isLoaded) LoadLibrary();
-  MOZ_ASSERT(sIOSurfaceFramework);
-  return sIOSurfaceFramework;
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceCreate(CFDictionaryRef properties) {
-  return sCreate(properties);
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceLookup(IOSurfaceID aIOSurfaceID) {
-  return sLookup(aIOSurfaceID);
-}
-
-IOSurfaceID MacIOSurfaceLib::IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr) {
-  return sGetID(aIOSurfacePtr);
-}
-
-void* MacIOSurfaceLib::IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr) {
-  return sGetBaseAddress(aIOSurfacePtr);
-}
-
-void* MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(
-    IOSurfacePtr aIOSurfacePtr, size_t planeIndex) {
-  return sGetBaseAddressOfPlane(aIOSurfacePtr, planeIndex);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr) {
-  return sPlaneCount(aIOSurfacePtr);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr,
-                                          size_t plane) {
-  return sWidth(aIOSurfacePtr, plane);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr,
-                                           size_t plane) {
-  return sHeight(aIOSurfacePtr, plane);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr,
-                                                size_t plane) {
-  return sBytesPerRow(aIOSurfacePtr, plane);
-}
-
-size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
-  return sGetPropertyMaximum(property);
-}
-
-OSType MacIOSurfaceLib::IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr) {
-  return sPixelFormat(aIOSurfacePtr);
-}
-
-IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr,
-                                        uint32_t options, uint32_t* seed) {
-  return sLock(aIOSurfacePtr, options, seed);
-}
-
-IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr,
-                                          uint32_t options, uint32_t* seed) {
-  return sUnlock(aIOSurfacePtr, options, seed);
-}
-
-void MacIOSurfaceLib::IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr) {
-  sIncrementUseCount(aIOSurfacePtr);
-}
-
-void MacIOSurfaceLib::IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr) {
-  sDecrementUseCount(aIOSurfacePtr);
-}
-
-CGLError MacIOSurfaceLib::CGLTexImageIOSurface2D(
-    CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width,
-    GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface,
-    GLuint plane) {
-  return sTexImage(ctxt, target, internalFormat, width, height, format, type,
-                   ioSurface, plane);
-}
-
-IOSurfacePtr MacIOSurfaceLib::CVPixelBufferGetIOSurface(
-    CVPixelBufferRef aPixelBuffer) {
-  return sCVPixelBufferGetIOSurface(aPixelBuffer);
-}
-
-CGContextRef MacIOSurfaceLib::IOSurfaceContextCreate(
-    IOSurfacePtr aIOSurfacePtr, unsigned aWidth, unsigned aHeight,
-    unsigned aBitsPerComponent, unsigned aBytes, CGColorSpaceRef aColorSpace,
-    CGBitmapInfo bitmapInfo) {
-  if (!sIOSurfaceContextCreate) return nullptr;
-  return sIOSurfaceContextCreate(aIOSurfacePtr, aWidth, aHeight,
-                                 aBitsPerComponent, aBytes, aColorSpace,
-                                 bitmapInfo);
-}
-
-CGImageRef MacIOSurfaceLib::IOSurfaceContextCreateImage(CGContextRef aContext) {
-  if (!sIOSurfaceContextCreateImage) return nullptr;
-  return sIOSurfaceContextCreateImage(aContext);
-}
-
-IOSurfacePtr MacIOSurfaceLib::IOSurfaceContextGetSurface(
-    CGContextRef aContext) {
-  if (!sIOSurfaceContextGetSurface) return nullptr;
-  return sIOSurfaceContextGetSurface(aContext);
-}
-
-CFStringRef MacIOSurfaceLib::GetIOConst(const char* symbole) {
-  CFStringRef* address = (CFStringRef*)dlsym(sIOSurfaceFramework, symbole);
-  if (!address) return nullptr;
-
-  return *address;
-}
-
-void MacIOSurfaceLib::LoadLibrary() {
-  if (isLoaded) {
-    return;
-  }
-  isLoaded = true;
-  sIOSurfaceFramework =
-      dlopen(IOSURFACE_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
-  sOpenGLFramework = dlopen(OPENGL_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
-
-  sCoreGraphicsFramework =
-      dlopen(COREGRAPHICS_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
-
-  sCoreVideoFramework =
-      dlopen(COREVIDEO_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
-
-  if (!sIOSurfaceFramework || !sOpenGLFramework || !sCoreGraphicsFramework ||
-      !sCoreVideoFramework) {
-    if (sIOSurfaceFramework) dlclose(sIOSurfaceFramework);
-    if (sOpenGLFramework) dlclose(sOpenGLFramework);
-    if (sCoreGraphicsFramework) dlclose(sCoreGraphicsFramework);
-    if (sCoreVideoFramework) dlclose(sCoreVideoFramework);
-    sIOSurfaceFramework = nullptr;
-    sOpenGLFramework = nullptr;
-    sCoreGraphicsFramework = nullptr;
-    sCoreVideoFramework = nullptr;
-    return;
-  }
-
-  kPropWidth = GetIOConst("kIOSurfaceWidth");
-  kPropHeight = GetIOConst("kIOSurfaceHeight");
-  kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
-  kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
-  kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
-  sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
-  sGetID = GET_IOSYM(sGetID, "IOSurfaceGetID");
-  sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidthOfPlane");
-  sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeightOfPlane");
-  sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRowOfPlane");
-  sGetPropertyMaximum =
-      GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
-  sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
-  sLock = GET_IOSYM(sLock, "IOSurfaceLock");
-  sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
-  sIncrementUseCount =
-      GET_IOSYM(sIncrementUseCount, "IOSurfaceIncrementUseCount");
-  sDecrementUseCount =
-      GET_IOSYM(sDecrementUseCount, "IOSurfaceDecrementUseCount");
-  sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
-  sGetBaseAddressOfPlane =
-      GET_IOSYM(sGetBaseAddressOfPlane, "IOSurfaceGetBaseAddressOfPlane");
-  sPlaneCount = GET_IOSYM(sPlaneCount, "IOSurfaceGetPlaneCount");
-  sPixelFormat = GET_IOSYM(sPixelFormat, "IOSurfaceGetPixelFormat");
-
-  sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
-  sCGContextGetTypePtr =
-      (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
-
-  sCVPixelBufferGetIOSurface =
-      GET_CVSYM(sCVPixelBufferGetIOSurface, "CVPixelBufferGetIOSurface");
-
-  // Optional symbols
-  sIOSurfaceContextCreate =
-      GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
-  sIOSurfaceContextCreateImage =
-      GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
-  sIOSurfaceContextGetSurface =
-      GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
-
-  if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
-      !sGetBaseAddressOfPlane || !sPlaneCount || !kPropWidth || !kPropHeight ||
-      !kPropBytesPerElem || !kPropIsGlobal || !sLock || !sUnlock ||
-      !sIncrementUseCount || !sDecrementUseCount || !sWidth || !sHeight ||
-      !kPropBytesPerRow || !sBytesPerRow || !sGetPropertyMaximum ||
-      !sCVPixelBufferGetIOSurface) {
-    CloseLibrary();
-  }
-}
-
-void MacIOSurfaceLib::CloseLibrary() {
-  if (sIOSurfaceFramework) {
-    dlclose(sIOSurfaceFramework);
-  }
-  if (sOpenGLFramework) {
-    dlclose(sOpenGLFramework);
-  }
-  if (sCoreVideoFramework) {
-    dlclose(sCoreVideoFramework);
-  }
-  sIOSurfaceFramework = nullptr;
-  sOpenGLFramework = nullptr;
-  sCoreVideoFramework = nullptr;
-}
 
 MacIOSurface::MacIOSurface(IOSurfacePtr aIOSurfacePtr,
                            double aContentsScaleFactor, bool aHasAlpha,
                            gfx::YUVColorSpace aColorSpace)
     : mIOSurfacePtr(aIOSurfacePtr),
       mContentsScaleFactor(aContentsScaleFactor),
       mHasAlpha(aHasAlpha),
       mColorSpace(aColorSpace) {
@@ -293,17 +30,17 @@ MacIOSurface::MacIOSurface(IOSurfacePtr 
 MacIOSurface::~MacIOSurface() {
   DecrementUseCount();
   CFRelease(mIOSurfacePtr);
 }
 
 /* static */
 already_AddRefed<MacIOSurface> MacIOSurface::CreateIOSurface(
     int aWidth, int aHeight, double aContentsScaleFactor, bool aHasAlpha) {
-  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
+  if (aContentsScaleFactor <= 0) return nullptr;
 
   CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
       kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks,
       &kCFTypeDictionaryValueCallBacks);
   if (!props) return nullptr;
 
   MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
   MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
@@ -312,26 +49,25 @@ already_AddRefed<MacIOSurface> MacIOSurf
   size_t intScaleFactor = ceil(aContentsScaleFactor);
   aWidth *= intScaleFactor;
   aHeight *= intScaleFactor;
   CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
   CFNumberRef cfHeight =
       ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
   CFNumberRef cfBytesPerElem =
       ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
-  ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth, cfWidth);
+  ::CFDictionaryAddValue(props, kIOSurfaceWidth, cfWidth);
   ::CFRelease(cfWidth);
-  ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropHeight, cfHeight);
+  ::CFDictionaryAddValue(props, kIOSurfaceHeight, cfHeight);
   ::CFRelease(cfHeight);
-  ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropBytesPerElem,
-                         cfBytesPerElem);
+  ::CFDictionaryAddValue(props, kIOSurfaceBytesPerElement, cfBytesPerElem);
   ::CFRelease(cfBytesPerElem);
-  ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropIsGlobal, kCFBooleanTrue);
+  ::CFDictionaryAddValue(props, kIOSurfaceIsGlobal, kCFBooleanTrue);
 
-  IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceCreate(props);
+  IOSurfacePtr surfaceRef = ::IOSurfaceCreate(props);
   ::CFRelease(props);
 
   if (!surfaceRef) return nullptr;
 
   RefPtr<MacIOSurface> ioSurface =
       new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
 
   // Release the IOSurface because MacIOSurface retained it
@@ -339,104 +75,99 @@ already_AddRefed<MacIOSurface> MacIOSurf
 
   return ioSurface.forget();
 }
 
 /* static */
 already_AddRefed<MacIOSurface> MacIOSurface::LookupSurface(
     IOSurfaceID aIOSurfaceID, double aContentsScaleFactor, bool aHasAlpha,
     gfx::YUVColorSpace aColorSpace) {
-  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
+  if (aContentsScaleFactor <= 0) return nullptr;
 
-  IOSurfacePtr surfaceRef = MacIOSurfaceLib::IOSurfaceLookup(aIOSurfaceID);
+  IOSurfacePtr surfaceRef = (IOSurfacePtr)::IOSurfaceLookup(aIOSurfaceID);
   if (!surfaceRef) return nullptr;
 
   RefPtr<MacIOSurface> ioSurface = new MacIOSurface(
       surfaceRef, aContentsScaleFactor, aHasAlpha, aColorSpace);
 
   // Release the IOSurface because MacIOSurface retained it
   CFRelease(surfaceRef);
 
   return ioSurface.forget();
 }
 
 IOSurfaceID MacIOSurface::GetIOSurfaceID() const {
-  return MacIOSurfaceLib::IOSurfaceGetID(mIOSurfacePtr);
+  return ::IOSurfaceGetID((IOSurfaceRef)mIOSurfacePtr);
 }
 
 void* MacIOSurface::GetBaseAddress() const {
-  return MacIOSurfaceLib::IOSurfaceGetBaseAddress(mIOSurfacePtr);
+  return ::IOSurfaceGetBaseAddress((IOSurfaceRef)mIOSurfacePtr);
 }
 
 void* MacIOSurface::GetBaseAddressOfPlane(size_t aPlaneIndex) const {
-  return MacIOSurfaceLib::IOSurfaceGetBaseAddressOfPlane(mIOSurfacePtr,
-                                                         aPlaneIndex);
+  return ::IOSurfaceGetBaseAddressOfPlane((IOSurfaceRef)mIOSurfacePtr,
+                                          aPlaneIndex);
 }
 
 size_t MacIOSurface::GetWidth(size_t plane) const {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
   return GetDevicePixelWidth(plane) / intScaleFactor;
 }
 
 size_t MacIOSurface::GetHeight(size_t plane) const {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
   return GetDevicePixelHeight(plane) / intScaleFactor;
 }
 
 size_t MacIOSurface::GetPlaneCount() const {
-  return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
+  return ::IOSurfaceGetPlaneCount((IOSurfaceRef)mIOSurfacePtr);
 }
 
 /*static*/
 size_t MacIOSurface::GetMaxWidth() {
-  if (!MacIOSurfaceLib::isInit()) return -1;
-  return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(
-      MacIOSurfaceLib::kPropWidth);
+  return ::IOSurfaceGetPropertyMaximum(kIOSurfaceWidth);
 }
 
 /*static*/
 size_t MacIOSurface::GetMaxHeight() {
-  if (!MacIOSurfaceLib::isInit()) return -1;
-  return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(
-      MacIOSurfaceLib::kPropHeight);
+  return ::IOSurfaceGetPropertyMaximum(kIOSurfaceHeight);
 }
 
 size_t MacIOSurface::GetDevicePixelWidth(size_t plane) const {
-  return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr, plane);
+  return ::IOSurfaceGetWidthOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
 }
 
 size_t MacIOSurface::GetDevicePixelHeight(size_t plane) const {
-  return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr, plane);
+  return ::IOSurfaceGetHeightOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
 }
 
 size_t MacIOSurface::GetBytesPerRow(size_t plane) const {
-  return MacIOSurfaceLib::IOSurfaceGetBytesPerRow(mIOSurfacePtr, plane);
+  return ::IOSurfaceGetBytesPerRowOfPlane((IOSurfaceRef)mIOSurfacePtr, plane);
 }
 
 OSType MacIOSurface::GetPixelFormat() const {
-  return MacIOSurfaceLib::IOSurfaceGetPixelFormat(mIOSurfacePtr);
+  return ::IOSurfaceGetPixelFormat((IOSurfaceRef)mIOSurfacePtr);
 }
 
 void MacIOSurface::IncrementUseCount() {
-  MacIOSurfaceLib::IOSurfaceIncrementUseCount(mIOSurfacePtr);
+  ::IOSurfaceIncrementUseCount((IOSurfaceRef)mIOSurfacePtr);
 }
 
 void MacIOSurface::DecrementUseCount() {
-  MacIOSurfaceLib::IOSurfaceDecrementUseCount(mIOSurfacePtr);
+  ::IOSurfaceDecrementUseCount((IOSurfaceRef)mIOSurfacePtr);
 }
 
-#define READ_ONLY 0x1
 void MacIOSurface::Lock(bool aReadOnly) {
-  MacIOSurfaceLib::IOSurfaceLock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0,
-                                 nullptr);
+  ::IOSurfaceLock((IOSurfaceRef)mIOSurfacePtr,
+                  aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr);
 }
 
 void MacIOSurface::Unlock(bool aReadOnly) {
-  MacIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, aReadOnly ? READ_ONLY : 0,
-                                   nullptr);
+  ::IOSurfaceUnlock((IOSurfaceRef)mIOSurfacePtr,
+                    aReadOnly ? kIOSurfaceLockReadOnly : 0, nullptr);
 }
 
 using mozilla::gfx::IntSize;
 using mozilla::gfx::SourceSurface;
 using mozilla::gfx::SurfaceFormat;
 
 static void MacIOSurfaceBufferDeallocator(void* aClosure) {
   MOZ_ASSERT(aClosure);
@@ -494,19 +225,19 @@ SurfaceFormat MacIOSurface::GetReadForma
   }
 }
 
 CGLError MacIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctx, GLenum target,
                                               GLenum internalFormat,
                                               GLsizei width, GLsizei height,
                                               GLenum format, GLenum type,
                                               GLuint plane) const {
-  return MacIOSurfaceLib::CGLTexImageIOSurface2D(ctx, target, internalFormat,
-                                                 width, height, format, type,
-                                                 mIOSurfacePtr, plane);
+  return ::CGLTexImageIOSurface2D(ctx, target, internalFormat, width, height,
+                                  format, type, (IOSurfaceRef)mIOSurfacePtr,
+                                  plane);
 }
 
 CGLError MacIOSurface::CGLTexImageIOSurface2D(
     mozilla::gl::GLContext* aGL, CGLContextObj ctx, size_t plane,
     mozilla::gfx::SurfaceFormat* aOutReadFormat) {
   MOZ_ASSERT(plane >= 0);
   bool isCompatibilityProfile = aGL->IsCompatibilityProfile();
   OSType pixelFormat = GetPixelFormat();
@@ -569,59 +300,8 @@ CGLError MacIOSurface::CGLTexImageIOSurf
     }
   }
 
   return CGLTexImageIOSurface2D(ctx, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
                                 internalFormat, GetDevicePixelWidth(plane),
                                 GetDevicePixelHeight(plane), format, type,
                                 plane);
 }
-
-static CGColorSpaceRef CreateSystemColorSpace() {
-  CGColorSpaceRef cspace = ::CGDisplayCopyColorSpace(::CGMainDisplayID());
-  if (!cspace) {
-    cspace = ::CGColorSpaceCreateDeviceRGB();
-  }
-  return cspace;
-}
-
-CGContextRef MacIOSurface::CreateIOSurfaceContext() {
-  CGColorSpaceRef cspace = CreateSystemColorSpace();
-  CGContextRef ref = MacIOSurfaceLib::IOSurfaceContextCreate(
-      mIOSurfacePtr, GetDevicePixelWidth(), GetDevicePixelHeight(), 8, 32,
-      cspace, 0x2002);
-  ::CGColorSpaceRelease(cspace);
-  return ref;
-}
-
-CGImageRef MacIOSurface::CreateImageFromIOSurfaceContext(
-    CGContextRef aContext) {
-  if (!MacIOSurfaceLib::isInit()) return nullptr;
-
-  return MacIOSurfaceLib::IOSurfaceContextCreateImage(aContext);
-}
-
-already_AddRefed<MacIOSurface> MacIOSurface::IOSurfaceContextGetSurface(
-    CGContextRef aContext, double aContentsScaleFactor, bool aHasAlpha) {
-  if (!MacIOSurfaceLib::isInit() || aContentsScaleFactor <= 0) return nullptr;
-
-  IOSurfacePtr surfaceRef =
-      MacIOSurfaceLib::IOSurfaceContextGetSurface(aContext);
-  if (!surfaceRef) return nullptr;
-
-  RefPtr<MacIOSurface> ioSurface =
-      new MacIOSurface(surfaceRef, aContentsScaleFactor, aHasAlpha);
-  return ioSurface.forget();
-}
-
-CGContextType GetContextType(CGContextRef ref) {
-  if (!MacIOSurfaceLib::isInit() || !MacIOSurfaceLib::sCGContextGetTypePtr)
-    return CG_CONTEXT_TYPE_UNKNOWN;
-
-  unsigned int type = MacIOSurfaceLib::sCGContextGetTypePtr(ref);
-  if (type == CG_CONTEXT_TYPE_BITMAP) {
-    return CG_CONTEXT_TYPE_BITMAP;
-  } else if (type == CG_CONTEXT_TYPE_IOSURFACE) {
-    return CG_CONTEXT_TYPE_IOSURFACE;
-  } else {
-    return CG_CONTEXT_TYPE_UNKNOWN;
-  }
-}
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -17,76 +17,35 @@ namespace mozilla {
 namespace gl {
 class GLContext;
 }
 }  // namespace mozilla
 
 struct _CGLContextObject;
 
 typedef _CGLContextObject* CGLContextObj;
-typedef struct CGContext* CGContextRef;
-typedef struct CGImage* CGImageRef;
 typedef uint32_t IOSurfaceID;
 
 #  ifdef XP_IOS
 typedef kern_return_t IOReturn;
 typedef int CGLError;
 #  endif
 
 typedef CFTypeRef IOSurfacePtr;
-typedef IOSurfacePtr (*IOSurfaceCreateFunc)(CFDictionaryRef properties);
-typedef IOSurfacePtr (*IOSurfaceLookupFunc)(uint32_t io_surface_id);
-typedef IOSurfaceID (*IOSurfaceGetIDFunc)(IOSurfacePtr io_surface);
-typedef void (*IOSurfaceVoidFunc)(IOSurfacePtr io_surface);
-typedef IOReturn (*IOSurfaceLockFunc)(IOSurfacePtr io_surface, uint32_t options,
-                                      uint32_t* seed);
-typedef IOReturn (*IOSurfaceUnlockFunc)(IOSurfacePtr io_surface,
-                                        uint32_t options, uint32_t* seed);
-typedef void* (*IOSurfaceGetBaseAddressFunc)(IOSurfacePtr io_surface);
-typedef void* (*IOSurfaceGetBaseAddressOfPlaneFunc)(IOSurfacePtr io_surface,
-                                                    size_t planeIndex);
-typedef size_t (*IOSurfaceSizeTFunc)(IOSurfacePtr io_surface);
-typedef size_t (*IOSurfaceSizePlaneTFunc)(IOSurfacePtr io_surface,
-                                          size_t plane);
-typedef size_t (*IOSurfaceGetPropertyMaximumFunc)(CFStringRef property);
-typedef CGLError (*CGLTexImageIOSurface2DFunc)(
-    CGLContextObj ctxt, GLenum target, GLenum internalFormat, GLsizei width,
-    GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface,
-    GLuint plane);
-typedef CGContextRef (*IOSurfaceContextCreateFunc)(
-    CFTypeRef io_surface, unsigned width, unsigned height,
-    unsigned bitsPerComponent, unsigned bytes, CGColorSpaceRef colorSpace,
-    CGBitmapInfo bitmapInfo);
-typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
-typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
-
-typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
-    CVPixelBufferRef pixelBuffer);
-
-typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
 
 #  ifdef XP_MACOSX
 #    import <OpenGL/OpenGL.h>
 #  else
 #    import <OpenGLES/ES2/gl.h>
 #  endif
 
 #  include "2D.h"
 #  include "mozilla/RefCounted.h"
 #  include "mozilla/RefPtr.h"
 
-enum CGContextType {
-  CG_CONTEXT_TYPE_UNKNOWN = 0,
-  // These are found by inspection, it's possible they could be changed
-  CG_CONTEXT_TYPE_BITMAP = 4,
-  CG_CONTEXT_TYPE_IOSURFACE = 8
-};
-
-CGContextType GetContextType(CGContextRef ref);
-
 class MacIOSurface final
     : public mozilla::external::AtomicRefCounted<MacIOSurface> {
  public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(MacIOSurface)
   typedef mozilla::gfx::SourceSurface SourceSurface;
 
   // The usage count of the IOSurface is increased by 1 during the lifetime
   // of the MacIOSurface instance.
@@ -146,110 +105,24 @@ class MacIOSurface final
   CGLError CGLTexImageIOSurface2D(
       mozilla::gl::GLContext* aGL, CGLContextObj ctxt, size_t plane,
       mozilla::gfx::SurfaceFormat* aOutReadFormat = nullptr);
   CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum target,
                                   GLenum internalFormat, GLsizei width,
                                   GLsizei height, GLenum format, GLenum type,
                                   GLuint plane) const;
   already_AddRefed<SourceSurface> GetAsSurface();
-  CGContextRef CreateIOSurfaceContext();
 
-  // FIXME This doesn't really belong here
-  static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
-  static already_AddRefed<MacIOSurface> IOSurfaceContextGetSurface(
-      CGContextRef aContext, double aContentsScaleFactor = 1.0,
-      bool aHasAlpha = true);
   static size_t GetMaxWidth();
   static size_t GetMaxHeight();
   const void* GetIOSurfacePtr() { return mIOSurfacePtr; }
 
  private:
   friend class nsCARenderer;
   const IOSurfacePtr mIOSurfacePtr;
   double mContentsScaleFactor;
   bool mHasAlpha;
   mozilla::gfx::YUVColorSpace mColorSpace =
       mozilla::gfx::YUVColorSpace::UNKNOWN;
 };
 
-class MacIOSurfaceLib {
- public:
-  MacIOSurfaceLib() = delete;
-  static void* sIOSurfaceFramework;
-  static void* sOpenGLFramework;
-  static void* sCoreGraphicsFramework;
-  static void* sCoreVideoFramework;
-  static bool isLoaded;
-  static IOSurfaceCreateFunc sCreate;
-  static IOSurfaceGetIDFunc sGetID;
-  static IOSurfaceLookupFunc sLookup;
-  static IOSurfaceGetBaseAddressFunc sGetBaseAddress;
-  static IOSurfaceGetBaseAddressOfPlaneFunc sGetBaseAddressOfPlane;
-  static IOSurfaceSizeTFunc sPlaneCount;
-  static IOSurfaceLockFunc sLock;
-  static IOSurfaceUnlockFunc sUnlock;
-  static IOSurfaceVoidFunc sIncrementUseCount;
-  static IOSurfaceVoidFunc sDecrementUseCount;
-  static IOSurfaceSizePlaneTFunc sWidth;
-  static IOSurfaceSizePlaneTFunc sHeight;
-  static IOSurfaceSizePlaneTFunc sBytesPerRow;
-  static IOSurfaceGetPropertyMaximumFunc sGetPropertyMaximum;
-  static CGLTexImageIOSurface2DFunc sTexImage;
-  static IOSurfaceContextCreateFunc sIOSurfaceContextCreate;
-  static IOSurfaceContextCreateImageFunc sIOSurfaceContextCreateImage;
-  static IOSurfaceContextGetSurfaceFunc sIOSurfaceContextGetSurface;
-  static CVPixelBufferGetIOSurfaceFunc sCVPixelBufferGetIOSurface;
-  static IOSurfacePixelFormatFunc sPixelFormat;
-  static CFStringRef kPropWidth;
-  static CFStringRef kPropHeight;
-  static CFStringRef kPropBytesPerElem;
-  static CFStringRef kPropBytesPerRow;
-  static CFStringRef kPropIsGlobal;
-
-  static bool isInit();
-  static CFStringRef GetIOConst(const char* symbole);
-  static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
-  static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
-  static IOSurfaceID IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
-  static void* IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
-  static void* IOSurfaceGetBaseAddressOfPlane(IOSurfacePtr aIOSurfacePtr,
-                                              size_t aPlaneIndex);
-  static size_t IOSurfaceGetPlaneCount(IOSurfacePtr aIOSurfacePtr);
-  static size_t IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr, size_t plane);
-  static size_t IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr, size_t plane);
-  static size_t IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr,
-                                        size_t plane);
-  static size_t IOSurfaceGetPropertyMaximum(CFStringRef property);
-  static IOReturn IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, uint32_t options,
-                                uint32_t* seed);
-  static IOReturn IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, uint32_t options,
-                                  uint32_t* seed);
-  static void IOSurfaceIncrementUseCount(IOSurfacePtr aIOSurfacePtr);
-  static void IOSurfaceDecrementUseCount(IOSurfacePtr aIOSurfacePtr);
-  static CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, GLenum target,
-                                         GLenum internalFormat, GLsizei width,
-                                         GLsizei height, GLenum format,
-                                         GLenum type, IOSurfacePtr ioSurface,
-                                         GLuint plane);
-  static CGContextRef IOSurfaceContextCreate(IOSurfacePtr aIOSurfacePtr,
-                                             unsigned aWidth, unsigned aHeight,
-                                             unsigned aBitsPerCompoent,
-                                             unsigned aBytes,
-                                             CGColorSpaceRef aColorSpace,
-                                             CGBitmapInfo bitmapInfo);
-  static CGImageRef IOSurfaceContextCreateImage(CGContextRef ref);
-  static IOSurfacePtr IOSurfaceContextGetSurface(CGContextRef ref);
-  static IOSurfacePtr CVPixelBufferGetIOSurface(CVPixelBufferRef apixelBuffer);
-  static OSType IOSurfaceGetPixelFormat(IOSurfacePtr aIOSurfacePtr);
-  static unsigned int (*sCGContextGetTypePtr)(CGContextRef);
-  static void LoadLibrary();
-  static void CloseLibrary();
-
-  // Static deconstructor
-  static class LibraryUnloader {
-   public:
-    ~LibraryUnloader() { CloseLibrary(); }
-  } sLibraryUnloader;
-};
-
 #endif
 #endif
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -6,16 +6,17 @@
 
 #include "QuartzSupport.h"
 #include "nsDebug.h"
 #include "MacIOSurface.h"
 #include "mozilla/Sprintf.h"
 
 #import <QuartzCore/QuartzCore.h>
 #import <AppKit/NSOpenGL.h>
+#import <OpenGL/CGLIOSurface.h>
 #include <dlfcn.h>
 #include "GLDefs.h"
 
 #define IOSURFACE_FRAMEWORK_PATH "/System/Library/Frameworks/IOSurface.framework/IOSurface"
 #define OPENGL_FRAMEWORK_PATH "/System/Library/Frameworks/OpenGL.framework/OpenGL"
 #define COREGRAPHICS_FRAMEWORK_PATH                                                             \
   "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/" \
   "CoreGraphics"
@@ -195,20 +196,20 @@ nsresult nsCARenderer::SetupRenderer(voi
   ::CGLSetCurrentContext(mOpenGLContext);
 
   if (mIOSurface) {
     // Create the IOSurface mapped texture.
     ::glGenTextures(1, &mIOTexture);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    MacIOSurfaceLib::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
-                                            aWidth * intScaleFactor, aHeight * intScaleFactor,
-                                            GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
-                                            mIOSurface->mIOSurfacePtr, 0);
+    ::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
+                             aWidth * intScaleFactor, aHeight * intScaleFactor, GL_BGRA,
+                             GL_UNSIGNED_INT_8_8_8_8_REV, (IOSurfaceRef)mIOSurface->mIOSurfacePtr,
+                             0);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
   } else {
     ::glGenTextures(1, &mFBOTexture);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mFBOTexture);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     ::glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
   }
@@ -327,20 +328,20 @@ void nsCARenderer::AttachIOSurface(MacIO
     CARenderer* caRenderer = (CARenderer*)mCARenderer;
     size_t intScaleFactor = ceil(mContentsScaleFactor);
     int width = caRenderer.bounds.size.width / intScaleFactor;
     int height = caRenderer.bounds.size.height / intScaleFactor;
 
     CGLContextObj oldContext = ::CGLGetCurrentContext();
     ::CGLSetCurrentContext(mOpenGLContext);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, mIOTexture);
-    MacIOSurfaceLib::CGLTexImageIOSurface2D(
-        mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, mIOSurface->GetDevicePixelWidth(),
-        mIOSurface->GetDevicePixelHeight(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
-        mIOSurface->mIOSurfacePtr, 0);
+    ::CGLTexImageIOSurface2D(mOpenGLContext, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA,
+                             mIOSurface->GetDevicePixelWidth(), mIOSurface->GetDevicePixelHeight(),
+                             GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+                             (IOSurfaceRef)mIOSurface->mIOSurfacePtr, 0);
     ::glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
 
     // Rebind the FBO to make it live
     ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFBO);
 
     if (static_cast<int>(mIOSurface->GetWidth()) != width ||
         static_cast<int>(mIOSurface->GetHeight()) != height) {
       width = mIOSurface->GetWidth();
--- a/gfx/ipc/SharedDIB.cpp
+++ b/gfx/ipc/SharedDIB.cpp
@@ -17,21 +17,17 @@ nsresult SharedDIB::Create(uint32_t aSiz
   Close();
 
   mShMem = new base::SharedMemory();
   if (!mShMem || !mShMem->Create(aSize)) return NS_ERROR_OUT_OF_MEMORY;
 
   return NS_OK;
 }
 
-bool SharedDIB::IsValid() {
-  if (!mShMem) return false;
-
-  return base::SharedMemory::IsHandleValid(mShMem->handle());
-}
+bool SharedDIB::IsValid() { return mShMem && mShMem->IsValid(); }
 
 nsresult SharedDIB::Close() {
   delete mShMem;
 
   mShMem = nullptr;
 
   return NS_OK;
 }
--- a/gfx/ipc/SharedDIBWin.cpp
+++ b/gfx/ipc/SharedDIBWin.cpp
@@ -95,18 +95,19 @@ uint32_t SharedDIBWin::SetupBitmapHeader
           (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel));
 }
 
 nsresult SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER* aHdr) {
   mSharedHdc = ::CreateCompatibleDC(aHdc);
 
   if (!mSharedHdc) return NS_ERROR_FAILURE;
 
-  mSharedBmp = ::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
-                                  &mBitmapBits, mShMem->handle(), kHeaderBytes);
+  mSharedBmp =
+      ::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
+                         &mBitmapBits, mShMem->GetHandle(), kHeaderBytes);
   if (!mSharedBmp) return NS_ERROR_FAILURE;
 
   mOldObj = SelectObject(mSharedHdc, mSharedBmp);
 
   return NS_OK;
 }
 
 }  // namespace gfx
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -34,17 +34,16 @@
 #include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/Unused.h"
 
 #include "base/eintr_wrapper.h"
-#include "base/file_util.h"
 
 #include <locale.h>
 
 using namespace mozilla;
 using mozilla::intl::Locale;
 using mozilla::intl::LocaleService;
 using mozilla::intl::OSPreferences;
 
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -2,17 +2,16 @@
  * 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 "gfxPlatformMac.h"
 
 #include "gfxQuartzSurface.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/gfx/MacIOSurface.h"
 
 #include "gfxMacPlatformFontList.h"
 #include "gfxMacFont.h"
 #include "gfxCoreTextShaper.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 
 #include "nsTArray.h"
@@ -71,18 +70,16 @@ static void DisableFontActivation() {
 }
 
 gfxPlatformMac::gfxPlatformMac() {
   DisableFontActivation();
   mFontAntiAliasingThreshold = ReadAntiAliasingThreshold();
 
   InitBackendPrefs(GetBackendPrefs());
 
-  MacIOSurfaceLib::LoadLibrary();
-
   if (nsCocoaFeatures::OnHighSierraOrLater()) {
     mHasNativeColrFontSupport = true;
   }
 }
 
 gfxPlatformMac::~gfxPlatformMac() { gfxCoreTextShaper::Shutdown(); }
 
 BackendPrefsData gfxPlatformMac::GetBackendPrefs() const {
--- a/gfx/wr/webrender/res/cs_blur.glsl
+++ b/gfx/wr/webrender/res/cs_blur.glsl
@@ -20,24 +20,26 @@ flat varying int vSupport;
 
 in int aBlurRenderTaskAddress;
 in int aBlurSourceTaskAddress;
 in int aBlurDirection;
 
 struct BlurTask {
     RenderTaskCommonData common_data;
     float blur_radius;
+    vec2 blur_region;
 };
 
 BlurTask fetch_blur_task(int address) {
     RenderTaskData task_data = fetch_render_task_data(address);
 
     BlurTask task = BlurTask(
         task_data.common_data,
-        task_data.user_data.x
+        task_data.user_data.x,
+        task_data.user_data.yz
     );
 
     return task;
 }
 
 void main(void) {
     BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress);
     RenderTaskCommonData src_task = fetch_render_task_common_data(aBlurSourceTaskAddress);
@@ -67,17 +69,17 @@ void main(void) {
         case DIR_VERTICAL:
             vOffsetScale = vec2(0.0, 1.0 / texture_size.y);
             break;
         default:
             vOffsetScale = vec2(0.0);
     }
 
     vUvRect = vec4(src_rect.p0 + vec2(0.5),
-                   src_rect.p0 + src_rect.size - vec2(0.5));
+                   src_rect.p0 + blur_task.blur_region - vec2(0.5));
     vUvRect /= texture_size.xyxy;
 
     vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy;
 
     vec2 uv0 = src_rect.p0 / texture_size;
     vec2 uv1 = (src_rect.p0 + src_rect.size) / texture_size;
     vUv.xy = mix(uv0, uv1, aPosition.xy);
 
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -2558,17 +2558,17 @@ impl PicturePrimitive {
                 let dep_info = match raster_config.composite_mode {
                     PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
                         let blur_std_deviation = blur_radius * device_pixel_scale.0;
                         let scale_factors = scale_factors(&transform);
                         let blur_std_deviation = DeviceSize::new(
                             blur_std_deviation * scale_factors.0,
                             blur_std_deviation * scale_factors.1
                         );
-                        let device_rect = if self.options.inflate_if_required {
+                        let mut device_rect = if self.options.inflate_if_required {
                             let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor;
                             let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil();
 
                             // The clipped field is the part of the picture that is visible
                             // on screen. The unclipped field is the screen-space rect of
                             // the complete picture, if no screen / clip-chain was applied
                             // (this includes the extra space for blur region). To ensure
                             // that we draw a large enough part of the picture to get correct
@@ -2577,36 +2577,36 @@ impl PicturePrimitive {
                             // allocation size.
                             // We cast clipped to f32 instead of casting unclipped to i32
                             // because unclipped can overflow an i32.
                             let device_rect = clipped.to_f32()
                                 .inflate(inflation_factor, inflation_factor)
                                 .intersection(&unclipped)
                                 .unwrap();
 
-                            let mut device_rect = match device_rect.try_cast::<i32>() {
+                            match device_rect.try_cast::<i32>() {
                                 Some(rect) => rect,
                                 None => {
                                     return None
                                 }
-                            };
-
-                            // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
-                            // what would be even better is to rasterize the picture at the down-scaled size
-                            // directly.
-                            device_rect.size = RenderTask::adjusted_blur_source_size(
-                                device_rect.size,
-                                blur_std_deviation,
-                            );
-
-                            device_rect
+                            }
                         } else {
                             clipped
                         };
 
+                        let original_size = device_rect.size;
+
+                        // Adjust the size to avoid introducing sampling errors during the down-scaling passes.
+                        // what would be even better is to rasterize the picture at the down-scaled size
+                        // directly.
+                        device_rect.size = RenderTask::adjusted_blur_source_size(
+                            device_rect.size,
+                            blur_std_deviation,
+                        );
+
                         let uv_rect_kind = calculate_uv_rect_kind(
                             &pic_rect,
                             &transform,
                             &device_rect,
                             device_pixel_scale,
                             true,
                         );
 
@@ -2625,16 +2625,17 @@ impl PicturePrimitive {
 
                         let blur_render_task_id = RenderTask::new_blur(
                             blur_std_deviation,
                             picture_task_id,
                             frame_state.render_tasks,
                             RenderTargetKind::Color,
                             ClearMode::Transparent,
                             None,
+                            original_size,
                         );
 
                         Some((blur_render_task_id, picture_task_id))
                     }
                     PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => {
                         let mut max_std_deviation = 0.0;
                         for shadow in shadows {
                             // TODO(nical) presumably we should compute the clipped rect for each shadow
@@ -2696,16 +2697,17 @@ impl PicturePrimitive {
                             let std_dev = f32::round(shadow.blur_radius * device_pixel_scale.0);
                             blur_render_task_id = RenderTask::new_blur(
                                 DeviceSize::new(std_dev, std_dev),
                                 picture_task_id,
                                 frame_state.render_tasks,
                                 RenderTargetKind::Color,
                                 ClearMode::Transparent,
                                 Some(&mut blur_tasks),
+                                device_rect.size,
                             );
                         }
 
                         // TODO(nical) the second one should to be the blur's task id but we have several blurs now
                         Some((blur_render_task_id, picture_task_id))
                     }
                     PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {
                         let uv_rect_kind = calculate_uv_rect_kind(
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -554,16 +554,17 @@ pub struct PictureTask {
 
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct BlurTask {
     pub blur_std_deviation: f32,
     pub target_kind: RenderTargetKind,
     pub uv_rect_handle: GpuCacheHandle,
+    pub blur_region: DeviceIntSize,
     uv_rect_kind: UvRectKind,
 }
 
 impl BlurTask {
     #[cfg(feature = "debugger")]
     fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
         pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
         pt.add_item(format!("target: {:?}", self.target_kind));
@@ -970,16 +971,17 @@ impl RenderTask {
                             // Blur it
                             RenderTask::new_blur(
                                 DeviceSize::new(blur_radius_dp, blur_radius_dp),
                                 mask_task_id,
                                 render_tasks,
                                 RenderTargetKind::Alpha,
                                 ClearMode::Zero,
                                 None,
+                                cache_size,
                             )
                         }
                     ));
                 }
                 ClipItem::Rectangle(_, ClipMode::Clip) => {
                     if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) {
                         // This is conservative - it's only the case that we actually need
                         // a clear here if we end up adding this mask via add_tiled_clip_mask,
@@ -1081,16 +1083,17 @@ impl RenderTask {
     //
     pub fn new_blur(
         blur_std_deviation: DeviceSize,
         src_task_id: RenderTaskId,
         render_tasks: &mut RenderTaskGraph,
         target_kind: RenderTargetKind,
         clear_mode: ClearMode,
         mut blur_cache: Option<&mut BlurTaskCache>,
+        blur_region: DeviceIntSize,
     ) -> RenderTaskId {
         // Adjust large std deviation value.
         let mut adjusted_blur_std_deviation = blur_std_deviation;
         let (blur_target_size, uv_rect_kind) = {
             let src_task = &render_tasks[src_task_id];
             (src_task.get_dynamic_size(), src_task.uv_rect_kind())
         };
         let mut adjusted_blur_target_size = blur_target_size;
@@ -1132,38 +1135,42 @@ impl RenderTask {
 
         let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
 
         let cached_task = match blur_cache {
             Some(ref mut cache) => cache.get(&blur_key).cloned(),
             None => None,
         };
 
+        let blur_region = blur_region / (scale_factor as i32);
+
         let blur_task_id = cached_task.unwrap_or_else(|| {
             let blur_task_v = RenderTask::with_dynamic_location(
                 adjusted_blur_target_size,
                 vec![downscaling_src_task_id],
                 RenderTaskKind::VerticalBlur(BlurTask {
                     blur_std_deviation: adjusted_blur_std_deviation.height,
                     target_kind,
                     uv_rect_handle: GpuCacheHandle::new(),
+                    blur_region,
                     uv_rect_kind,
                 }),
                 clear_mode,
             );
 
             let blur_task_v_id = render_tasks.add(blur_task_v);
 
             let blur_task_h = RenderTask::with_dynamic_location(
                 adjusted_blur_target_size,
                 vec![blur_task_v_id],
                 RenderTaskKind::HorizontalBlur(BlurTask {
                     blur_std_deviation: adjusted_blur_std_deviation.width,
                     target_kind,
                     uv_rect_handle: GpuCacheHandle::new(),
+                    blur_region,
                     uv_rect_kind,
                 }),
                 clear_mode,
             );
 
             render_tasks.add(blur_task_h)
         });
 
@@ -1358,16 +1365,17 @@ impl RenderTask {
 
                     RenderTask::new_blur(
                         DeviceSize::new(blur_std_deviation, blur_std_deviation),
                         render_tasks.add(svg_task),
                         render_tasks,
                         RenderTargetKind::Color,
                         ClearMode::Transparent,
                         None,
+                        content_size,
                     )
                 }
                 FilterPrimitiveKind::Opacity(ref opacity) => {
                     let input_task_id = get_task_input(
                         &opacity.input,
                         filter_primitives,
                         render_tasks,
                         cur_index,
@@ -1427,16 +1435,17 @@ impl RenderTask {
 
                     let blur_task_id = RenderTask::new_blur(
                         DeviceSize::new(blur_std_deviation, blur_std_deviation),
                         offset_task_id,
                         render_tasks,
                         RenderTargetKind::Color,
                         ClearMode::Transparent,
                         None,
+                        content_size,
                     );
 
                     let task = RenderTask::new_svg_filter_primitive(
                         vec![input_task_id, blur_task_id],
                         content_size,
                         uv_rect_kind,
                         SvgFilterInfo::DropShadow(drop_shadow.shadow.color),
                     );
@@ -1580,18 +1589,18 @@ impl RenderTask {
                     0.0,
                     0.0,
                 ]
             }
             RenderTaskKind::VerticalBlur(ref task) |
             RenderTaskKind::HorizontalBlur(ref task) => {
                 [
                     task.blur_std_deviation,
-                    0.0,
-                    0.0,
+                    task.blur_region.width as f32,
+                    task.blur_region.height as f32,
                 ]
             }
             RenderTaskKind::Readback(..) |
             RenderTaskKind::Scaling(..) |
             RenderTaskKind::Border(..) |
             RenderTaskKind::LineDecoration(..) |
             RenderTaskKind::Gradient(..) |
             RenderTaskKind::Blit(..) => {
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -6,17 +6,16 @@
 
 libevent_path_prefix = 'src/third_party'
 include(libevent_path_prefix + '/libeventcommon.mozbuild')
 
 UNIFIED_SOURCES += [
     'src/base/at_exit.cc',
     'src/base/command_line.cc',
     'src/base/file_path.cc',
-    'src/base/file_util.cc',
     'src/base/histogram.cc',
     'src/base/logging.cc',
     'src/base/message_loop.cc',
     'src/base/message_pump_default.cc',
     'src/base/pickle.cc',
     'src/base/rand_util.cc',
     'src/base/revocable_store.cc',
     'src/base/string_piece.cc',
@@ -30,17 +29,16 @@ UNIFIED_SOURCES += [
     'src/chrome/common/chrome_switches.cc',
     'src/chrome/common/ipc_channel.cc',
     'src/chrome/common/ipc_message.cc',
 ]
 
 if os_win:
     SOURCES += [
         'src/base/condition_variable_win.cc',
-        'src/base/file_util_win.cc',
         'src/base/lock_impl_win.cc',
         'src/base/message_pump_win.cc',
         'src/base/object_watcher.cc',
         'src/base/platform_thread_win.cc',
         'src/base/process_util_win.cc',
         'src/base/shared_memory_win.cc',
         'src/base/sys_string_conversions_win.cc',
         'src/base/thread_local_win.cc',
@@ -52,34 +50,32 @@ if os_win:
     ]
 
 elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
     DIRS += ['src/third_party']
 
 if os_posix:
     UNIFIED_SOURCES += [
         'src/base/condition_variable_posix.cc',
-        'src/base/file_util_posix.cc',
         'src/base/lock_impl_posix.cc',
         'src/base/message_pump_libevent.cc',
         'src/base/platform_thread_posix.cc',
         'src/base/process_util_posix.cc',
         'src/base/shared_memory_posix.cc',
         'src/base/string16.cc',
         'src/base/thread_local_posix.cc',
         'src/base/waitable_event_posix.cc',
         'src/chrome/common/file_descriptor_set_posix.cc',
         'src/chrome/common/ipc_channel_posix.cc',
         'src/chrome/common/process_watcher_posix_sigchld.cc',
     ]
 
 if os_macosx:
     UNIFIED_SOURCES += [
         'src/base/chrome_application_mac.mm',
-        'src/base/file_util_mac.mm',
         'src/base/mac_util.mm',
         'src/base/message_pump_mac.mm',
         'src/base/process_util_mac.mm',
         'src/base/scoped_nsautorelease_pool.mm',
         'src/base/sys_string_conversions_mac.mm',
         'src/base/time_mac.cc',
         'src/chrome/common/mach_ipc_mac.mm',
         'src/chrome/common/mach_message_source_mac.cc',
deleted file mode 100644
--- a/ipc/chromium/src/base/file_util.cc
+++ /dev/null
@@ -1,234 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/file_util.h"
-
-#if defined(OS_WIN)
-#  include <io.h>
-#endif
-#include <stdio.h>
-#if defined(ANDROID) || defined(OS_POSIX)
-#  include <unistd.h>
-#endif
-
-#include <fstream>
-
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/string_util.h"
-
-#include "base/string_piece.h"
-#include "base/sys_string_conversions.h"
-
-namespace {
-
-const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
-
-}  // namespace
-
-namespace file_util {
-
-bool EndsWithSeparator(const FilePath& path) {
-  FilePath::StringType value = path.value();
-  if (value.empty()) return false;
-
-  return FilePath::IsSeparator(value[value.size() - 1]);
-}
-
-void TrimTrailingSeparator(std::wstring* dir) {
-  while (dir->length() > 1 && EndsWithSeparator(dir))
-    dir->resize(dir->length() - 1);
-}
-
-FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
-  FilePath::StringType file_name = path.BaseName().value();
-  const FilePath::StringType::size_type last_dot =
-      file_name.rfind(kExtensionSeparator);
-  return FilePath::StringType(last_dot == FilePath::StringType::npos
-                                  ? FILE_PATH_LITERAL("")
-                                  : file_name,
-                              last_dot + 1);
-}
-
-void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
-  FilePath::StringType& value =
-      const_cast<FilePath::StringType&>(path->value());
-
-  const FilePath::StringType::size_type last_dot =
-      value.rfind(kExtensionSeparator);
-  const FilePath::StringType::size_type last_separator =
-      value.find_last_of(FilePath::StringType(FilePath::kSeparators));
-
-  if (last_dot == FilePath::StringType::npos ||
-      (last_separator != std::wstring::npos && last_dot < last_separator)) {
-    // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
-    // We should just append the suffix to the entire path.
-    value.append(suffix);
-    return;
-  }
-
-  value.insert(last_dot, suffix);
-}
-
-void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
-  FilePath::StringType clean_extension;
-  // If the new extension is "" or ".", then we will just remove the current
-  // extension.
-  if (!extension.empty() &&
-      extension != FilePath::StringType(&kExtensionSeparator, 1)) {
-    if (extension[0] != kExtensionSeparator)
-      clean_extension.append(&kExtensionSeparator, 1);
-    clean_extension.append(extension);
-  }
-
-  FilePath::StringType& value =
-      const_cast<FilePath::StringType&>(path->value());
-  const FilePath::StringType::size_type last_dot =
-      value.rfind(kExtensionSeparator);
-  const FilePath::StringType::size_type last_separator =
-      value.find_last_of(FilePath::StringType(FilePath::kSeparators));
-
-  // Erase the current extension, if any.
-  if ((last_dot > last_separator ||
-       last_separator == FilePath::StringType::npos) &&
-      last_dot != FilePath::StringType::npos)
-    value.erase(last_dot);
-
-  value.append(clean_extension);
-}
-
-FILE* CreateAndOpenTemporaryFile(FilePath* path) {
-  FilePath directory;
-  if (!GetTempDir(&directory)) return NULL;
-
-  return CreateAndOpenTemporaryFileInDir(directory, path);
-}
-
-bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
-  FileInfo info;
-  if (!GetFileInfo(file_path, &info)) return false;
-  *file_size = info.size;
-  return true;
-}
-
-bool CloseFile(FILE* file) {
-  if (file == NULL) return true;
-  return fclose(file) == 0;
-}
-
-// Deprecated functions ----------------------------------------------------
-
-bool AbsolutePath(std::wstring* path_str) {
-  FilePath path(FilePath::FromWStringHack(*path_str));
-  if (!AbsolutePath(&path)) return false;
-  *path_str = path.ToWStringHack();
-  return true;
-}
-void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
-  if (!path) {
-    NOTREACHED();
-    return;  // Don't crash in this function in release builds.
-  }
-
-  if (!EndsWithSeparator(path)) path->push_back(FilePath::kSeparators[0]);
-  path->append(new_ending);
-}
-bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
-  return CopyFile(FilePath::FromWStringHack(from_path),
-                  FilePath::FromWStringHack(to_path));
-}
-bool CreateDirectory(const std::wstring& full_path) {
-  return CreateDirectory(FilePath::FromWStringHack(full_path));
-}
-bool CreateNewTempDirectory(const std::wstring& prefix,
-                            std::wstring* new_temp_path) {
-#if defined(OS_WIN)
-  FilePath::StringType dir_prefix(prefix);
-#elif defined(OS_POSIX)
-  FilePath::StringType dir_prefix = WideToUTF8(prefix);
-#endif
-  FilePath temp_path;
-  if (!CreateNewTempDirectory(dir_prefix, &temp_path)) return false;
-  *new_temp_path = temp_path.ToWStringHack();
-  return true;
-}
-bool CreateTemporaryFileName(std::wstring* temp_file) {
-  FilePath temp_file_path;
-  if (!CreateTemporaryFileName(&temp_file_path)) return false;
-  *temp_file = temp_file_path.ToWStringHack();
-  return true;
-}
-bool Delete(const std::wstring& path) {
-  return Delete(FilePath::FromWStringHack(path));
-}
-bool DirectoryExists(const std::wstring& path) {
-  return DirectoryExists(FilePath::FromWStringHack(path));
-}
-bool EndsWithSeparator(std::wstring* path) {
-  return EndsWithSeparator(FilePath::FromWStringHack(*path));
-}
-bool EndsWithSeparator(const std::wstring& path) {
-  return EndsWithSeparator(FilePath::FromWStringHack(path));
-}
-bool GetCurrentDirectory(std::wstring* path_str) {
-  FilePath path;
-  if (!GetCurrentDirectory(&path)) return false;
-  *path_str = path.ToWStringHack();
-  return true;
-}
-std::wstring GetFileExtensionFromPath(const std::wstring& path) {
-  FilePath::StringType extension =
-      GetFileExtensionFromPath(FilePath::FromWStringHack(path));
-#if defined(OS_WIN)
-  return extension;
-#elif defined(OS_POSIX)
-  return UTF8ToWide(extension);
-#endif
-}
-bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
-  return GetFileInfo(FilePath::FromWStringHack(file_path), results);
-}
-std::wstring GetFilenameFromPath(const std::wstring& path) {
-  if (path.empty() || EndsWithSeparator(path)) return std::wstring();
-
-  return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
-}
-bool GetFileSize(const std::wstring& file_path, int64_t* file_size) {
-  return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
-}
-bool GetTempDir(std::wstring* path_str) {
-  FilePath path;
-  if (!GetTempDir(&path)) return false;
-  *path_str = path.ToWStringHack();
-  return true;
-}
-FILE* OpenFile(const std::wstring& filename, const char* mode) {
-  return OpenFile(FilePath::FromWStringHack(filename), mode);
-}
-bool PathExists(const std::wstring& path) {
-  return PathExists(FilePath::FromWStringHack(path));
-}
-bool PathIsWritable(const std::wstring& path) {
-  return PathIsWritable(FilePath::FromWStringHack(path));
-}
-int ReadFile(const std::wstring& filename, char* data, int size) {
-  return ReadFile(FilePath::FromWStringHack(filename), data, size);
-}
-bool SetCurrentDirectory(const std::wstring& directory) {
-  return SetCurrentDirectory(FilePath::FromWStringHack(directory));
-}
-void UpOneDirectory(std::wstring* dir) {
-  FilePath path = FilePath::FromWStringHack(*dir);
-  FilePath directory = path.DirName();
-  // If there is no separator, we will get back kCurrentDirectory.
-  // In this case don't change |dir|.
-  if (directory.value() != FilePath::kCurrentDirectory)
-    *dir = directory.ToWStringHack();
-}
-int WriteFile(const std::wstring& filename, const char* data, int size) {
-  return WriteFile(FilePath::FromWStringHack(filename), data, size);
-}
-}  // namespace file_util
deleted file mode 100644
--- a/ipc/chromium/src/base/file_util.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file contains utility functions for dealing with the local
-// filesystem.
-
-#ifndef BASE_FILE_UTIL_H_
-#define BASE_FILE_UTIL_H_
-
-#include "build/build_config.h"
-
-#if defined(OS_WIN)
-#  include <windows.h>
-#elif defined(ANDROID)
-#  include <sys/stat.h>
-#elif defined(OS_POSIX)
-#  include <sys/types.h>
-#  include <sys/stat.h>
-#endif
-
-#include <stdio.h>
-
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/file_path.h"
-
-namespace file_util {
-
-//-----------------------------------------------------------------------------
-// Functions that operate purely on a path string w/o touching the filesystem:
-
-// Returns true if the given path ends with a path separator character.
-bool EndsWithSeparator(const FilePath& path);
-// These two versions are both deprecated. TODO(estade): remove them.
-bool EndsWithSeparator(std::wstring* path);
-bool EndsWithSeparator(const std::wstring& path);
-
-// Modifies a string by trimming all trailing separators from the end.
-// Deprecated. FilePath does this automatically, and if it's constructed from a
-// path with a trailing separator, StripTrailingSeparators() may be used.
-void TrimTrailingSeparator(std::wstring* dir);
-
-// Strips the topmost directory from the end of 'dir'.  Assumes 'dir' does not
-// refer to a file.
-// If 'dir' is a root directory, return without change.
-// Deprecated. Use FilePath::DirName instead.
-void UpOneDirectory(std::wstring* dir);
-
-// Returns the filename portion of 'path', without any leading \'s or /'s.
-// Deprecated. Use FilePath::BaseName instead.
-std::wstring GetFilenameFromPath(const std::wstring& path);
-
-// Deprecated compatibility function.  Use FilePath::Extension.
-FilePath::StringType GetFileExtensionFromPath(const FilePath& path);
-// Deprecated temporary compatibility function.
-std::wstring GetFileExtensionFromPath(const std::wstring& path);
-
-// Appends new_ending to path, adding a separator between the two if necessary.
-void AppendToPath(std::wstring* path, const std::wstring& new_ending);
-
-// Convert provided relative path into an absolute path.  Returns false on
-// error. On POSIX, this function fails if the path does not exist.
-bool AbsolutePath(FilePath* path);
-// Deprecated temporary compatibility function.
-bool AbsolutePath(std::wstring* path);
-
-// Deprecated compatibility function.  Use FilePath::InsertBeforeExtension.
-void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix);
-
-// Deprecated compatibility function.  Use FilePath::ReplaceExtension.
-void ReplaceExtension(FilePath* file_name,
-                      const FilePath::StringType& extension);
-
-#if defined(OS_WIN)
-// Deprecated temporary compatibility functions.
-void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
-void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
-#endif
-
-//-----------------------------------------------------------------------------
-// Functions that involve filesystem access or modification:
-
-// Deletes the given path, whether it's a file or a directory.
-// If it's a directory, it's perfectly happy to delete all of the
-// directory's contents.
-// Returns true if successful, false otherwise.
-bool Delete(const FilePath& path);
-// Deprecated temporary compatibility function.
-bool Delete(const std::wstring& path);
-
-// Copies a single file. Use CopyDirectory to copy directories.
-bool CopyFile(const FilePath& from_path, const FilePath& to_path);
-// Deprecated temporary compatibility function.
-bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
-
-// Returns true if the given path exists on the local filesystem,
-// false otherwise.
-bool PathExists(const FilePath& path);
-// Deprecated temporary compatibility function.
-bool PathExists(const std::wstring& path);
-
-// Returns true if the given path is writable by the user, false otherwise.
-bool PathIsWritable(const FilePath& path);
-// Deprecated temporary compatibility function.
-bool PathIsWritable(const std::wstring& path);
-
-// Returns true if the given path exists and is a directory, false otherwise.
-bool DirectoryExists(const FilePath& path);
-// Deprecated temporary compatibility function.
-bool DirectoryExists(const std::wstring& path);
-
-#if defined(OS_POSIX)
-// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
-// in |buffer|. This function is protected against EINTR and partial reads.
-// Returns true iff |bytes| bytes have been successfuly read from |fd|.
-bool ReadFromFD(int fd, char* buffer, size_t bytes);
-#endif  // defined(OS_POSIX)
-
-// Get the temporary directory provided by the system.
-bool GetTempDir(FilePath* path);
-// Deprecated temporary compatibility function.
-bool GetTempDir(std::wstring* path);
-// Get a temporary directory for shared memory files.
-// Only useful on POSIX; redirects to GetTempDir() on Windows.
-bool GetShmemTempDir(FilePath* path);
-
-// Creates a temporary file. The full path is placed in |path|, and the
-// function returns true if was successful in creating the file. The file will
-// be empty and all handles closed after this function returns.
-// TODO(erikkay): rename this function and track down all of the callers.
-// (Clarification of erik's comment: the intent is to rename the BlahFileName()
-//  calls into BlahFile(), since they create temp files (not temp filenames).)
-bool CreateTemporaryFileName(FilePath* path);
-// Deprecated temporary compatibility function.
-bool CreateTemporaryFileName(std::wstring* temp_file);
-
-// Create and open a temporary file.  File is opened for read/write.
-// The full path is placed in |path|, and the function returns true if
-// was successful in creating and opening the file.
-FILE* CreateAndOpenTemporaryFile(FilePath* path);
-// Like above but for shmem files.  Only useful for POSIX.
-FILE* CreateAndOpenTemporaryShmemFile(FilePath* path);
-
-// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path);
-
-// Same as CreateTemporaryFileName but the file is created in |dir|.
-bool CreateTemporaryFileNameInDir(const std::wstring& dir,
-                                  std::wstring* temp_file);
-
-// Create a new directory under TempPath. If prefix is provided, the new
-// directory name is in the format of prefixyyyy.
-// NOTE: prefix is ignored in the POSIX implementation.
-// TODO(erikkay): is this OK?
-// If success, return true and output the full path of the directory created.
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
-                            FilePath* new_temp_path);
-// Deprecated temporary compatibility function.
-bool CreateNewTempDirectory(const std::wstring& prefix,
-                            std::wstring* new_temp_path);
-
-// Creates a directory, as well as creating any parent directories, if they
-// don't exist. Returns 'true' on successful creation, or if the directory
-// already exists.
-bool CreateDirectory(const FilePath& full_path);
-// Deprecated temporary compatibility function.
-bool CreateDirectory(const std::wstring& full_path);
-
-// Returns the file size. Returns true on success.
-bool GetFileSize(const FilePath& file_path, int64_t* file_size);
-// Deprecated temporary compatibility function.
-bool GetFileSize(const std::wstring& file_path, int64_t* file_size);
-
-// Used to hold information about a given file path.  See GetFileInfo below.
-struct FileInfo {
-  // The size of the file in bytes.  Undefined when is_directory is true.
-  int64_t size;
-
-  // True if the file corresponds to a directory.
-  bool is_directory;
-
-  // Add additional fields here as needed.
-};
-
-// Returns information about the given file path.
-bool GetFileInfo(const FilePath& file_path, FileInfo* info);
-// Deprecated temporary compatibility function.
-bool GetFileInfo(const std::wstring& file_path, FileInfo* info);
-
-// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
-FILE* OpenFile(const FilePath& filename, const char* mode);
-// Deprecated temporary compatibility functions.
-FILE* OpenFile(const std::string& filename, const char* mode);
-FILE* OpenFile(const std::wstring& filename, const char* mode);
-
-// Closes file opened by OpenFile. Returns true on success.
-bool CloseFile(FILE* file);
-
-// Reads the given number of bytes from the file into the buffer.  Returns
-// the number of read bytes, or -1 on error.
-int ReadFile(const FilePath& filename, char* data, int size);
-// Deprecated temporary compatibility function.
-int ReadFile(const std::wstring& filename, char* data, int size);
-
-// Writes the given buffer into the file, overwriting any data that was
-// previously there.  Returns the number of bytes written, or -1 on error.
-int WriteFile(const FilePath& filename, const char* data, int size);
-// Deprecated temporary compatibility function.
-int WriteFile(const std::wstring& filename, const char* data, int size);
-
-// Gets the current working directory for the process.
-bool GetCurrentDirectory(FilePath* path);
-// Deprecated temporary compatibility function.
-bool GetCurrentDirectory(std::wstring* path);
-
-// Sets the current working directory for the process.
-bool SetCurrentDirectory(const FilePath& path);
-// Deprecated temporary compatibility function.
-bool SetCurrentDirectory(const std::wstring& current_directory);
-
-}  // namespace file_util
-
-#endif  // BASE_FILE_UTIL_H_
deleted file mode 100644
--- a/ipc/chromium/src/base/file_util_mac.mm
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/file_util.h"
-
-#import <Cocoa/Cocoa.h>
-#include <copyfile.h>
-
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/string_util.h"
-#include "base/scoped_nsautorelease_pool.h"
-
-namespace file_util {
-
-bool GetTempDir(FilePath* path) {
-  base::ScopedNSAutoreleasePool autorelease_pool;
-  NSString* tmp = NSTemporaryDirectory();
-  if (tmp == nil) return false;
-  *path = FilePath([tmp fileSystemRepresentation]);
-  return true;
-}
-
-bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
-
-bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
-  return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
-}
-
-}  // namespace
deleted file mode 100644
--- a/ipc/chromium/src/base/file_util_posix.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/file_util.h"
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/mman.h>
-#define _DARWIN_USE_64_BIT_INODE  // Use 64-bit inode data structures
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <fstream>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/eintr_wrapper.h"
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/string_util.h"
-#include "base/time.h"
-
-namespace file_util {
-
-#if defined(GOOGLE_CHROME_BUILD)
-static const char* kTempFileName = "com.google.chrome.XXXXXX";
-#else
-static const char* kTempFileName = "org.chromium.XXXXXX";
-#endif
-
-bool AbsolutePath(FilePath* path) {
-  char full_path[PATH_MAX];
-  if (realpath(path->value().c_str(), full_path) == NULL) return false;
-  *path = FilePath(full_path);
-  return true;
-}
-
-// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
-// which works both with and without the recursive flag.  I'm not sure we need
-// that functionality. If not, remove from file_util_win.cc, otherwise add it
-// here.
-bool Delete(const FilePath& path) {
-  const char* path_str = path.value().c_str();
-  struct stat file_info;
-  int test = stat(path_str, &file_info);
-  if (test != 0) {
-    // The Windows version defines this condition as success.
-    bool ret = (errno == ENOENT || errno == ENOTDIR);
-    return ret;
-  }
-  if (!S_ISDIR(file_info.st_mode)) return (unlink(path_str) == 0);
-
-  return (rmdir(path_str) == 0);
-}
-
-bool PathExists(const FilePath& path) {
-  struct stat file_info;
-  return (stat(path.value().c_str(), &file_info) == 0);
-}
-
-bool PathIsWritable(const FilePath& path) {
-  FilePath test_path(path);
-  struct stat file_info;
-  if (stat(test_path.value().c_str(), &file_info) != 0) {
-    // If the path doesn't exist, test the parent dir.
-    test_path = test_path.DirName();
-    // If the parent dir doesn't exist, then return false (the path is not
-    // directly writable).
-    if (stat(test_path.value().c_str(), &file_info) != 0) return false;
-  }
-  if (S_IWOTH & file_info.st_mode) return true;
-  if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
-    return true;
-  if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
-    return true;
-  return false;
-}
-
-bool DirectoryExists(const FilePath& path) {
-  struct stat file_info;
-  if (stat(path.value().c_str(), &file_info) == 0)
-    return S_ISDIR(file_info.st_mode);
-  return false;
-}
-
-bool ReadFromFD(int fd, char* buffer, size_t bytes) {
-  size_t total_read = 0;
-  while (total_read < bytes) {
-    ssize_t bytes_read =
-        HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
-    if (bytes_read <= 0) break;
-    total_read += bytes_read;
-  }
-  return total_read == bytes;
-}
-
-// Creates and opens a temporary file in |directory|, returning the
-// file descriptor.  |path| is set to the temporary file path.
-// Note TODO(erikkay) comment in header for BlahFileName() calls; the
-// intent is to rename these files BlahFile() (since they create
-// files, not filenames).  This function does NOT unlink() the file.
-int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
-  *path = directory.Append(kTempFileName);
-  const std::string& tmpdir_string = path->value();
-  // this should be OK since mkstemp just replaces characters in place
-  char* buffer = const_cast<char*>(tmpdir_string.c_str());
-
-  return mkstemp(buffer);
-}
-
-bool CreateTemporaryFileName(FilePath* path) {
-  FilePath directory;
-  if (!GetTempDir(&directory)) return false;
-  int fd = CreateAndOpenFdForTemporaryFile(directory, path);
-  if (fd < 0) return false;
-  close(fd);
-  return true;
-}
-
-FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
-  FilePath directory;
-  if (!GetShmemTempDir(&directory)) return NULL;
-
-  return CreateAndOpenTemporaryFileInDir(directory, path);
-}
-
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
-  int fd = CreateAndOpenFdForTemporaryFile(dir, path);
-  if (fd < 0) return NULL;
-
-  return fdopen(fd, "a+");
-}
-
-bool CreateTemporaryFileNameInDir(const std::wstring& dir,
-                                  std::wstring* temp_file) {
-  // Not implemented yet.
-  NOTREACHED();
-  return false;
-}
-
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
-                            FilePath* new_temp_path) {
-  FilePath tmpdir;
-  if (!GetTempDir(&tmpdir)) return false;
-  tmpdir = tmpdir.Append(kTempFileName);
-  std::string tmpdir_string = tmpdir.value();
-#ifdef ANDROID
-  char* dtemp = NULL;
-#else
-  // this should be OK since mkdtemp just replaces characters in place
-  char* buffer = const_cast<char*>(tmpdir_string.c_str());
-  char* dtemp = mkdtemp(buffer);
-#endif
-  if (!dtemp) return false;
-  *new_temp_path = FilePath(dtemp);
-  return true;
-}
-
-bool CreateDirectory(const FilePath& full_path) {
-  std::vector<FilePath> subpaths;
-
-  // Collect a list of all parent directories.
-  FilePath last_path = full_path;
-  subpaths.push_back(full_path);
-  for (FilePath path = full_path.DirName(); path.value() != last_path.value();
-       path = path.DirName()) {
-    subpaths.push_back(path);
-    last_path = path;
-  }
-
-  // Iterate through the parents and create the missing ones.
-  for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
-       i != subpaths.rend(); ++i) {
-    if (!DirectoryExists(*i)) {
-      if (mkdir(i->value().c_str(), 0777) != 0) return false;
-    }
-  }
-  return true;
-}
-
-bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
-  struct stat file_info;
-  if (stat(file_path.value().c_str(), &file_info) != 0) return false;
-  results->is_directory = S_ISDIR(file_info.st_mode);
-  results->size = file_info.st_size;
-  return true;
-}
-
-FILE* OpenFile(const std::string& filename, const char* mode) {
-  return OpenFile(FilePath(filename), mode);
-}
-
-FILE* OpenFile(const FilePath& filename, const char* mode) {
-  return fopen(filename.value().c_str(), mode);
-}
-
-int ReadFile(const FilePath& filename, char* data, int size) {
-  int fd = open(filename.value().c_str(), O_RDONLY);
-  if (fd < 0) return -1;
-
-  int ret_value = HANDLE_EINTR(read(fd, data, size));
-  IGNORE_EINTR(close(fd));
-  return ret_value;
-}
-
-int WriteFile(const FilePath& filename, const char* data, int size) {
-  int fd = creat(filename.value().c_str(), 0666);
-  if (fd < 0) return -1;
-
-  // Allow for partial writes
-  ssize_t bytes_written_total = 0;
-  do {
-    ssize_t bytes_written_partial = HANDLE_EINTR(
-        write(fd, data + bytes_written_total, size - bytes_written_total));
-    if (bytes_written_partial < 0) {
-      IGNORE_EINTR(close(fd));
-      return -1;
-    }
-    bytes_written_total += bytes_written_partial;
-  } while (bytes_written_total < size);
-
-  IGNORE_EINTR(close(fd));
-  return bytes_written_total;
-}
-
-// Gets the current working directory for the process.
-bool GetCurrentDirectory(FilePath* dir) {
-  char system_buffer[PATH_MAX] = "";
-  if (!getcwd(system_buffer, sizeof(system_buffer))) {
-    NOTREACHED();
-    return false;
-  }
-  *dir = FilePath(system_buffer);
-  return true;
-}
-
-// Sets the current working directory for the process.
-bool SetCurrentDirectory(const FilePath& path) {
-  int ret = chdir(path.value().c_str());
-  return !ret;
-}
-
-#if !defined(OS_MACOSX)
-bool GetTempDir(FilePath* path) {
-  const char* tmp = getenv("TMPDIR");
-  if (tmp)
-    *path = FilePath(tmp);
-  else
-    *path = FilePath("/tmp");
-  return true;
-}
-
-bool GetShmemTempDir(FilePath* path) {
-#  if defined(OS_LINUX) && !defined(ANDROID)
-  *path = FilePath("/dev/shm");
-  return true;
-#  else
-  return GetTempDir(path);
-#  endif
-}
-
-bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
-  int infile = open(from_path.value().c_str(), O_RDONLY);
-  if (infile < 0) return false;
-
-  int outfile = creat(to_path.value().c_str(), 0666);
-  if (outfile < 0) {
-    close(infile);
-    return false;
-  }
-
-  const size_t kBufferSize = 32768;
-  std::vector<char> buffer(kBufferSize);
-  bool result = true;
-
-  while (result) {
-    ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
-    if (bytes_read < 0) {
-      result = false;
-      break;
-    }
-    if (bytes_read == 0) break;
-    // Allow for partial writes
-    ssize_t bytes_written_per_read = 0;
-    do {
-      ssize_t bytes_written_partial =
-          HANDLE_EINTR(write(outfile, &buffer[bytes_written_per_read],
-                             bytes_read - bytes_written_per_read));
-      if (bytes_written_partial < 0) {
-        result = false;
-        break;
-      }
-      bytes_written_per_read += bytes_written_partial;
-    } while (bytes_written_per_read < bytes_read);
-  }
-
-  if (IGNORE_EINTR(close(infile)) < 0) result = false;
-  if (IGNORE_EINTR(close(outfile)) < 0) result = false;
-
-  return result;
-}
-#endif  // !defined(OS_MACOSX)
-
-}  // namespace file_util
deleted file mode 100644
--- a/ipc/chromium/src/base/file_util_win.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/file_util.h"
-
-#include <windows.h>
-#include <shellapi.h>
-#include <shlobj.h>
-#include <time.h>
-#include <string>
-
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/scoped_handle.h"
-#include "base/string_util.h"
-#include "base/time.h"
-#include "base/win_util.h"
-
-namespace file_util {
-
-bool AbsolutePath(FilePath* path) {
-  wchar_t file_path_buf[MAX_PATH];
-  if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) return false;
-  *path = FilePath(file_path_buf);
-  return true;
-}
-
-bool Delete(const FilePath& path) {
-  if (path.value().length() >= MAX_PATH) return false;
-
-  // Use DeleteFile; it should be faster. DeleteFile
-  // fails if passed a directory though, which is why we fall through on
-  // failure to the SHFileOperation.
-  if (DeleteFile(path.value().c_str()) != 0) return true;
-
-  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
-  // so we have to use wcscpy because wcscpy_s writes non-NULLs
-  // into the rest of the buffer.
-  wchar_t double_terminated_path[MAX_PATH + 1] = {0};
-#pragma warning(suppress : 4996)  // don't complain about wcscpy deprecation
-  wcscpy(double_terminated_path, path.value().c_str());
-
-  SHFILEOPSTRUCT file_operation = {0};
-  file_operation.wFunc = FO_DELETE;
-  file_operation.pFrom = double_terminated_path;
-  file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
-  file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
-  int err = SHFileOperation(&file_operation);
-  // Some versions of Windows return ERROR_FILE_NOT_FOUND when
-  // deleting an empty directory.
-  return (err == 0 || err == ERROR_FILE_NOT_FOUND);
-}
-
-bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
-  // NOTE: I suspect we could support longer paths, but that would involve
-  // analyzing all our usage of files.
-  if (from_path.value().length() >= MAX_PATH ||
-      to_path.value().length() >= MAX_PATH) {
-    return false;
-  }
-  return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
-                     false) != 0);
-}
-
-bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
-               bool recursive) {
-  // NOTE: I suspect we could support longer paths, but that would involve
-  // analyzing all our usage of files.
-  if (from_path.value().length() >= MAX_PATH ||
-      to_path.value().length() >= MAX_PATH) {
-    return false;
-  }
-
-  // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
-  // so we have to use wcscpy because wcscpy_s writes non-NULLs
-  // into the rest of the buffer.
-  wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
-  wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
-#pragma warning(suppress : 4996)  // don't complain about wcscpy deprecation
-  wcscpy(double_terminated_path_from, from_path.value().c_str());
-#pragma warning(suppress : 4996)  // don't complain about wcscpy deprecation
-  wcscpy(double_terminated_path_to, to_path.value().c_str());
-
-  SHFILEOPSTRUCT file_operation = {0};
-  file_operation.wFunc = FO_COPY;
-  file_operation.pFrom = double_terminated_path_from;
-  file_operation.pTo = double_terminated_path_to;
-  file_operation.fFlags =
-      FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
-  if (!recursive) file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
-
-  return (SHFileOperation(&file_operation) == 0);
-}
-
-bool PathExists(const FilePath& path) {
-  return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
-}
-
-bool PathIsWritable(const FilePath& path) {
-  HANDLE dir =
-      CreateFile(path.value().c_str(), FILE_ADD_FILE,
-                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
-                 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
-
-  if (dir == INVALID_HANDLE_VALUE) return false;
-
-  CloseHandle(dir);
-  return true;
-}
-
-bool DirectoryExists(const FilePath& path) {
-  DWORD fileattr = GetFileAttributes(path.value().c_str());
-  if (fileattr != INVALID_FILE_ATTRIBUTES)
-    return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
-  return false;
-}
-
-bool GetTempDir(FilePath* path) {
-  wchar_t temp_path[MAX_PATH + 1];
-  DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
-  if (path_len >= MAX_PATH || path_len <= 0) return false;
-  // TODO(evanm): the old behavior of this function was to always strip the
-  // trailing slash.  We duplicate this here, but it shouldn't be necessary
-  // when everyone is using the appropriate FilePath APIs.
-  std::wstring path_str(temp_path);
-  TrimTrailingSeparator(&path_str);
-  *path = FilePath(path_str);
-  return true;
-}
-
-bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
-
-bool CreateTemporaryFileName(FilePath* path) {
-  std::wstring temp_path, temp_file;
-
-  if (!GetTempDir(&temp_path)) return false;
-
-  if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) {
-    *path = FilePath(temp_file);
-    return true;
-  }
-
-  return false;
-}
-
-FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
-  return CreateAndOpenTemporaryFile(path);
-}
-
-// On POSIX we have semantics to create and open a temporary file
-// atomically.
-// TODO(jrg): is there equivalent call to use on Windows instead of
-// going 2-step?
-FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
-  std::wstring wstring_path;
-  if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) {
-    return NULL;
-  }
-  *path = FilePath(wstring_path);
-  // Open file in binary mode, to avoid problems with fwrite. On Windows
-  // it replaces \n's with \r\n's, which may surprise you.
-  // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
-  return OpenFile(*path, "wb+");
-}
-
-bool CreateTemporaryFileNameInDir(const std::wstring& dir,
-                                  std::wstring* temp_file) {
-  wchar_t temp_name[MAX_PATH + 1];
-
-  if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) return false;  // fail!
-
-  DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
-  if (path_len > MAX_PATH + 1 || path_len == 0) return false;  // fail!
-
-  temp_file->assign(temp_name, path_len);
-  return true;
-}
-
-bool CreateNewTempDirectory(const FilePath::StringType& prefix,
-                            FilePath* new_temp_path) {
-  FilePath system_temp_dir;
-  if (!GetTempDir(&system_temp_dir)) return false;
-
-  FilePath path_to_create;
-  srand(static_cast<uint32_t>(time(NULL)));
-
-  int count = 0;
-  while (count < 50) {
-    // Try create a new temporary directory with random generated name. If
-    // the one exists, keep trying another path name until we reach some limit.
-    path_to_create = system_temp_dir;
-    std::wstring new_dir_name;
-    new_dir_name.assign(prefix);
-    new_dir_name.append(IntToWString(rand() % kint16max));
-    path_to_create = path_to_create.Append(new_dir_name);
-
-    if (::CreateDirectory(path_to_create.value().c_str(), NULL)) break;
-    count++;
-  }
-
-  if (count == 50) {
-    return false;
-  }
-
-  *new_temp_path = path_to_create;
-  return true;
-}
-
-bool CreateDirectory(const FilePath& full_path) {
-  if (DirectoryExists(full_path)) return true;
-  int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL);
-  return err == ERROR_SUCCESS;
-}
-
-bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
-  WIN32_FILE_ATTRIBUTE_DATA attr;
-  if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(),
-                           GetFileExInfoStandard, &attr)) {
-    return false;
-  }
-
-  ULARGE_INTEGER size;
-  size.HighPart = attr.nFileSizeHigh;
-  size.LowPart = attr.nFileSizeLow;
-  results->size = size.QuadPart;
-
-  results->is_directory =
-      (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-  return true;
-}
-
-FILE* OpenFile(const FilePath& filename, const char* mode) {
-  std::wstring w_mode = ASCIIToWide(std::string(mode));
-  FILE* file;
-  if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) {
-    return NULL;
-  }
-  return file;
-}
-
-FILE* OpenFile(const std::string& filename, const char* mode) {
-  FILE* file;
-  if (fopen_s(&file, filename.c_str(), mode) != 0) {
-    return NULL;
-  }
-  return file;
-}
-
-int ReadFile(const FilePath& filename, char* data, int size) {
-  ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ,
-                               FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
-                               OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL));
-  if (file == INVALID_HANDLE_VALUE) return -1;
-
-  int ret_value;
-  DWORD read;
-  if (::ReadFile(file, data, size, &read, NULL) && read == size) {
-    ret_value = static_cast<int>(read);
-  } else {
-    ret_value = -1;
-  }
-
-  return ret_value;
-}
-
-int WriteFile(const FilePath& filename, const char* data, int size) {
-  ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, NULL,
-                               CREATE_ALWAYS, 0, NULL));
-  if (file == INVALID_HANDLE_VALUE) {
-    CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value()
-                          << " error code=" << GetLastError()
-                          << " error text=" << win_util::FormatLastWin32Error();
-    return -1;
-  }
-
-  DWORD written;
-  BOOL result = ::WriteFile(file, data, size, &written, NULL);
-  if (result && written == size) return static_cast<int>(written);
-
-  if (!result) {
-    // WriteFile failed.
-    CHROMIUM_LOG(WARNING) << "writing file " << filename.value()
-                          << " failed, error code=" << GetLastError()
-                          << " description="
-                          << win_util::FormatLastWin32Error();
-  } else {
-    // Didn't write all the bytes.
-    CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to "
-                          << filename.value() << " expected " << size;
-  }
-  return -1;
-}
-
-// Gets the current working directory for the process.
-bool GetCurrentDirectory(FilePath* dir) {
-  wchar_t system_buffer[MAX_PATH];
-  system_buffer[0] = 0;
-  DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
-  if (len == 0 || len > MAX_PATH) return false;
-  // TODO(evanm): the old behavior of this function was to always strip the
-  // trailing slash.  We duplicate this here, but it shouldn't be necessary
-  // when everyone is using the appropriate FilePath APIs.
-  std::wstring dir_str(system_buffer);
-  file_util::TrimTrailingSeparator(&dir_str);
-  *dir = FilePath(dir_str);
-  return true;
-}
-
-// Sets the current working directory for the process.
-bool SetCurrentDirectory(const FilePath& directory) {
-  BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
-  return ret != 0;
-}
-
-// Deprecated functions ----------------------------------------------------
-
-void InsertBeforeExtension(std::wstring* path_str, const std::wstring& suffix) {
-  FilePath path(*path_str);
-  InsertBeforeExtension(&path, suffix);
-  path_str->assign(path.value());
-}
-void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
-  FilePath path(*file_name);
-  ReplaceExtension(&path, extension);
-  file_name->assign(path.value());
-}
-}  // namespace file_util
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -13,16 +13,18 @@
 #  include <sys/types.h>
 #  include <semaphore.h>
 #  include "base/file_descriptor_posix.h"
 #endif
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtrExtensions.h"
 
 namespace base {
 
 // SharedMemoryHandle is a platform specific type which represents
 // the underlying OS handle to a shared memory segment.
 #if defined(OS_WIN)
 typedef HANDLE SharedMemoryHandle;
 #elif defined(OS_POSIX)
@@ -52,22 +54,31 @@ class SharedMemory {
   // Initialize a new SharedMemory object from an existing, open
   // shared memory file.
   bool SetHandle(SharedMemoryHandle handle, bool read_only);
 
   // Return true iff the given handle is valid (i.e. not the distingished
   // invalid value; NULL for a HANDLE and -1 for a file descriptor)
   static bool IsHandleValid(const SharedMemoryHandle& handle);
 
+  // IsHandleValid applied to this object's handle.
+  bool IsValid() const;
+
   // Return invalid handle (see comment above for exact definition).
   static SharedMemoryHandle NULLHandle();
 
   // Creates a shared memory segment.
   // Returns true on success, false on failure.
-  bool Create(size_t size);
+  bool Create(size_t size) { return CreateInternal(size, false); }
+
+  // Creates a shared memory segment that supports the Freeze()
+  // method; see below.  (Warning: creating freezeable shared memory
+  // within a sandboxed process isn't possible on some platforms.)
+  // Returns true on success, false on failure.
+  bool CreateFreezeable(size_t size) { return CreateInternal(size, true); }
 
   // Maps the shared memory into the caller's address space.
   // Returns true on success, false otherwise.  The memory address
   // is accessed via the memory() accessor.
   //
   // If the specified fixed address is not null, it is the address that the
   // shared memory must be mapped at.  Returns false if the shared memory
   // could not be mapped at that address.
@@ -84,20 +95,39 @@ class SharedMemory {
   // created externally.
   // Returns 0 if not opened or unknown.
   size_t max_size() const { return max_size_; }
 
   // Gets a pointer to the opened memory space if it has been
   // Mapped via Map().  Returns NULL if it is not mapped.
   void* memory() const { return memory_; }
 
-  // Get access to the underlying OS handle for this segment.
-  // Use of this handle for anything other than an opaque
-  // identifier is not portable.
-  SharedMemoryHandle handle() const;
+  // Extracts the underlying file handle; similar to
+  // GiveToProcess(GetCurrentProcId(), ...) but returns a RAII type.
+  // Like GiveToProcess, this unmaps the memory as a side-effect.
+  mozilla::UniqueFileHandle TakeHandle();
+
+#ifdef OS_WIN
+  // Used only in gfx/ipc/SharedDIBWin.cpp; should be removable once
+  // NPAPI goes away.
+  HANDLE GetHandle() {
+    freezeable_ = false;
+    return mapped_file_;
+  }
+#endif
+
+  // Make the shared memory object read-only, such that it cannot be
+  // written even if it's sent to an untrusted process.  If it was
+  // mapped in this process, it will be unmapped.  The object must
+  // have been created with CreateFreezeable(), and must not have
+  // already been shared to another process.
+  //
+  // (See bug 1479960 comment #0 for OS-specific implementation
+  // details.)
+  MOZ_MUST_USE bool Freeze();
 
   // Closes the open shared memory segment.
   // It is safe to call Close repeatedly.
   void Close(bool unmap_view = true);
 
   // Returns a page-aligned address at which the given number of bytes could
   // probably be mapped.  Returns NULL on error or if there is insufficient
   // contiguous address space to map the required number of pages.
@@ -135,26 +165,31 @@ class SharedMemory {
   // (This is public so that the Linux sandboxing code can use it.)
   static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
 #endif
 
  private:
   bool ShareToProcessCommon(ProcessId target_pid,
                             SharedMemoryHandle* new_handle, bool close_self);
 
+  bool CreateInternal(size_t size, bool freezeable);
+
 #if defined(OS_WIN)
   // If true indicates this came from an external source so needs extra checks
   // before being mapped.
   bool external_section_;
   HANDLE mapped_file_;
 #elif defined(OS_POSIX)
   int mapped_file_;
+  int frozen_file_;
+  size_t mapped_size_;
 #endif
   void* memory_;
   bool read_only_;
+  bool freezeable_;
   size_t max_size_;
 
   DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);
 };
 
 }  // namespace base
 
 #endif  // BASE_SHARED_MEMORY_H_
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -15,58 +15,157 @@
 #ifdef ANDROID
 #  include <linux/ashmem.h>
 #endif
 
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
 #include "base/string_util.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "prenv.h"
 
 namespace base {
 
 SharedMemory::SharedMemory()
-    : mapped_file_(-1), memory_(NULL), read_only_(false), max_size_(0) {}
+    : mapped_file_(-1),
+      frozen_file_(-1),
+      mapped_size_(0),
+      memory_(nullptr),
+      read_only_(false),
+      freezeable_(false),
+      max_size_(0) {}
 
 SharedMemory::SharedMemory(SharedMemory&& other) {
   if (this == &other) {
     return;
   }
 
   mapped_file_ = other.mapped_file_;
+  mapped_size_ = other.mapped_size_;
+  frozen_file_ = other.frozen_file_;
   memory_ = other.memory_;
   read_only_ = other.read_only_;
+  freezeable_ = other.freezeable_;
   max_size_ = other.max_size_;
 
   other.mapped_file_ = -1;
+  other.mapped_size_ = 0;
+  other.frozen_file_ = -1;
   other.memory_ = nullptr;
 }
 
 SharedMemory::~SharedMemory() { Close(); }
 
 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
   DCHECK(mapped_file_ == -1);
+  DCHECK(frozen_file_ == -1);
 
+  freezeable_ = false;
   mapped_file_ = handle.fd;
   read_only_ = read_only;
   return true;
 }
 
 // static
 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
   return handle.fd >= 0;
 }
 
+bool SharedMemory::IsValid() const { return mapped_file_ >= 0; }
+
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
 
+// Workaround for CVE-2018-4435 (https://crbug.com/project-zero/1671);
+// can be removed when minimum OS version is at least 10.12.
+#ifdef OS_MACOSX
+static const char* GetTmpDir() {
+  static const char* const kTmpDir = [] {
+    const char* tmpdir = PR_GetEnv("TMPDIR");
+    if (tmpdir) {
+      return tmpdir;
+    }
+    return "/tmp";
+  }();
+  return kTmpDir;
+}
+
+static int FakeShmOpen(const char* name, int oflag, int mode) {
+  CHECK(name[0] == '/');
+  std::string path(GetTmpDir());
+  path += name;
+  return open(path.c_str(), oflag | O_CLOEXEC | O_NOCTTY, mode);
+}
+
+static int FakeShmUnlink(const char* name) {
+  CHECK(name[0] == '/');
+  std::string path(GetTmpDir());
+  path += name;
+  return unlink(path.c_str());
+}
+
+static bool IsShmOpenSecure() {
+  static const bool kIsSecure = [] {
+    mozilla::UniqueFileHandle rwfd, rofd;
+    std::string name;
+    CHECK(SharedMemory::AppendPosixShmPrefix(&name, getpid()));
+    name += "sectest";
+    // The prefix includes the pid and this will be called at most
+    // once per process, so no need for a counter.
+    rwfd.reset(
+        HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)));
+    // An adversary could steal the name.  Handle this semi-gracefully.
+    DCHECK(rwfd);
+    if (!rwfd) {
+      return false;
+    }
+    rofd.reset(shm_open(name.c_str(), O_RDONLY, 0400));
+    CHECK(rofd);
+    CHECK(shm_unlink(name.c_str()) == 0);
+    CHECK(ftruncate(rwfd.get(), 1) == 0);
+    rwfd = nullptr;
+    void* map = mmap(nullptr, 1, PROT_READ, MAP_SHARED, rofd.get(), 0);
+    CHECK(map != MAP_FAILED);
+    bool ok = mprotect(map, 1, PROT_READ | PROT_WRITE) != 0;
+    munmap(map, 1);
+    return ok;
+  }();
+  return kIsSecure;
+}
+
+static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
+  if (!freezeable || IsShmOpenSecure()) {
+    return shm_open(name, oflag, mode);
+  } else {
+    return FakeShmOpen(name, oflag, mode);
+  }
+}
+
+static int SafeShmUnlink(bool freezeable, const char* name) {
+  if (!freezeable || IsShmOpenSecure()) {
+    return shm_unlink(name);
+  } else {
+    return FakeShmUnlink(name);
+  }
+}
+
+#elif !defined(ANDROID)
+static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
+  return shm_open(name, oflag, mode);
+}
+
+static int SafeShmUnlink(bool freezeable, const char* name) {
+  return shm_unlink(name);
+}
+#endif
+
 // static
 bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
-#if defined(ANDROID) || defined(SHM_ANON)
+#if defined(ANDROID)
   return false;
 #else
   *str += '/';
 #  ifdef OS_LINUX
   // The Snap package environment doesn't provide a private /dev/shm
   // (it's used for communication with services like PulseAudio);
   // instead AppArmor is used to restrict access to it.  Anything with
   // this prefix is allowed:
@@ -82,131 +181,164 @@ bool SharedMemory::AppendPosixShmPrefix(
   if (kSnap) {
     StringAppendF(str, "snap.%s.", kSnap);
   }
 #  endif  // OS_LINUX
   // Hopefully the "implementation defined" name length limit is long
   // enough for this.
   StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
   return true;
-#endif    // !ANDROID && !SHM_ANON
+#endif    // !ANDROID
 }
 
-bool SharedMemory::Create(size_t size) {
+bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
   read_only_ = false;
 
   DCHECK(size > 0);
   DCHECK(mapped_file_ == -1);
+  DCHECK(frozen_file_ == -1);
 
-  int fd;
+  mozilla::UniqueFileHandle fd;
+  mozilla::UniqueFileHandle frozen_fd;
   bool needs_truncate = true;
 
 #ifdef ANDROID
   // Android has its own shared memory facility:
-  fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
-  if (fd < 0) {
+  fd.reset(open("/" ASHMEM_NAME_DEF, O_RDWR, 0600));
+  if (!fd) {
     CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
     return false;
   }
-  if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
+  if (ioctl(fd.get(), ASHMEM_SET_SIZE, size) != 0) {
     CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
-    close(fd);
     return false;
   }
   needs_truncate = false;
-#elif defined(SHM_ANON)
-  // FreeBSD (or any other Unix that might decide to implement this
-  // nice, simple API):
-  fd = shm_open(SHM_ANON, O_RDWR, 0600);
 #else
   // Generic Unix: shm_open + shm_unlink
   do {
     // The names don't need to be unique, but it saves time if they
     // usually are.
     static mozilla::Atomic<size_t> sNameCounter;
     std::string name;
     CHECK(AppendPosixShmPrefix(&name, getpid()));
     StringAppendF(&name, "%zu", sNameCounter++);
     // O_EXCL means the names being predictable shouldn't be a problem.
-    fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
-    if (fd >= 0) {
-      if (shm_unlink(name.c_str()) != 0) {
+    fd.reset(HANDLE_EINTR(SafeShmOpen(freezeable, name.c_str(),
+                                      O_RDWR | O_CREAT | O_EXCL, 0600)));
+    if (fd) {
+      if (freezeable) {
+        frozen_fd.reset(HANDLE_EINTR(
+            SafeShmOpen(freezeable, name.c_str(), O_RDONLY, 0400)));
+        if (!frozen_fd) {
+          int open_err = errno;
+          SafeShmUnlink(freezeable, name.c_str());
+          DLOG(FATAL) << "failed to re-open freezeable shm: "
+                      << strerror(open_err);
+          return false;
+        }
+      }
+      if (SafeShmUnlink(freezeable, name.c_str()) != 0) {
         // This shouldn't happen, but if it does: assume the file is
         // in fact leaked, and bail out now while it's still 0-length.
         DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
         return false;
       }
     }
-  } while (fd < 0 && errno == EEXIST);
+  } while (!fd && errno == EEXIST);
 #endif
 
-  if (fd < 0) {
+  if (!fd) {
     CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
     return false;
   }
 
   if (needs_truncate) {
-    if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
+    if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))) != 0) {
       CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
-      close(fd);
       return false;
     }
   }
 
-  mapped_file_ = fd;
+  mapped_file_ = fd.release();
+  frozen_file_ = frozen_fd.release();
   max_size_ = size;
+  freezeable_ = freezeable;
+  return true;
+}
+
+bool SharedMemory::Freeze() {
+  DCHECK(!read_only_);
+  CHECK(freezeable_);
+  Unmap();
+
+#ifdef ANDROID
+  if (ioctl(mapped_file_, ASHMEM_SET_PROT_MASK, PROT_READ) != 0) {
+    CHROMIUM_LOG(WARNING) << "failed to freeze shm: " << strerror(errno);
+    return false;
+  }
+#else
+  DCHECK(frozen_file_ >= 0);
+  DCHECK(mapped_file_ >= 0);
+  close(mapped_file_);
+  mapped_file_ = frozen_file_;
+  frozen_file_ = -1;
+#endif
+
+  read_only_ = true;
+  freezeable_ = false;
   return true;
 }
 
 bool SharedMemory::Map(size_t bytes, void* fixed_address) {
   if (mapped_file_ == -1) return false;
+  DCHECK(!memory_);
 
   // Don't use MAP_FIXED when a fixed_address was specified, since that can
   // replace pages that are alread mapped at that address.
-  memory_ =
+  void* mem =
       mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
            MAP_SHARED, mapped_file_, 0);
 
-  bool mmap_succeeded = memory_ != MAP_FAILED;
-
-  DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
-
-  if (mmap_succeeded) {
-    if (fixed_address && memory_ != fixed_address) {
-      bool munmap_succeeded = munmap(memory_, bytes) == 0;
-      DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
-      memory_ = NULL;
-      return false;
-    }
-
-    max_size_ = bytes;
+  if (mem == MAP_FAILED) {
+    CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno);
+    return false;
   }
 
-  return mmap_succeeded;
+  if (fixed_address && mem != fixed_address) {
+    bool munmap_succeeded = munmap(mem, bytes) == 0;
+    DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
+    return false;
+  }
+
+  memory_ = mem;
+  mapped_size_ = bytes;
+  return true;
 }
 
 bool SharedMemory::Unmap() {
   if (memory_ == NULL) return false;
 
-  munmap(memory_, max_size_);
+  munmap(memory_, mapped_size_);
   memory_ = NULL;
-  max_size_ = 0;
+  mapped_size_ = 0;
   return true;
 }
 
 void* SharedMemory::FindFreeAddressSpace(size_t size) {
   void* memory =
       mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
   munmap(memory, size);
   return memory != MAP_FAILED ? memory : NULL;
 }
 
 bool SharedMemory::ShareToProcessCommon(ProcessId processId,
                                         SharedMemoryHandle* new_handle,
                                         bool close_self) {
+  freezeable_ = false;
   const int new_fd = dup(mapped_file_);
   DCHECK(new_fd >= -1);
   new_handle->fd = new_fd;
   new_handle->auto_close = true;
 
   if (close_self) Close();
 
   return true;
@@ -216,15 +348,25 @@ void SharedMemory::Close(bool unmap_view
   if (unmap_view) {
     Unmap();
   }
 
   if (mapped_file_ >= 0) {
     close(mapped_file_);
     mapped_file_ = -1;
   }
+  if (frozen_file_ >= 0) {
+    CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen";
+    close(frozen_file_);
+    frozen_file_ = -1;
+  }
 }
 
-SharedMemoryHandle SharedMemory::handle() const {
-  return FileDescriptor(mapped_file_, false);
+mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
+  mozilla::UniqueFileHandle fh(mapped_file_);
+  mapped_file_ = -1;
+  // Now that the main fd is removed, reset everything else: close the
+  // frozen fd if present and unmap the memory if mapped.
+  Close();
+  return fh;
 }
 
 }  // namespace base
--- a/ipc/chromium/src/base/shared_memory_win.cc
+++ b/ipc/chromium/src/base/shared_memory_win.cc
@@ -5,16 +5,20 @@
 // found in the LICENSE file.
 
 #include "base/shared_memory.h"
 
 #include "base/logging.h"
 #include "base/win_util.h"
 #include "base/string_util.h"
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/RandomNum.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDebug.h"
+#include "nsString.h"
 
 namespace {
 // NtQuerySection is an internal (but believed to be stable) API and the
 // structures it uses are defined in nt_internals.h.
 // So we have to define them ourselves.
 typedef enum _SECTION_INFORMATION_CLASS {
   SectionBasicInformation,
 } SECTION_INFORMATION_CLASS;
@@ -54,63 +58,114 @@ bool IsSectionSafeToMap(HANDLE handle) {
 
 namespace base {
 
 SharedMemory::SharedMemory()
     : external_section_(false),
       mapped_file_(NULL),
       memory_(NULL),
       read_only_(false),
+      freezeable_(false),
       max_size_(0) {}
 
 SharedMemory::SharedMemory(SharedMemory&& other) {
   if (this == &other) {
     return;
   }
 
   mapped_file_ = other.mapped_file_;
   memory_ = other.memory_;
   read_only_ = other.read_only_;
   max_size_ = other.max_size_;
+  freezeable_ = other.freezeable_;
   external_section_ = other.external_section_;
 
   other.mapped_file_ = nullptr;
   other.memory_ = nullptr;
 }
 
 SharedMemory::~SharedMemory() {
   external_section_ = true;
   Close();
 }
 
 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
   DCHECK(mapped_file_ == NULL);
 
   external_section_ = true;
+  freezeable_ = false;  // just in case
   mapped_file_ = handle;
   read_only_ = read_only;
   return true;
 }
 
 // static
 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
   return handle != NULL;
 }
 
+bool SharedMemory::IsValid() const { return mapped_file_ != NULL; }
+
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() { return NULL; }
 
-bool SharedMemory::Create(size_t size) {
+bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
   DCHECK(mapped_file_ == NULL);
   read_only_ = false;
-  mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
-                                   0, static_cast<DWORD>(size), NULL);
+
+  SECURITY_ATTRIBUTES sa;
+  SECURITY_DESCRIPTOR sd;
+  ACL dacl;
+
+  sa.nLength = sizeof(sa);
+  sa.lpSecurityDescriptor = &sd;
+  sa.bInheritHandle = FALSE;
+
+  if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) ||
+      NS_WARN_IF(
+          !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) ||
+      NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) {
+    return false;
+  }
+
+  nsAutoStringN<sizeof("MozSharedMem_") + 16 * 4> name;
+  if (!mozilla::IsWin8Point1OrLater()) {
+    name.AssignLiteral("MozSharedMem_");
+    for (size_t i = 0; i < 4; ++i) {
+      mozilla::Maybe<uint64_t> randomNum = mozilla::RandomUint64();
+      if (NS_WARN_IF(randomNum.isNothing())) {
+        return false;
+      }
+      name.AppendPrintf("%016llx", *randomNum);
+    }
+  }
+
+  mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE,
+                                   0, static_cast<DWORD>(size),
+                                   name.IsEmpty() ? nullptr : name.get());
   if (!mapped_file_) return false;
 
   max_size_ = size;
+  freezeable_ = freezeable;
+  return true;
+}
+
+bool SharedMemory::Freeze() {
+  DCHECK(!read_only_);
+  CHECK(freezeable_);
+  Unmap();
+
+  if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_, GetCurrentProcess(),
+                         &mapped_file_, GENERIC_READ | FILE_MAP_READ, false,
+                         DUPLICATE_CLOSE_SOURCE)) {
+    return false;
+  }
+
+  read_only_ = true;
+  freezeable_ = false;
   return true;
 }
 
 bool SharedMemory::Map(size_t bytes, void* fixed_address) {
   if (mapped_file_ == NULL) return false;
 
   if (external_section_ && !IsSectionSafeToMap(mapped_file_)) {
     return false;
@@ -141,16 +196,17 @@ void* SharedMemory::FindFreeAddressSpace
     VirtualFree(memory, 0, MEM_RELEASE);
   }
   return memory;
 }
 
 bool SharedMemory::ShareToProcessCommon(ProcessId processId,
                                         SharedMemoryHandle* new_handle,
                                         bool close_self) {
+  freezeable_ = false;
   *new_handle = 0;
   DWORD access = FILE_MAP_READ | SECTION_QUERY;
   DWORD options = 0;
   HANDLE mapped_file = mapped_file_;
   HANDLE result;
   if (!read_only_) access |= FILE_MAP_WRITE;
   if (close_self) {
     // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
@@ -179,11 +235,16 @@ void SharedMemory::Close(bool unmap_view
   }
 
   if (mapped_file_ != NULL) {
     CloseHandle(mapped_file_);
     mapped_file_ = NULL;
   }
 }
 
-SharedMemoryHandle SharedMemory::handle() const { return mapped_file_; }
+mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
+  mozilla::UniqueFileHandle fh(mapped_file_);
+  mapped_file_ = NULL;
+  Unmap();
+  return fh;
+}
 
 }  // namespace base
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -22,37 +22,32 @@ void SetThisProcessName(const char* aNam
 class SharedPreferenceSerializer final {
  public:
   SharedPreferenceSerializer();
   SharedPreferenceSerializer(SharedPreferenceSerializer&& aOther);
   ~SharedPreferenceSerializer();
 
   bool SerializeToSharedMemory();
 
-  base::SharedMemoryHandle GetSharedMemoryHandle() const {
-    return mShm.handle();
-  }
+  size_t GetPrefMapSize() const { return mPrefMapSize; }
+  size_t GetPrefsLength() const { return mPrefsLength; }
 
-  const FileDescriptor::UniquePlatformHandle& GetPrefMapHandle() const {
-    return mPrefMapHandle;
-  }
+  const UniqueFileHandle& GetPrefsHandle() const { return mPrefsHandle; }
 
-  nsACString::size_type GetPrefLength() const { return mPrefs.Length(); }
-
-  size_t GetPrefMapSize() const { return mPrefMapSize; }
+  const UniqueFileHandle& GetPrefMapHandle() const { return mPrefMapHandle; }
 
   void AddSharedPrefCmdLineArgs(GeckoChildProcessHost& procHost,
                                 std::vector<std::string>& aExtraOpts) const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer);
   size_t mPrefMapSize;
-  FileDescriptor::UniquePlatformHandle mPrefMapHandle;
-  base::SharedMemory mShm;
-  nsAutoCStringN<1024> mPrefs;
+  size_t mPrefsLength;
+  UniqueFileHandle mPrefMapHandle;
+  UniqueFileHandle mPrefsHandle;
 };
 
 class SharedPreferenceDeserializer final {
  public:
   SharedPreferenceDeserializer();
   ~SharedPreferenceDeserializer();
 
   bool DeserializeFromSharedMemory(char* aPrefsHandleStr,
--- a/ipc/glue/ProcessUtils_common.cpp
+++ b/ipc/glue/ProcessUtils_common.cpp
@@ -19,79 +19,82 @@ SharedPreferenceSerializer::SharedPrefer
 
 SharedPreferenceSerializer::~SharedPreferenceSerializer() {
   MOZ_COUNT_DTOR(SharedPreferenceSerializer);
 }
 
 SharedPreferenceSerializer::SharedPreferenceSerializer(
     SharedPreferenceSerializer&& aOther)
     : mPrefMapSize(aOther.mPrefMapSize),
+      mPrefsLength(aOther.mPrefsLength),
       mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
-      mShm(std::move(aOther.mShm)),
-      mPrefs(std::move(aOther.mPrefs)) {
+      mPrefsHandle(std::move(aOther.mPrefsHandle)) {
   MOZ_COUNT_CTOR(SharedPreferenceSerializer);
 }
 
 bool SharedPreferenceSerializer::SerializeToSharedMemory() {
   mPrefMapHandle =
-      Preferences::EnsureSnapshot(&mPrefMapSize).ClonePlatformHandle();
+      Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle();
 
   // Serialize the early prefs.
-  Preferences::SerializePreferences(mPrefs);
+  nsAutoCStringN<1024> prefs;
+  Preferences::SerializePreferences(prefs);
+  mPrefsLength = prefs.Length();
 
+  base::SharedMemory shm;
   // Set up the shared memory.
-  if (!mShm.Create(mPrefs.Length())) {
+  if (!shm.Create(prefs.Length())) {
     NS_ERROR("failed to create shared memory in the parent");
     return false;
   }
-  if (!mShm.Map(mPrefs.Length())) {
+  if (!shm.Map(prefs.Length())) {
     NS_ERROR("failed to map shared memory in the parent");
     return false;
   }
 
   // Copy the serialized prefs into the shared memory.
-  memcpy(static_cast<char*>(mShm.memory()), mPrefs.get(), mPrefs.Length());
+  memcpy(static_cast<char*>(shm.memory()), prefs.get(), mPrefsLength);
 
+  mPrefsHandle = shm.TakeHandle();
   return true;
 }
 
 void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
     mozilla::ipc::GeckoChildProcessHost& procHost,
     std::vector<std::string>& aExtraOpts) const {
   // Formats a pointer or pointer-sized-integer as a string suitable for passing
   // in an arguments list.
   auto formatPtrArg = [](auto arg) {
     return nsPrintfCString("%zu", uintptr_t(arg));
   };
 
 #if defined(XP_WIN)
   // Record the handle as to-be-shared, and pass it via a command flag. This
   // works because Windows handles are system-wide.
-  HANDLE prefsHandle = GetSharedMemoryHandle();
-  procHost.AddHandleToShare(prefsHandle);
+  procHost.AddHandleToShare(GetPrefsHandle().get());
   procHost.AddHandleToShare(GetPrefMapHandle().get());
   aExtraOpts.push_back("-prefsHandle");
-  aExtraOpts.push_back(formatPtrArg(prefsHandle).get());
+  aExtraOpts.push_back(formatPtrArg(GetPrefsHandle().get()).get());
   aExtraOpts.push_back("-prefMapHandle");
   aExtraOpts.push_back(formatPtrArg(GetPrefMapHandle().get()).get());
 #else
   // In contrast, Unix fds are per-process. So remap the fd to a fixed one that
   // will be used in the child.
   // XXX: bug 1440207 is about improving how fixed fds are used.
   //
   // Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
   // and the fixed fd isn't used. However, we still need to mark it for
   // remapping so it doesn't get closed in the child.
-  procHost.AddFdToRemap(GetSharedMemoryHandle().fd, kPrefsFileDescriptor);
+  procHost.AddFdToRemap(GetPrefsHandle().get(), kPrefsFileDescriptor);
   procHost.AddFdToRemap(GetPrefMapHandle().get(), kPrefMapFileDescriptor);
 #endif
 
   // Pass the lengths via command line flags.
   aExtraOpts.push_back("-prefsLen");
-  aExtraOpts.push_back(formatPtrArg(GetPrefLength()).get());
+  aExtraOpts.push_back(formatPtrArg(GetPrefsLength()).get());
   aExtraOpts.push_back("-prefMapSize");
   aExtraOpts.push_back(formatPtrArg(GetPrefMapSize()).get());
 }
 
 #ifdef ANDROID
 static int gPrefsFd = -1;
 static int gPrefMapFd = -1;
 
--- a/ipc/glue/SharedMemory.h
+++ b/ipc/glue/SharedMemory.h
@@ -78,16 +78,18 @@ class SharedMemory {
   }
 
   // bug 1168843, compositor thread may create shared memory instances that are
   // destroyed by main thread on shutdown, so this must use thread-safe RC to
   // avoid hitting assertion
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedMemory)
 
   static void SystemProtect(char* aAddr, size_t aSize, int aRights);
+  static MOZ_MUST_USE bool SystemProtectFallible(char* aAddr, size_t aSize,
+                                                 int aRights);
   static size_t SystemPageSize();
   static size_t PageAlignedSize(size_t aSize);
 
  protected:
   SharedMemory();
 
   // Implementations should call these methods on shmem usage changes,
   // but *only if* the OS-specific calls are known to have succeeded.
--- a/ipc/glue/SharedMemory_posix.cpp
+++ b/ipc/glue/SharedMemory_posix.cpp
@@ -8,20 +8,27 @@
 #include <unistd.h>    // sysconf
 
 #include "mozilla/ipc/SharedMemory.h"
 
 namespace mozilla {
 namespace ipc {
 
 void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
+  if (!SystemProtectFallible(aAddr, aSize, aRights)) {
+    MOZ_CRASH("can't mprotect()");
+  }
+}
+
+bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
+                                         int aRights) {
   int flags = 0;
   if (aRights & RightsRead) flags |= PROT_READ;
   if (aRights & RightsWrite) flags |= PROT_WRITE;
   if (RightsNone == aRights) flags = PROT_NONE;
 
-  if (0 < mprotect(aAddr, aSize, flags)) MOZ_CRASH("can't mprotect()");
+  return 0 == mprotect(aAddr, aSize, flags);
 }
 
 size_t SharedMemory::SystemPageSize() { return sysconf(_SC_PAGESIZE); }
 
 }  // namespace ipc
 }  // namespace mozilla
--- a/ipc/glue/SharedMemory_windows.cpp
+++ b/ipc/glue/SharedMemory_windows.cpp
@@ -7,27 +7,33 @@
 #include <windows.h>
 
 #include "mozilla/ipc/SharedMemory.h"
 
 namespace mozilla {
 namespace ipc {
 
 void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
+  if (!SystemProtectFallible(aAddr, aSize, aRights)) {
+    MOZ_CRASH("can't VirtualProtect()");
+  }
+}
+
+bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
+                                         int aRights) {
   DWORD flags;
   if ((aRights & RightsRead) && (aRights & RightsWrite))
     flags = PAGE_READWRITE;
   else if (aRights & RightsRead)
     flags = PAGE_READONLY;
   else
     flags = PAGE_NOACCESS;
 
   DWORD oldflags;
-  if (!VirtualProtect(aAddr, aSize, flags, &oldflags))
-    MOZ_CRASH("can't VirtualProtect()");
+  return VirtualProtect(aAddr, aSize, flags, &oldflags);
 }
 
 size_t SharedMemory::SystemPageSize() {
   SYSTEM_INFO si;
   GetSystemInfo(&si);
   return si.dwPageSize;
 }
 
new file mode 100644
--- /dev/null
+++ b/ipc/gtest/TestSharedMemory.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "base/shared_memory.h"
+
+#include "base/process_util.h"
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+
+// Try to map a frozen shm for writing.  Threat model: the process is
+// compromised and then receives a frozen handle.
+TEST(IPCSharedMemory, FreezeAndMapRW)
+{
+  base::SharedMemory shm;
+
+  // Create and initialize
+  ASSERT_TRUE(shm.CreateFreezeable(1));
+  ASSERT_TRUE(shm.Map(1));
+  auto mem = reinterpret_cast<char*>(shm.memory());
+  ASSERT_TRUE(mem);
+  *mem = 'A';
+
+  // Freeze
+  ASSERT_TRUE(shm.Freeze());
+  ASSERT_FALSE(shm.memory());
+
+  // Re-create as writeable
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
+  ASSERT_TRUE(shm.IsHandleValid(handle));
+  ASSERT_FALSE(shm.IsValid());
+  ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ false));
+  ASSERT_TRUE(shm.IsValid());
+
+  // This should fail
+  EXPECT_FALSE(shm.Map(1));
+}
+
+// Try to restore write permissions to a frozen mapping.  Threat
+// model: the process has mapped frozen shm normally and then is
+// compromised, or as for FreezeAndMapRW (see also the
+// proof-of-concept at https://crbug.com/project-zero/1671 ).
+TEST(IPCSharedMemory, FreezeAndReprotect)
+{
+  base::SharedMemory shm;
+
+  // Create and initialize
+  ASSERT_TRUE(shm.CreateFreezeable(1));
+  ASSERT_TRUE(shm.Map(1));
+  auto mem = reinterpret_cast<char*>(shm.memory());
+  ASSERT_TRUE(mem);
+  *mem = 'A';
+
+  // Freeze
+  ASSERT_TRUE(shm.Freeze());
+  ASSERT_FALSE(shm.memory());
+
+  // Re-map
+  ASSERT_TRUE(shm.Map(1));
+  mem = reinterpret_cast<char*>(shm.memory());
+  ASSERT_EQ(*mem, 'A');
+
+  // Try to alter protection; should fail
+  EXPECT_FALSE(ipc::SharedMemory::SystemProtectFallible(
+      mem, 1, ipc::SharedMemory::RightsReadWrite));
+}
+
+#ifndef XP_WIN
+// This essentially tests whether FreezeAndReprotect would have failed
+// without the freeze.  It doesn't work on Windows: VirtualProtect
+// can't exceed the permissions set in MapViewOfFile regardless of the
+// security status of the original handle.
+TEST(IPCSharedMemory, Reprotect)
+{
+  base::SharedMemory shm;
+
+  // Create and initialize
+  ASSERT_TRUE(shm.CreateFreezeable(1));
+  ASSERT_TRUE(shm.Map(1));
+  auto mem = reinterpret_cast<char*>(shm.memory());
+  ASSERT_TRUE(mem);
+  *mem = 'A';
+
+  // Re-create as read-only
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
+  ASSERT_TRUE(shm.IsHandleValid(handle));
+  ASSERT_FALSE(shm.IsValid());
+  ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ true));
+  ASSERT_TRUE(shm.IsValid());
+
+  // Re-map
+  ASSERT_TRUE(shm.Map(1));
+  mem = reinterpret_cast<char*>(shm.memory());
+  ASSERT_EQ(*mem, 'A');
+
+  // Try to alter protection; should succeed, because not frozen
+  EXPECT_TRUE(ipc::SharedMemory::SystemProtectFallible(
+      mem, 1, ipc::SharedMemory::RightsReadWrite));
+}
+#endif
+
+}  // namespace mozilla
copy from ipc/moz.build
copy to ipc/gtest/moz.build
--- a/ipc/moz.build
+++ b/ipc/gtest/moz.build
@@ -1,23 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIRS += [
-    'chromium',
-    'glue',
-    'ipdl',
-    'testshell',
+Library('ipctest')
+
+SOURCES += [
+    'TestSharedMemory.cpp',
 ]
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
-    DIRS += ['contentproc']
+include('/ipc/chromium/chromium-config.mozbuild')
 
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DIRS += ['mscom']
-
-DIRS += ['app']
-
-with Files("**"):
-    BUG_COMPONENT = ("Core", "IPC")
+FINAL_LIBRARY = 'xul-gtest'
--- a/ipc/moz.build
+++ b/ipc/moz.build
@@ -1,23 +1,26 @@
 # -*- 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/.
 
 DIRS += [
+    'app',
     'chromium',
     'glue',
     'ipdl',
     'testshell',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     DIRS += ['contentproc']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['mscom']
 
-DIRS += ['app']
+TEST_DIRS += [
+    'gtest',
+]
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "IPC")
--- a/js/src/debugger/Script.cpp
+++ b/js/src/debugger/Script.cpp
@@ -251,37 +251,36 @@ template <typename Result>
 Result CallScriptMethod(HandleDebuggerScript obj,
                         Result (JSScript::*ifJSScript)() const,
                         Result (LazyScript::*ifLazyScript)() const) {
   if (obj->getReferent().is<JSScript*>()) {
     JSScript* script = obj->getReferent().as<JSScript*>();
     return (script->*ifJSScript)();
   }
 
+  MOZ_ASSERT(obj->getReferent().is<LazyScript*>());
   LazyScript* lazyScript = obj->getReferent().as<LazyScript*>();
   return (lazyScript->*ifLazyScript)();
 }
 
 /* static */
 bool DebuggerScript::getIsGeneratorFunction(JSContext* cx, unsigned argc,
                                             Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isGeneratorFunction)",
                                      args, obj);
-  args.rval().setBoolean(
-      CallScriptMethod(obj, &JSScript::isGenerator, &LazyScript::isGenerator));
+  args.rval().setBoolean(obj->getReferentScript()->isGenerator());
   return true;
 }
 
 /* static */
 bool DebuggerScript::getIsAsyncFunction(JSContext* cx, unsigned argc,
                                         Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isAsyncFunction)",
                                      args, obj);
-  args.rval().setBoolean(
-      CallScriptMethod(obj, &JSScript::isAsync, &LazyScript::isAsync));
+  args.rval().setBoolean(obj->getReferentScript()->isAsync());
   return true;
 }
 
 /* static */
 bool DebuggerScript::getIsModule(JSContext* cx, unsigned argc, Value* vp) {
   THIS_DEBUGSCRIPT_SCRIPT_MAYBE_LAZY(cx, argc, vp, "(get isModule)", args, obj);
   DebuggerScriptReferent referent = obj->getReferent();
   args.rval().setBoolean(referent.is<JSScript*>() &&
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -1144,17 +1144,17 @@ XDRResult js::XDRScript(XDRState<mode>* 
       return xdr->fail(JS::TranscodeResult_Throw);
     }
     scriptp.set(script);
 
     script->lineno_ = lineno;
     script->column_ = column;
     script->immutableFlags_ = immutableFlags;
 
-    if (script->hasFlag(ImmutableFlags::ArgsHasVarBinding)) {
+    if (script->argumentsHasVarBinding()) {
       // Call setArgumentsHasVarBinding to initialize the
       // NeedsArgsAnalysis flag.
       script->setArgumentsHasVarBinding();
     }
 
     // Set the script in its function now so that inner scripts to be
     // decoded may iterate the static scope chain.
     if (fun) {
@@ -4926,17 +4926,17 @@ Scope* JSScript::lookupScope(jsbytecode*
 Scope* JSScript::innermostScope(jsbytecode* pc) {
   if (Scope* scope = lookupScope(pc)) {
     return scope;
   }
   return bodyScope();
 }
 
 void JSScript::setArgumentsHasVarBinding() {
-  setFlag(ImmutableFlags::ArgsHasVarBinding);
+  setFlag(ImmutableFlags::ArgumentsHasVarBinding);
   setFlag(MutableFlags::NeedsArgsAnalysis);
 }
 
 void JSScript::setNeedsArgsObj(bool needsArgsObj) {
   MOZ_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
   clearFlag(MutableFlags::NeedsArgsAnalysis);
   setFlag(MutableFlags::NeedsArgsObj, needsArgsObj);
 }
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1332,16 +1332,19 @@ class ScriptSourceObject : public Native
     ELEMENT_SLOT,
     ELEMENT_PROPERTY_SLOT,
     INTRODUCTION_SCRIPT_SLOT,
     PRIVATE_SLOT,
     RESERVED_SLOTS
   };
 };
 
+enum class GeneratorKind : bool { NotGenerator, Generator };
+enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
+
 // This class contains fields and accessors that are common to both lazy and
 // non-lazy interpreted scripts. This must be located at offset +0 of any
 // derived classes in order for the 'jitCodeRaw' mechanism to work with the
 // JITs.
 class BaseScript : public gc::TenuredCell {
  protected:
   // Pointer to baseline->method()->raw(), ion->method()->raw(), a wasm jit
   // entry, the JIT's EnterInterpreter stub, or the lazy link stub. Must be
@@ -1379,17 +1382,17 @@ class BaseScript : public gc::TenuredCel
   uint32_t toStringStart_ = 0;
   uint32_t toStringEnd_ = 0;
 
   // Line and column of |sourceStart_| position.
   uint32_t lineno_ = 0;
   uint32_t column_ = 0;  // Count of Code Points
 
   // See ImmutableFlags / MutableFlags below for definitions. These are stored
-  // as uint32_t instead of bitfields to make it more predicatable to access
+  // as uint32_t instead of bitfields to make it more predictable to access
   // from JIT code.
   uint32_t immutableFlags_ = 0;
   uint32_t mutableFlags_ = 0;
 
   BaseScript(uint8_t* stubEntry, ScriptSourceObject* sourceObject,
              uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
              uint32_t toStringEnd)
       : jitCodeRaw_(stubEntry),
@@ -1403,16 +1406,19 @@ class BaseScript : public gc::TenuredCel
     MOZ_ASSERT(sourceEnd <= toStringEnd);
   }
 
  public:
   // Immutable flags should not be modified after this script has been
   // initialized. These flags should likely be preserved when serializing
   // (XDR) or copying (CopyScript) this script. This is only public for the
   // JITs.
+  //
+  // Specific accessors for flag values are defined with
+  // IMMUTABLE_FLAG_* macros below.
   enum class ImmutableFlags : uint32_t {
     // No need for result value of last expression statement.
     NoScriptRval = 1 << 0,
 
     // Code is in strict mode.
     Strict = 1 << 1,
 
     // (1 << 2) is unused.
@@ -1465,17 +1471,17 @@ class BaseScript : public gc::TenuredCel
 
     // Set if this function is an async function or async generator.
     IsAsync = 1 << 19,
 
     // Set if this function has a rest parameter.
     HasRest = 1 << 20,
 
     // See comments below.
-    ArgsHasVarBinding = 1 << 21,
+    ArgumentsHasVarBinding = 1 << 21,
 
     // Script came from eval().
     IsForEval = 1 << 22,
 
     // Whether this is a top-level module script.
     IsModule = 1 << 23,
 
     // Whether this function needs a call object or named lambda environment.
@@ -1487,16 +1493,19 @@ class BaseScript : public gc::TenuredCel
     // (1 << 26) is unused.
 
     // Whether this script contains a direct eval statement.
     HasDirectEval = 1 << 27,
   };
 
   // Mutable flags typically store information about runtime or deoptimization
   // behavior of this script. This is only public for the JITs.
+  //
+  // Specific accessors for flag values are defined with
+  // MUTABLE_FLAG_* macros below.
   enum class MutableFlags : uint32_t {
     // Number of times the |warmUpCount| was forcibly discarded. The counter is
     // reset when a script is successfully jit-compiled.
     WarmupResets_MASK = 0xFF,
 
     // Have warned about uses of undefined properties in this script.
     WarnedAboutUndefinedProp = 1 << 8,
 
@@ -1606,31 +1615,155 @@ class BaseScript : public gc::TenuredCel
     } else {
       clearFlag(flag);
     }
   }
   void clearFlag(MutableFlags flag) { mutableFlags_ &= ~uint32_t(flag); }
 
   void traceChildren(JSTracer* trc);
 
+  // Specific flag accessors
+
+#define FLAG_GETTER(enumName, enumEntry, lowerName) \
+ public:                                            \
+  bool lowerName() const { return hasFlag(enumName::enumEntry); }
+
+#define FLAG_GETTER_SETTER(enumName, enumEntry, setterLevel, lowerName, name) \
+setterLevel:                                                                  \
+  void set##name() { setFlag(enumName::enumEntry); }                          \
+  void set##name(bool b) { setFlag(enumName::enumEntry, b); }                 \
+  void clear##name() { clearFlag(enumName::enumEntry); }                      \
+                                                                              \
+ public:                                                                      \
+  bool lowerName() const { return hasFlag(enumName::enumEntry); }
+
+#define IMMUTABLE_FLAG_GETTER(lowerName, name) \
+  FLAG_GETTER(ImmutableFlags, name, lowerName)
+#define IMMUTABLE_FLAG_GETTER_SETTER(lowerName, name) \
+  FLAG_GETTER_SETTER(ImmutableFlags, name, protected, lowerName, name)
+#define IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(lowerName, name) \
+  FLAG_GETTER_SETTER(ImmutableFlags, name, public, lowerName, name)
+#define IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(enumName, lowerName, name) \
+  FLAG_GETTER_SETTER(ImmutableFlags, enumName, public, lowerName, name)
+#define MUTABLE_FLAG_GETTER(lowerName, name) \
+  FLAG_GETTER(MutableFlags, name, lowerName)
+#define MUTABLE_FLAG_GETTER_SETTER(lowerName, name) \
+  FLAG_GETTER_SETTER(MutableFlags, name, public, lowerName, name)
+
+  IMMUTABLE_FLAG_GETTER(noScriptRval, NoScriptRval)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(strict, Strict)
+  IMMUTABLE_FLAG_GETTER(hasNonSyntacticScope, HasNonSyntacticScope)
+  IMMUTABLE_FLAG_GETTER(selfHosted, SelfHosted)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(bindingsAccessedDynamically,
+                                      BindingsAccessedDynamically)
+  IMMUTABLE_FLAG_GETTER(funHasExtensibleScope, FunHasExtensibleScope)
+  IMMUTABLE_FLAG_GETTER(hasCallSiteObj, HasCallSiteObj)
+  IMMUTABLE_FLAG_GETTER_SETTER(functionHasThisBinding, FunctionHasThisBinding)
+  // Alternate shorter name used throughout the codebase
+  IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(FunctionHasThisBinding,
+                                             hasThisBinding, HasThisBinding)
+  // FunctionHasExtraBodyVarScope: custom logic below.
+  IMMUTABLE_FLAG_GETTER_SETTER(hasMappedArgsObj, HasMappedArgsObj)
+  IMMUTABLE_FLAG_GETTER_SETTER(hasInnerFunctions, HasInnerFunctions)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(needsHomeObject, NeedsHomeObject)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(isDerivedClassConstructor,
+                                      IsDerivedClassConstructor)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(isDefaultClassConstructor,
+                                      IsDefaultClassConstructor)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(treatAsRunOnce, TreatAsRunOnce)
+  IMMUTABLE_FLAG_GETTER_SETTER(isLikelyConstructorWrapper,
+                               IsLikelyConstructorWrapper)
+  // alternate name used throughout the codebase
+  IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC(IsLikelyConstructorWrapper,
+                                             likelyConstructorWrapper,
+                                             LikelyConstructorWrapper)
+  IMMUTABLE_FLAG_GETTER(isGenerator, IsGenerator)
+  IMMUTABLE_FLAG_GETTER(isAsync, IsAsync)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasRest, HasRest)
+  // See ContextFlags::funArgumentsHasLocalBinding comment.
+  // N.B.: no setter -- custom logic in JSScript.
+  IMMUTABLE_FLAG_GETTER(argumentsHasVarBinding, ArgumentsHasVarBinding)
+  // IsForEval: custom logic below.
+  // IsModule: custom logic below.
+  IMMUTABLE_FLAG_GETTER_SETTER(needsFunctionEnvironmentObjects,
+                               NeedsFunctionEnvironmentObjects)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(shouldDeclareArguments,
+                                      ShouldDeclareArguments)
+  IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC(hasDirectEval, HasDirectEval)
+
+  MUTABLE_FLAG_GETTER_SETTER(warnedAboutUndefinedProp, WarnedAboutUndefinedProp)
+  MUTABLE_FLAG_GETTER_SETTER(hasRunOnce, HasRunOnce)
+  MUTABLE_FLAG_GETTER_SETTER(hasBeenCloned, HasBeenCloned)
+  MUTABLE_FLAG_GETTER_SETTER(trackRecordReplayProgress,
+                             TrackRecordReplayProgress)
+  // N.B.: no setter -- custom logic in JSScript.
+  MUTABLE_FLAG_GETTER(hasScriptCounts, HasScriptCounts)
+  // Access the flag for whether this script has a DebugScript in its realm's
+  // map. This should only be used by the DebugScript class.
+  MUTABLE_FLAG_GETTER_SETTER(hasDebugScript, HasDebugScript)
+  MUTABLE_FLAG_GETTER_SETTER(doNotRelazify, DoNotRelazify)
+  MUTABLE_FLAG_GETTER_SETTER(failedBoundsCheck, FailedBoundsCheck)
+  MUTABLE_FLAG_GETTER_SETTER(failedShapeGuard, FailedShapeGuard)
+  MUTABLE_FLAG_GETTER_SETTER(hadFrequentBailouts, HadFrequentBailouts)
+  MUTABLE_FLAG_GETTER_SETTER(hadOverflowBailout, HadOverflowBailout)
+  MUTABLE_FLAG_GETTER_SETTER(uninlineable, Uninlineable)
+  MUTABLE_FLAG_GETTER_SETTER(invalidatedIdempotentCache,
+                             InvalidatedIdempotentCache)
+  MUTABLE_FLAG_GETTER_SETTER(failedLexicalCheck, FailedLexicalCheck)
+  MUTABLE_FLAG_GETTER_SETTER(needsArgsAnalysis, NeedsArgsAnalysis)
+  // NeedsArgsObj: custom logic below.
+  MUTABLE_FLAG_GETTER_SETTER(hideScriptFromDebugger, HideScriptFromDebugger)
+  MUTABLE_FLAG_GETTER_SETTER(spewEnabled, SpewEnabled)
+
+#undef IMMUTABLE_FLAG_GETTER
+#undef IMMUTABLE_FLAG_GETTER_SETTER
+#undef IMMUTABLE_FLAG_GETTER_SETTER_PUBLIC
+#undef IMMUTABLE_FLAG_GETTER_SETTER_CUSTOM_PUBLIC
+#undef MUTABLE_FLAG_GETTER
+#undef MUTABLE_FLAG_GETTER_SETTER
+#undef FLAG_GETTER
+#undef FLAG_GETTER_SETTER
+
+  GeneratorKind generatorKind() const {
+    return isGenerator() ? GeneratorKind::Generator
+                         : GeneratorKind::NotGenerator;
+  }
+
+  void setGeneratorKind(GeneratorKind kind) {
+    // A script only gets its generator kind set as part of initialization,
+    // so it can only transition from NotGenerator.
+    MOZ_ASSERT(!isGenerator());
+    if (kind == GeneratorKind::Generator) {
+      setFlag(ImmutableFlags::IsGenerator);
+    }
+  }
+
+  FunctionAsyncKind asyncKind() const {
+    return isAsync() ? FunctionAsyncKind::AsyncFunction
+                     : FunctionAsyncKind::SyncFunction;
+  }
+
+  void setAsyncKind(FunctionAsyncKind kind) {
+    if (kind == FunctionAsyncKind::AsyncFunction) {
+      setFlag(ImmutableFlags::IsAsync);
+    }
+  }
+
   // JIT accessors
   static constexpr size_t offsetOfJitCodeRaw() {
     return offsetof(BaseScript, jitCodeRaw_);
   }
   static size_t offsetOfImmutableFlags() {
     return offsetof(BaseScript, immutableFlags_);
   }
   static constexpr size_t offsetOfMutableFlags() {
     return offsetof(BaseScript, mutableFlags_);
   }
 };
 
-enum class GeneratorKind : bool { NotGenerator, Generator };
-enum class FunctionAsyncKind : bool { SyncFunction, AsyncFunction };
-
 struct FieldInitializers {
 #ifdef DEBUG
   bool valid;
 #endif
   // This struct will eventually have a vector of constant values for optimizing
   // field initializers.
   size_t numFieldInitializers;
 
@@ -2385,152 +2518,40 @@ class JSScript : public js::BaseScript {
   size_t numBytecodeTypeSets() const {
     return immutableScriptData()->numBytecodeTypeSets;
   }
 
   size_t numICEntries() const { return immutableScriptData()->numICEntries; }
 
   size_t funLength() const { return immutableScriptData()->funLength; }
 
-  bool noScriptRval() const { return hasFlag(ImmutableFlags::NoScriptRval); }
-
-  bool strict() const { return hasFlag(ImmutableFlags::Strict); }
-
-  bool hasNonSyntacticScope() const {
-    return hasFlag(ImmutableFlags::HasNonSyntacticScope);
-  }
-
-  bool selfHosted() const { return hasFlag(ImmutableFlags::SelfHosted); }
-  bool bindingsAccessedDynamically() const {
-    return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
-  }
-  bool funHasExtensibleScope() const {
-    return hasFlag(ImmutableFlags::FunHasExtensibleScope);
-  }
-  bool hasDirectEval() const { return hasFlag(ImmutableFlags::HasDirectEval); }
-
-  bool hasCallSiteObj() const {
-    return hasFlag(ImmutableFlags::HasCallSiteObj);
-  }
-
-  bool treatAsRunOnce() const {
-    return hasFlag(ImmutableFlags::TreatAsRunOnce);
-  }
-  bool hasRunOnce() const { return hasFlag(MutableFlags::HasRunOnce); }
-  bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
-
-  void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
-  void setHasRunOnce() { setFlag(MutableFlags::HasRunOnce); }
-  void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
-
   void cacheForEval() {
     MOZ_ASSERT(isForEval());
     // IsEvalCacheCandidate will make sure that there's nothing in this
     // script that would prevent reexecution even if isRunOnce is
     // true.  So just pretend like we never ran this script.
     clearFlag(MutableFlags::HasRunOnce);
   }
 
-  bool isLikelyConstructorWrapper() const {
-    return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
-  }
-  void setLikelyConstructorWrapper() {
-    setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
-  }
-
-  bool failedBoundsCheck() const {
-    return hasFlag(MutableFlags::FailedBoundsCheck);
-  }
-  bool failedShapeGuard() const {
-    return hasFlag(MutableFlags::FailedShapeGuard);
-  }
-  bool hadFrequentBailouts() const {
-    return hasFlag(MutableFlags::HadFrequentBailouts);
-  }
-  bool hadOverflowBailout() const {
-    return hasFlag(MutableFlags::HadOverflowBailout);
-  }
-  bool uninlineable() const { return hasFlag(MutableFlags::Uninlineable); }
-  bool invalidatedIdempotentCache() const {
-    return hasFlag(MutableFlags::InvalidatedIdempotentCache);
-  }
-  bool failedLexicalCheck() const {
-    return hasFlag(MutableFlags::FailedLexicalCheck);
-  }
-  bool isDefaultClassConstructor() const {
-    return hasFlag(ImmutableFlags::IsDefaultClassConstructor);
-  }
-
-  void setFailedBoundsCheck() { setFlag(MutableFlags::FailedBoundsCheck); }
-  void setFailedShapeGuard() { setFlag(MutableFlags::FailedShapeGuard); }
-  void setHadFrequentBailouts() { setFlag(MutableFlags::HadFrequentBailouts); }
-  void setHadOverflowBailout() { setFlag(MutableFlags::HadOverflowBailout); }
-  void setUninlineable() { setFlag(MutableFlags::Uninlineable); }
-  void setInvalidatedIdempotentCache() {
-    setFlag(MutableFlags::InvalidatedIdempotentCache);
-  }
-  void setFailedLexicalCheck() { setFlag(MutableFlags::FailedLexicalCheck); }
-  void setIsDefaultClassConstructor() {
-    setFlag(ImmutableFlags::IsDefaultClassConstructor);
-  }
-
-  bool hasScriptCounts() const {
-    return hasFlag(MutableFlags::HasScriptCounts);
-  }
   bool hasScriptName();
 
-  bool warnedAboutUndefinedProp() const {
-    return hasFlag(MutableFlags::WarnedAboutUndefinedProp);
-  }
-  void setWarnedAboutUndefinedProp() {
-    setFlag(MutableFlags::WarnedAboutUndefinedProp);
-  }
-
-  /* See ContextFlags::funArgumentsHasLocalBinding comment. */
-  bool argumentsHasVarBinding() const {
-    return hasFlag(ImmutableFlags::ArgsHasVarBinding);
-  }
   void setArgumentsHasVarBinding();
   bool argumentsAliasesFormals() const {
     return argumentsHasVarBinding() && hasMappedArgsObj();
   }
 
   js::GeneratorKind generatorKind() const {
     return isGenerator() ? js::GeneratorKind::Generator
                          : js::GeneratorKind::NotGenerator;
   }
-  bool isGenerator() const { return hasFlag(ImmutableFlags::IsGenerator); }
 
   js::FunctionAsyncKind asyncKind() const {
     return isAsync() ? js::FunctionAsyncKind::AsyncFunction
                      : js::FunctionAsyncKind::SyncFunction;
   }
-  bool isAsync() const { return hasFlag(ImmutableFlags::IsAsync); }
-
-  bool hasRest() const { return hasFlag(ImmutableFlags::HasRest); }
-
-  bool hideScriptFromDebugger() const {
-    return hasFlag(MutableFlags::HideScriptFromDebugger);
-  }
-  void clearHideScriptFromDebugger() {
-    clearFlag(MutableFlags::HideScriptFromDebugger);
-  }
-
-  bool spewEnabled() const { return hasFlag(MutableFlags::SpewEnabled); }
-  void setSpewEnabled(bool enabled) {
-    setFlag(MutableFlags::SpewEnabled, enabled);
-  }
-
-  bool needsHomeObject() const {
-    return hasFlag(ImmutableFlags::NeedsHomeObject);
-  }
-
-  bool isDerivedClassConstructor() const {
-    return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
-  }
 
   /*
    * As an optimization, even when argsHasLocalBinding, the function prologue
    * may not need to create an arguments object. This is determined by
    * needsArgsObj which is set by AnalyzeArgumentsUsage. When !needsArgsObj,
    * the prologue may simply write MagicValue(JS_OPTIMIZED_ARGUMENTS) to
    * 'arguments's slot and any uses of 'arguments' will be guaranteed to
    * handle this magic value. To avoid spurious arguments object creation, we
@@ -2544,24 +2565,16 @@ class JSScript : public js::BaseScript {
   bool needsArgsObj() const {
     MOZ_ASSERT(analyzedArgsUsage());
     return hasFlag(MutableFlags::NeedsArgsObj);
   }
   void setNeedsArgsObj(bool needsArgsObj);
   static void argumentsOptimizationFailed(JSContext* cx,
                                           js::HandleScript script);
 
-  bool hasMappedArgsObj() const {
-    return hasFlag(ImmutableFlags::HasMappedArgsObj);
-  }
-
-  bool functionHasThisBinding() const {
-    return hasFlag(ImmutableFlags::FunctionHasThisBinding);
-  }
-
   void setFieldInitializers(js::FieldInitializers fieldInitializers) {
     MOZ_ASSERT(data_);
     data_->setFieldInitializers(fieldInitializers);
   }
 
   const js::FieldInitializers& getFieldInitializers() const {
     MOZ_ASSERT(data_);
     return data_->getFieldInitializers();
@@ -2574,23 +2587,16 @@ class JSScript : public js::BaseScript {
    * arguments object. Otherwise, the local slot is the canonical location for
    * the arguments. Note: if a formal is aliased through the scope chain, then
    * script->formalIsAliased and JSOP_*ARG* opcodes won't be emitted at all.
    */
   bool argsObjAliasesFormals() const {
     return needsArgsObj() && hasMappedArgsObj();
   }
 
-  bool hasDoNotRelazify() const { return hasFlag(MutableFlags::DoNotRelazify); }
-  void setDoNotRelazify(bool b) { setFlag(MutableFlags::DoNotRelazify, b); }
-
-  bool hasInnerFunctions() const {
-    return hasFlag(ImmutableFlags::HasInnerFunctions);
-  }
-
   static constexpr size_t offsetOfScriptData() {
     return offsetof(JSScript, scriptData_);
   }
   static constexpr size_t offsetOfPrivateScriptData() {
     return offsetof(JSScript, data_);
   }
   static constexpr size_t offsetOfJitScript() {
     return offsetof(JSScript, jitScript_);
@@ -2643,17 +2649,17 @@ class JSScript : public js::BaseScript {
   static size_t offsetOfIonScript() { return offsetof(JSScript, ion); }
 
   // We don't relazify functions with a JitScript or JIT code, but some
   // callers (XDR, testing functions) want to know whether this script is
   // relazifiable ignoring (or after) discarding JIT code.
   bool isRelazifiableIgnoringJitCode() const {
     return (selfHosted() || lazyScript) && !hasInnerFunctions() &&
            !isGenerator() && !isAsync() && !isDefaultClassConstructor() &&
-           !hasDoNotRelazify() && !hasCallSiteObj();
+           !doNotRelazify() && !hasCallSiteObj();
   }
   bool isRelazifiable() const {
     MOZ_ASSERT_IF(hasBaselineScript() || hasIonScript(), jitScript_);
     return isRelazifiableIgnoringJitCode() && !jitScript_;
   }
   void setLazyScript(js::LazyScript* lazy) { lazyScript = lazy; }
   js::LazyScript* maybeLazyScript() { return lazyScript; }
 
@@ -2764,20 +2770,16 @@ class JSScript : public js::BaseScript {
 
   js::Scope* outermostScope() const {
     // The body scope may not be the outermost scope in the script when
     // the decl env scope is present.
     size_t index = 0;
     return getScope(index);
   }
 
-  bool needsFunctionEnvironmentObjects() const {
-    return hasFlag(ImmutableFlags::NeedsFunctionEnvironmentObjects);
-  }
-
   bool functionHasExtraBodyVarScope() const {
     bool res = hasFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
     MOZ_ASSERT_IF(res, functionHasParameterExprs());
     return res;
   }
 
   js::VarScope* functionExtraBodyVarScope() const {
     MOZ_ASSERT(functionHasExtraBodyVarScope());
@@ -3034,21 +3036,16 @@ class JSScript : public js::BaseScript {
 
   bool formalIsAliased(unsigned argSlot);
   bool formalLivesInArgumentsObject(unsigned argSlot);
 
   // See comment above 'debugMode' in Realm.h for explanation of
   // invariants of debuggee compartments, scripts, and frames.
   inline bool isDebuggee() const;
 
-  // Access the flag for whether this script has a DebugScript in its realm's
-  // map. This should only be used by the DebugScript class.
-  bool hasDebugScript() const { return hasFlag(MutableFlags::HasDebugScript); }
-  void setHasDebugScript(bool b) { setFlag(MutableFlags::HasDebugScript, b); }
-
   void finalize(JSFreeOp* fop);
 
   static const JS::TraceKind TraceKind = JS::TraceKind::Script;
 
   void traceChildren(JSTracer* trc);
 
   // A helper class to prevent relazification of the given function's script
   // while it's holding on to it.  This class automatically roots the script.
@@ -3075,20 +3072,16 @@ class JSScript : public js::BaseScript {
 
     operator JS::HandleScript() const { return script_; }
     explicit operator bool() const { return script_; }
 
    private:
     void holdScript(JS::HandleFunction fun);
     void dropScript();
   };
-
-  bool trackRecordReplayProgress() const {
-    return hasFlag(MutableFlags::TrackRecordReplayProgress);
-  }
 };
 
 /* If this fails, add/remove padding within JSScript. */
 static_assert(
     sizeof(JSScript) % js::gc::CellAlignBytes == 0,
     "Size of JSScript must be an integral multiple of js::gc::CellAlignBytes");
 
 namespace js {
@@ -3331,110 +3324,25 @@ class LazyScript : public BaseScript {
   mozilla::Span<GCPtrFunction> innerFunctions() {
     return lazyData_ ? lazyData_->innerFunctions()
                      : mozilla::Span<GCPtrFunction>();
   }
   uint32_t numInnerFunctions() const {
     return lazyData_ ? lazyData_->innerFunctions().size() : 0;
   }
 
-  GeneratorKind generatorKind() const {
-    return hasFlag(ImmutableFlags::IsGenerator) ? GeneratorKind::Generator
-                                                : GeneratorKind::NotGenerator;
-  }
-
-  bool isGenerator() const {
-    return generatorKind() == GeneratorKind::Generator;
-  }
-
-  void setGeneratorKind(GeneratorKind kind) {
-    // A script only gets its generator kind set as part of initialization,
-    // so it can only transition from NotGenerator.
-    MOZ_ASSERT(!isGenerator());
-    if (kind == GeneratorKind::Generator) {
-      setFlag(ImmutableFlags::IsGenerator);
-    }
-  }
-
-  bool isAsync() const { return hasFlag(ImmutableFlags::IsAsync); }
-  FunctionAsyncKind asyncKind() const {
-    return isAsync() ? FunctionAsyncKind::AsyncFunction
-                     : FunctionAsyncKind::SyncFunction;
-  }
-
-  void setAsyncKind(FunctionAsyncKind kind) {
-    if (kind == FunctionAsyncKind::AsyncFunction) {
-      setFlag(ImmutableFlags::IsAsync);
-    }
-  }
-
-  bool hasRest() const { return hasFlag(ImmutableFlags::HasRest); }
-  void setHasRest() { setFlag(ImmutableFlags::HasRest); }
-
   frontend::ParseGoal parseGoal() const {
     if (hasFlag(ImmutableFlags::IsModule)) {
       return frontend::ParseGoal::Module;
     }
     return frontend::ParseGoal::Script;
   }
 
   bool isBinAST() const { return scriptSource()->hasBinASTSource(); }
 
-  bool strict() const { return hasFlag(ImmutableFlags::Strict); }
-  void setStrict() { setFlag(ImmutableFlags::Strict); }
-
-  bool bindingsAccessedDynamically() const {
-    return hasFlag(ImmutableFlags::BindingsAccessedDynamically);
-  }
-  void setBindingsAccessedDynamically() {
-    setFlag(ImmutableFlags::BindingsAccessedDynamically);
-  }
-
-  bool hasDirectEval() const { return hasFlag(ImmutableFlags::HasDirectEval); }
-  void setHasDirectEval() { setFlag(ImmutableFlags::HasDirectEval); }
-
-  bool isLikelyConstructorWrapper() const {
-    return hasFlag(ImmutableFlags::IsLikelyConstructorWrapper);
-  }
-  void setLikelyConstructorWrapper() {
-    setFlag(ImmutableFlags::IsLikelyConstructorWrapper);
-  }
-
-  bool hasBeenCloned() const { return hasFlag(MutableFlags::HasBeenCloned); }
-  void setHasBeenCloned() { setFlag(MutableFlags::HasBeenCloned); }
-
-  bool treatAsRunOnce() const {
-    return hasFlag(ImmutableFlags::TreatAsRunOnce);
-  }
-  void setTreatAsRunOnce() { setFlag(ImmutableFlags::TreatAsRunOnce); }
-
-  bool isDerivedClassConstructor() const {
-    return hasFlag(ImmutableFlags::IsDerivedClassConstructor);
-  }
-  void setIsDerivedClassConstructor() {
-    setFlag(ImmutableFlags::IsDerivedClassConstructor);
-  }
-
-  bool needsHomeObject() const {
-    return hasFlag(ImmutableFlags::NeedsHomeObject);
-  }
-  void setNeedsHomeObject() { setFlag(ImmutableFlags::NeedsHomeObject); }
-
-  bool shouldDeclareArguments() const {
-    return hasFlag(ImmutableFlags::ShouldDeclareArguments);
-  }
-  void setShouldDeclareArguments() {
-    setFlag(ImmutableFlags::ShouldDeclareArguments);
-  }
-
-  bool hasThisBinding() const {
-    return hasFlag(ImmutableFlags::FunctionHasThisBinding);
-  }
-  void setHasThisBinding() { setFlag(ImmutableFlags::FunctionHasThisBinding); }
-
   void setFieldInitializers(FieldInitializers fieldInitializers) {
     MOZ_ASSERT(lazyData_);
     lazyData_->fieldInitializers_ = fieldInitializers;
   }
 
   const FieldInitializers& getFieldInitializers() const {
     MOZ_ASSERT(lazyData_);
     return lazyData_->fieldInitializers_;
--- a/js/xpconnect/loader/AutoMemMap.cpp
+++ b/js/xpconnect/loader/AutoMemMap.cpp
@@ -34,58 +34,60 @@ Result<Ok, nsresult> AutoMemMap::init(ns
   MOZ_ASSERT(!fd);
 
   MOZ_TRY(file->OpenNSPRFileDesc(flags, mode, &fd.rwget()));
 
   return initInternal(prot);
 }
 
 Result<Ok, nsresult> AutoMemMap::init(const FileDescriptor& file,
-                                      PRFileMapProtect prot,
-                                      size_t expectedSize) {
+                                      PRFileMapProtect prot, size_t maybeSize) {
   MOZ_ASSERT(!fd);
   if (!file.IsValid()) {
     return Err(NS_ERROR_INVALID_ARG);
   }
 
   auto handle = file.ClonePlatformHandle();
 
   fd = PR_ImportFile(PROsfd(handle.get()));
   if (!fd) {
     return Err(NS_ERROR_FAILURE);
   }
   Unused << handle.release();
 
-  return initInternal(prot, expectedSize);
+  return initInternal(prot, maybeSize);
 }
 
 Result<Ok, nsresult> AutoMemMap::initInternal(PRFileMapProtect prot,
-                                              size_t expectedSize) {
+                                              size_t maybeSize) {
   MOZ_ASSERT(!fileMap);
   MOZ_ASSERT(!addr);
 
-  PRFileInfo64 fileInfo;
-  MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
+  if (maybeSize > 0) {
+    // Some OSes' shared memory objects can't be stat()ed, either at
+    // all (Android) or without loosening the sandbox (Mac) so just
+    // use the size.
+    size_ = maybeSize;
+  } else {
+    // But if we don't have the size, assume it's a regular file and
+    // ask for it.
+    PRFileInfo64 fileInfo;
+    MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
 
-  if (fileInfo.size > UINT32_MAX) {
-    return Err(NS_ERROR_INVALID_ARG);
+    if (fileInfo.size > UINT32_MAX) {
+      return Err(NS_ERROR_INVALID_ARG);
+    }
+    size_ = fileInfo.size;
   }
 
   fileMap = PR_CreateFileMap(fd, 0, prot);
   if (!fileMap) {
     return Err(NS_ERROR_FAILURE);
   }
 
-  size_ = fileInfo.size;
-  // The memory region size passed in certain IPC messages isn't necessary on
-  // Unix-like systems, since we can always stat the file descriptor to
-  // determine it accurately. But since we have it, anyway, sanity check that
-  // it matches the size returned by the stat.
-  MOZ_ASSERT_IF(expectedSize > 0, size_ == expectedSize);
-
   addr = PR_MemMap(fileMap, 0, size_);
   if (!addr) {
     return Err(NS_ERROR_FAILURE);
   }
 
   return Ok();
 }
 
@@ -117,17 +119,18 @@ FileDescriptor AutoMemMap::cloneHandle()
   return FileDescriptor(handle_);
 }
 
 #else
 
 Result<Ok, nsresult> AutoMemMap::initWithHandle(const FileDescriptor& file,
                                                 size_t size,
                                                 PRFileMapProtect prot) {
-  return init(file, prot);
+  MOZ_DIAGNOSTIC_ASSERT(size > 0);
+  return init(file, prot, size);
 }
 
 FileDescriptor AutoMemMap::cloneHandle() const { return cloneFileDescriptor(); }
 
 #endif
 
 void AutoMemMap::reset() {
   if (addr && !persistent_) {
--- a/js/xpconnect/loader/AutoMemMap.h
+++ b/js/xpconnect/loader/AutoMemMap.h
@@ -31,17 +31,17 @@ class AutoMemMap {
 
   ~AutoMemMap();
 
   Result<Ok, nsresult> init(nsIFile* file, int flags = PR_RDONLY, int mode = 0,
                             PRFileMapProtect prot = PR_PROT_READONLY);
 
   Result<Ok, nsresult> init(const FileDescriptor& file,
                             PRFileMapProtect prot = PR_PROT_READONLY,
-                            size_t expectedSize = 0);
+                            size_t maybeSize = 0);
 
   // Initializes the mapped memory with a shared memory handle. On
   // Unix-like systems, this is identical to the above init() method. On
   // Windows, the FileDescriptor must be a handle for a file mapping,
   // rather than a file descriptor.
   Result<Ok, nsresult> initWithHandle(const FileDescriptor& file, size_t size,
                                       PRFileMapProtect prot = PR_PROT_READONLY);
 
@@ -68,18 +68,18 @@ class AutoMemMap {
   FileDescriptor cloneFileDescriptor() const;
   FileDescriptor cloneHandle() const;
 
   // Makes this mapping persistent. After calling this, the mapped memory
   // will remained mapped, even after this instance is destroyed.
   void setPersistent() { persistent_ = true; }
 
  private:
-  Result<Ok, nsresult> initInternal(PRFileMapProtect prot = PR_PROT_READONLY,
-                                    size_t expectedSize = 0);
+  Result<Ok, nsresult> initInternal(PRFileMapProtect prot,
+                                    size_t maybeSize = 0);
 
   AutoFDClose fd;
   PRFileMap* fileMap = nullptr;
 
 #ifdef XP_WIN
   // We can't include windows.h in this header, since it gets included
   // by some binding headers (which are explicitly incompatible with
   // windows.h). So we can't use the HANDLE type here.
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/Logging.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/PerfStats.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/RangeUtils.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticPrefs_apz.h"
 #include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_font.h"
 #include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include <algorithm>
 
@@ -1027,24 +1028,24 @@ void PresShell::Init(Document* aDocument
   for (DocumentTimeline* timeline : mDocument->Timelines()) {
     timeline->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
   }
 
   // Get our activeness from the docShell.
   QueryIsActive();
 
   // Setup our font inflation preferences.
-  mFontSizeInflationEmPerLine = nsLayoutUtils::FontSizeInflationEmPerLine();
-  mFontSizeInflationMinTwips = nsLayoutUtils::FontSizeInflationMinTwips();
+  mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
+  mFontSizeInflationMinTwips = StaticPrefs::font_size_inflation_minTwips();
   mFontSizeInflationLineThreshold =
-      nsLayoutUtils::FontSizeInflationLineThreshold();
+      StaticPrefs::font_size_inflation_lineThreshold();
   mFontSizeInflationForceEnabled =
-      nsLayoutUtils::FontSizeInflationForceEnabled();
+      StaticPrefs::font_size_inflation_forceEnabled();
   mFontSizeInflationDisabledInMasterProcess =
-      nsLayoutUtils::FontSizeInflationDisabledInMasterProcess();
+      StaticPrefs::font_size_inflation_disabledInMasterProcess();
   // We'll compute the font size inflation state in Initialize(), when we know
   // the document type.
 
   mTouchManager.Init(this, mDocument);
 
   if (mPresContext->IsRootContentDocumentCrossProcess()) {
     mZoomConstraintsClient = new ZoomConstraintsClient();
     mZoomConstraintsClient->Init(this, mDocument);
@@ -10745,17 +10746,18 @@ nsSize PresShell::GetLayoutViewportSize(
     result = sf->GetScrollPortRect().Size();
   }
   return result;
 }
 
 void PresShell::RecomputeFontSizeInflationEnabled() {
   mFontSizeInflationEnabled = DetermineFontSizeInflationState();
 
-  float fontScale = nsLayoutUtils::SystemFontScale();
+  // Divide by 100 to convert the pref from a percentage to a fraction.
+  float fontScale = StaticPrefs::font_size_systemFontScale() / 100.0f;
   if (fontScale == 0.0f) {
     return;
   }
 
   MOZ_ASSERT(mDocument);
   MOZ_ASSERT(mPresContext);
   if (mFontSizeInflationEnabled || mDocument->IsSyntheticDocument()) {
     mPresContext->SetSystemFontScale(1.0f);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PerfStats.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ServoStyleSetInlines.h"
 #include "mozilla/StaticPrefs_apz.h"
 #include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_font.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/StaticPrefs_layers.h"
 #include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/Unused.h"
 #include "nsCharTraits.h"
 #include "mozilla/dom/BrowserChild.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/DocumentInlines.h"
@@ -162,65 +163,24 @@ using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::gfx;
 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
 using mozilla::dom::HTMLMediaElement_Binding::HAVE_NOTHING;
 
-#define INTERCHARACTER_RUBY_ENABLED_PREF_NAME \
-  "layout.css.ruby.intercharacter.enabled"
-
-// The time in number of frames that we estimate for a refresh driver
-// to be quiescent
-#define DEFAULT_QUIESCENT_FRAMES 2
-// The time (milliseconds) we estimate is needed between the end of an
-// idle time and the next Tick.
-#define DEFAULT_IDLE_PERIOD_TIME_LIMIT 1.0f
-
 #ifdef DEBUG
 // TODO: remove, see bug 598468.
 bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
 #endif  // DEBUG
 
 typedef ScrollableLayerGuid::ViewID ViewID;
 typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
 
-/* static */
-uint32_t nsLayoutUtils::sFontSizeInflationEmPerLine;
-/* static */
-uint32_t nsLayoutUtils::sFontSizeInflationMinTwips;
-/* static */
-uint32_t nsLayoutUtils::sFontSizeInflationLineThreshold;
-/* static */
-int32_t nsLayoutUtils::sFontSizeInflationMappingIntercept;
-/* static */
-uint32_t nsLayoutUtils::sFontSizeInflationMaxRatio;
-/* static */
-bool nsLayoutUtils::sFontSizeInflationForceEnabled;
-/* static */
-bool nsLayoutUtils::sFontSizeInflationDisabledInMasterProcess;
-/* static */
-uint32_t nsLayoutUtils::sSystemFontScale;
-/* static */
-uint32_t nsLayoutUtils::sZoomMaxPercent;
-/* static */
-uint32_t nsLayoutUtils::sZoomMinPercent;
-/* static */
-bool nsLayoutUtils::sInvalidationDebuggingIsEnabled;
-/* static */
-bool nsLayoutUtils::sInterruptibleReflowEnabled;
-/* static */
-bool nsLayoutUtils::sSVGTransformBoxEnabled;
-/* static */
-uint32_t nsLayoutUtils::sIdlePeriodDeadlineLimit;
-/* static */
-uint32_t nsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;
-
 static ViewID sScrollIdCounter = ScrollableLayerGuid::START_SCROLL_ID;
 
 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
 static ContentMap* sContentMap = nullptr;
 static ContentMap& GetContentMap() {
   if (!sContentMap) {
     sContentMap = new ContentMap();
   }
@@ -512,43 +472,20 @@ Size nsLayoutUtils::ComputeSuitableScale
 
   return Size(GetSuitableScale(maxScale.width, minScale.width,
                                aVisibleSize.width, aDisplaySize.width),
               GetSuitableScale(maxScale.height, minScale.height,
                                aVisibleSize.height, aDisplaySize.height));
 }
 
 bool nsLayoutUtils::AreAsyncAnimationsEnabled() {
-  static bool sAreAsyncAnimationsEnabled;
-  static bool sAsyncPrefCached = false;
-
-  if (!sAsyncPrefCached) {
-    sAsyncPrefCached = true;
-    Preferences::AddBoolVarCache(
-        &sAreAsyncAnimationsEnabled,
-        "layers.offmainthreadcomposition.async-animations");
-  }
-
-  return sAreAsyncAnimationsEnabled &&
+  return StaticPrefs::layers_offmainthreadcomposition_async_animations() &&
          gfxPlatform::OffMainThreadCompositingEnabled();
 }
 
-bool nsLayoutUtils::IsAnimationLoggingEnabled() {
-  static bool sShouldLog;
-  static bool sShouldLogPrefCached;
-
-  if (!sShouldLogPrefCached) {
-    sShouldLogPrefCached = true;
-    Preferences::AddBoolVarCache(
-        &sShouldLog, "layers.offmainthreadcomposition.log-animations");
-  }
-
-  return sShouldLog;
-}
-
 bool nsLayoutUtils::AreRetainedDisplayListsEnabled() {
 #ifdef MOZ_WIDGET_ANDROID
   return StaticPrefs::layout_display_list_retain();
 #else
   if (XRE_IsContentProcess()) {
     return StaticPrefs::layout_display_list_retain();
   }
 
@@ -575,42 +512,16 @@ bool nsLayoutUtils::GPUImageScalingEnabl
     sGPUImageScalingPrefInitialised = true;
     sGPUImageScalingEnabled =
         Preferences::GetBool("layout.gpu-image-scaling.enabled", false);
   }
 
   return sGPUImageScalingEnabled;
 }
 
-bool nsLayoutUtils::AnimatedImageLayersEnabled() {
-  static bool sAnimatedImageLayersEnabled;
-  static bool sAnimatedImageLayersPrefCached = false;
-
-  if (!sAnimatedImageLayersPrefCached) {
-    sAnimatedImageLayersPrefCached = true;
-    Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled,
-                                 "layout.animated-image-layers.enabled", false);
-  }
-
-  return sAnimatedImageLayersEnabled;
-}
-
-bool nsLayoutUtils::IsInterCharacterRubyEnabled() {
-  static bool sInterCharacterRubyEnabled;
-  static bool sInterCharacterRubyEnabledPrefCached = false;
-
-  if (!sInterCharacterRubyEnabledPrefCached) {
-    sInterCharacterRubyEnabledPrefCached = true;
-    Preferences::AddBoolVarCache(&sInterCharacterRubyEnabled,
-                                 INTERCHARACTER_RUBY_ENABLED_PREF_NAME, false);
-  }
-
-  return sInterCharacterRubyEnabled;
-}
-
 void nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
                                        nsOverflowAreas& aOverflowAreas,
                                        FrameChildListIDs aSkipChildLists) {
   // Iterate over all children except pop-ups.
   FrameChildListIDs skip(aSkipChildLists);
   skip += {nsIFrame::kSelectPopupList, nsIFrame::kPopupList};
 
   for (nsIFrame::ChildListIterator childLists(aFrame); !childLists.IsDone();
@@ -8033,47 +7944,16 @@ size_t nsLayoutUtils::SizeOfTextRunsForF
       total += SizeOfTextRunsForFrames(e.get(), aMallocSizeOf, clear);
     }
   }
   return total;
 }
 
 /* static */
 void nsLayoutUtils::Initialize() {
-  Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio,
-                               "font.size.inflation.maxRatio");
-  Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine,
-                               "font.size.inflation.emPerLine");
-  Preferences::AddUintVarCache(&sFontSizeInflationMinTwips,
-                               "font.size.inflation.minTwips");
-  Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold,
-                               "font.size.inflation.lineThreshold");
-  Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept,
-                              "font.size.inflation.mappingIntercept");
-  Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled,
-                               "font.size.inflation.forceEnabled");
-  Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess,
-                               "font.size.inflation.disabledInMasterProcess");
-  Preferences::AddUintVarCache(&sSystemFontScale, "font.size.systemFontScale",
-                               100);
-  Preferences::AddUintVarCache(&sZoomMaxPercent, "zoom.maxPercent", 300);
-  Preferences::AddUintVarCache(&sZoomMinPercent, "zoom.minPercent", 30);
-  Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled,
-                               "nglayout.debug.invalidation");
-  Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,
-                               "layout.interruptible-reflow.enabled");
-  Preferences::AddBoolVarCache(&sSVGTransformBoxEnabled,
-                               "svg.transform-box.enabled");
-  Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,
-                               "layout.idle_period.time_limit",
-                               DEFAULT_IDLE_PERIOD_TIME_LIMIT);
-  Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,
-                               "layout.idle_period.required_quiescent_frames",
-                               DEFAULT_QUIESCENT_FRAMES);
-
   nsComputedDOMStyle::RegisterPrefChangeCallbacks();
 }
 
 /* static */
 void nsLayoutUtils::Shutdown() {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = nullptr;
@@ -8294,18 +8174,18 @@ float nsLayoutUtils::FontSizeInflationIn
       const auto& stylePosBSize = f->StylePosition()->BSize(wm);
       if (!stylePosISize.IsAuto() ||
           !stylePosBSize.BehavesLikeInitialValueOnBlockAxis()) {
         return 1.0;
       }
     }
   }
 
-  int32_t interceptParam = nsLayoutUtils::FontSizeInflationMappingIntercept();
-  float maxRatio = (float)nsLayoutUtils::FontSizeInflationMaxRatio() / 100.0f;
+  int32_t interceptParam = StaticPrefs::font_size_inflation_mappingIntercept();
+  float maxRatio = (float)StaticPrefs::font_size_inflation_maxRatio() / 100.0f;
 
   float ratio = float(styleFontSize) / float(aMinFontSize);
   float inflationRatio;
 
   // Given a minimum inflated font size m, a specified font size s, we want to
   // find the inflated font size i and then return the ratio of i to s (i/s).
   if (interceptParam >= 0) {
     // Since the mapping intercept parameter P is greater than zero, we use it
@@ -8851,30 +8731,16 @@ void MaybeSetupTransactionIdAllocator(la
     aManager->SetTransactionIdAllocator(aPresContext->RefreshDriver());
   }
 }
 
 }  // namespace layout
 }  // namespace mozilla
 
 /* static */
-bool nsLayoutUtils::IsOutlineStyleAutoEnabled() {
-  static bool sOutlineStyleAutoEnabled;
-  static bool sOutlineStyleAutoPrefCached = false;
-
-  if (!sOutlineStyleAutoPrefCached) {
-    sOutlineStyleAutoPrefCached = true;
-    Preferences::AddBoolVarCache(&sOutlineStyleAutoEnabled,
-                                 "layout.css.outline-style-auto.enabled",
-                                 false);
-  }
-  return sOutlineStyleAutoEnabled;
-}
-
-/* static */
 void nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
                                             ReflowOutput& aMetrics,
                                             const LogicalMargin& aFramePadding,
                                             WritingMode aLineWM,
                                             WritingMode aFrameWM) {
   RefPtr<nsFontMetrics> fm =
       nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -7,16 +7,17 @@
 #ifndef nsLayoutUtils_h__
 #define nsLayoutUtils_h__
 
 #include "LayoutConstants.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_nglayout.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsBoundingMetrics.h"
 #include "nsChangeHint.h"
 #include "mozilla/layout/FrameChildList.h"
 #include "mozilla/layers/ScrollableLayerGuid.h"
 #include "nsThreadUtils.h"
 #include "nsCSSPropertyIDSet.h"
 #include "nsStyleConsts.h"
@@ -2366,21 +2367,16 @@ class nsLayoutUtils {
       const nsIFrame* aStyleFrame);
 
   /**
    * Checks if off-main-thread animations are enabled.
    */
   static bool AreAsyncAnimationsEnabled();
 
   /**
-   * Checks if we should warn about animations that can't be async
-   */
-  static bool IsAnimationLoggingEnabled();
-
-  /**
    * Checks if retained display lists are enabled.
    */
   static bool AreRetainedDisplayListsEnabled();
 
   static bool DisplayRootHasRetainedDisplayListBuilder(nsIFrame* aFrame);
 
   /**
    * Find a suitable scale for a element (aFrame's content) over the course of
@@ -2398,30 +2394,16 @@ class nsLayoutUtils {
 
   /**
    * Checks whether we want to use the GPU to scale images when
    * possible.
    */
   static bool GPUImageScalingEnabled();
 
   /**
-   * Checks whether we want to layerize animated images whenever possible.
-   */
-  static bool AnimatedImageLayersEnabled();
-
-  /**
-   * Checks whether support for inter-character ruby is enabled.
-   */
-  static bool IsInterCharacterRubyEnabled();
-
-  static bool InterruptibleReflowEnabled() {
-    return sInterruptibleReflowEnabled;
-  }
-
-  /**
    * Unions the overflow areas of the children of aFrame with aOverflowAreas.
    * aSkipChildLists specifies any child lists that should be skipped.
    * kSelectPopupList and kPopupList are always skipped.
    */
   static void UnionChildOverflow(
       nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas,
       mozilla::layout::FrameChildListIDs aSkipChildLists =
           mozilla::layout::FrameChildListIDs());
@@ -2456,88 +2438,20 @@ class nsLayoutUtils {
    *   InflationMinFontSizeFor methods above.
    */
   static float FontSizeInflationInner(const nsIFrame* aFrame,
                                       nscoord aMinFontSize);
 
   static bool FontSizeInflationEnabled(nsPresContext* aPresContext);
 
   /**
-   * See comment above "font.size.inflation.maxRatio" in
-   * modules/libpref/src/init/all.js .
-   */
-  static uint32_t FontSizeInflationMaxRatio() {
-    return sFontSizeInflationMaxRatio;
-  }
-
-  /**
-   * See comment above "font.size.inflation.emPerLine" in
-   * modules/libpref/src/init/all.js .
-   */
-  static uint32_t FontSizeInflationEmPerLine() {
-    return sFontSizeInflationEmPerLine;
-  }
-
-  /**
-   * See comment above "font.size.inflation.minTwips" in
-   * modules/libpref/src/init/all.js .
-   */
-  static uint32_t FontSizeInflationMinTwips() {
-    return sFontSizeInflationMinTwips;
-  }
-
-  /**
-   * See comment above "font.size.inflation.lineThreshold" in
-   * modules/libpref/src/init/all.js .
-   */
-  static uint32_t FontSizeInflationLineThreshold() {
-    return sFontSizeInflationLineThreshold;
-  }
-
-  static bool FontSizeInflationForceEnabled() {
-    return sFontSizeInflationForceEnabled;
-  }
-
-  static bool FontSizeInflationDisabledInMasterProcess() {
-    return sFontSizeInflationDisabledInMasterProcess;
-  }
-
-  /**
-   * See comment above "font.size.systemFontScale" in
-   * modules/libpref/init/all.js.
-   */
-  static float SystemFontScale() { return sSystemFontScale / 100.0f; }
-
-  static float MaxZoom() { return sZoomMaxPercent / 100.0f; }
-
-  static float MinZoom() { return sZoomMinPercent / 100.0f; }
-
-  static bool SVGTransformBoxEnabled() { return sSVGTransformBoxEnabled; }
-
-  static uint32_t IdlePeriodDeadlineLimit() { return sIdlePeriodDeadlineLimit; }
-
-  static uint32_t QuiescentFramesBeforeIdlePeriod() {
-    return sQuiescentFramesBeforeIdlePeriod;
-  }
-
-  /**
-   * See comment above "font.size.inflation.mappingIntercept" in
-   * modules/libpref/src/init/all.js .
-   */
-  static int32_t FontSizeInflationMappingIntercept() {
-    return sFontSizeInflationMappingIntercept;
-  }
-
-  /**
    * Returns true if the nglayout.debug.invalidation pref is set to true.
-   * Note that sInvalidationDebuggingIsEnabled is declared outside this function
-   * to allow it to be accessed an manipulated from breakpoint conditions.
    */
   static bool InvalidationDebuggingIsEnabled() {
-    return sInvalidationDebuggingIsEnabled ||
+    return mozilla::StaticPrefs::nglayout_debug_invalidation() ||
            getenv("MOZ_DUMP_INVALIDATION") != 0;
   }
 
   static void Initialize();
   static void Shutdown();
 
   /**
    * Register an imgIRequest object with a refresh driver.
@@ -2839,18 +2753,16 @@ class nsLayoutUtils {
       nsIFrame* aFrame);
   /**
    * Finds the closest ancestor async scrollable frame from aFrame that has a
    * displayport and attempts to trigger the displayport expiry on that
    * ancestor.
    */
   static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame);
 
-  static bool IsOutlineStyleAutoEnabled();
-
   static void SetBSizeFromFontMetrics(
       const nsIFrame* aFrame, mozilla::ReflowOutput& aMetrics,
       const mozilla::LogicalMargin& aFramePadding, mozilla::WritingMode aLineWM,
       mozilla::WritingMode aFrameWM);
 
   static bool HasDocumentLevelListenersForApzAwareEvents(PresShell* aPresShell);
 
   /**
@@ -3067,32 +2979,16 @@ class nsLayoutUtils {
 
   /**
    * Generate the motion path transform result.
    **/
   static mozilla::Maybe<mozilla::MotionPathData> ResolveMotionPath(
       const nsIFrame* aFrame);
 
  private:
-  static uint32_t sFontSizeInflationEmPerLine;
-  static uint32_t sFontSizeInflationMinTwips;
-  static uint32_t sFontSizeInflationLineThreshold;
-  static int32_t sFontSizeInflationMappingIntercept;
-  static uint32_t sFontSizeInflationMaxRatio;
-  static bool sFontSizeInflationForceEnabled;
-  static bool sFontSizeInflationDisabledInMasterProcess;
-  static uint32_t sSystemFontScale;
-  static uint32_t sZoomMaxPercent;
-  static uint32_t sZoomMinPercent;
-  static bool sInvalidationDebuggingIsEnabled;
-  static bool sInterruptibleReflowEnabled;
-  static bool sSVGTransformBoxEnabled;
-  static uint32_t sIdlePeriodDeadlineLimit;
-  static uint32_t sQuiescentFramesBeforeIdlePeriod;
-
   /**
    * Helper function for LogTestDataForPaint().
    */
   static void DoLogTestDataForPaint(mozilla::layers::LayerManager* aManager,
                                     ViewID aScrollId, const std::string& aKey,
                                     const std::string& aValue);
 
   static bool IsAPZTestLoggingEnabled();
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -71,16 +71,18 @@
 #include "nsFrameLoader.h"
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
 #include "gfxTextRun.h"
 #include "nsFontFaceUtils.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPrefs_zoom.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceTiming.h"
 #include "mozilla/layers/APZThreadUtils.h"
 
 // Needed for Start/Stop of Image Animation
@@ -914,18 +916,18 @@ void nsPresContext::SetImageAnimationMod
     }
   }
 
   mImageAnimationMode = aMode;
 }
 
 void nsPresContext::UpdateEffectiveTextZoom() {
   float newZoom = mSystemFontScale * mTextZoom;
-  float minZoom = nsLayoutUtils::MinZoom();
-  float maxZoom = nsLayoutUtils::MaxZoom();
+  float minZoom = StaticPrefs::zoom_minPercent() / 100.0f;
+  float maxZoom = StaticPrefs::zoom_maxPercent() / 100.0f;
 
   if (newZoom < minZoom) {
     newZoom = minZoom;
   } else if (newZoom > maxZoom) {
     newZoom = maxZoom;
   }
 
   mEffectiveTextZoom = newZoom;
@@ -2222,17 +2224,17 @@ void nsPresContext::ReflowStarted(bool a
 #ifdef NOISY_INTERRUPTIBLE_REFLOW
   if (!aInterruptible) {
     printf("STARTING NONINTERRUPTIBLE REFLOW\n");
   }
 #endif
   // We don't support interrupting in paginated contexts, since page
   // sequences only handle initial reflow
   mInterruptsEnabled = aInterruptible && !IsPaginated() &&
-                       nsLayoutUtils::InterruptibleReflowEnabled();
+                       StaticPrefs::layout_interruptible_reflow_enabled();
 
   // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here.  If
   // we ever change that, then we need to update the code in
   // PresShell::DoReflow to only add the just-reflown root to dirty roots if
   // it's actually dirty.  Otherwise we can end up adding a root that has no
   // interruptible descendants, just because we detected an interrupt at reflow
   // start.
   mHasPendingInterrupt = false;
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -266,23 +266,24 @@ class RefreshDriverTimer {
   TimeStamp GetIdleDeadlineHint(TimeStamp aDefault) {
     MOZ_ASSERT(NS_IsMainThread());
 
     TimeStamp mostRecentRefresh = MostRecentRefresh();
     TimeDuration refreshRate = GetTimerRate();
     TimeStamp idleEnd = mostRecentRefresh + refreshRate;
 
     if (idleEnd +
-            refreshRate * nsLayoutUtils::QuiescentFramesBeforeIdlePeriod() <
+            refreshRate *
+                StaticPrefs::layout_idle_period_required_quiescent_frames() <
         TimeStamp::Now()) {
       return aDefault;
     }
 
     idleEnd = idleEnd - TimeDuration::FromMilliseconds(
-                            nsLayoutUtils::IdlePeriodDeadlineLimit());
+                            StaticPrefs::layout_idle_period_time_limit());
     return idleEnd < aDefault ? idleEnd : aDefault;
   }
 
   Maybe<TimeStamp> GetNextTickHint() {
     MOZ_ASSERT(NS_IsMainThread());
     TimeStamp nextTick = MostRecentRefresh() + GetTimerRate();
     return nextTick < TimeStamp::Now() ? Nothing() : Some(nextTick);
   }
--- a/layout/generic/nsFontInflationData.cpp
+++ b/layout/generic/nsFontInflationData.cpp
@@ -189,17 +189,17 @@ void nsFontInflationData::UpdateISize(co
       firstInflatableDescendant, lastInflatableDescendant, bfc);
   while (!nca->IsContainerForFontSizeInflation()) {
     nca = nca->GetParent()->FirstInFlow();
   }
 
   nscoord newNCAISize = ComputeDescendantISize(aReflowInput, nca);
 
   // See comment above "font.size.inflation.lineThreshold" in
-  // modules/libpref/src/init/all.js .
+  // modules/libpref/src/init/StaticPrefList.yaml .
   PresShell* presShell = bfc->PresShell();
   uint32_t lineThreshold = presShell->FontSizeInflationLineThreshold();
   nscoord newTextThreshold = (newNCAISize * lineThreshold) / 100;
 
   if (mTextThreshold <= mTextAmount && mTextAmount < newTextThreshold) {
     // Because we truncate our scan when we hit sufficient text, we now
     // need to rescan.
     mTextDirty = true;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9315,17 +9315,17 @@ static void ComputeAndIncludeOutlineArea
   }
 
   // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
   SetOrUpdateRectValuedProperty(aFrame, nsIFrame::OutlineInnerRectProperty(),
                                 innerRect);
   const nscoord offset = outline->mOutlineOffset.ToAppUnits();
   nsRect outerRect(innerRect);
   bool useOutlineAuto = false;
-  if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
+  if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
     useOutlineAuto = outline->mOutlineStyle.IsAuto();
     if (MOZ_UNLIKELY(useOutlineAuto)) {
       nsPresContext* presContext = aFrame->PresContext();
       nsITheme* theme = presContext->GetTheme();
       if (theme && theme->ThemeSupportsWidget(presContext, aFrame,
                                               StyleAppearance::FocusOutline)) {
         outerRect.Inflate(offset);
         theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/dom/HTMLAreaElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/ResponsiveImageSelector.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
+#include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/Unused.h"
 
 #include "nsCOMPtr.h"
 #include "nsFontMetrics.h"
 #include "nsIImageLoadingContent.h"
 #include "nsImageLoadingContent.h"
 #include "nsString.h"
 #include "nsPrintfCString.h"
@@ -1772,17 +1773,17 @@ nsRect nsDisplayImage::GetDestRect() con
   return imageFrame->PredictedDestRect(frameContentBox);
 }
 
 LayerState nsDisplayImage::GetLayerState(
     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
     const ContainerLayerParameters& aParameters) {
   if (!nsDisplayItem::ForceActiveLayers()) {
     bool animated = false;
-    if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
+    if (!StaticPrefs::layout_animated_image_layers_enabled() ||
         mImage->GetType() != imgIContainer::TYPE_RASTER ||
         NS_FAILED(mImage->GetAnimated(&animated)) || !animated) {
       if (!aManager->IsCompositingCheap() ||
           !nsLayoutUtils::GPUImageScalingEnabled()) {
         return LayerState::LAYER_NONE;
       }
     }
 
--- a/layout/generic/nsRubyFrame.cpp
+++ b/layout/generic/nsRubyFrame.cpp
@@ -7,16 +7,17 @@
 /* rendering object for CSS "display: ruby" */
 
 #include "nsRubyFrame.h"
 
 #include "RubyUtils.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/WritingModes.h"
 #include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsRubyBaseContainerFrame.h"
 #include "nsRubyTextContainerFrame.h"
 
 using namespace mozilla;
 
@@ -307,17 +308,17 @@ void nsRubyFrame::ReflowSegment(nsPresCo
       side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder));
     } else {
       // XXX inter-character support in bug 1055672
       MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position");
     }
 
     LogicalPoint position(lineWM);
     if (side.isSome()) {
-      if (nsLayoutUtils::IsInterCharacterRubyEnabled() &&
+      if (StaticPrefs::layout_css_ruby_intercharacter_enabled() &&
           rtcWM.IsVerticalRL() &&
           lineWM.GetInlineDir() == WritingMode::eInlineLTR) {
         // Inter-character ruby annotations are only supported for vertical-rl
         // in ltr horizontal writing. Fall back to non-inter-character behavior
         // otherwise.
         LogicalPoint offset(
             lineWM, offsetRect.ISize(lineWM),
             offsetRect.BSize(lineWM) > size.BSize(lineWM)
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -54,16 +54,17 @@
 #include "nsContentUtils.h"
 #include "SVGObserverUtils.h"
 #include "nsSVGIntegrationUtils.h"
 #include "gfxDrawable.h"
 #include "GeckoProfiler.h"
 #include "nsCSSRenderingBorders.h"
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
+#include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
 #include "gfxGradientCache.h"
 #include "nsInlineFrame.h"
 #include "nsRubyTextContainerFrame.h"
 #include <algorithm>
 #include "SVGImageContext.h"
 #include "TextDrawTarget.h"
@@ -1033,17 +1034,17 @@ Maybe<nsCSSBorderRenderer> nsCSSRenderin
 
   // convert the radii
   nsMargin outlineMargin(width, width, width, width);
   RectCornerRadii outlineRadii;
   ComputePixelRadii(twipsRadii, oneDevPixel, &outlineRadii);
 
   StyleBorderStyle outlineStyle;
   if (ourOutline->mOutlineStyle.IsAuto()) {
-    if (nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
+    if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
       nsITheme* theme = aPresContext->GetTheme();
       if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
                                               StyleAppearance::FocusOutline)) {
         theme->DrawWidgetBackground(aRenderingContext, aForFrame,
                                     StyleAppearance::FocusOutline, innerRect,
                                     aDirtyRect);
         return Nothing();
       }
@@ -3737,37 +3738,66 @@ static bool GetSkFontFromGfxFont(DrawTar
   if (!typeface) {
     return false;
   }
 
   aSkFont = SkFont(sk_ref_sp(typeface), SkFloatToScalar(fontBase->GetSize()));
   return true;
 }
 
-// Computes data used to position text and the decoration line within a
-// SkTextBlob, data is returned through aTextPos and aBounds
+// Computes data used to position the decoration line within a
+// SkTextBlob, data is returned through aBounds
 static void GetPositioning(
     const nsCSSRendering::PaintDecorationLineParams& aParams, const Rect& aRect,
-    Float aOneCSSPixel, SkScalar aBounds[], SkPoint& aTextPos) {
+    Float aOneCSSPixel, Float aCenterBaselineOffset, SkScalar aBounds[]) {
+  /**
+   * How Positioning in Skia Works
+   *  Take the letter "n" for example
+   *  We set textPos as 0, 0
+   *  This is represented in Skia like so (not to scale)
+   *        ^
+   *  -10px |  _ __
+   *        | | '_ \
+   *   -5px | | | | |
+   * y-axis | |_| |_|
+   *  (0,0) ----------------------->
+   *        |     5px        10px
+   *    5px |
+   *        |
+   *   10px |
+   *        v
+   *  0 on the x axis is a line that touches the bottom of the n
+   *  (0,0) is the bottom left-hand corner of the n character
+   *  Moving "up" from the n is going in a negative y direction
+   *  Moving "down" from the n is going in a positive y direction
+   *
+   *  The intercepts that are returned in this arrangement will be
+   *  offset by the original point it starts at. (This happens in
+   *  the SkipInk function below).
+   *
+   *  In Skia, text MUST be laid out such that the next character
+   *  in the RunBuffer is further along the x-axis than the previous
+   *  character, otherwise there is undefined/strange behavior.
+   */
+
+  Float rectThickness = aParams.vertical ? aRect.Width() : aRect.Height();
+
   // the upper and lower lines/edges of the under or over line
   SkScalar upperLine, lowerLine;
-
-  // TextPos is the x,y coordinates of where the text is positioned, offset
-  // from the page boundaries. It should be the baseline of the text
-  // on the y axis, and offset to the start of the text for the x axis
   if (aParams.decoration == mozilla::StyleTextDecorationLine_OVERLINE) {
-    aTextPos = {aParams.pt.x,
-                aParams.pt.y + aParams.offset + (2 * aParams.lineSize.height)};
-    lowerLine = aTextPos.fY - aParams.offset + aParams.defaultLineThickness;
-    upperLine = lowerLine - aRect.Height();
+    lowerLine =
+        -aParams.offset + aParams.defaultLineThickness - aCenterBaselineOffset;
+    upperLine = lowerLine - rectThickness;
   } else {
-    aTextPos = {aParams.pt.x,
-                aParams.pt.y - aParams.offset - aParams.lineSize.height};
-    upperLine = aTextPos.fY - aParams.offset;
-    lowerLine = upperLine + aRect.Height();
+    // underlines in vertical text are offset from the center of
+    // the text, and not the baseline
+    // Skia sets the text at it's baseline so we have to offset it
+    // for text in vertical-* writing modes
+    upperLine = -aParams.offset - aCenterBaselineOffset;
+    lowerLine = upperLine + rectThickness;
   }
 
   // set up the bounds, add in a little padding to the thickness of the line
   // (unless the line is <= 1 CSS pixel thick)
   Float lineThicknessPadding = aParams.lineSize.height > aOneCSSPixel
                                    ? 0.25f * aParams.lineSize.height
                                    : 0;
   // don't allow padding greater than 0.75 CSS pixel
@@ -3943,41 +3973,64 @@ static void SkipInk(nsIFrame* aFrame, Dr
   double padding = aParams.lineSize.height;
   double oneCSSPixel = aFrame->PresContext()->CSSPixelsToDevPixels(1.0f);
   padding = std::max(padding, oneCSSPixel);
   int length = aIntercepts.Length();
 
   SkScalar startIntercept = 0;
   SkScalar endIntercept = 0;
 
+  // keep track of the direction we are drawing the clipped rects in
+  // for sideways text, our intercepts from the first glyph are actually
+  // decreasing (towards the top edge of the page), so we use a negative
+  // direction
+  Float dir = 1.0f;
+  Float lineStart = aParams.vertical ? aParams.pt.y : aParams.pt.x;
+  Float lineEnd = lineStart + aParams.lineSize.width;
+  if (aParams.sidewaysLeft) {
+    dir = -1.0f;
+    std::swap(lineStart, lineEnd);
+  }
+
   for (int i = 0; i <= length; i += 2) {
     // handle start/end edge cases and set up general case
-    startIntercept = (i > 0) ? aIntercepts[i - 1] : aParams.pt.x - padding;
-    endIntercept = (i < length)
-                       ? aIntercepts[i]
-                       : aParams.pt.x + aParams.lineSize.width + padding;
+    startIntercept = (i > 0) ? (dir * aIntercepts[i - 1]) + lineStart
+                             : lineStart - (dir * padding);
+    endIntercept = (i < length) ? (dir * aIntercepts[i]) + lineStart
+                                : lineEnd + (dir * padding);
 
     // remove padding at both ends for width
     // the start of the line is calculated so the padding removes just
     // enough so that the line starts at its normal position
     clipParams.lineSize.width =
-        (endIntercept - startIntercept) - (2.0 * padding);
-    aRect.width = clipParams.lineSize.width;
+        (dir * (endIntercept - startIntercept)) - (2.0 * padding);
+
+    if (aParams.vertical) {
+      aRect.height = clipParams.lineSize.width;
+    } else {
+      aRect.width = clipParams.lineSize.width;
+    }
 
     // Don't draw decoration lines that have a smaller width than 1, or half the
     // decoration thickness
     if (clipParams.lineSize.width <
         std::max(0.5 * clipParams.lineSize.height, 1.0)) {
       continue;
     }
 
     // start the line right after the intercept's location plus room for
     // padding
-    clipParams.pt.x = startIntercept + padding;
-    aRect.x = clipParams.pt.x;
+    if (aParams.vertical) {
+      clipParams.pt.y = aParams.sidewaysLeft ? endIntercept + padding
+                                             : startIntercept + padding;
+      aRect.y = clipParams.pt.y;
+    } else {
+      clipParams.pt.x = startIntercept + padding;
+      aRect.x = clipParams.pt.x;
+    }
 
     nsCSSRendering::PaintDecorationLineInternal(aFrame, aDrawTarget, clipParams,
                                                 aRect);
   }
 }
 
 void nsCSSRendering::PaintDecorationLine(
     nsIFrame* aFrame, DrawTarget& aDrawTarget,
@@ -4029,17 +4082,19 @@ void nsCSSRendering::PaintDecorationLine
 
   // pointer to the array of glyphs for this TextRun
   gfxTextRun::CompressedGlyph* characterGlyphs = textRun->GetCharacterGlyphs();
 
   // get positioning info
   SkPoint textPos = {0, 0};
   SkScalar bounds[] = {0, 0};
   Float oneCSSPixel = aFrame->PresContext()->CSSPixelsToDevPixels(1.0f);
-  GetPositioning(aParams, rect, oneCSSPixel, bounds, textPos);
+  if (!textRun->UseCenterBaseline()) {
+    GetPositioning(aParams, rect, oneCSSPixel, 0, bounds);
+  }
 
   // array for the text intercepts
   nsTArray<SkScalar> intercepts;
 
   // array for spacing data
   AutoTArray<gfxTextRun::PropertyProvider::Spacing, 64> spacing;
   spacing.SetLength(aParams.glyphRange.Length());
   if (aParams.provider != nullptr) {
@@ -4048,29 +4103,47 @@ void nsCSSRendering::PaintDecorationLine
 
   // loop through each glyph run
   // in most cases there will only be one
   bool isRTL = textRun->IsRightToLeft();
   int32_t spacingOffset = isRTL ? aParams.glyphRange.Length() - 1 : 0;
   gfxTextRun::GlyphRunIterator iter(textRun, aParams.glyphRange, isRTL);
 
   while (iter.NextRun()) {
+    if (iter.GetGlyphRun()->mOrientation ==
+        mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) {
+      // we don't support upright text in vertical modes currently
+      // see Bug 1572294 (https://bugzilla.mozilla.org/show_bug.cgi?id=1572294)
+      continue;
+    }
+
     // get the glyph run's font
     SkFont font;
     if (!GetSkFontFromGfxFont(aDrawTarget, iter.GetGlyphRun()->mFont, font)) {
       PaintDecorationLineInternal(aFrame, aDrawTarget, aParams, rect);
       return;
     }
 
     // create a text blob with correctly positioned glyphs
     sk_sp<const SkTextBlob> textBlob =
         CreateTextBlob(textRun, characterGlyphs, font, spacing.Elements(),
                        iter.GetStringStart(), iter.GetStringEnd(),
                        (float)appUnitsPerDevPixel, textPos, spacingOffset);
 
+    if (textRun->UseCenterBaseline()) {
+      // writing modes that use a center baseline need to be adjusted on a
+      // font-by-font basis since Skia lines up the text on a alphabetic
+      // baseline, but for some vertical-* writing modes the offset is from the
+      // center.
+      gfxFont::Metrics metrics =
+          iter.GetGlyphRun()->mFont->GetMetrics(nsFontMetrics::eHorizontal);
+      Float centerToBaseline = (metrics.emAscent - metrics.emDescent) / 2.0f;
+      GetPositioning(aParams, rect, oneCSSPixel, centerToBaseline, bounds);
+    }
+
     // compute the text intercepts that need to be skipped
     GetTextIntercepts(textBlob, bounds, intercepts);
   }
   bool needsSkipInk = intercepts.Length() > 0;
 
   if (needsSkipInk) {
     SkipInk(aFrame, aDrawTarget, aParams, intercepts, rect);
   } else {
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4451,17 +4451,17 @@ nsDisplayBackgroundImage::ShouldCreateOw
 
   nsIFrame* backgroundStyleFrame =
       nsCSSRendering::FindBackgroundStyleFrame(StyleFrame());
   if (ActiveLayerTracker::IsBackgroundPositionAnimated(aBuilder,
                                                        backgroundStyleFrame)) {
     return WHENEVER_POSSIBLE;
   }
 
-  if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) {
+  if (StaticPrefs::layout_animated_image_layers_enabled() && mBackgroundStyle) {
     const nsStyleImageLayers::Layer& layer =
         mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
     const nsStyleImage* image = &layer.mImage;
     if (image->GetType() == eStyleImageType_Image) {
       imgIRequest* imgreq = image->GetImageData();
       nsCOMPtr<imgIContainer> image;
       if (imgreq && NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) &&
           image) {
@@ -5399,17 +5399,18 @@ bool nsDisplayOutline::CreateWebRenderCo
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc,
     mozilla::layers::RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
   ContainerLayerParameters parameter;
 
   const auto& outlineStyle = mFrame->StyleOutline()->mOutlineStyle;
-  if (outlineStyle.IsAuto() && nsLayoutUtils::IsOutlineStyleAutoEnabled()) {
+  if (outlineStyle.IsAuto() &&
+      StaticPrefs::layout_css_outline_style_auto_enabled()) {
     nsITheme* theme = mFrame->PresContext()->GetTheme();
     if (theme && theme->ThemeSupportsWidget(mFrame->PresContext(), mFrame,
                                             StyleAppearance::FocusOutline)) {
       return false;
     }
   }
 
   nsPoint offset = ToReferenceFrame();
--- a/layout/reftests/bugs/456147-ref.html
+++ b/layout/reftests/bugs/456147-ref.html
@@ -1,18 +1,19 @@
 <!doctype html>
 <html><head><title>Colored strikethrough test</title>
 <style>
 /* In order to ensure consistency between the HTML reference and XUL
    test case, we explicitly specify all relevant style properties.  */
-* { 
+* {
   margin: 0;
   padding: 0;
-  border: none; 
-  background-color: transparent; 
+  border: none;
+  background-color: transparent;
+  text-decoration-skip-ink: none;
 }
 body {
   padding: 8px;
   background-color: white;
 }
 span {
   color: black;
   font: normal normal normal 12pt/14pt serif;
--- a/layout/reftests/bugs/456147.css
+++ b/layout/reftests/bugs/456147.css
@@ -1,25 +1,27 @@
 /* In order to ensure consistency between the HTML reference and XUL
    test case, we explicitly specify all relevant style properties.  */
-* { 
+* {
   margin: 0;
   padding: 0;
-  border: none; 
-  background-color: transparent; 
+  border: none;
+  background-color: transparent;
 }
 window {
   padding: 8px;
   background-color: white;
 }
 label, text, description {
   color: black;
   font: normal normal normal 12pt/14pt serif;
   height: 30px;
 }
 vbox#under {
   text-decoration: underline;
+  text-decoration-skip-ink: none;
   color: orange;
 }
 vbox#strike {
   text-decoration: line-through;
+  text-decoration-skip-ink: none;
   color: blue;
 }
--- a/layout/reftests/selection/rtl-selection-with-decoration-ref.html
+++ b/layout/reftests/selection/rtl-selection-with-decoration-ref.html
@@ -2,16 +2,17 @@
 <html>
   <head>
     <meta charset="utf-8" />
     <title>rtl-selection-with-decoration-ref</title>
     <style>
       p {
         font-size: 2em;
         text-decoration-color: rgba(0, 0, 0, 0.3);
+        text-decoration-skip-ink: none;
       }
       #text1 {
         text-decoration-line: line-through underline;
       }
       #text2 {
         text-decoration-line: line-through overline;
       }
     </style>
--- a/layout/reftests/selection/rtl-selection-with-decoration.html
+++ b/layout/reftests/selection/rtl-selection-with-decoration.html
@@ -2,16 +2,17 @@
 <html>
   <head>
     <meta charset="utf-8" />
     <title>rtl-selection-with-decoration</title>
     <style>
       p {
         font-size: 2em;
         text-decoration-color: rgba(0, 0, 0, 0.3);
+        text-decoration-skip-ink: none;
       }
       #text1 {
         text-decoration-line: line-through underline;
       }
       #text2 {
         text-decoration-line: line-through overline;
       }
       ::-moz-selection {
--- a/layout/reftests/selection/semitransparent-decoration-line-ref.html
+++ b/layout/reftests/selection/semitransparent-decoration-line-ref.html
@@ -1,14 +1,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8" />
     <title>semitransparent-decoration-line-ref</title>
     <style>
+      *{
+         text-decoration-skip-ink: none;
+      }
       .underline {
         text-decoration: underline rgba(0,0,0,0.3);
         font-size: 2em;
       }
       .overline {
         text-decoration: overline rgba(0,0,0,0.3);
         font-size: 2em;
       }
--- a/layout/reftests/selection/semitransparent-decoration-line.html
+++ b/layout/reftests/selection/semitransparent-decoration-line.html
@@ -1,14 +1,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8" />
     <title>semitransparent-decoration-line</title>
     <style>
+      * {
+         text-decoration-skip-ink: none;
+      }
       .underline {
         text-decoration: underline rgba(0,0,0,0.3);
         font-size: 2em;
       }
       .overline {
         text-decoration: overline rgba(0,0,0,0.3);
         font-size: 2em;
       }
--- a/layout/reftests/svg/text/pseudo-first-line-2-ref.svg
+++ b/layout/reftests/svg/text/pseudo-first-line-2-ref.svg
@@ -1,10 +1,10 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
-  <g style="font: 16px sans-serif">
+  <g style="font: 16px sans-serif; text-decoration-skip-ink: none;">
     <text x="100" y="100" text-decoration="underline">hello there everyone</text>
     <text x="100" y="120">and good night</text>
   </g>
 </svg>
--- a/layout/reftests/svg/text/pseudo-first-line-2.svg
+++ b/layout/reftests/svg/text/pseudo-first-line-2.svg
@@ -1,11 +1,14 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
   <style>
-    text::first-line { text-decoration: underline }
+    text::first-line {
+       text-decoration: underline;
+       text-decoration-skip-ink: none;
+    }
   </style>
   <text x="100" y="100" style="font: 16px sans-serif; white-space: pre-line; line-height: 20px">hello there everyone
     and good night</text>
 </svg>
--- a/layout/reftests/text-decoration/decoration-color-quirks-ref.html
+++ b/layout/reftests/text-decoration/decoration-color-quirks-ref.html
@@ -1,8 +1,13 @@
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <p style="color: #008000;
           text-decoration: underline line-through overline;">
   <span style="color: #0000FF;">This blue text has green decoration lines</span>
 </p>
 <p style="color: yellow;
           text-decoration: underline line-through overline;">
   <span style="color: red;">This red text has yellow decoration lines</span>
 </p>
--- a/layout/reftests/text-decoration/decoration-color-quirks.html
+++ b/layout/reftests/text-decoration/decoration-color-quirks.html
@@ -1,8 +1,13 @@
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <p style="color: blue;
           text-decoration: underline line-through overline;
           text-decoration-color: green;">
   This blue text has green decoration lines
 </p>
 <p style="color: #ff0000;
           text-decoration: underline line-through overline;
           text-decoration-color: #ffff00;">
--- a/layout/reftests/text-decoration/decoration-color-standards-ref.html
+++ b/layout/reftests/text-decoration/decoration-color-standards-ref.html
@@ -1,9 +1,14 @@
 <!DOCTYPE html>
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <p style="color: #008000;
           text-decoration: underline line-through overline;">
   <span style="color: #0000FF;">This blue text has green decoration lines</span>
 </p>
 <p style="color: yellow;
           text-decoration: underline line-through overline;">
   <span style="color: red;">This red text has yellow decoration lines</span>
 </p>
--- a/layout/reftests/text-decoration/decoration-color-standards.html
+++ b/layout/reftests/text-decoration/decoration-color-standards.html
@@ -1,9 +1,14 @@
 <!DOCTYPE html>
+<style>
+ *{
+    text-decoration-skip-ink: none;
+ }
+</style>
 <p style="color: blue;
           text-decoration: underline line-through overline;
           text-decoration-color: green;">
   This blue text has green decoration lines
 </p>
 <p style="color: #ff0000;
           text-decoration: underline line-through overline;
           text-decoration-color: #ffff00;">
--- a/layout/reftests/text-decoration/decoration-css21-ref.html
+++ b/layout/reftests/text-decoration/decoration-css21-ref.html
@@ -3,16 +3,17 @@
 		<style>
 			.sup {vertical-align: super;}
 			.transparent {color: transparent;}
 			.alllines {text-decoration:line-through overline underline; color: purple;}
 			.highRel {position: relative; top: -4em;}
 			.lowRel {position: relative; top: 4em;}
 			.lowVert {vertical-align: -4em;}
 			.highVert {vertical-align: 4em;}
+      * { text-decoration-skip-ink: none;}
 		</style>
 	</head>
 	<body>
 		<p>
 			<span style="text-decoration: underline">Underlined <span class="transparent">still underlined</span></span>
 			<span style="text-decoration: underline">Underlined <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span>
 			<span class="sup transparent">Offset</span>
 		</p>
--- a/layout/reftests/text-decoration/decoration-css21.html
+++ b/layout/reftests/text-decoration/decoration-css21.html
@@ -3,16 +3,17 @@
 		<style>
 			.sup {vertical-align: super;}
 			.transparent {color: transparent;}
 			.alllines {text-decoration:line-through overline underline; color: purple;}
 			.highRel {position: relative; top: -4em;}
 			.lowRel {position: relative; top: 4em;}
 			.lowVert {vertical-align: -4em;}
 			.highVert {vertical-align: 4em;}
+      * { text-decoration-skip-ink: none;}
 		</style>
 	</head>
 	<body>
 		<p>
 			<span style="text-decoration: underline">Underlined <span class="sup transparent">still underlined</span></span>
 			<span style="text-decoration: underline">Underlined <span class="sup">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span>
 		</p>
 		<p>
--- a/layout/reftests/text-decoration/decoration-style-quirks-ref.html
+++ b/layout/reftests/text-decoration/decoration-style-quirks-ref.html
@@ -1,8 +1,13 @@
+<style>
+ p{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <p>
   Here is specified the decoration style as dotted but no decoration lines,
   however,
   <span style="font-size: 2em;
                text-decoration: underline line-through overline;
                text-decoration-style: solid;">
     here has solid decoration lines</span>,
   and here has no decoration lines.
--- a/layout/reftests/text-decoration/decoration-style-quirks.html
+++ b/layout/reftests/text-decoration/decoration-style-quirks.html
@@ -1,8 +1,13 @@
+<style>
+ p{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <p style="text-decoration-style: dotted;">
   Here is specified the decoration style as dotted but no decoration lines, however,
   <span style="font-size: 2em;
                text-decoration: underline line-through overline;">
     here has solid decoration lines</span>,
   and here has no decoration lines.
 </p>
 <p style="text-decoration-style: dashed;">
--- a/layout/reftests/text-decoration/text-decoration-propagation-1-quirks-ref.html
+++ b/layout/reftests/text-decoration/text-decoration-propagation-1-quirks-ref.html
@@ -1,10 +1,14 @@
 <title>text-decoration</title>
-
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <h1>text-decoration on a block</h1>
 
   <u>text directly in parent</u>
 
   <div><u>text in block</u></div>
 
   <div style="float:left; clear: left">text in float</div>
   <div style="clear:left"></div>
--- a/layout/reftests/text-decoration/text-decoration-propagation-1-quirks.html
+++ b/layout/reftests/text-decoration/text-decoration-propagation-1-quirks.html
@@ -1,10 +1,14 @@
 <title>text-decoration</title>
-
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <h1>text-decoration on a block</h1>
 
 <div style="text-decoration: underline">
 
   text directly in parent
 
   <div>text in block</div>
 
--- a/layout/reftests/text-decoration/text-decoration-propagation-1-standards-ref.html
+++ b/layout/reftests/text-decoration/text-decoration-propagation-1-standards-ref.html
@@ -1,11 +1,15 @@
 <!DOCTYPE HTML>
 <title>text-decoration</title>
-
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <h1>text-decoration on a block</h1>
 
   <u>text directly in parent</u>
 
   <div><u>text in block</u></div>
 
   <div style="float:left; clear: left">text in float</div>
   <div style="clear:left"></div>
--- a/layout/reftests/text-decoration/text-decoration-propagation-1-standards.html
+++ b/layout/reftests/text-decoration/text-decoration-propagation-1-standards.html
@@ -1,11 +1,15 @@
 <!DOCTYPE html>
 <title>text-decoration</title>
-
+<style>
+ *{
+     text-decoration-skip-ink: none;
+ }
+</style>
 <h1>text-decoration on a block</h1>
 
 <div style="text-decoration: underline">
 
   text directly in parent
 
   <div>text in block</div>
 
--- a/layout/reftests/text-decoration/underline-block-propagation-2-standards-ref.html
+++ b/layout/reftests/text-decoration/underline-block-propagation-2-standards-ref.html
@@ -1,14 +1,15 @@
 <!DOCTYPE HTML>
 <html><head>
 <title>More tests of propagation of text-decoration</title>
 <style>
 textarea { -moz-appearance: none }
 textarea + textarea { margin-left: 10px }
+* { text-decoration-skip-ink: none }
 </style>
 </head>
 <body>
 <!-- t-d should not propagate to the content of a form control -->
 <form>
 <span style="text-decoration:underline">This text should be underlined.</span><br>
 <textarea rows="2" cols="40">This text should not be underlined.</textarea
 ><textarea rows="2" cols="40" style="text-decoration:line-through"
--- a/layout/reftests/text-decoration/underline-block-propagation-2-standards.html
+++ b/layout/reftests/text-decoration/underline-block-propagation-2-standards.html
@@ -1,14 +1,15 @@
 <!DOCTYPE HTML>
 <html><head>
 <title>More tests of propagation of text-decoration</title>
 <style>
 textarea { -moz-appearance: none }
 textarea + textarea { margin-left: 10px }
+* { text-decoration-skip-ink: none }
 </style>
 </head>
 <body>
 <!-- t-d should not propagate to the content of a form control -->
 <form style="text-decoration:underline">
 This text should be underlined.<br>
 <textarea rows="2" cols="40">This text should not be underlined.</textarea
 ><textarea rows="2" cols="40" style="text-decoration:line-through"
--- a/layout/reftests/text-overflow/block-padding-ref.html
+++ b/layout/reftests/text-overflow/block-padding-ref.html
@@ -8,22 +8,22 @@
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <title>text-overflow: text-overflow block padding </title>
 <style type="text/css">
 @font-face {
   font-family: DejaVuSansMono;
   src: url(../fonts/DejaVuSansMono.woff);
 }
 html,body {
-    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono;
+    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono; text-decoration-skip-ink: none;
 }
 body { width:24ch; overflow:hidden; }
 
-.test { 
-  overflow: hidden; 
+.test {
+  overflow: hidden;
   white-space: nowrap;
   padding-left: 1ch;
   padding-right: 3ch;
   height: 3em;
   margin-bottom:1em;
 }
 .s {
   overflow: auto; position:relative;
--- a/layout/reftests/text-overflow/block-padding.html
+++ b/layout/reftests/text-overflow/block-padding.html
@@ -10,32 +10,32 @@
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <title>text-overflow: text-overflow block padding </title>
 <style type="text/css">
 @font-face {
   font-family: DejaVuSansMono;
   src: url(../fonts/DejaVuSansMono.woff);
 }
 html,body {
-    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono;
+    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono; text-decoration-skip-ink: none;
 }
 body { width:24ch; overflow:hidden; }
 
-.test { 
-  overflow: hidden; 
+.test {
+  overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-decoration: line-through;
   padding-left: 1ch;
   padding-right: 3ch;
   height: 3em;
   margin-bottom:1em;
 }
 .s {
-  overflow: auto; 
+  overflow: auto;
 }
 span {
   text-decoration: underline overline;
   background:yellow;
 }
 .rtl {
   direction:rtl;
 }
--- a/layout/reftests/text-overflow/vertical-decorations-3-ref.html
+++ b/layout/reftests/text-overflow/vertical-decorations-3-ref.html
@@ -7,16 +7,17 @@ div {
   font: 25px monospace;
   white-space: pre;
   overflow: hidden;
   text-overflow: ellipsis;
   border: 1px solid gray;
   padding: 5px;
   display: inline-block;
   vertical-align: top;
+  text-decoration-skip-ink: none;
 }
 .vrl {
   writing-mode: vertical-rl;
   text-orientation: upright;
   height: 6em;
 }
 </style>
 </head>
--- a/layout/reftests/text-overflow/vertical-decorations-3.html
+++ b/layout/reftests/text-overflow/vertical-decorations-3.html
@@ -7,16 +7,17 @@ div {
   font: 25px monospace;
   white-space: pre;
   overflow: hidden;
   text-overflow: ellipsis;
   border: 1px solid gray;
   padding: 5px;
   display: inline-block;
   vertical-align: top;
+  text-decoration-skip-ink:none;
 }
 .vrl {
   writing-mode: vertical-rl;
   text-orientation: upright;
   height: 6em;
 }
 </style>
 </head>
--- a/layout/reftests/text-shadow/decorations-multiple-zorder-ref.html
+++ b/layout/reftests/text-shadow/decorations-multiple-zorder-ref.html
@@ -1,10 +1,14 @@
 <!DOCTYPE HTML>
-
+<style>
+div{
+    text-decoration-skip-ink: none;
+ }
+</style>
 <!-- Shadows -->
 <!-- Blue underline/text -->
 <div style="position: absolute; top: 22px; left: 22px;"><span style="color: blue; text-decoration: underline">test</span></div>
 <!-- Red overline/text -->
 <!--
 There are some additional pixels appearing when two red texts are overlap. We
 use transparent color for the first one to prevent the situation and the failure 
 of reftest.
--- a/layout/reftests/text-shadow/decorations-multiple-zorder.html
+++ b/layout/reftests/text-shadow/decorations-multiple-zorder.html
@@ -1,3 +1,7 @@
 <!DOCTYPE HTML>
-
+<style>
+div{
+    text-decoration-skip-ink: none;
+ }
+</style>
 <div style="text-shadow: 2px 2px; color: blue; text-decoration: underline; position: absolute; top: 20px; left: 20px;">test<span style="text-decoration: overline; color: red;">for<span style="text-decoration: underline; color: green;">quirks</span></span></div>
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -8,16 +8,17 @@
  * A class used for intermediate representations of the -moz-transform property.
  */
 
 #include "nsStyleTransformMatrix.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsSVGUtils.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/StaticPrefs_svg.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "gfxMatrix.h"
 #include "gfxQuaternion.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 namespace nsStyleTransformMatrix {
@@ -42,17 +43,17 @@ void TransformReferenceBox::EnsureDimens
     return;
   }
 
   MOZ_ASSERT(mFrame);
 
   mIsCached = true;
 
   if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
-    if (!nsLayoutUtils::SVGTransformBoxEnabled()) {
+    if (!StaticPrefs::svg_transform_box_enabled()) {
       mX = -mFrame->GetPosition().x;
       mY = -mFrame->GetPosition().y;
       Size contextSize = nsSVGUtils::GetContextSize(mFrame);
       mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
       mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
     } else if (mFrame->StyleDisplay()->mTransformBox ==
                StyleGeometryBox::FillBox) {
       // Percentages in transforms resolve against the SVG bbox, and the
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -133,18 +133,16 @@ nsSVGFilterFrame* nsSVGFilterInstance::G
     }
 
     url = urlExtraReferrer->GetURI();
   } else {
     url = mFilter.AsUrl().ResolveLocalRef(mTargetContent);
   }
 
   if (!url) {
-    MOZ_ASSERT_UNREACHABLE(
-        "an StyleFilter of type URL should have a non-null URL");
     return nullptr;
   }
 
   // Look up the filter element by URL.
   IDTracker idTracker;
   bool watch = false;
   idTracker.ResetToURIFragmentID(
       mTargetContent, url, mFilter.AsUrl().ExtraData().ReferrerInfo(), watch);
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -36,19 +36,16 @@ pref("browser.tabs.expireTime", 900);
 // test harness code to be zombified.
 pref("browser.tabs.disableBackgroundZombification", false);
 
 // Control whether tab content should try to load from disk cache when network
 // is offline.
 // Controlled by Switchboard experiment "offline-cache".
 pref("browser.tabs.useCache", false);
 
-// From libpref/src/init/all.js, extended to allow a slightly wider zoom range.
-pref("zoom.minPercent", 20);
-pref("zoom.maxPercent", 400);
 pref("toolkit.zoomManager.zoomValues", ".2,.3,.5,.67,.8,.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3,4");
 
 // Mobile will use faster, less durable mode.
 pref("toolkit.storage.synchronous", 0);
 
 // Android needs concurrent access to the same database from multiple processes,
 // thus we can't use exclusive locking on it.
 pref("storage.multiProcessAccess.enabled", true);
@@ -341,18 +338,16 @@ pref("dom.max_script_run_time", 20);
 
 // Absolute path to the devtools unix domain socket file used
 // to communicate with a usb cable via adb forward.
 pref("devtools.debugger.unix-domain-socket", "@ANDROID_PACKAGE_NAME@/firefox-debugger-socket");
 
 pref("devtools.remote.usb.enabled", false);
 pref("devtools.remote.wifi.enabled", false);
 
-pref("font.size.inflation.minTwips", 0);
-
 // When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
 pref("browser.ui.zoom.force-user-scalable", false);
 
 // With the typical screen sizes on mobile devices, we want to wrap page sources by default.
 pref("view_source.wrap_long_lines", true);
 
 
 pref("ui.touch.radius.enabled", false);
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -324,17 +324,16 @@ public class BrowserApp extends GeckoApp
 
     private final TelemetryCorePingDelegate mTelemetryCorePingDelegate = new TelemetryCorePingDelegate();
     private final TelemetryActivationPingDelegate mTelemetryActivationPingDelegate = new TelemetryActivationPingDelegate();
 
     private final List<BrowserAppDelegate> delegates = Collections.unmodifiableList(Arrays.asList(
             new ScreenshotDelegate(),
             new BookmarkStateChangeDelegate(),
             new ReaderViewBookmarkPromotion(),
-            new PostUpdateHandler(),
             mTelemetryCorePingDelegate,
             mTelemetryActivationPingDelegate,
             new OfflineTabStatusDelegate(),
             new AdjustBrowserAppDelegate(mTelemetryCorePingDelegate)
     ));
 
     @NonNull
     private SearchEngineManager mSearchEngineManager; // Contains reference to Context - DO NOT LEAK!
@@ -645,16 +644,21 @@ public class BrowserApp extends GeckoApp
         }
 
         // This has to be prepared prior to calling GeckoApp.onCreate, because
         // widget code and BrowserToolbar need it, and they're created by the
         // layout, which GeckoApp takes care of.
         final GeckoApplication app = (GeckoApplication) getApplication();
         app.prepareLightweightTheme();
 
+        // Copying features out the APK races Gecko startup: the first time the profile is read by
+        // Gecko, it needs to find the copied features.  `super.onCreate(...)` initiates Gecko
+        // startup, so this must come first -- and be synchronous!
+        new PostUpdateHandler().onCreate(this, savedInstanceState);
+
         super.onCreate(savedInstanceState);
 
         if (isShutDownOrAbort()) {
             return;
         }