Merge mozilla-central to inbound. a=merge CLOSED TREE
authorOana Pop Rus <opoprus@mozilla.com>
Thu, 09 May 2019 01:01:06 +0300
changeset 531977 7244f626713e301a1e3a338c57ad027864c57fe5
parent 531976 d513a09214181f9bd07747593ff8eb46d05f4bb4 (current diff)
parent 531871 fd9b8188501938a9306105a01db5beeefeab1a19 (diff)
child 531978 465fa362c7020a91a653ba36f068487558114b89
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.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 mozilla-central to inbound. a=merge CLOSED TREE
browser/components/search/extensions/amazon/_locales/br/messages.json
browser/components/search/extensions/amazondotcom/_locales/de/messages.json
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_content_mainthreadio.js
@@ -0,0 +1,419 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test records I/O syscalls done on the main thread during startup.
+ *
+ * To run this test similar to try server, you need to run:
+ *   ./mach package
+ *   ./mach test --appname=dist <path to test>
+ *
+ * If you made changes that cause this test to fail, it's likely because you
+ * are touching more files or directories during startup.
+ * Most code has no reason to use main thread I/O.
+ * If for some reason accessing the file system on the main thread is currently
+ * unavoidable, consider defering the I/O as long as you can, ideally after
+ * the end of startup.
+ */
+
+"use strict";
+
+/* Set this to true only for debugging purpose; it makes the output noisy. */
+const kDumpAllStacks = false;
+
+// Shortcuts for conditions.
+const LINUX = AppConstants.platform == "linux";
+const WIN = AppConstants.platform == "win";
+const MAC = AppConstants.platform == "macosx";
+
+/* Paths in the whitelist can:
+ *  - be a full path, eg. "/etc/mime.types"
+ *  - have a prefix which will be resolved using Services.dirsvc
+ *    eg. "GreD:omni.ja"
+ *    It's possible to have only a prefix, in thise case the directory will
+ *    still be resolved, eg. "UAppData:"
+ *  - use * at the begining and/or end as a wildcard
+ * The folder separator is '/' even for Windows paths, where it'll be
+ * automatically converted to '\'.
+ *
+ * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
+ * without this the test is strict and will fail if a whitelist entry isn't used.
+ *
+ * Each entry specifies the maximum number of times an operation is expected to
+ * occur.
+ * The operations currently reported by the I/O interposer are:
+ *   create/open: only supported on Windows currently. The test currently
+ *     ignores these markers to have a shorter initial whitelist.
+ *     Adding Unix support is bug 1533779.
+ *   stat: supported on all platforms when checking the last modified date or
+ *     file size. Supported only on Windows when checking if a file exists;
+ *     fixing this inconsistency is bug 1536109.
+ *   read: supported on all platforms, but unix platforms will only report read
+ *     calls going through NSPR.
+ *   write: supported on all platforms, but Linux will only report write calls
+ *     going through NSPR.
+ *   close: supported only on Unix, and only for close calls going through NSPR.
+ *     Adding Windows support is bug 1524574.
+ *   fsync: supported only on Windows.
+ *
+ * If an entry specifies more than one operation, if at least one of them is
+ * encountered, the test won't report a failure for the entry. This helps when
+ * whitelisting cases where the reported operations aren't the same on all
+ * platforms due to the I/O interposer inconsistencies across platforms
+ * documented above.
+ */
+const processes = {
+  "Web Content": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+  ],
+  "Privileged Content": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+  ],
+  "WebExtensions": [
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // Exists call in ScopedXREEmbed::SetAppDir
+      path: "XCurProcD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:webcompat@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+    },
+    { // bug 1357205
+      path: "XREAppFeat:screenshots@mozilla.org.xpi",
+      condition: !WIN,
+      close: 1,
+    },
+  ],
+};
+
+function expandWhitelistPath(path) {
+  if (path.includes(":")) {
+    let [prefix, suffix] = path.split(":");
+    let [key, property] = prefix.split(".");
+    let dir = Services.dirsvc.get(key, Ci.nsIFile);
+    if (property) {
+      dir = dir[property];
+    }
+
+    // Resolve symLinks.
+    let dirPath = dir.path;
+    while (dir && !dir.isSymlink()) {
+      dir = dir.parent;
+    }
+    if (dir) {
+      dirPath = dirPath.replace(dir.path, dir.target);
+    }
+
+    path = dirPath;
+
+    if (suffix) {
+      path += "/" + suffix;
+    }
+  }
+  if (AppConstants.platform == "win") {
+    path = path.replace(/\//g, "\\");
+  }
+  return path;
+}
+
+function getStackFromProfile(profile, stack) {
+  const stackPrefixCol = profile.stackTable.schema.prefix;
+  const stackFrameCol = profile.stackTable.schema.frame;
+  const frameLocationCol = profile.frameTable.schema.location;
+
+  let result = [];
+  while (stack) {
+    let sp = profile.stackTable.data[stack];
+    let frame = profile.frameTable.data[sp[stackFrameCol]];
+    stack = sp[stackPrefixCol];
+    frame = profile.stringTable[frame[frameLocationCol]];
+    if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
+      result.push(frame);
+    }
+  }
+  return result;
+}
+
+function getIOMarkersFromProfile(profile) {
+  const nameCol = profile.markers.schema.name;
+  const dataCol = profile.markers.schema.data;
+
+  let markers = [];
+  for (let m of profile.markers.data) {
+    let markerName = profile.stringTable[m[nameCol]];
+
+    if (markerName != "FileIO")
+      continue;
+
+    let markerData = m[dataCol];
+    if (markerData.source == "sqlite-mainthread") {
+      continue;
+    }
+
+    let samples = markerData.stack.samples;
+    let stack = samples.data[0][samples.schema.stack];
+    markers.push({operation: markerData.operation,
+                  filename: markerData.filename,
+                  source: markerData.source,
+                  stackId: stack});
+  }
+
+  return markers;
+}
+
+function pathMatches(path, filename) {
+  path = path.toLowerCase();
+  return path == filename || // Full match
+    // Wildcard on both sides of the path
+    (path.startsWith("*") && path.endsWith("*") &&
+     filename.includes(path.slice(1, -1))) ||
+    // Wildcard suffix
+    (path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
+    // Wildcard prefix
+    (path.startsWith("*") && filename.endsWith(path.slice(1)));
+}
+
+add_task(async function() {
+  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
+    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
+       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
+       "non-debug build.");
+    return;
+  }
+
+  {
+    let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+    omniJa.append("omni.ja");
+    if (!omniJa.exists()) {
+      ok(false, "This test requires a packaged build, " +
+                "run 'mach package' and then use --appname=dist");
+      return;
+    }
+  }
+
+  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
+  await startupRecorder.done;
+
+  for (let process in processes) {
+    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 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).filter(k => !["path", "condition"].includes(k))
+                                  .map(k => `${k}: ${e[k]}`);
+           return `  ${e.path} - ${operations.join(", ")}`;
+         }).join("\n"));
+
+    let profile;
+    for (let process of startupRecorder.data.profile.processes) {
+      if (process.threads[0].processName == procName) {
+        profile = process.threads[0];
+        break;
+      }
+    }
+    if (procName == "Privileged Content" && !profile) {
+      // The Privileged Content is started from an idle task that may not have
+      // been executed yet at the time we captured the startup profile in
+      // startupRecorder.
+      todo(false, `profile for ${procName} process not found`);
+    } else {
+      ok(profile, `Found profile for ${procName} process`);
+    }
+    if (!profile) {
+      continue;
+    }
+
+    let markers = getIOMarkersFromProfile(profile);
+    for (let marker of markers) {
+      if (marker.operation == "create/open") {
+        // TODO: handle these I/O markers once they are supported on
+        // non-Windows platforms.
+        continue;
+      }
+
+      // Convert to lower case before comparing because the OS X test slaves
+      // have the 'Firefox' folder in 'Library/Application Support' created
+      // as 'firefox' for some reason.
+      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;
+        }
+
+        // Ignore I/O due to IPC. This doesn't really touch the disk.
+        if (filename.startsWith(tmpPath + "/org.chromium.")) {
+          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;
+          break;
+        }
+      }
+      if (!expected) {
+        record(false,
+               `unexpected ${marker.operation} on ${marker.filename} in ${procName} process`,
+               undefined,
+               "  " + getStackFromProfile(profile, marker.stackId).join("\n  "));
+        shouldPass = false;
+      }
+      info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
+      if (kDumpAllStacks) {
+        info(getStackFromProfile(profile, marker.stackId).map(f => "  " + f)
+                                                         .join("\n"));
+      }
+    }
+
+    for (let entry of whitelist) {
+      for (let op in entry) {
+        if (["path", "condition", "ignoreIfUnused", "_used"].includes(op)) {
+          continue;
+        }
+        let message = `${op} on ${entry.path} `;
+        if (entry[op] == 0) {
+          message += "as many times as expected";
+        } else if (entry[op] > 0) {
+          message += `allowed ${entry[op]} more times`;
+        } else {
+          message += `${entry[op] * -1} more times than expected`;
+        }
+        ok(entry[op] >= 0, `${message} in ${procName} process`);
+      }
+      if (!("_used" in entry) && !entry.ignoreIfUnused) {
+        ok(false, `unused whitelist entry ${procName}: ${entry.path}`);
+      }
+    }
+  }
+
+  if (shouldPass) {
+    ok(shouldPass, "No unexpected main thread I/O during startup");
+  } else {
+    const filename = "child-startup-mainthreadio-profile.json";
+    let path = Cc["@mozilla.org/process/environment;1"]
+                 .getService(Ci.nsIEnvironment)
+                 .get("MOZ_UPLOAD_DIR");
+    let encoder = new TextEncoder();
+    let profilePath = OS.Path.join(path, filename);
+    await OS.File.writeAtomic(profilePath,
+                              encoder.encode(JSON.stringify(startupRecorder.data.profile)));
+    ok(false,
+       "Found some unexpected main thread I/O during child process startup; " +
+       "profile uploaded in " + filename);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js
@@ -0,0 +1,933 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This test records I/O syscalls done on the main thread during startup.
+ *
+ * To run this test similar to try server, you need to run:
+ *   ./mach package
+ *   ./mach test --appname=dist <path to test>
+ *
+ * If you made changes that cause this test to fail, it's likely because you
+ * are touching more files or directories during startup.
+ * Most code has no reason to use main thread I/O.
+ * If for some reason accessing the file system on the main thread is currently
+ * unavoidable, consider defering the I/O as long as you can, ideally after
+ * the end of startup.
+ * If your code isn't strictly required to show the first browser window,
+ * it shouldn't be loaded before we are done with first paint.
+ * Finally, if your code isn't really needed during startup, it should not be
+ * loaded before we have started handling user events.
+ */
+
+"use strict";
+
+const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
+
+/* Set this to true only for debugging purpose; it makes the output noisy. */
+const kDumpAllStacks = false;
+
+// Shortcuts for conditions.
+const LINUX = AppConstants.platform == "linux";
+const WIN = AppConstants.platform == "win";
+const MAC = AppConstants.platform == "macosx";
+
+/* Paths in the whitelist can:
+ *  - be a full path, eg. "/etc/mime.types"
+ *  - have a prefix which will be resolved using Services.dirsvc
+ *    eg. "GreD:omni.ja"
+ *    It's possible to have only a prefix, in thise case the directory will
+ *    still be resolved, eg. "UAppData:"
+ *  - use * at the begining and/or end as a wildcard
+ *  - For Windows specific entries that require resolving the path to its
+ *    canonical form, ie. the old DOS 8.3 format, use canonicalize: true.
+ *    This is needed for stat calls to non-existent files.
+ * The folder separator is '/' even for Windows paths, where it'll be
+ * automatically converted to '\'.
+ *
+ * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
+ * without this the test is strict and will fail if a whitelist entry isn't used.
+ *
+ * Each entry specifies the maximum number of times an operation is expected to
+ * occur.
+ * The operations currently reported by the I/O interposer are:
+ *   create/open: only supported on Windows currently. The test currently
+ *     ignores these markers to have a shorter initial whitelist.
+ *     Adding Unix support is bug 1533779.
+ *   stat: supported on all platforms when checking the last modified date or
+ *     file size. Supported only on Windows when checking if a file exists;
+ *     fixing this inconsistency is bug 1536109.
+ *   read: supported on all platforms, but unix platforms will only report read
+ *     calls going through NSPR.
+ *   write: supported on all platforms, but Linux will only report write calls
+ *     going through NSPR.
+ *   close: supported only on Unix, and only for close calls going through NSPR.
+ *     Adding Windows support is bug 1524574.
+ *   fsync: supported only on Windows.
+ *
+ * If an entry specifies more than one operation, if at least one of them is
+ * encountered, the test won't report a failure for the entry. This helps when
+ * whitelisting cases where the reported operations aren't the same on all
+ * platforms due to the I/O interposer inconsistencies across platforms
+ * documented above.
+ */
+const startupPhases = {
+  // Anything done before or during app-startup must have a compelling reason
+  // to run before we have even selected the user profile.
+  "before profile selection": [
+    { // bug 1541226
+      path: "UAppData:",
+      condition: WIN,
+      stat: 3,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/InstallTime20*",
+      condition: AppConstants.MOZ_CRASHREPORTER,
+      stat: 1, // only caught on Windows.
+      read: 1,
+      write: 2,
+      close: 1,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/LastCrash",
+      condition: WIN && AppConstants.MOZ_CRASHREPORTER,
+      stat: 1, // only caught on Windows.
+      read: 1,
+    },
+    { // bug 1541200
+      path: "UAppData:Crash Reports/LastCrash",
+      condition: !WIN && AppConstants.MOZ_CRASHREPORTER,
+      ignoreIfUnused: true, // only if we ever crashed on this machine
+      read: 1,
+      close: 1,
+    },
+    { // bug 1541226
+      path: "DefProfLRt.parent:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // At least the read seems unavoidable for a regular startup.
+      path: "UAppData:profiles.ini",
+      condition: MAC,
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1546931
+      path: "UAppData:installs.ini",
+      condition: WIN || MAC,
+      ignoreIfUnused: true, // only if a real profile exists on the system.
+      read: 1,
+      stat: 2,
+      close: 1,
+    },
+    { // At least the read seems unavoidable for a regular startup.
+      path: "UAppData:profiles.ini",
+      condition: WIN,
+      ignoreIfUnused: true, // only if a real profile exists on the system.
+      read: 1,
+      stat: 1,
+    },
+    { // bug 1541226, bug 1363586, bug 1541593
+      path: "ProfD:",
+      condition: WIN,
+      stat: 3,
+    },
+    {
+      path: "ProfLD:.startup-incomplete",
+      condition: !WIN, // Visible on Windows with an open marker
+      close: 1,
+    },
+    { // bug 1541491 to stop using this file, bug 1541494 to write correctly.
+      path: "ProfLD:compatibility.ini",
+      condition: !WIN, // Visible on Windows with an open marker
+      write: 18,
+      close: 1,
+    },
+    {
+      path: "GreD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    { // bug 1376994
+      path: "XCurProcD:omni.ja",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+    },
+    {
+      path: "ProfD:parent.lock",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541603
+      path: "ProfD:minidumps",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543746
+      path: "XCurProcD:defaults/preferences",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-child-current.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-child.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache-current.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544034
+      path: "ProfLDS:startupCache/scriptCache.bin",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1544037
+      path: "ProfLDS:startupCache/startupCache." +
+             (Services.appinfo.is64Bit ? 8 : 4) + ".little",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541601
+      path: "PrfDef:channel-prefs.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1543761
+      path: "GreD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1376994, bug 1543761
+      path: "XCurProcD:chrome.manifest",
+      condition: !WIN, // Visible on Windows with an open marker
+      stat: 1,
+      close: 1,
+    },
+    { // At least the read seems unavoidable
+      path: "PrefD:prefs.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    { // bug 1543752
+      path: "PrefD:user.js",
+      stat: 1,
+      read: 1,
+      close: 1,
+    },
+    {
+      path: "*ld.so.conf*",
+      condition: LINUX,
+      ignoreIfUnused: true,
+      read: 22,
+      close: 11,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: WIN,
+      write: 1,
+      fsync: 1,
+    },
+  ],
+
+  "before opening first browser window": [
+    { // bug 1541226
+      path: "ProfD:",
+      condition: WIN,
+      stat: 2,
+    },
+    {
+      path: "XCurProcD:blocklist.xml",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite-journal",
+      condition: !LINUX,
+      stat: 3,
+      write: 4,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite",
+      condition: !LINUX,
+      stat: 2,
+      read: 2,
+      write: 1,
+    },
+    { // bug 1534745
+      path: "ProfD:cookies.sqlite-wal",
+      condition: WIN,
+      stat: 2,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite",
+      condition: WIN || MAC,
+      fsync: 7,
+      read: 2,
+      stat: 1,
+      write: 10,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite-journal",
+      condition: WIN || MAC,
+      fsync: 7,
+      stat: 26,
+      write: 38,
+    },
+    { // bug 975996
+      path: "ProfD:permissions.sqlite-wal",
+      condition: WIN,
+      stat: 20,
+    },
+    { // Seems done by OS X and outside of our control.
+      path: "*.savedState/restorecount.plist",
+      condition: MAC,
+      ignoreIfUnused: true,
+      write: 1,
+    },
+    {
+      path: "*ld.so.conf*",
+      condition: LINUX,
+      ignoreIfUnused: true,
+      read: 22,
+      close: 11,
+    },
+    { // bug 1545167
+      path: "/etc/mime.types",
+      condition: LINUX,
+      read: 3,
+      close: 3,
+    },
+    {
+      path: "UChrm:userChrome.css",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541233
+      path: "UChrm:userContent.css",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XREUSysExt:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XRESysExtDev:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "ProfD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "XCurProcD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1541246
+      path: "UAppData:",
+      ignoreIfUnused: true, // sometimes before opening first browser window,
+                            // sometimes before first paint
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: WIN,
+      read: 1,
+    },
+  ],
+
+  // We reach this phase right after showing the first browser window.
+  // This means that any I/O at this point delayed first paint.
+  "before first paint": [
+    { // bug 1541226
+      path: "ProfD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545119
+      path: "OldUpdRootD:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1446012
+      path: "UpdRootD:updates/0/update.status",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:pluginreg.dat",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:pluginreg.dat.tmp",
+      stat: 1,
+      write: 64,
+      close: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "APlugns:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "UserPlugins.parent:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "UserPlugins:",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/nptest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npsecondtest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npthirdtest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1545123
+      path: "ProfD:plugins/npswftest.dll",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "XREAppFeat:formautofill@mozilla.org.xpi",
+      condition: !WIN,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1545167
+      path: "/etc/mime.types",
+      condition: LINUX,
+      read: 1,
+      close: 1,
+    },
+    { // We only hit this for new profiles.
+      path: "XREAppDist:distribution.ini",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "*WindowsApps/microsoft.windowscommunicationsapps*",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 3,
+    },
+    { // bug 1545139
+      path: "*Fonts/StaticCache.dat",
+      condition: WIN,
+      ignoreIfUnused: true, // Only on Win7
+      read: 1,
+    },
+    { // bug 1541246
+      path: "UAppData:",
+      ignoreIfUnused: true, // sometimes before opening first browser window,
+                            // sometimes before first paint
+      condition: WIN,
+      stat: 1,
+    },
+    { // Not in packaged builds; useful for artifact builds.
+      path: "GreD:ScalarArtifactDefinitions.json",
+      condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
+      stat: 1,
+    },
+    { // Not in packaged builds; useful for artifact builds.
+      path: "GreD:EventArtifactDefinitions.json",
+      condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
+      stat: 1,
+    },
+    { // bug 1546838
+      path: "ProfD:xulstore/data.mdb",
+      condition: MAC,
+      write: 3,
+    },
+    { // bug 1543090
+      path: "GreD:omni.ja",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 2,
+    },
+  ],
+
+  // We are at this phase once we are ready to handle user events.
+  // Any IO at this phase or before gets in the way of the user
+  // interacting with the first browser window.
+  "before handling user events": [
+    {
+      path: "GreD:update.test",
+      ignoreIfUnused: true,
+      condition: LINUX,
+      close: 1,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db",
+      condition: WIN,
+      read: 2,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db",
+      condition: WIN,
+      ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:cert9.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:pkcs11.txt",
+      condition: WIN,
+      read: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db",
+      condition: WIN,
+      read: 2,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db",
+      condition: WIN,
+      ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
+      canonicalize: true,
+      stat: 2,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 5,
+    },
+    { // bug 1370516 - NSS should be initialized off main thread.
+      path: "ProfD:key4.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 5,
+    },
+    {
+      path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
+      condition: !WIN,
+      ignoreIfUnused: true,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1003968
+      path: "XREAppDist:searchplugins",
+      condition: WIN,
+      stat: 1,
+    },
+    {
+      path: "XCurProcD:extensions",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "GreD:omni.ja",
+      condition: WIN,
+      stat: 1,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 2,
+    },
+  ],
+
+  // Things that are expected to be completely out of the startup path
+  // and loaded lazily when used for the first time by the user should
+  // be blacklisted here.
+  "before becoming idle": [
+    {
+      path: "XREAppFeat:screenshots@mozilla.org.xpi",
+      ignoreIfUnused: true,
+      close: 1,
+    },
+    {
+      path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
+      ignoreIfUnused: true,
+      stat: 1,
+      close: 1,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-journal",
+      ignoreIfUnused: true,
+      fsync: 1,
+      stat: 4,
+      write: 2,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-wal",
+      ignoreIfUnused: true,
+      stat: 4,
+      fsync: 3,
+      write: 148,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite-shm",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 1,
+    },
+    { // bug 1391590
+      path: "ProfD:places.sqlite",
+      ignoreIfUnused: true,
+      fsync: 2,
+      read: 1,
+      stat: 3,
+      write: 1310,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-journal",
+      ignoreIfUnused: true,
+      fsync: 2,
+      stat: 7,
+      write: 7,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-wal",
+      ignoreIfUnused: true,
+      fsync: 2,
+      stat: 7,
+      write: 15,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite-shm",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 2,
+    },
+    { // bug 1391590
+      path: "ProfD:favicons.sqlite",
+      ignoreIfUnused: true,
+      fsync: 3,
+      read: 4,
+      stat: 4,
+      write: 1300,
+    },
+    {
+      path: "ProfD:key4.db-journal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    {
+      path: "ProfD:key4.db-wal",
+      condition: WIN,
+      canonicalize: true,
+      stat: 2,
+    },
+    {
+      path: "ProfD:",
+      condition: WIN,
+      ignoreIfUnused: true,
+      stat: 3,
+    },
+    { // bug 1543090
+      path: "XCurProcD:omni.ja",
+      condition: WIN,
+      stat: 7,
+    },
+  ],
+};
+
+for (let name of ["d3d11layers", "d3d9video", "glcontext", "d3d11video", "wmfvpxvideo"]) {
+  startupPhases["before first paint"].push({
+    path: `ProfD:${name}.guard`,
+    ignoreIfUnused: true,
+    stat: 1,
+  });
+}
+
+function expandWhitelistPath(path, canonicalize = false) {
+  if (path.includes(":")) {
+    let [prefix, suffix] = path.split(":");
+    let [key, property] = prefix.split(".");
+    let dir = Services.dirsvc.get(key, Ci.nsIFile);
+    if (property) {
+      dir = dir[property];
+    }
+
+    if (canonicalize) {
+      path = dir.QueryInterface(Ci.nsILocalFileWin).canonicalPath;
+    } else {
+      // Resolve symLinks.
+      let dirPath = dir.path;
+      while (dir && !dir.isSymlink()) {
+        dir = dir.parent;
+      }
+      if (dir) {
+        dirPath = dirPath.replace(dir.path, dir.target);
+      }
+
+      path = dirPath;
+    }
+
+    if (suffix) {
+      path += "/" + suffix;
+    }
+  }
+  if (AppConstants.platform == "win") {
+    path = path.replace(/\//g, "\\");
+  }
+  return path;
+}
+
+function getStackFromProfile(profile, stack) {
+  const stackPrefixCol = profile.stackTable.schema.prefix;
+  const stackFrameCol = profile.stackTable.schema.frame;
+  const frameLocationCol = profile.frameTable.schema.location;
+
+  let result = [];
+  while (stack) {
+    let sp = profile.stackTable.data[stack];
+    let frame = profile.frameTable.data[sp[stackFrameCol]];
+    stack = sp[stackPrefixCol];
+    frame = profile.stringTable[frame[frameLocationCol]];
+    if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
+      result.push(frame);
+    }
+  }
+  return result;
+}
+
+function pathMatches(path, filename) {
+  path = path.toLowerCase();
+  return path == filename || // Full match
+    // Wildcard on both sides of the path
+    (path.startsWith("*") && path.endsWith("*") &&
+     filename.includes(path.slice(1, -1))) ||
+    // Wildcard suffix
+    (path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
+    // Wildcard prefix
+    (path.startsWith("*") && filename.endsWith(path.slice(1)));
+}
+
+add_task(async function() {
+  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
+    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
+       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
+       "non-debug build.");
+    return;
+  }
+
+  {
+    let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+    omniJa.append("omni.ja");
+    if (!omniJa.exists()) {
+      ok(false, "This test requires a packaged build, " +
+                "run 'mach package' and then use --appname=dist");
+      return;
+    }
+  }
+
+  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
+  await startupRecorder.done;
+
+  // Add system add-ons to the whitelist dynamically.
+  // They should go in the omni.ja file (bug 1357205).
+  {
+    let addons = await AddonManager.getAddonsByTypes(["extension"]);
+    for (let addon of addons) {
+      if (addon.isSystem) {
+        startupPhases["before opening first browser window"].push({
+          path: `XREAppFeat:${addon.id}.xpi`,
+          stat: 3,
+          close: 2,
+        });
+        startupPhases["before handling user events"].push({
+          path: `XREAppFeat:${addon.id}.xpi`,
+          condition: WIN,
+          stat: 2,
+        });
+      }
+    }
+  }
+
+  // Check for main thread I/O markers in the startup profile.
+  let profile = startupRecorder.data.profile.threads[0];
+
+  let phases = {};
+  {
+    const nameCol = profile.markers.schema.name;
+    const dataCol = profile.markers.schema.data;
+
+    let markersForCurrentPhase = [];
+
+    for (let m of profile.markers.data) {
+      let markerName = profile.stringTable[m[nameCol]];
+      if (markerName.startsWith("startupRecorder:")) {
+        phases[markerName.split("startupRecorder:")[1]] = markersForCurrentPhase;
+        markersForCurrentPhase = [];
+        continue;
+      }
+
+      if (markerName != "FileIO")
+        continue;
+
+      let markerData = m[dataCol];
+      if (markerData.source == "sqlite-mainthread") {
+        continue;
+      }
+
+      let samples = markerData.stack.samples;
+      let stack = samples.data[0][samples.schema.stack];
+      markersForCurrentPhase.push({operation: markerData.operation,
+                                   filename: markerData.filename,
+                                   source: markerData.source,
+                                   stackId: stack});
+    }
+  }
+
+  for (let phase in startupPhases) {
+    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 shouldPass = true;
+  for (let phase in phases) {
+    let whitelist = startupPhases[phase];
+    info(`whitelisted paths ${phase}:\n` +
+         whitelist.map(e => {
+           let operations = Object.keys(e).filter(k => k != "path")
+                                  .map(k => `${k}: ${e[k]}`);
+           return `  ${e.path} - ${operations.join(", ")}`;
+         }).join("\n"));
+
+    let markers = phases[phase];
+    for (let marker of markers) {
+      if (marker.operation == "create/open") {
+        // TODO: handle these I/O markers once they are supported on
+        // non-Windows platforms.
+        continue;
+      }
+
+      // Convert to lower case before comparing because the OS X test slaves
+      // have the 'Firefox' folder in 'Library/Application Support' created
+      // as 'firefox' for some reason.
+      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;
+        }
+
+        // Ignore I/O due to IPC. This doesn't really touch the disk.
+        if (filename.startsWith(tmpPath + "/org.chromium.")) {
+          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;
+          break;
+        }
+      }
+      if (!expected) {
+        record(false,
+               `unexpected ${marker.operation} on ${marker.filename} ${phase}`,
+               undefined,
+               "  " + getStackFromProfile(profile, marker.stackId).join("\n  "));
+        shouldPass = false;
+      }
+      info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
+      if (kDumpAllStacks) {
+        info(getStackFromProfile(profile, marker.stackId).map(f => "  " + f)
+                                                         .join("\n"));
+      }
+    }
+
+    for (let entry of whitelist) {
+      for (let op in entry) {
+        if (["path", "condition", "canonicalize", "ignoreIfUnused", "_used"].includes(op)) {
+          continue;
+        }
+        let message = `${op} on ${entry.path} `;
+        if (entry[op] == 0) {
+          message += "as many times as expected";
+        } else if (entry[op] > 0) {
+          message += `allowed ${entry[op]} more times`;
+        } else {
+          message += `${entry[op] * -1} more times than expected`;
+        }
+        ok(entry[op] >= 0, `${message} ${phase}`);
+      }
+      if (!("_used" in entry) && !entry.ignoreIfUnused) {
+        ok(false, `unused whitelist entry ${phase}: ${entry.path}`);
+      }
+    }
+  }
+
+  if (shouldPass) {
+    ok(shouldPass, "No unexpected main thread I/O during startup");
+  } else {
+    const filename = "startup-mainthreadio-profile.json";
+    let path = Cc["@mozilla.org/process/environment;1"]
+                 .getService(Ci.nsIEnvironment)
+                 .get("MOZ_UPLOAD_DIR");
+    let encoder = new TextEncoder();
+    let profilePath = OS.Path.join(path, filename);
+    await OS.File.writeAtomic(profilePath,
+                              encoder.encode(JSON.stringify(startupRecorder.data.profile)));
+    ok(false,
+       "Found some unexpected main thread I/O during startup; profile uploaded in " +
+       filename);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/performance/io/browser.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+# Currently disabled on debug due to debug-only failures, see bug 1549723.
+skip-if = debug || (os == "linux" && asan) # bug 1549729
+# to avoid overhead when running the browser normally, startupRecorder.js will
+# do almost nothing unless browser.startup.record is true.
+# gfx.canvas.willReadFrequently.enable is just an optimization, but needs to be
+# set during early startup to have an impact as a canvas will be used by
+# startupRecorder.js
+prefs =
+  # Skip migration work in BG__migrateUI for browser_startup.js since it isn't
+  # representative of common startup, and triggers Places I/O.
+  browser.migration.version=9999999
+  browser.startup.record=true
+  gfx.canvas.willReadFrequently.enable=true
+environment =
+  MOZ_PROFILER_STARTUP=1
+  MOZ_PROFILER_STARTUP_FEATURES=js,mainthreadio
+  MOZ_PROFILER_STARTUP_ENTRIES=10000000
+[../browser_startup_mainthreadio.js]
+[../browser_startup_content_mainthreadio.js]
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -29,16 +29,17 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/historySwipeAnimation/browser.ini',
     'content/test/keyboard/browser.ini',
     'content/test/menubar/browser.ini',
     'content/test/metaTags/browser.ini',
     'content/test/pageActions/browser.ini',
     'content/test/pageinfo/browser.ini',
     'content/test/performance/browser.ini',
     'content/test/performance/hidpi/browser.ini',
+    'content/test/performance/io/browser.ini',
     'content/test/performance/legacyurlbar/browser.ini',
     'content/test/performance/lowdpi/browser.ini',
     'content/test/permissions/browser.ini',
     'content/test/plugins/browser.ini',
     'content/test/popupNotifications/browser.ini',
     'content/test/popups/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/sanitize/browser.ini',
--- a/browser/components/aboutconfig/content/aboutconfig.css
+++ b/browser/components/aboutconfig/content/aboutconfig.css
@@ -19,17 +19,16 @@ body.config-background {
 }
 
 body.config-warning {
   background-image: url("chrome://browser/content/aboutconfig/background.svg#warning");
 }
 
 .title {
   background-image: url("chrome://global/skin/icons/warning.svg");
-  -moz-context-properties: fill;
   fill: #fcd100;
 }
 
 #search-container {
   position: sticky;
   top: 0;
   box-sizing: border-box;
   width: 100%;
@@ -92,16 +91,18 @@ body.config-warning {
 }
 
 #prefs > tr.locked {
   opacity: 0.4;
   background-image: url("chrome://browser/skin/preferences/in-content/privacy-security.svg");
   background-repeat: no-repeat;
   background-position: 9px center;
   background-size: 16px 16px;
+  -moz-context-properties: fill;
+  fill: currentColor;
 }
 
 #prefs > tr.locked:dir(rtl) {
   background-position-x: right 9px;
 }
 
 #prefs > tr > td,
 #prefs > tr > th {
--- a/browser/components/newtab/docs/v2-system-addon/remote_cfr.md
+++ b/browser/components/newtab/docs/v2-system-addon/remote_cfr.md
@@ -32,17 +32,21 @@ curl -X POST ${SERVER}/buckets/main/coll
 Now there should be a message listed: https://kinto.dev.mozaws.net/v1//buckets/main/collections/cfr/records
 
 NOTE: The collection and messages can also be created manually using the [admin interface](https://kinto.dev.mozaws.net/v1/admin/).
 
 **2. Set Remote Settings prefs to use the dev server.**
 
 ```javascript
 Services.prefs.setStringPref("services.settings.server", "https://kinto.dev.mozaws.net/v1");
-Services.prefs.setBoolPref("services.settings.verify_signature", false);
+
+// Disable signature verification
+const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js", {});
+
+RemoteSettings("cfr").verifySignature = false;
 ```
 
 **3. Set ASRouter CFR pref to use Remote Settings provider and enable asrouter devtools.**
 
 ```javascript
 Services.prefs.setStringPref("browser.newtabpage.activity-stream.asrouter.providers.cfr", JSON.stringify({"id":"cfr-remote","enabled":true,"type":"remote-settings","bucket":"cfr","frequency":{"custom":[{"period":"daily","cap":1}]},"categories":["cfrAddons","cfrFeatures"]}));
 Services.prefs.setBoolPref("browser.newtabpage.activity-stream.asrouter.devtoolsEnabled", true);
 ```
deleted file mode 100644
--- a/browser/components/search/extensions/amazon/_locales/br/messages.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "extensionName": {
-    "message": "Amazon.com.br"
-  },
-  "extensionDescription": {
-    "message": "Amazon.com.br Search"
-  },
-  "searchUrl": {
-    "message": "https://www.amazon.com.br/exec/obidos/external-search/"
-  },
-  "searchForm": {
-    "message": "https://www.amazon.com.br/exec/obidos/external-search/?field-keywords={searchTerms}&ie={inputEncoding}&mode=blended"
-  },
-  "searchUrlGetParams": {
-    "message": "field-keywords={searchTerms}&ie={inputEncoding}&mode=blended"
-  }
-}
\ No newline at end of file
rename from browser/components/search/extensions/amazondotcom/_locales/de/messages.json
rename to browser/components/search/extensions/amazon/_locales/de/messages.json
--- a/browser/components/search/extensions/amazon/manifest.json
+++ b/browser/components/search/extensions/amazon/manifest.json
@@ -1,13 +1,13 @@
 {
   "name": "__MSG_extensionName__",
   "description": "__MSG_extensionDescription__",
   "manifest_version": 2,
-  "version": "1.0",
+  "version": "1.1",
   "applications": {
     "gecko": {
       "id": "amazon@search.mozilla.org"
     }
   },
   "hidden": true,
   "default_locale": "au",
   "icons": {
@@ -19,9 +19,9 @@
   "chrome_settings_overrides": {
     "search_provider": {
       "name": "__MSG_extensionName__",
       "search_url": "__MSG_searchUrl__",
       "search_form": "__MSG_searchForm__",
       "search_url_get_params": "__MSG_searchUrlGetParams__"
     }
   }
-}
\ No newline at end of file
+}
--- a/browser/components/search/extensions/amazondotcom/manifest.json
+++ b/browser/components/search/extensions/amazondotcom/manifest.json
@@ -1,13 +1,13 @@
 {
   "name": "__MSG_extensionName__",
   "description": "__MSG_extensionDescription__",
   "manifest_version": 2,
-  "version": "1.0",
+  "version": "1.1",
   "applications": {
     "gecko": {
       "id": "amazondotcom@search.mozilla.org"
     }
   },
   "hidden": true,
   "default_locale": "en",
   "icons": {
@@ -20,9 +20,9 @@
     "search_provider": {
       "name": "__MSG_extensionName__",
       "search_url": "__MSG_searchUrl__",
       "search_form": "__MSG_searchForm__",
       "suggest_url": "__MSG_suggestUrl__",
       "search_url_get_params": "__MSG_searchUrlGetParams__"
     }
   }
-}
\ No newline at end of file
+}
--- a/browser/components/search/extensions/list.json
+++ b/browser/components/search/extensions/list.json
@@ -209,24 +209,24 @@
         "visibleDefaultEngines": [
           "google-b-d", "bing", "amazon-en-GB", "ddg", "wikipedia-da"
         ]
       }
     },
     "de": {
       "default": {
         "visibleDefaultEngines": [
-          "google-b-d", "amazondotcom-de", "bing", "ddg", "ebay-de", "ecosia", "leo_ende_de", "wikipedia-de"
+          "google-b-d", "amazon-de", "bing", "ddg", "ebay-de", "ecosia", "leo_ende_de", "wikipedia-de"
         ]
       }
     },
     "dsb": {
       "default": {
         "visibleDefaultEngines": [
-          "google-b-d", "bing", "amazondotcom-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-dsb"
+          "google-b-d", "bing", "amazon-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-dsb"
         ]
       }
     },
     "el": {
       "default": {
         "visibleDefaultEngines": [
           "google-b-d", "amazon-en-GB", "bing", "ddg", "wikipedia-el"
         ]
@@ -391,17 +391,17 @@
         "visibleDefaultEngines": [
           "google-b-d", "amazon-en-GB", "bing", "ddg", "eudict", "twitter", "wikipedia-hr"
         ]
       }
     },
     "hsb": {
       "default": {
         "visibleDefaultEngines": [
-          "google-b-d", "bing", "amazondotcom-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-hsb"
+          "google-b-d", "bing", "amazon-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-hsb"
         ]
       }
     },
     "hu": {
       "default": {
         "visibleDefaultEngines": [
           "google-b-d", "ddg", "vatera", "wikipedia-hu"
         ]
--- a/browser/components/shell/content/setDesktopBackground.js
+++ b/browser/components/shell/content/setDesktopBackground.js
@@ -21,28 +21,31 @@ var gSetBackground = {
   load() {
     this._canvas = document.getElementById("screen");
     this._screenWidth = screen.width;
     this._screenHeight = screen.height;
     // Cap ratio to 4 so the dialog width doesn't get ridiculous. Highest
     // regular screens seem to be 32:9 (3.56) according to Wikipedia.
     let screenRatio = Math.min(this._screenWidth / this._screenHeight, 4);
     this._canvas.width = this._canvas.height * screenRatio;
+    document.getElementById("preview-unavailable").style.width =
+      this._canvas.width + "px";
 
     if (AppConstants.platform == "macosx") {
       document.documentElement.getButton("accept").hidden = true;
     } else {
       let multiMonitors = false;
-      try {
+      if (AppConstants.platform == "linux") {
+        // getMonitors only ever returns the primary monitor on Linux, so just
+        // always show the option
+        multiMonitors = true;
+      } else {
         const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
         const monitors = gfxInfo.getMonitors();
         multiMonitors = monitors.length > 1;
-      } catch (e) {
-        // getMonitors() isn't implemented on Linux
-        multiMonitors = true;
       }
 
       if (!multiMonitors || AppConstants.isPlatformAndVersionAtMost("win", 6.1)) {
         // Hide span option if < Win8 since that's when it was introduced.
         document.getElementById("spanPosition").hidden = true;
       }
     }
 
@@ -96,16 +99,17 @@ var gSetBackground = {
       this._image,
       Ci.nsIShellService["BACKGROUND_" + this._position],
       this._imageName);
   },
 
   updatePosition() {
     var ctx = this._canvas.getContext("2d");
     ctx.clearRect(0, 0, this._screenWidth, this._screenHeight);
+    document.getElementById("preview-unavailable").hidden = true;
 
     if (AppConstants.platform != "macosx") {
       this._position = document.getElementById("menuPosition").value;
     }
 
     switch (this._position) {
       case "TILE":
         ctx.save();
@@ -152,28 +156,19 @@ var gSetBackground = {
           height = this._image.naturalHeight * heightRatio;
           x = (this._screenWidth - width) / 2;
           y = 0;
         }
         ctx.drawImage(this._image, x, y, width, height);
         break;
       }
       case "SPAN": {
-        ctx.fillStyle = "black";
+        document.getElementById("preview-unavailable").hidden = false;
+        ctx.fillStyle = "#222";
         ctx.fillRect(0, 0, this._screenWidth, this._screenHeight);
-        let x = this._screenWidth / 2;
-        let y = this._screenHeight / 2;
-        let radius = this._screenHeight * .4;
-        let delta = Math.sin(.25 * Math.PI) * radius; // opp = sin * hyp
-        ctx.lineWidth = radius / 4.5;
-        ctx.strokeStyle = "#9B2423";
-        ctx.arc(x, y, radius, -.75 * Math.PI, 1.25 * Math.PI);
-        ctx.stroke();
-        ctx.lineWidth *= .8;
-        ctx.lineTo(x + delta, y + delta);
         ctx.stroke();
       }
     }
   },
 };
 
 if (AppConstants.platform != "macosx") {
   gSetBackground._initColor = function() {
--- a/browser/components/shell/content/setDesktopBackground.xul
+++ b/browser/components/shell/content/setDesktopBackground.xul
@@ -59,17 +59,22 @@
       <html:input id="desktopColor"
                   type="color"
                   onchange="gSetBackground.updateColor(this.value);"/> 
     </hbox>
 #endif
 
     <vbox align="center">
       <!-- default to 16:9, will be adjusted to match user's actual screen -->
-      <html:canvas id="screen" width="202" height="114" role="presentation"/>
+      <stack>
+        <html:canvas id="screen" width="202" height="114" role="presentation"/>
+        <vbox pack="center">
+          <html:p id="preview-unavailable" hidden="">&previewUnavailable;</html:p>
+        </vbox>
+      </stack>
       <image id="monitor-base"/>
     </vbox>
 
 #ifdef XP_MACOSX
     <separator/>
 
     <hbox align="right">
       <button id="setDesktopBackground"
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -58,16 +58,17 @@ startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
 
   record(name) {
     if (!Services.prefs.getBoolPref("browser.startup.record", false))
       return;
 
+    Services.profiler.AddMarker("startupRecorder:" + name);
     this.data.code[name] = {
       components: Cu.loadedComponents,
       modules: Cu.loadedModules,
       services: Object.keys(Cc).filter(c => {
         try {
           return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
@@ -158,18 +159,34 @@ startupRecorder.prototype = {
         win.removeEventListener("MozAfterPaint", afterPaintListener);
         win = null;
         this.data.frames = paints;
         this.data.prefStats = {};
         if (AppConstants.DEBUG) {
           Services.prefs.readStats((key, value) => this.data.prefStats[key] = value);
         }
         paints = null;
-        this._resolve();
-        this._resolve = null;
+
+        let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+        if (!env.exists("MOZ_PROFILER_STARTUP")) {
+          this._resolve();
+          this._resolve = null;
+          return;
+        }
+
+        Services.profiler.getProfileDataAsync().then(profileData => {
+          this.data.profile = profileData;
+          // There's no equivalent StartProfiler call in this file because the
+          // profiler is started using the MOZ_PROFILER_STARTUP environment
+          // variable in browser/base/content/test/performance/browser.ini
+          Services.profiler.StopProfiler();
+
+          this._resolve();
+          this._resolve = null;
+        });
       });
     } else {
       const topicsToNames = {
         "profile-do-change": "before profile selection",
         "toplevel-window-ready": "before opening first browser window",
       };
       topicsToNames[firstPaintNotification] = "before first paint";
       this.record(topicsToNames[topic]);
--- a/browser/locales/en-US/chrome/browser/setDesktopBackground.dtd
+++ b/browser/locales/en-US/chrome/browser/setDesktopBackground.dtd
@@ -13,8 +13,9 @@
                        some of the entire image will be on each monitor.
                        This should ideally match the wording in Windows' own
                        Desktop Background settings page. -->
 <!ENTITY span.label                 "Span">
 <!ENTITY color.label                "Color:">
 <!ENTITY setDesktopBackground.title "Set Desktop Background">
 <!ENTITY openDesktopPrefs.label     "Open Desktop Preferences">
 <!ENTITY closeWindow.key            "w">
+<!ENTITY previewUnavailable         "Preview unavailable">
--- a/browser/themes/shared/blockedSite.css
+++ b/browser/themes/shared/blockedSite.css
@@ -9,18 +9,16 @@ html {
 }
 
 body {
   color: white;
 }
 
 .title {
   background-image: url("chrome://global/skin/icons/blocked.svg");
-  fill: currentColor;
-  -moz-context-properties: fill;
 }
 
 .title-text {
   color: white;
 }
 
 .button-container button {
   background-color: transparent;
--- a/browser/themes/shared/setDesktopBackground.css
+++ b/browser/themes/shared/setDesktopBackground.css
@@ -9,8 +9,15 @@ html|canvas#screen {
   border-style: solid;
   border-width: 12px 11px;
   border-image: url("chrome://browser/skin/monitor-border.png") 12 11 stretch;
 }
 
 #monitor-base {
   list-style-image: url("chrome://browser/skin/monitor-base.png");
 }
+
+html|p#preview-unavailable {
+  margin: 12px 11px;
+  text-align: center;
+  color: #9B2423;
+  font-weight: bold;
+}
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -6,28 +6,31 @@
 
 const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
 const { remoteClientManager } =
   require("devtools/client/shared/remote-debugging/remote-client-manager");
 const Services = require("Services");
 
 const { l10n } = require("../modules/l10n");
 
+const { isSupportedDebugTargetPane } = require("../modules/debug-target-support");
+
 const {
   openTemporaryExtension,
   uninstallAddon,
 } = require("../modules/extensions-helper");
 
 const {
   getCurrentClient,
   getCurrentRuntime,
 } = require("../modules/runtimes-state-helper");
 
 const {
   DEBUG_TARGETS,
+  DEBUG_TARGET_PANE,
   REQUEST_EXTENSIONS_FAILURE,
   REQUEST_EXTENSIONS_START,
   REQUEST_EXTENSIONS_SUCCESS,
   REQUEST_PROCESSES_FAILURE,
   REQUEST_PROCESSES_START,
   REQUEST_PROCESSES_SUCCESS,
   REQUEST_TABS_FAILURE,
   REQUEST_TABS_START,
@@ -157,20 +160,23 @@ function removeTemporaryExtension(id) {
     }
   };
 }
 
 function requestTabs() {
   return async (dispatch, getState) => {
     dispatch({ type: REQUEST_TABS_START });
 
+    const runtime = getCurrentRuntime(getState().runtimes);
     const clientWrapper = getCurrentClient(getState().runtimes);
 
     try {
-      const tabs = await clientWrapper.listTabs({ favicons: true });
+      const isSupported = isSupportedDebugTargetPane(runtime.runtimeDetails.info.type,
+                                                     DEBUG_TARGET_PANE.TAB);
+      const tabs = isSupported ? (await clientWrapper.listTabs({ favicons: true })) : [];
 
       dispatch({ type: REQUEST_TABS_SUCCESS, tabs });
     } catch (e) {
       dispatch({ type: REQUEST_TABS_FAILURE, error: e });
     }
   };
 }
 
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox.js
@@ -37,16 +37,34 @@ add_task(async function testThisFirefoxW
   // Expect all target panes but tabs to be displayed.
   const expectedTargetPanesWithoutTabs = EXPECTED_TARGET_PANES.filter(p => p !== "Tabs");
   await checkThisFirefoxTargetPanes(document, expectedTargetPanesWithoutTabs);
 
   await removeTab(tab);
 });
 
 /**
+ * Check that the tab which is discarded keeps the state after open the aboutdebugging.
+ */
+add_task(async function testThisFirefoxKeepDiscardedTab() {
+  const targetTab = await addTab("https://example.com/");
+  const blankTab = await addTab("about:blank");
+  targetTab.ownerGlobal.gBrowser.discardBrowser(targetTab);
+
+  const { document, tab, window } = await openAboutDebugging({ enableLocalTabs: false });
+  await selectThisFirefoxPage(document, window.AboutDebugging.store);
+
+  ok(!targetTab.linkedPanel, "The target tab is still discarded");
+
+  await removeTab(blankTab);
+  await removeTab(targetTab);
+  await removeTab(tab);
+});
+
+/**
  * Check that the Temporary Extensions is hidden if "xpinstall.enabled" is set to false.
  */
 add_task(async function testThisFirefoxWithXpinstallDisabled() {
   await pushPref("xpinstall.enabled", false);
 
   const { document, tab, window } = await openAboutDebugging();
   await selectThisFirefoxPage(document, window.AboutDebugging.store);
 
@@ -76,9 +94,8 @@ async function checkThisFirefoxTargetPan
 
   for (let i = 0; i < expectedTargetPanes.length; i++) {
     const expectedPaneTitle = expectedTargetPanes[i];
     const actualPaneTitle = paneTitles[i];
     ok(actualPaneTitle.startsWith(expectedPaneTitle),
        `Expected debug target category found: ${ expectedPaneTitle }`);
   }
 }
-
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -262,38 +262,39 @@ void ShadowRoot::RemoveSlot(HTMLSlotElem
       aSlot->EnqueueSlotChangeEvent();
     }
 
     return;
   }
 
   const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
   currentSlots.RemoveElement(*aSlot);
-
-  // Move assigned nodes from removed slot to the next slot in
-  // tree order with the same name.
   if (!wasFirstSlot) {
     return;
   }
 
+  // Move assigned nodes from removed slot to the next slot in
+  // tree order with the same name.
   InvalidateStyleAndLayoutOnSubtree(aSlot);
   HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
   const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
-  bool slottedNodesChanged = !assignedNodes.IsEmpty();
+  if (assignedNodes.IsEmpty()) {
+    return;
+  }
+
+  InvalidateStyleAndLayoutOnSubtree(replacementSlot);
   while (!assignedNodes.IsEmpty()) {
     nsINode* assignedNode = assignedNodes[0];
 
     aSlot->RemoveAssignedNode(assignedNode);
     replacementSlot->AppendAssignedNode(assignedNode);
   }
 
-  if (slottedNodesChanged) {
-    aSlot->EnqueueSlotChangeEvent();
-    replacementSlot->EnqueueSlotChangeEvent();
-  }
+  aSlot->EnqueueSlotChangeEvent();
+  replacementSlot->EnqueueSlotChangeEvent();
 }
 
 // FIXME(emilio): There's a bit of code duplication between this and the
 // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
 void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
   if (!aSheet.IsApplicable()) {
     return;
   }
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -57,23 +57,23 @@ class nsDocumentEncoder : public nsIDocu
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder)
   NS_DECL_NSIDOCUMENTENCODER
 
  protected:
   virtual ~nsDocumentEncoder();
 
   void Initialize(bool aClearCachedSerializer = true);
-  nsresult SerializeNodeStart(nsINode* aNode, int32_t aStartOffset,
+  nsresult SerializeNodeStart(nsINode& aOriginalNode, int32_t aStartOffset,
                               int32_t aEndOffset, nsAString& aStr,
-                              nsINode* aOriginalNode = nullptr);
+                              nsINode* aFixupNode = nullptr);
   nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr,
                                       bool aDontSerializeRoot,
                                       uint32_t aMaxLength = 0);
-  nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr);
+  nsresult SerializeNodeEnd(nsINode& aNode, nsAString& aStr);
   // This serializes the content of aNode.
   nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr);
   nsresult SerializeRangeToString(nsRange* aRange, nsAString& aOutputString);
   nsresult SerializeRangeNodes(nsRange* aRange, nsINode* aNode,
                                nsAString& aString, int32_t aDepth);
   nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
                                       nsAString& aString);
   nsresult SerializeRangeContextEnd(nsAString& aString);
@@ -291,52 +291,77 @@ nsDocumentEncoder::SetCharset(const nsAC
 NS_IMETHODIMP
 nsDocumentEncoder::GetMimeType(nsAString& aMimeType) {
   aMimeType = mMimeType;
   return NS_OK;
 }
 
 bool nsDocumentEncoder::IncludeInContext(nsINode* aNode) { return false; }
 
-nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
+class FixupNodeDeterminer {
+ public:
+  FixupNodeDeterminer(nsIDocumentEncoderNodeFixup* aNodeFixup,
+                      nsINode* aFixupNode, nsINode& aOriginalNode)
+      : mIsSerializationOfFixupChildrenNeeded{false},
+        mNodeFixup(aNodeFixup),
+        mOriginalNode(aOriginalNode) {
+    if (mNodeFixup) {
+      if (aFixupNode) {
+        mFixupNode = aFixupNode;
+      } else {
+        mNodeFixup->FixupNode(&mOriginalNode,
+                              &mIsSerializationOfFixupChildrenNeeded,
+                              getter_AddRefs(mFixupNode));
+      }
+    }
+  }
+
+  bool IsSerializationOfFixupChildrenNeeded() const {
+    return mIsSerializationOfFixupChildrenNeeded;
+  }
+
+  /**
+   * @return The fixup node, if available, otherwise the original node. The
+   * former is kept alive by this object.
+   */
+  nsINode& GetFixupNodeFallBackToOriginalNode() const {
+    return mFixupNode ? *mFixupNode : mOriginalNode;
+  }
+
+ private:
+  bool mIsSerializationOfFixupChildrenNeeded;
+  nsIDocumentEncoderNodeFixup* mNodeFixup;
+  nsCOMPtr<nsINode> mFixupNode;
+  nsINode& mOriginalNode;
+};
+
+nsresult nsDocumentEncoder::SerializeNodeStart(nsINode& aOriginalNode,
                                                int32_t aStartOffset,
                                                int32_t aEndOffset,
                                                nsAString& aStr,
-                                               nsINode* aOriginalNode) {
-  if (mNeedsPreformatScanning && aNode->IsElement()) {
-    mSerializer->ScanElementForPreformat(aNode->AsElement());
+                                               nsINode* aFixupNode) {
+  if (mNeedsPreformatScanning && aOriginalNode.IsElement()) {
+    mSerializer->ScanElementForPreformat(aOriginalNode.AsElement());
   }
 
-  if (!IsVisibleNode(aNode)) return NS_OK;
-
-  nsINode* node = nullptr;
-  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
-
-  // Caller didn't do fixup, so we'll do it ourselves
-  if (!aOriginalNode) {
-    aOriginalNode = aNode;
-    if (mNodeFixup) {
-      bool dummy;
-      mNodeFixup->FixupNode(aNode, &dummy,
-                            getter_AddRefs(fixedNodeKungfuDeathGrip));
-      node = fixedNodeKungfuDeathGrip;
-    }
+  if (!IsVisibleNode(&aOriginalNode)) {
+    return NS_OK;
   }
 
-  // Either there was no fixed-up node,
-  // or the caller did fixup themselves and aNode is already fixed
-  if (!node) node = aNode;
+  FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode,
+                                          aOriginalNode};
+  nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
 
   if (node->IsElement()) {
     if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
                    nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
         nsLayoutUtils::IsInvisibleBreak(node)) {
       return NS_OK;
     }
-    Element* originalElement = Element::FromNodeOrNull(aOriginalNode);
+    Element* originalElement = aOriginalNode.AsElement();
     mSerializer->AppendElementStart(node->AsElement(), originalElement, aStr);
     return NS_OK;
   }
 
   switch (node->NodeType()) {
     case nsINode::TEXT_NODE: {
       mSerializer->AppendText(static_cast<nsIContent*>(node), aStartOffset,
                               aEndOffset, aStr);
@@ -362,52 +387,47 @@ nsresult nsDocumentEncoder::SerializeNod
       mSerializer->AppendDoctype(static_cast<DocumentType*>(node), aStr);
       break;
     }
   }
 
   return NS_OK;
 }
 
-nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, nsAString& aStr) {
-  if (mNeedsPreformatScanning && aNode->IsElement()) {
-    mSerializer->ForgetElementForPreformat(aNode->AsElement());
+nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
+  if (mNeedsPreformatScanning && aNode.IsElement()) {
+    mSerializer->ForgetElementForPreformat(aNode.AsElement());
   }
 
-  if (!IsVisibleNode(aNode)) return NS_OK;
+  if (!IsVisibleNode(&aNode)) {
+    return NS_OK;
+  }
 
-  if (aNode->IsElement()) {
-    mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
+  if (aNode.IsElement()) {
+    mSerializer->AppendElementEnd(aNode.AsElement(), aStr);
   }
   return NS_OK;
 }
 
 nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
                                                        nsAString& aStr,
                                                        bool aDontSerializeRoot,
                                                        uint32_t aMaxLength) {
   if (aMaxLength > 0 && aStr.Length() >= aMaxLength) {
     return NS_OK;
   }
 
   if (!IsVisibleNode(aNode)) return NS_OK;
 
   nsresult rv = NS_OK;
-  bool serializeClonedChildren = false;
-  nsINode* maybeFixedNode = nullptr;
 
-  // Keep the node from FixupNode alive.
-  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
-  if (mNodeFixup) {
-    mNodeFixup->FixupNode(aNode, &serializeClonedChildren,
-                          getter_AddRefs(fixedNodeKungfuDeathGrip));
-    maybeFixedNode = fixedNodeKungfuDeathGrip;
-  }
-
-  if (!maybeFixedNode) maybeFixedNode = aNode;
+  MOZ_ASSERT(aNode, "aNode shouldn't be nullptr.");
+  FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode};
+  nsINode* maybeFixedNode =
+      &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
 
   if ((mFlags & SkipInvisibleContent) &&
       !(mFlags & OutputNonTextContentAsPlaceholder)) {
     if (aNode->IsContent()) {
       if (nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame()) {
         if (!frame->IsSelectable(nullptr)) {
           aDontSerializeRoot = true;
         }
@@ -416,48 +436,50 @@ nsresult nsDocumentEncoder::SerializeToS
   }
 
   if (!aDontSerializeRoot) {
     int32_t endOffset = -1;
     if (aMaxLength > 0) {
       MOZ_ASSERT(aMaxLength >= aStr.Length());
       endOffset = aMaxLength - aStr.Length();
     }
-    rv = SerializeNodeStart(maybeFixedNode, 0, endOffset, aStr, aNode);
+    rv = SerializeNodeStart(*aNode, 0, endOffset, aStr, maybeFixedNode);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode;
+  nsINode* node = fixupNodeDeterminer.IsSerializationOfFixupChildrenNeeded()
+                      ? maybeFixedNode
+                      : aNode;
 
   for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child;
        child = child->GetNextSibling()) {
     rv = SerializeToStringRecursive(child, aStr, false, aMaxLength);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!aDontSerializeRoot) {
-    rv = SerializeNodeEnd(maybeFixedNode, aStr);
+    rv = SerializeNodeEnd(*maybeFixedNode, aStr);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return FlushText(aStr, false);
 }
 
 nsresult nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
                                                        nsAString& aStr) {
   nsresult rv;
 
   nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode);
   while (node) {
     nsINode* current = node;
-    rv = SerializeNodeStart(current, 0, -1, aStr, current);
+    rv = SerializeNodeStart(*current, 0, -1, aStr, current);
     NS_ENSURE_SUCCESS(rv, rv);
     node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current);
     while (!node && current && current != aNode) {
-      rv = SerializeNodeEnd(current, aStr);
+      rv = SerializeNodeEnd(*current, aStr);
       NS_ENSURE_SUCCESS(rv, rv);
       // Check if we have siblings.
       node = current->GetNextSibling();
       if (!node) {
         // Perhaps parent node has siblings.
         current = current->GetParentNode();
 
         // Handle template element. If the parent is a template's content,
@@ -572,37 +594,37 @@ nsresult nsDocumentEncoder::SerializeRan
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     // due to implementation it is impossible for text node to be both start and
     // end of range.  We would have handled that case without getting here.
     // XXXsmaug What does this all mean?
     if (IsTextNode(aNode)) {
       if (startNode == content) {
         int32_t startOffset = aRange->StartOffset();
-        rv = SerializeNodeStart(aNode, startOffset, -1, aString);
+        rv = SerializeNodeStart(*aNode, startOffset, -1, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         int32_t endOffset = aRange->EndOffset();
-        rv = SerializeNodeStart(aNode, 0, endOffset, aString);
+        rv = SerializeNodeStart(*aNode, 0, endOffset, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      rv = SerializeNodeEnd(aNode, aString);
+      rv = SerializeNodeEnd(*aNode, aString);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       if (aNode != mCommonParent) {
         if (IncludeInContext(aNode)) {
           // halt the incrementing of mStartDepth/mEndDepth.  This is
           // so paste client will include this node in paste.
           mHaltRangeHint = true;
         }
         if ((startNode == content) && !mHaltRangeHint) mStartDepth++;
         if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
 
         // serialize the start of this node
-        rv = SerializeNodeStart(aNode, 0, -1, aString);
+        rv = SerializeNodeStart(*aNode, 0, -1, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // do some calculations that will tell us which children of this
       // node are in the range.
       int32_t startOffset = 0, endOffset = -1;
       if (startNode == content && mStartRootIndex >= aDepth)
         startOffset = mStartOffsets[mStartRootIndex - aDepth];
@@ -648,17 +670,17 @@ nsresult nsDocumentEncoder::SerializeRan
           NS_ENSURE_SUCCESS(rv, rv);
 
           childAsNode = childAsNode->GetNextSibling();
         }
       }
 
       // serialize the end of this node
       if (aNode != mCommonParent) {
-        rv = SerializeNodeEnd(aNode, aString);
+        rv = SerializeNodeEnd(*aNode, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   return NS_OK;
 }
 
 nsresult nsDocumentEncoder::SerializeRangeContextStart(
@@ -677,17 +699,17 @@ nsresult nsDocumentEncoder::SerializeRan
 
   while (i > 0) {
     nsINode* node = aAncestorArray.ElementAt(--i);
 
     if (!node) break;
 
     // Either a general inclusion or as immediate context
     if (IncludeInContext(node) || i < j) {
-      rv = SerializeNodeStart(node, 0, -1, aString);
+      rv = SerializeNodeStart(*node, 0, -1, aString);
       serializedContext->AppendElement(node);
       if (NS_FAILED(rv)) break;
     }
   }
 
   return rv;
 }
 
@@ -697,17 +719,17 @@ nsresult nsDocumentEncoder::SerializeRan
   }
 
   MOZ_RELEASE_ASSERT(!mRangeContexts.IsEmpty(),
                      "Tried to end context without starting one.");
   AutoTArray<nsINode*, 8>& serializedContext = mRangeContexts.LastElement();
 
   nsresult rv = NS_OK;
   for (nsINode* node : Reversed(serializedContext)) {
-    rv = SerializeNodeEnd(node, aString);
+    rv = SerializeNodeEnd(*node, aString);
 
     if (NS_FAILED(rv)) break;
   }
 
   mRangeContexts.RemoveLastElement();
   return rv;
 }
 
@@ -754,20 +776,20 @@ nsresult nsDocumentEncoder::SerializeRan
       // Check that the parent is visible if we don't a frame.
       // IsVisibleNode() will do it when there's a frame.
       nsCOMPtr<nsIContent> content = do_QueryInterface(startContainer);
       if (content && !content->GetPrimaryFrame()) {
         nsIContent* parent = content->GetParent();
         if (!parent || !IsVisibleNode(parent)) return NS_OK;
       }
     }
-    rv = SerializeNodeStart(startContainer, startOffset, endOffset,
+    rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
                             aOutputString);
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = SerializeNodeEnd(startContainer, aOutputString);
+    rv = SerializeNodeEnd(*startContainer, aOutputString);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   rv = SerializeRangeContextEnd(aOutputString);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -856,34 +878,34 @@ nsDocumentEncoder::EncodeToStringWithMax
       // needed Bug 137450: Problem copying/pasting a table from a web page to
       // Excel. Each separate block of <tr></tr> produced above will be wrapped
       // by the immediate context. This assumes that you can't select cells that
       // are multiple selections from two tables simultaneously.
       node = range->GetStartContainer();
       NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
       if (node != prevNode) {
         if (prevNode) {
-          rv = SerializeNodeEnd(prevNode, output);
+          rv = SerializeNodeEnd(*prevNode, output);
           NS_ENSURE_SUCCESS(rv, rv);
         }
         nsCOMPtr<nsIContent> content = do_QueryInterface(node);
         if (content && content->IsHTMLElement(nsGkAtoms::tr) &&
             !ParentIsTR(content)) {
           if (!prevNode) {
             // Went from a non-<tr> to a <tr>
             mCommonAncestors.Clear();
             nsContentUtils::GetAncestors(node->GetParentNode(),
                                          mCommonAncestors);
             rv = SerializeRangeContextStart(mCommonAncestors, output);
             NS_ENSURE_SUCCESS(rv, rv);
             // Don't let SerializeRangeToString serialize the context again
             mDisableContextSerialize = true;
           }
 
-          rv = SerializeNodeStart(node, 0, -1, output);
+          rv = SerializeNodeStart(*node, 0, -1, output);
           NS_ENSURE_SUCCESS(rv, rv);
           prevNode = node;
         } else if (prevNode) {
           // Went from a <tr> to a non-<tr>
           mDisableContextSerialize = false;
           rv = SerializeRangeContextEnd(output);
           NS_ENSURE_SUCCESS(rv, rv);
           prevNode = nullptr;
@@ -894,17 +916,17 @@ nsDocumentEncoder::EncodeToStringWithMax
       NS_ENSURE_SUCCESS(rv, rv);
       if (i == 0) {
         firstRangeStartDepth = mStartDepth;
       }
     }
     mStartDepth = firstRangeStartDepth;
 
     if (prevNode) {
-      rv = SerializeNodeEnd(prevNode, output);
+      rv = SerializeNodeEnd(*prevNode, output);
       NS_ENSURE_SUCCESS(rv, rv);
       mDisableContextSerialize = false;
       rv = SerializeRangeContextEnd(output);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Just to be safe
     mDisableContextSerialize = false;
@@ -1261,22 +1283,22 @@ nsHTMLCopyEncoder::EncodeToStringWithCon
     if (mEndDepth) mEndDepth--;
     // and the count
     count--;
   }
 
   i = count;
   while (i > 0) {
     node = mCommonAncestors.ElementAt(--i);
-    SerializeNodeStart(node, 0, -1, aContextString);
+    SerializeNodeStart(*node, 0, -1, aContextString);
   }
   // i = 0; guaranteed by above
   while (i < count) {
     node = mCommonAncestors.ElementAt(i++);
-    SerializeNodeEnd(node, aContextString);
+    SerializeNodeEnd(*node, aContextString);
   }
 
   // encode range info : the start and end depth of the selection, where the
   // depth is distance down in the parent hierarchy.  Later we will need to add
   // leading/trailing whitespace info to this.
   nsAutoString infoString;
   infoString.AppendInt(mStartDepth);
   infoString.Append(char16_t(','));
--- a/dom/media/test/test_can_play_type_mpeg.html
+++ b/dom/media/test/test_can_play_type_mpeg.html
@@ -91,22 +91,17 @@ function check_mp4(v, enabled) {
   [ "video/mp4; codecs=vp9",
     "video/mp4; codecs=\"vp9\"",
     "video/mp4; codecs=\"vp9.0\""
   ].forEach((codec) => {
     // canPlayType should support VP9 in MP4...
     check(codec, "probably");
     ok(MediaSource.isTypeSupported(codec), "VP9 in MP4 should be supported in MSE");
   });
-
-  if (IsAndroid()) {
-    check("video/mp4; codecs=\"av1\"", "");
-  } else {
-    check("video/mp4; codecs=\"av1\"", "probably");
-  }
+  check("video/mp4; codecs=\"av1\"", "probably");
 }
 
 function check_mp3(v, enabled) {
   function check(type, expected) {
     var ex = enabled ? expected : "";
     is(v.canPlayType(type), ex, type + "='" + ex + "'");
   }
 
@@ -138,20 +133,16 @@ function IsLinux() {
 function getPref(name) {
   var pref = false;
   try {
     pref = SpecialPowers.getBoolPref(name);
   } catch(ex) { }
   return pref;
 }
 
-function IsAndroid() {
-  return getAndroidVersion() >= 0;
-}
-
 function IsSupportedAndroid() {
   return getAndroidVersion() >= 14;
 }
 
 function IsJellyBeanOrLater() {
   return getAndroidVersion() >= 16;
 }
 
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -118,40 +118,16 @@ namespace mozilla {
 
 using namespace dom;
 using namespace widget;
 
 /*****************************************************************************
  * mozilla::EditorBase
  *****************************************************************************/
 
-template already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
-    nsAtom& aTag, const EditorDOMPoint& aPointToInsert);
-template already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
-    nsAtom& aTag, const EditorRawDOMPoint& aPointToInsert);
-template nsresult EditorBase::InsertNodeWithTransaction(
-    nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
-template nsresult EditorBase::InsertNodeWithTransaction(
-    nsIContent& aContentToInsert, const EditorRawDOMPoint& aPointToInsert);
-template already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
-    const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError);
-template already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
-    const EditorRawDOMPoint& aStartOfRightNode, ErrorResult& aError);
-template SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
-    nsIContent& aMostAncestorToSplit,
-    const EditorDOMPoint& aStartOfDeepestRightNode, SplitAtEdges aSplitAtEdges);
-template SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
-    nsIContent& aMostAncestorToSplit,
-    const EditorRawDOMPoint& aStartOfDeepestRightNode,
-    SplitAtEdges aSplitAtEdges);
-template nsresult EditorBase::MoveNodeWithTransaction(
-    nsIContent& aContent, const EditorDOMPoint& aPointToInsert);
-template nsresult EditorBase::MoveNodeWithTransaction(
-    nsIContent& aContent, const EditorRawDOMPoint& aPointToInsert);
-
 EditorBase::EditorBase()
     : mEditActionData(nullptr),
       mPlaceholderName(nullptr),
       mModCount(0),
       mFlags(0),
       mUpdateCount(0),
       mPlaceholderBatch(0),
       mDocDirtyState(-1),
@@ -1337,19 +1313,18 @@ void EditorBase::SyncRealTimeSpell() {
 NS_IMETHODIMP
 EditorBase::SetSpellcheckUserOverride(bool enable) {
   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
 
   SyncRealTimeSpell();
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
-    nsAtom& aTagName, const EditorDOMPointBase<PT, CT>& aPointToInsert) {
+    nsAtom& aTagName, const EditorDOMPoint& aPointToInsert) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   // XXX We need offset at new node for RangeUpdaterRef().  Therefore, we need
   //     to compute the offset now but this is expensive.  So, if it's possible,
   //     we need to redesign RangeUpdaterRef() as avoiding using indices.
   Unused << aPointToInsert.Offset();
@@ -1409,28 +1384,26 @@ EditorBase::InsertNode(nsINode* aNodeToI
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   int32_t offset =
       aOffset < 0
           ? static_cast<int32_t>(aContainer->Length())
           : std::min(aOffset, static_cast<int32_t>(aContainer->Length()));
-  nsresult rv = InsertNodeWithTransaction(
-      *contentToInsert, EditorRawDOMPoint(aContainer, offset));
+  nsresult rv = InsertNodeWithTransaction(*contentToInsert,
+                                          EditorDOMPoint(aContainer, offset));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 nsresult EditorBase::InsertNodeWithTransaction(
-    nsIContent& aContentToInsert,
-    const EditorDOMPointBase<PT, CT>& aPointToInsert) {
+    nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
@@ -1467,27 +1440,26 @@ EditorBase::SplitNode(nsINode* aNode, in
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   int32_t offset =
       std::min(std::max(aOffset, 0), static_cast<int32_t>(aNode->Length()));
   ErrorResult error;
   nsCOMPtr<nsIContent> newNode =
-      SplitNodeWithTransaction(EditorRawDOMPoint(aNode, offset), error);
+      SplitNodeWithTransaction(EditorDOMPoint(aNode, offset), error);
   newNode.forget(aNewLeftNode);
   if (NS_WARN_IF(error.Failed())) {
     return EditorBase::ToGenericNSResult(error.StealNSResult());
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
-    const EditorDOMPointBase<PT, CT>& aStartOfRightNode, ErrorResult& aError) {
+    const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
       NS_WARN_IF(!aStartOfRightNode.GetContainerAsContent())) {
     aError.Throw(NS_ERROR_INVALID_ARG);
     return nullptr;
   }
   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
@@ -1707,17 +1679,17 @@ already_AddRefed<Element> EditorBase::Re
         return nullptr;
       }
       nsresult rv = DeleteNodeWithTransaction(*child);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return nullptr;
       }
 
       rv = InsertNodeWithTransaction(
-          *child, EditorRawDOMPoint(newContainer, newContainer->Length()));
+          *child, EditorDOMPoint(newContainer, newContainer->Length()));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return nullptr;
       }
     }
   }
 
   // Insert new container into tree.
   NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
@@ -1759,18 +1731,18 @@ nsresult EditorBase::RemoveContainerWith
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Insert the last child before the previous last child.  So, we need to
     // use offset here because previous child might have been moved to
     // container.
     rv = InsertNodeWithTransaction(
-        *child, EditorRawDOMPoint(pointToInsertChildren.GetContainer(),
-                                  pointToInsertChildren.Offset()));
+        *child, EditorDOMPoint(pointToInsertChildren.GetContainer(),
+                               pointToInsertChildren.Offset()));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   nsresult rv = DeleteNodeWithTransaction(aElement);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1815,59 +1787,56 @@ already_AddRefed<Element> EditorBase::In
   // Put aNode in the new container, first.
   nsresult rv = DeleteNodeWithTransaction(aContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   {
     AutoTransactionsConserveSelection conserveSelection(*this);
-    rv =
-        InsertNodeWithTransaction(aContent, EditorRawDOMPoint(newContainer, 0));
+    rv = InsertNodeWithTransaction(aContent, EditorDOMPoint(newContainer, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
   }
 
   // Put the new container where aNode was.
   rv = InsertNodeWithTransaction(*newContainer, pointToInsertNewContainer);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return newContainer.forget();
 }
 
-template <typename PT, typename CT>
 nsresult EditorBase::MoveNodeWithTransaction(
-    nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert) {
+    nsIContent& aContent, const EditorDOMPoint& aPointToInsert) {
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   EditorDOMPoint oldPoint(&aContent);
   if (NS_WARN_IF(!oldPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't do anything if it's already in right place.
   if (aPointToInsert == oldPoint) {
     return NS_OK;
   }
 
   // Notify our internal selection state listener
-  EditorDOMPoint newPoint(aPointToInsert);
-  AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, newPoint);
+  AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, aPointToInsert);
 
   // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
   nsresult rv = DeleteNodeWithTransaction(aContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Mutation event listener could break insertion point. Let's check it.
-  EditorRawDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
+  EditorDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
   if (NS_WARN_IF(!pointToInsert.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   // If some children have removed from the container, let's append to the
   // container.
   // XXX Perhaps, if mutation event listener inserts or removes some children
   //     but the child node referring with aPointToInsert is still available,
   //     we should insert aContent before it.  However, we should keep
@@ -3723,20 +3692,19 @@ bool EditorBase::IsPreformatted(nsINode*
     return false;
   }
 
   const nsStyleText* styleText = elementStyle->StyleText();
 
   return styleText->WhiteSpaceIsSignificant();
 }
 
-template <typename PT, typename CT>
 SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
     nsIContent& aMostAncestorToSplit,
-    const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode,
+    const EditorDOMPoint& aStartOfDeepestRightNode,
     SplitAtEdges aSplitAtEdges) {
   MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
   MOZ_ASSERT(
       aStartOfDeepestRightNode.GetContainer() == &aMostAncestorToSplit ||
       EditorUtils::IsDescendantOf(*aStartOfDeepestRightNode.GetContainer(),
                                   aMostAncestorToSplit));
 
   if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -931,20 +931,18 @@ class EditorBase : public nsIEditor,
    *
    * @param aContentToInsert    The node to be inserted.
    * @param aPointToInsert      The insertion point of aContentToInsert.
    *                            If this refers end of the container, the
    *                            transaction will append the node to the
    *                            container.  Otherwise, will insert the node
    *                            before child node referred by this.
    */
-  template <typename PT, typename CT>
-  MOZ_CAN_RUN_SCRIPT nsresult
-  InsertNodeWithTransaction(nsIContent& aContentToInsert,
-                            const EditorDOMPointBase<PT, CT>& aPointToInsert);
+  MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
+      nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
 
   /**
    * ReplaceContainerWithTransaction() creates new element whose name is
    * aTagName, moves all children in aOldContainer to the new element, then,
    * removes aOldContainer from the DOM tree.
    *
    * @param aOldContainer       The element node which should be replaced
    *                            with new element.
@@ -1066,20 +1064,18 @@ class EditorBase : public nsIEditor,
    *
    * @param aStartOfRightNode   The point to split.  Its container will be
    *                            the right node, i.e., become the new node's
    *                            next sibling.  And the point will be start
    *                            of the right node.
    * @param aError              If succeed, returns no error.  Otherwise, an
    *                            error.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT already_AddRefed<nsIContent> SplitNodeWithTransaction(
-      const EditorDOMPointBase<PT, CT>& aStartOfRightNode,
-      ErrorResult& aResult);
+      const EditorDOMPoint& aStartOfRightNode, ErrorResult& aResult);
 
   /**
    * JoinNodesWithTransaction() joins aLeftNode and aRightNode.  Content of
    * aLeftNode will be merged into aRightNode.  Actual implemenation of this
    * method is JoinNodesImpl().  So, see its explanation for the detail.
    *
    * @param aLeftNode   Will be removed from the DOM tree.
    * @param aRightNode  The node which will be new container of the content of
@@ -1088,31 +1084,30 @@ class EditorBase : public nsIEditor,
   MOZ_CAN_RUN_SCRIPT
   nsresult JoinNodesWithTransaction(nsINode& aLeftNode, nsINode& aRightNode);
 
   /**
    * MoveNodeWithTransaction() moves aContent to aPointToInsert.
    *
    * @param aContent        The node to be moved.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT nsresult MoveNodeWithTransaction(
-      nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert);
+      nsIContent& aContent, const EditorDOMPoint& aPointToInsert);
 
   /**
    * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer.
    *
    * @param aContent        The node to be moved.
    * @param aNewContainer   The new container which will contain aContent as
    *                        its last child.
    */
   MOZ_CAN_RUN_SCRIPT
   nsresult MoveNodeToEndWithTransaction(nsIContent& aContent,
                                         nsINode& aNewContainer) {
-    EditorRawDOMPoint pointToInsert;
+    EditorDOMPoint pointToInsert;
     pointToInsert.SetToEndOf(&aNewContainer);
     return MoveNodeWithTransaction(aContent, pointToInsert);
   }
 
   /**
    * MoveAllChildren() moves all children of aContainer to before
    * aPointToInsert.GetChild().
    * See explanation of MoveChildren() for the detail of the behavior.
@@ -1238,19 +1233,18 @@ class EditorBase : public nsIEditor,
    * @param aTag            The element name to create.
    * @param aPointToInsert  The insertion point of new element.  If this refers
    *                        end of the container or after, the transaction
    *                        will append the element to the container.
    *                        Otherwise, will insert the element before the
    *                        child node referred by this.
    * @return                The created new element node.
    */
-  template <typename PT, typename CT>
-  already_AddRefed<Element> CreateNodeWithTransaction(
-      nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aPointToInsert);
+  MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateNodeWithTransaction(
+      nsAtom& aTag, const EditorDOMPoint& aPointToInsert);
 
   /**
    * Create an aggregate transaction for delete selection.  The result may
    * include DeleteNodeTransactions and/or DeleteTextTransactions as its
    * children.
    *
    * @param aAction             The action caused removing the selection.
    * @param aRemovingNode       The node to be removed.
@@ -1282,16 +1276,17 @@ class EditorBase : public nsIEditor,
 
   /**
    * DeleteTextWithTransaction() removes text in the range from aCharData.
    *
    * @param aCharData           The data node which should be modified.
    * @param aOffset             Start offset of removing text in aCharData.
    * @param aLength             Length of removing text.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData,
                                      uint32_t aOffset, uint32_t aLength);
 
   /**
    * ReplaceContainerWithTransactionInternal() is implementation of
    * ReplaceContainerWithTransaction() and
    * ReplaceContainerAndCloneAttributesWithTransaction().
    *
@@ -1384,21 +1379,20 @@ class EditorBase : public nsIEditor,
    *                                    create empty container element when
    *                                    split point is start or end of an
    *                                    element.
    * @return                            SplitPoint() returns split point in
    *                                    aMostAncestorToSplit.  The point must
    *                                    be good to insert something if the
    *                                    caller want to do it.
    */
-  template <typename PT, typename CT>
-  MOZ_CAN_RUN_SCRIPT SplitNodeResult SplitNodeDeepWithTransaction(
-      nsIContent& aMostAncestorToSplit,
-      const EditorDOMPointBase<PT, CT>& aDeepestStartOfRightNode,
-      SplitAtEdges aSplitAtEdges);
+  MOZ_CAN_RUN_SCRIPT SplitNodeResult
+  SplitNodeDeepWithTransaction(nsIContent& aMostAncestorToSplit,
+                               const EditorDOMPoint& aDeepestStartOfRightNode,
+                               SplitAtEdges aSplitAtEdges);
 
   /**
    * JoinNodesDeepWithTransaction() joins aLeftNode and aRightNode "deeply".
    * First, they are joined simply, then, new right node is assumed as the
    * child at length of the left node before joined and new left node is
    * assumed as its previous sibling.  Then, they will be joined again.
    * And then, these steps are repeated.
    *
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -235,27 +235,27 @@ class MOZ_STACK_CLASS SplitNodeResult fi
    * SplitPoint() returns the split point in the container.
    * This is useful when callers insert an element at split point with
    * EditorBase::CreateNodeWithTransaction() or something similar methods.
    *
    * Note that the result is EditorRawDOMPoint but the nodes are grabbed
    * by this instance.  Therefore, the life time of both container node
    * and child node are guaranteed while using the result temporarily.
    */
-  EditorRawDOMPoint SplitPoint() const {
+  EditorDOMPoint SplitPoint() const {
     if (Failed()) {
-      return EditorRawDOMPoint();
+      return EditorDOMPoint();
     }
     if (mGivenSplitPoint.IsSet()) {
-      return EditorRawDOMPoint(mGivenSplitPoint);
+      return EditorDOMPoint(mGivenSplitPoint);
     }
     if (!mPreviousNode) {
-      return EditorRawDOMPoint(mNextNode);
+      return EditorDOMPoint(mNextNode);
     }
-    EditorRawDOMPoint point(mPreviousNode);
+    EditorDOMPoint point(mPreviousNode);
     DebugOnly<bool> advanced = point.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
                          "Failed to advance offset to after previous node");
     return point;
   }
 
   /**
    * This constructor shouldn't be used by anybody except methods which
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -514,17 +514,17 @@ nsresult HTMLEditor::SetPositionToAbsolu
   SnapToGrid(x, y);
   SetTopAndLeft(aElement, x, y);
 
   // we may need to create a br if the positioned element is alone in its
   // container
   nsINode* parentNode = aElement.GetParentNode();
   if (parentNode->GetChildCount() == 1) {
     RefPtr<Element> newBrElement =
-        InsertBrElementWithTransaction(EditorRawDOMPoint(parentNode, 0));
+        InsertBrElementWithTransaction(EditorDOMPoint(parentNode, 0));
     if (NS_WARN_IF(!newBrElement)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1828,17 +1828,17 @@ EditActionResult HTMLEditRules::WillInse
   }
 
   // If block is empty, populate with br.  (For example, imagine a div that
   // contains the word "text".  The user selects "text" and types return.
   // "Text" is deleted leaving an empty block.  We want to put in one br to
   // make block have a line.  Then code further below will put in a second br.)
   if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
     AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
-    EditorRawDOMPoint endOfBlockParent;
+    EditorDOMPoint endOfBlockParent;
     endOfBlockParent.SetToEndOf(blockParent);
     RefPtr<Element> brElement =
         MOZ_KnownLive(HTMLEditorRef())
             .InsertBrElementWithTransaction(endOfBlockParent);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(!brElement)) {
@@ -2055,17 +2055,17 @@ nsresult HTMLEditRules::InsertBRElement(
     return error.StealNSResult();
   }
   return NS_OK;
 }
 
 EditActionResult HTMLEditRules::SplitMailCites() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  EditorRawDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
+  EditorDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!pointToSplit.IsSet())) {
     return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   RefPtr<Element> citeNode =
       GetTopEnclosingMailCite(*pointToSplit.GetContainer());
   if (!citeNode) {
     return EditActionIgnored();
@@ -2123,33 +2123,33 @@ EditActionResult HTMLEditRules::SplitMai
   nsIContent* previousNodeOfSplitPoint = splitCiteNodeResult.GetPreviousNode();
   if (previousNodeOfSplitPoint &&
       previousNodeOfSplitPoint->IsHTMLElement(nsGkAtoms::span) &&
       previousNodeOfSplitPoint->GetPrimaryFrame() &&
       previousNodeOfSplitPoint->GetPrimaryFrame()->IsBlockFrameOrSubclass()) {
     nsCOMPtr<nsINode> lastChild = previousNodeOfSplitPoint->GetLastChild();
     if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
       // We ignore the result here.
-      EditorRawDOMPoint endOfPreviousNodeOfSplitPoint;
+      EditorDOMPoint endOfPreviousNodeOfSplitPoint;
       endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
       RefPtr<Element> invisibleBrElement =
           MOZ_KnownLive(HTMLEditorRef())
               .InsertBrElementWithTransaction(endOfPreviousNodeOfSplitPoint);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       NS_WARNING_ASSERTION(invisibleBrElement,
                            "Failed to create an invisible <br> element");
     }
   }
 
   // In most cases, <br> should be inserted after current cite.  However, if
   // left cite hasn't been created because the split point was start of the
   // cite node, <br> should be inserted before the current cite.
-  EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
+  EditorDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
   RefPtr<Element> brElement =
       MOZ_KnownLive(HTMLEditorRef())
           .InsertBrElementWithTransaction(pointToInsertBrNode);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(!brElement)) {
     return EditActionIgnored(NS_ERROR_FAILURE);
@@ -2174,18 +2174,18 @@ EditActionResult HTMLEditRules::SplitMai
   }
 
   // if citeNode wasn't a block, we might also want another break before it.
   // We need to examine the content both before the br we just added and also
   // just after it.  If we don't have another br or block boundary adjacent,
   // then we will need a 2nd br added to achieve blank line that user expects.
   if (IsInlineNode(*citeNode)) {
     // Use DOM point which we tried to collapse to.
-    EditorRawDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
-                                             atBrNode.Offset());
+    EditorDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
+                                          atBrNode.Offset());
 
     WSRunObject wsObj(&HTMLEditorRef(), pointToCreateNewBrNode);
     WSType wsType;
     wsObj.PriorVisibleNode(pointToCreateNewBrNode, nullptr, nullptr, &wsType);
     if (wsType == WSType::normalWS || wsType == WSType::text ||
         wsType == WSType::special) {
       EditorRawDOMPoint pointAfterNewBrNode(pointToCreateNewBrNode);
       DebugOnly<bool> advanced = pointAfterNewBrNode.AdvanceOffset();
@@ -2452,18 +2452,19 @@ nsresult HTMLEditRules::WillDeleteSelect
                                              address_of(visNode), &eo);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       *aHandled = true;
-      rv = HTMLEditorRef().DeleteTextWithTransaction(
-          nodeAsText, std::min(so, eo), DeprecatedAbs(eo - so));
+      rv = MOZ_KnownLive(HTMLEditorRef())
+               .DeleteTextWithTransaction(nodeAsText, std::min(so, eo),
+                                          DeprecatedAbs(eo - so));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // XXX When Backspace key is pressed, Chromium removes following empty
@@ -3048,31 +3049,33 @@ nsresult HTMLEditRules::WillDeleteSelect
         // text node is found, we can delete to end or to begining as
         // appropriate, since the case where both sel endpoints in same text
         // node was already handled (we wouldn't be here)
         if (startNode->GetAsText() &&
             startNode->Length() > static_cast<uint32_t>(startOffset)) {
           // Delete to last character
           OwningNonNull<CharacterData> dataNode =
               *static_cast<CharacterData*>(startNode.get());
-          rv = HTMLEditorRef().DeleteTextWithTransaction(
-              dataNode, startOffset, startNode->Length() - startOffset);
+          rv =
+              MOZ_KnownLive(HTMLEditorRef())
+                  .DeleteTextWithTransaction(dataNode, startOffset,
+                                             startNode->Length() - startOffset);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
         if (endNode->GetAsText() && endOffset) {
           // Delete to first character
           OwningNonNull<CharacterData> dataNode =
               *static_cast<CharacterData*>(endNode.get());
-          rv =
-              HTMLEditorRef().DeleteTextWithTransaction(dataNode, 0, endOffset);
+          rv = MOZ_KnownLive(HTMLEditorRef())
+                   .DeleteTextWithTransaction(dataNode, 0, endOffset);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
         }
 
@@ -3160,17 +3163,17 @@ nsresult HTMLEditRules::DeleteNodeIfColl
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::InsertBRIfNeeded() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  EditorRawDOMPoint atStartOfSelection(
+  EditorDOMPoint atStartOfSelection(
       EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // inline elements don't need any br
   if (!IsBlockNode(*atStartOfSelection.GetContainer())) {
     return NS_OK;
@@ -3679,17 +3682,17 @@ EditActionResult HTMLEditRules::MoveNode
                         .MoveNodeToEndWithTransaction(aNode, aDestElement);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
     } else {
-      EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
+      EditorDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
       nsresult rv = MOZ_KnownLive(HTMLEditorRef())
                         .MoveNodeWithTransaction(aNode, pointToInsert);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
@@ -3953,28 +3956,30 @@ nsresult HTMLEditRules::MakeList(nsAtom&
     }
 
     SplitNodeResult splitAtSelectionStartResult =
         MaybeSplitAncestorsForInsertWithTransaction(aListType,
                                                     atStartOfSelection);
     if (NS_WARN_IF(splitAtSelectionStartResult.Failed())) {
       return splitAtSelectionStartResult.Rv();
     }
-    RefPtr<Element> theList = HTMLEditorRef().CreateNodeWithTransaction(
-        aListType, splitAtSelectionStartResult.SplitPoint());
+    RefPtr<Element> theList =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(
+                aListType, splitAtSelectionStartResult.SplitPoint());
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!theList)) {
       return NS_ERROR_FAILURE;
     }
 
-    EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0);
-    RefPtr<Element> theListItem = HTMLEditorRef().CreateNodeWithTransaction(
-        aItemType, atFirstListItemToInsertBefore);
+    RefPtr<Element> theListItem =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(aItemType, EditorDOMPoint(theList, 0));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!theListItem)) {
       return NS_ERROR_FAILURE;
     }
 
     // remember our new block for postprocessing
@@ -4074,17 +4079,17 @@ nsresult HTMLEditRules::MakeList(nsAtom&
           return convertListTypeResult.Rv();
         }
         curList = convertListTypeResult.forget();
       }
       prevListItem = nullptr;
       continue;
     }
 
-    EditorRawDOMPoint atCurNode(curNode);
+    EditorDOMPoint atCurNode(curNode);
     if (NS_WARN_IF(!atCurNode.IsSet())) {
       return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(atCurNode.IsSetAndValid());
     if (HTMLEditUtils::IsListItem(curNode)) {
       if (!atCurNode.IsContainerHTMLElement(&aListType)) {
         // list item is in wrong type of list. if we don't have a curList,
         // split the old list and make a new list of correct type.
@@ -4099,19 +4104,20 @@ nsresult HTMLEditRules::MakeList(nsAtom&
           if (NS_WARN_IF(!CanHandleEditAction())) {
             error.SuppressException();
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(error.Failed())) {
             return error.StealNSResult();
           }
           newBlock = newLeftNode ? newLeftNode->AsElement() : nullptr;
-          EditorRawDOMPoint atParentOfCurNode(atCurNode.GetContainer());
-          curList = HTMLEditorRef().CreateNodeWithTransaction(
-              aListType, atParentOfCurNode);
+          curList =
+              MOZ_KnownLive(HTMLEditorRef())
+                  .CreateNodeWithTransaction(
+                      aListType, EditorDOMPoint(atCurNode.GetContainer()));
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!curList)) {
             return NS_ERROR_FAILURE;
           }
         }
         // move list item to new list
@@ -4211,18 +4217,19 @@ nsresult HTMLEditRules::MakeList(nsAtom&
 
     // need to make a list to put things in if we haven't already,
     if (!curList) {
       SplitNodeResult splitCurNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(aListType, atCurNode);
       if (NS_WARN_IF(splitCurNodeResult.Failed())) {
         return splitCurNodeResult.Rv();
       }
-      curList = HTMLEditorRef().CreateNodeWithTransaction(
-          aListType, splitCurNodeResult.SplitPoint());
+      curList = MOZ_KnownLive(HTMLEditorRef())
+                    .CreateNodeWithTransaction(aListType,
+                                               splitCurNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curList)) {
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curList;
@@ -4462,17 +4469,17 @@ nsresult HTMLEditRules::MakeBasicBlock(n
                   *curBlock, pointToInsertBlock,
                   SplitAtEdges::eDoNotCreateEmptyContainer);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
+      EditorDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
       // Put a <br> element at the split point
       brContent = MOZ_KnownLive(HTMLEditorRef())
                       .InsertBrElementWithTransaction(pointToInsertBrNode);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brContent)) {
         return NS_ERROR_FAILURE;
@@ -4510,18 +4517,19 @@ nsresult HTMLEditRules::MakeBasicBlock(n
     }
     // Make sure we can put a block here.
     SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(blockType,
                                                     pointToInsertBlock);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
-    RefPtr<Element> block = HTMLEditorRef().CreateNodeWithTransaction(
-        blockType, splitNodeResult.SplitPoint());
+    RefPtr<Element> block =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(blockType, splitNodeResult.SplitPoint());
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!block)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = block;
@@ -4701,18 +4709,20 @@ nsresult HTMLEditRules::IndentAroundSele
 
     // make sure we can put a block here
     SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                     atStartOfSelection);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
-    RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
-        *nsGkAtoms::div, splitNodeResult.SplitPoint());
+    RefPtr<Element> theBlock =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(*nsGkAtoms::div,
+                                       splitNodeResult.SplitPoint());
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!theBlock)) {
       return NS_ERROR_FAILURE;
     }
     // remember our new block for postprocessing
     mNewBlock = theBlock;
@@ -4774,17 +4784,17 @@ nsresult HTMLEditRules::IndentAroundSele
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
         nsresult rv =
             MOZ_KnownLive(HTMLEditorRef())
                 .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
-                                         EditorRawDOMPoint(sibling, 0));
+                                         EditorDOMPoint(sibling, 0));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -4822,18 +4832,19 @@ nsresult HTMLEditRules::IndentAroundSele
             atCurNode.GetContainer()->NodeInfo()->NameAtom();
         // Create a new nested list of correct type.
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(
                 MOZ_KnownLive(*containerName), atCurNode);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
-        curList = HTMLEditorRef().CreateNodeWithTransaction(
-            *containerName, splitNodeResult.SplitPoint());
+        curList = MOZ_KnownLive(HTMLEditorRef())
+                      .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                                 splitNodeResult.SplitPoint());
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
@@ -4873,18 +4884,19 @@ nsresult HTMLEditRules::IndentAroundSele
       }
 
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                       atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curQuote = HTMLEditorRef().CreateNodeWithTransaction(
-          *nsGkAtoms::div, splitNodeResult.SplitPoint());
+      curQuote = MOZ_KnownLive(HTMLEditorRef())
+                     .CreateNodeWithTransaction(*nsGkAtoms::div,
+                                                splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curQuote)) {
         return NS_ERROR_FAILURE;
       }
       nsresult rv = IncreaseMarginToIndent(*curQuote);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
@@ -4980,18 +4992,20 @@ nsresult HTMLEditRules::IndentAroundSele
 
     // Make sure we can put a block here.
     SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
                                                     atStartOfSelection);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
-    RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
-        *nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
+    RefPtr<Element> theBlock =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(*nsGkAtoms::blockquote,
+                                       splitNodeResult.SplitPoint());
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!theBlock)) {
       return NS_ERROR_FAILURE;
     }
     // remember our new block for postprocessing
     mNewBlock = theBlock;
@@ -5046,17 +5060,17 @@ nsresult HTMLEditRules::IndentAroundSele
       sibling = HTMLEditorRef().GetNextHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
               sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
               sibling->NodeInfo()->NamespaceID()) {
         rv = MOZ_KnownLive(HTMLEditorRef())
                  .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
-                                          EditorRawDOMPoint(sibling, 0));
+                                          EditorDOMPoint(sibling, 0));
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
         continue;
       }
@@ -5094,18 +5108,19 @@ nsresult HTMLEditRules::IndentAroundSele
             atCurNode.GetContainer()->NodeInfo()->NameAtom();
         // Create a new nested list of correct type.
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(
                 MOZ_KnownLive(*containerName), atCurNode);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
-        curList = HTMLEditorRef().CreateNodeWithTransaction(
-            *containerName, splitNodeResult.SplitPoint());
+        curList = MOZ_KnownLive(HTMLEditorRef())
+                      .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                                 splitNodeResult.SplitPoint());
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
@@ -5156,18 +5171,19 @@ nsresult HTMLEditRules::IndentAroundSele
             atListItem.GetContainer()->NodeInfo()->NameAtom();
         // Create a new nested list of correct type.
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(
                 MOZ_KnownLive(*containerName), atListItem);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
-        curList = HTMLEditorRef().CreateNodeWithTransaction(
-            *containerName, splitNodeResult.SplitPoint());
+        curList = MOZ_KnownLive(HTMLEditorRef())
+                      .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                                 splitNodeResult.SplitPoint());
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
 
@@ -5202,18 +5218,19 @@ nsresult HTMLEditRules::IndentAroundSele
       }
 
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
                                                       atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curQuote = HTMLEditorRef().CreateNodeWithTransaction(
-          *nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
+      curQuote = MOZ_KnownLive(HTMLEditorRef())
+                     .CreateNodeWithTransaction(*nsGkAtoms::blockquote,
+                                                splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curQuote)) {
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curQuote;
@@ -5542,17 +5559,17 @@ SplitRangeOffFromNodeResult HTMLEditRule
           rv = PopListItem(*child);
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return SplitRangeOffFromNodeResult(rv);
           }
         } else if (HTMLEditUtils::IsList(child)) {
           // We have an embedded list, so move it out from under the parent
           // list. Be sure to put it after the parent list because this
           // loop iterates backwards through the parent's list of children.
-          EditorRawDOMPoint afterCurrentList(curParent, offset + 1);
+          EditorDOMPoint afterCurrentList(curParent, offset + 1);
           rv = MOZ_KnownLive(HTMLEditorRef())
                    .MoveNodeWithTransaction(*child, afterCurrentList);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
           }
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return SplitRangeOffFromNodeResult(rv);
           }
@@ -5651,26 +5668,26 @@ SplitRangeOffFromNodeResult HTMLEditRule
   // descendants of aBlockElement.
   MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartOfMiddleElement, aBlockElement));
   MOZ_ASSERT(EditorUtils::IsDescendantOf(aEndOfMiddleElement, aBlockElement));
 
   // Split at the start.
   SplitNodeResult splitAtStartResult =
       MOZ_KnownLive(HTMLEditorRef())
           .SplitNodeDeepWithTransaction(
-              aBlockElement, EditorRawDOMPoint(&aStartOfMiddleElement),
+              aBlockElement, EditorDOMPoint(&aStartOfMiddleElement),
               SplitAtEdges::eDoNotCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
   }
   NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
                        "Failed to split aBlockElement at start");
 
   // Split at after the end
-  EditorRawDOMPoint atAfterEnd(&aEndOfMiddleElement);
+  EditorDOMPoint atAfterEnd(&aEndOfMiddleElement);
   DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
   NS_WARNING_ASSERTION(advanced, "Failed to advance offset after the end node");
   SplitNodeResult splitAtEndResult =
       MOZ_KnownLive(HTMLEditorRef())
           .SplitNodeDeepWithTransaction(
               aBlockElement, atAfterEnd,
               SplitAtEdges::eDoNotCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -5821,36 +5838,36 @@ nsresult HTMLEditRules::CreateStyleForIn
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
     if (RefPtr<Text> text = node->GetAsText()) {
       // if we are in a text node, split it
       SplitNodeResult splitTextNodeResult =
           MOZ_KnownLive(HTMLEditorRef())
               .SplitNodeDeepWithTransaction(
-                  *text, EditorRawDOMPoint(text, offset),
+                  *text, EditorDOMPoint(text, offset),
                   SplitAtEdges::eAllowToCreateEmptyContainer);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(splitTextNodeResult.Failed())) {
         return splitTextNodeResult.Rv();
       }
       EditorRawDOMPoint splitPoint(splitTextNodeResult.SplitPoint());
       node = splitPoint.GetContainer();
       offset = splitPoint.Offset();
     }
     if (!HTMLEditorRef().IsContainer(node)) {
       return NS_OK;
     }
     OwningNonNull<Text> newNode =
         EditorBase::CreateTextNode(aDocument, EmptyString());
-    nsresult rv = MOZ_KnownLive(HTMLEditorRef())
-                      .InsertNodeWithTransaction(
-                          *newNode, EditorRawDOMPoint(node, offset));
+    nsresult rv =
+        MOZ_KnownLive(HTMLEditorRef())
+            .InsertNodeWithTransaction(*newNode, EditorDOMPoint(node, offset));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     node = newNode;
     offset = 0;
@@ -6049,34 +6066,34 @@ nsresult HTMLEditRules::AlignContentsAtS
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
-    RefPtr<Element> div = HTMLEditorRef().CreateNodeWithTransaction(
-        *nsGkAtoms::div, pointToInsertDiv);
+    RefPtr<Element> div =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!div)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = div;
     // Set up the alignment on the div, using HTML or CSS
     rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // Put in a moz-br so that it won't get deleted
-    CreateElementResult createMozBrResult =
-        CreateMozBR(EditorRawDOMPoint(div, 0));
+    CreateElementResult createMozBrResult = CreateMozBR(EditorDOMPoint(div, 0));
     if (NS_WARN_IF(createMozBrResult.Failed())) {
       return createMozBrResult.Rv();
     }
     EditorRawDOMPoint atStartOfDiv(div, 0);
     // Don't restore the selection
     restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRefPtr()->Collapse(atStartOfDiv, error);
@@ -6188,18 +6205,19 @@ nsresult HTMLEditRules::AlignContentsAtS
       }
 
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                       atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curDiv = HTMLEditorRef().CreateNodeWithTransaction(
-          *nsGkAtoms::div, splitNodeResult.SplitPoint());
+      curDiv = MOZ_KnownLive(HTMLEditorRef())
+                   .CreateNodeWithTransaction(*nsGkAtoms::div,
+                                              splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curDiv)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curDiv;
@@ -6272,19 +6290,19 @@ nsresult HTMLEditRules::AlignBlockConten
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // else we need to put in a div, set the alignment, and toss in all the
   // children
-  EditorRawDOMPoint atStartOfNode(&aNode, 0);
-  RefPtr<Element> divElem =
-      HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div, atStartOfNode);
+  RefPtr<Element> divElem = MOZ_KnownLive(HTMLEditorRef())
+                                .CreateNodeWithTransaction(
+                                    *nsGkAtoms::div, EditorDOMPoint(&aNode, 0));
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(!divElem)) {
     return NS_ERROR_FAILURE;
   }
   // set up the alignment on the div
   nsresult rv = MOZ_KnownLive(HTMLEditorRef())
@@ -6295,17 +6313,17 @@ nsresult HTMLEditRules::AlignBlockConten
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   // tuck the children into the end of the active div
   while (lastChild && (lastChild != divElem)) {
     nsresult rv =
         MOZ_KnownLive(HTMLEditorRef())
-            .MoveNodeWithTransaction(*lastChild, EditorRawDOMPoint(divElem, 0));
+            .MoveNodeWithTransaction(*lastChild, EditorDOMPoint(divElem, 0));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     lastChild = HTMLEditorRef().GetLastEditableChild(aNode);
   }
@@ -7466,18 +7484,17 @@ nsresult HTMLEditRules::BustUpInlinesAtR
 
   // XXX Oh, then, if the range is collapsed, we don't need to call
   //     GetHighestInlineParent(), isn't it?
   if (endInline && !isCollapsed) {
     SplitNodeResult splitEndInlineResult =
         MOZ_KnownLive(HTMLEditorRef())
             .SplitNodeDeepWithTransaction(
                 *endInline,
-                EditorRawDOMPoint(aRangeItem.mEndContainer,
-                                  aRangeItem.mEndOffset),
+                EditorDOMPoint(aRangeItem.mEndContainer, aRangeItem.mEndOffset),
                 SplitAtEdges::eDoNotCreateEmptyContainer);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(splitEndInlineResult.Failed())) {
       return splitEndInlineResult.Rv();
     }
     EditorRawDOMPoint splitPointAtEnd(splitEndInlineResult.SplitPoint());
@@ -7491,18 +7508,18 @@ nsresult HTMLEditRules::BustUpInlinesAtR
   nsCOMPtr<nsIContent> startInline =
       GetHighestInlineParent(*aRangeItem.mStartContainer);
 
   if (startInline) {
     SplitNodeResult splitStartInlineResult =
         MOZ_KnownLive(HTMLEditorRef())
             .SplitNodeDeepWithTransaction(
                 *startInline,
-                EditorRawDOMPoint(aRangeItem.mStartContainer,
-                                  aRangeItem.mStartOffset),
+                EditorDOMPoint(aRangeItem.mStartContainer,
+                               aRangeItem.mStartOffset),
                 SplitAtEdges::eDoNotCreateEmptyContainer);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(splitStartInlineResult.Failed())) {
       return splitStartInlineResult.Rv();
     }
     // XXX If we split only here because of collapsed range, we're modifying
@@ -7533,17 +7550,17 @@ nsresult HTMLEditRules::BustUpInlinesAtB
   if (arrayOfBreaks.IsEmpty()) {
     aOutArrayOfNodes.AppendElement(aNode);
     return NS_OK;
   }
 
   // Else we need to bust up aNode along all the breaks
   nsCOMPtr<nsIContent> nextNode = &aNode;
   for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
-    EditorRawDOMPoint atBrNode(brNode);
+    EditorDOMPoint atBrNode(brNode);
     if (NS_WARN_IF(!atBrNode.IsSet())) {
       return NS_ERROR_FAILURE;
     }
     SplitNodeResult splitNodeResult =
         MOZ_KnownLive(HTMLEditorRef())
             .SplitNodeDeepWithTransaction(
                 *nextNode, atBrNode,
                 SplitAtEdges::eAllowToCreateEmptyContainer);
@@ -7558,17 +7575,17 @@ nsresult HTMLEditRules::BustUpInlinesAtB
     if (splitNodeResult.GetPreviousNode()) {
       // Might not be a left node.  A break might have been at the very
       // beginning of inline container, in which case
       // SplitNodeDeepWithTransaction() would not actually split anything.
       aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
     }
 
     // Move break outside of container and also put in node list
-    EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
+    EditorDOMPoint atNextNode(splitNodeResult.GetNextNode());
     nsresult rv = MOZ_KnownLive(HTMLEditorRef())
                       .MoveNodeWithTransaction(
                           MOZ_KnownLive(*brNode->AsContent()), atNextNode);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -7736,17 +7753,17 @@ nsresult HTMLEditRules::ReturnInHeader(E
   if (NS_WARN_IF(!node->IsContent())) {
     return NS_ERROR_FAILURE;
   }
 
   // Split the header
   SplitNodeResult splitHeaderResult =
       MOZ_KnownLive(HTMLEditorRef())
           .SplitNodeDeepWithTransaction(
-              aHeader, EditorRawDOMPoint(node, aOffset),
+              aHeader, EditorDOMPoint(node, aOffset),
               SplitAtEdges::eAllowToCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
                        "Failed to split aHeader");
 
   // If the previous heading of split point is empty, put a mozbr into it.
@@ -7755,17 +7772,17 @@ nsresult HTMLEditRules::ReturnInHeader(E
     MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsHeader(*prevItem));
     bool isEmptyNode;
     rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (isEmptyNode) {
       CreateElementResult createMozBrResult =
-          CreateMozBR(EditorRawDOMPoint(prevItem, 0));
+          CreateMozBR(EditorDOMPoint(prevItem, 0));
       if (NS_WARN_IF(createMozBrResult.Failed())) {
         return createMozBrResult.Rv();
       }
     }
   }
 
   // If the new (righthand) header node is empty, delete it
   if (IsEmptyBlockElement(aHeader, IgnoreSingleBR::eYes)) {
@@ -7784,30 +7801,34 @@ nsresult HTMLEditRules::ReturnInHeader(E
     }
     if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
       ClearCachedStyles();
       HTMLEditorRef().mTypeInState->ClearAllProps();
 
       // Create a paragraph
       nsAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper element even if we separate with <br>
-      EditorRawDOMPoint nextToHeader(headerParent, offset + 1);
-      RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction(
-          &paraAtom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom, nextToHeader);
+      EditorDOMPoint nextToHeader(headerParent, offset + 1);
+      RefPtr<Element> pNode =
+          MOZ_KnownLive(HTMLEditorRef())
+              .CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br
+                                             ? *nsGkAtoms::p
+                                             : MOZ_KnownLive(paraAtom),
+                                         nextToHeader);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!pNode)) {
         return NS_ERROR_FAILURE;
       }
 
       // Append a <br> to it
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0));
+              .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
 
       // Set selection to before the break
@@ -7933,17 +7954,17 @@ EditActionResult HTMLEditRules::ReturnIn
   bool doesCRCreateNewP =
       HTMLEditorRef().GetReturnInParagraphCreatesNewParagraph();
 
   bool splitAfterNewBR = false;
   nsCOMPtr<nsIContent> brContent;
 
   EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection);
 
-  EditorRawDOMPoint pointToInsertBR;
+  EditorDOMPoint pointToInsertBR;
   if (doesCRCreateNewP && atStartOfSelection.GetContainer() == &aParentDivOrP) {
     // We are at the edges of the block, so, we don't need to create new <br>.
     brContent = nullptr;
   } else if (atStartOfSelection.IsInTextNode()) {
     // at beginning of text node?
     if (atStartOfSelection.IsStartOfContainer()) {
       // is there a BR prior to it?
       brContent = HTMLEditorRef().GetPriorHTMLSibling(
@@ -8060,17 +8081,17 @@ nsresult HTMLEditRules::SplitParagraph(
   if (NS_WARN_IF(!selNode->IsContent())) {
     return NS_ERROR_FAILURE;
   }
 
   // Split the paragraph.
   SplitNodeResult splitDivOrPResult =
       MOZ_KnownLive(HTMLEditorRef())
           .SplitNodeDeepWithTransaction(
-              aParentDivOrP, EditorRawDOMPoint(selNode, selOffset),
+              aParentDivOrP, EditorDOMPoint(selNode, selOffset),
               SplitAtEdges::eAllowToCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(splitDivOrPResult.Failed())) {
     return splitDivOrPResult.Rv();
   }
   if (NS_WARN_IF(!splitDivOrPResult.DidSplit())) {
@@ -8152,31 +8173,31 @@ nsresult HTMLEditRules::ReturnInListItem
   // only if prefs say it's okay and if the parent isn't the active editing
   // host.
   if (mReturnInEmptyLIKillsList && host != aListItem.GetParentElement() &&
       IsEmptyBlockElement(aListItem, IgnoreSingleBR::eYes)) {
     nsCOMPtr<nsIContent> leftListNode = aListItem.GetParent();
     // Are we the last list item in the list?
     if (!HTMLEditorRef().IsLastEditableChild(&aListItem)) {
       // We need to split the list!
-      EditorRawDOMPoint atListItem(&aListItem);
+      EditorDOMPoint atListItem(&aListItem);
       ErrorResult error;
       leftListNode = MOZ_KnownLive(HTMLEditorRef())
                          .SplitNodeWithTransaction(atListItem, error);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         error.SuppressException();
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
     }
 
     // Are we in a sublist?
-    EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode);
+    EditorDOMPoint atNextSiblingOfLeftList(leftListNode);
     DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
                          "Failed to advance offset after the right list node");
     if (HTMLEditUtils::IsList(atNextSiblingOfLeftList.GetContainer())) {
       // If so, move item out of this list and into the grandparent list
       nsresult rv =
           MOZ_KnownLive(HTMLEditorRef())
               .MoveNodeWithTransaction(aListItem, atNextSiblingOfLeftList);
@@ -8204,30 +8225,33 @@ nsresult HTMLEditRules::ReturnInListItem
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // Time to insert a paragraph
       nsAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper even if we separate with <br>
-      RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction(
-          &paraAtom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom,
-          atNextSiblingOfLeftList);
+      RefPtr<Element> pNode =
+          MOZ_KnownLive(HTMLEditorRef())
+              .CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br
+                                             ? *nsGkAtoms::p
+                                             : MOZ_KnownLive(paraAtom),
+                                         atNextSiblingOfLeftList);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!pNode)) {
         return NS_ERROR_FAILURE;
       }
 
       // Append a <br> to it
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0));
+              .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
 
       // Set selection to before the break
@@ -8258,17 +8282,17 @@ nsresult HTMLEditRules::ReturnInListItem
   if (NS_WARN_IF(!selNode->IsContent())) {
     return NS_ERROR_FAILURE;
   }
 
   // Now split the list item.
   SplitNodeResult splitListItemResult =
       MOZ_KnownLive(HTMLEditorRef())
           .SplitNodeDeepWithTransaction(
-              aListItem, EditorRawDOMPoint(selNode, aOffset),
+              aListItem, EditorDOMPoint(selNode, aOffset),
               SplitAtEdges::eAllowToCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(splitListItemResult.Succeeded(),
                        "Failed to split the list item");
 
   // Hack: until I can change the damaged doc range code back to being
@@ -8279,17 +8303,17 @@ nsresult HTMLEditRules::ReturnInListItem
   if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
     bool isEmptyNode;
     rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (isEmptyNode) {
       CreateElementResult createMozBrResult =
-          CreateMozBR(EditorRawDOMPoint(prevItem, 0));
+          CreateMozBR(EditorDOMPoint(prevItem, 0));
       if (NS_WARN_IF(createMozBrResult.Failed())) {
         return createMozBrResult.Rv();
       }
     } else {
       rv = HTMLEditorRef().IsEmptyNode(&aListItem, &isEmptyNode, true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
@@ -8297,21 +8321,22 @@ nsresult HTMLEditRules::ReturnInListItem
         RefPtr<nsAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
         if (nodeAtom == nsGkAtoms::dd || nodeAtom == nsGkAtoms::dt) {
           nsCOMPtr<nsINode> list = aListItem.GetParentNode();
           int32_t itemOffset = list ? list->ComputeIndexOf(&aListItem) : -1;
 
           nsAtom* listAtom =
               nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd : nsGkAtoms::dt;
           MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
-          EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
-                                           itemOffset + 1);
+          EditorDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
+                                        itemOffset + 1);
           RefPtr<Element> newListItem =
-              HTMLEditorRef().CreateNodeWithTransaction(*listAtom,
-                                                        atNextListItem);
+              MOZ_KnownLive(HTMLEditorRef())
+                  .CreateNodeWithTransaction(MOZ_KnownLive(*listAtom),
+                                             atNextListItem);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!newListItem)) {
             return NS_ERROR_FAILURE;
           }
           rv = MOZ_KnownLive(HTMLEditorRef())
                    .DeleteNodeWithTransaction(aListItem);
@@ -8449,25 +8474,25 @@ nsresult HTMLEditRules::MakeBlockquote(
         prevParent = curNode->GetParentNode();
       }
     } else {
       prevParent = curNode->GetParentNode();
     }
 
     // If no curBlock, make one
     if (!curBlock) {
-      EditorDOMPoint atCurNode(curNode);
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
-                                                      atCurNode);
+                                                      EditorDOMPoint(curNode));
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curBlock = HTMLEditorRef().CreateNodeWithTransaction(
-          *nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
+      curBlock = MOZ_KnownLive(HTMLEditorRef())
+                     .CreateNodeWithTransaction(*nsGkAtoms::blockquote,
+                                                splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curBlock;
@@ -8669,18 +8694,20 @@ nsresult HTMLEditRules::ApplyBlockStyle(
       }
 
       // Make sure we can put a block here
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
-          aBlockTag, splitNodeResult.SplitPoint());
+      RefPtr<Element> theBlock =
+          MOZ_KnownLive(HTMLEditorRef())
+              .CreateNodeWithTransaction(aBlockTag,
+                                         splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!theBlock)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = theBlock;
@@ -8706,18 +8733,19 @@ nsresult HTMLEditRules::ApplyBlockStyle(
 
       // The break is the first (or even only) node we encountered.  Create a
       // block for it.
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curBlock = HTMLEditorRef().CreateNodeWithTransaction(
-          aBlockTag, splitNodeResult.SplitPoint());
+      curBlock = MOZ_KnownLive(HTMLEditorRef())
+                     .CreateNodeWithTransaction(aBlockTag,
+                                                splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curBlock;
@@ -8752,18 +8780,19 @@ nsresult HTMLEditRules::ApplyBlockStyle(
       if (!curBlock) {
         AutoEditorDOMPointOffsetInvalidator lockChild(atCurNode);
 
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(aBlockTag, atCurNode);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
-        curBlock = HTMLEditorRef().CreateNodeWithTransaction(
-            aBlockTag, splitNodeResult.SplitPoint());
+        curBlock = MOZ_KnownLive(HTMLEditorRef())
+                       .CreateNodeWithTransaction(aBlockTag,
+                                                  splitNodeResult.SplitPoint());
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curBlock)) {
           return NS_ERROR_FAILURE;
         }
         // Remember our new block for postprocessing
         mNewBlock = curBlock;
@@ -8788,19 +8817,18 @@ nsresult HTMLEditRules::ApplyBlockStyle(
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 SplitNodeResult HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction(
-    nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode) {
+    nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
     return SplitNodeResult(NS_ERROR_INVALID_ARG);
   }
   MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
 
   RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
@@ -8868,17 +8896,17 @@ nsresult HTMLEditRules::JoinNearestEdita
   nsCOMPtr<nsINode> rightParent = aNodeRight.GetParentNode();
 
   // If they don't have the same parent, first move the right node to after the
   // left one
   if (parent != rightParent) {
     int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft);
     nsresult rv = MOZ_KnownLive(HTMLEditorRef())
                       .MoveNodeWithTransaction(
-                          aNodeRight, EditorRawDOMPoint(parent, parOffset));
+                          aNodeRight, EditorDOMPoint(parent, parOffset));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
@@ -9105,17 +9133,17 @@ HTMLEditRules::InsertBRElementToEmptyLis
   iter.AppendList(functor, nodeArray);
 
   // Put moz-br's into these empty li's and td's
   for (auto& node : nodeArray) {
     // Need to put br at END of node.  It may have empty containers in it and
     // still pass the "IsEmptyNode" test, and we want the br's to be after
     // them.  Also, we want the br to be after the selection if the selection
     // is in this node.
-    EditorRawDOMPoint endOfNode;
+    EditorDOMPoint endOfNode;
     endOfNode.SetToEndOf(node);
     // XXX This method should return nsreuslt due to may be destroyed by this
     //     CreateMozBr() call.
     CreateElementResult createMozBrResult = CreateMozBR(endOfNode);
     if (NS_WARN_IF(createMozBrResult.Failed())) {
       return createMozBrResult.Rv();
     }
   }
@@ -9623,17 +9651,17 @@ nsresult HTMLEditRules::RemoveEmptyNodes
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (!isEmptyNode) {
       // We are deleting a cite that has just a br.  We want to delete cite,
       // but preserve br.
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorRawDOMPoint(delNode));
+              .InsertBrElementWithTransaction(EditorDOMPoint(delNode));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
     }
     rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*delNode);
@@ -10024,18 +10052,18 @@ nsresult HTMLEditRules::InsertBRIfNeeded
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (!isEmpty) {
     return NS_OK;
   }
 
   CreateElementResult createBrResult =
-      !aInsertMozBR ? CreateBR(EditorRawDOMPoint(&aNode, 0))
-                    : CreateMozBR(EditorRawDOMPoint(&aNode, 0));
+      !aInsertMozBR ? CreateBR(EditorDOMPoint(&aNode, 0))
+                    : CreateMozBR(EditorDOMPoint(&aNode, 0));
   if (NS_WARN_IF(createBrResult.Failed())) {
     return createBrResult.Rv();
   }
   return NS_OK;
 }
 
 void HTMLEditRules::DidCreateNode(Element& aNewElement) {
   if (!mListenerEnabled) {
@@ -10340,17 +10368,17 @@ nsresult HTMLEditRules::MakeSureElemStar
       if (IsBlockNode(*sibling) || sibling->IsHTMLElement(nsGkAtoms::br)) {
         foundCR = true;
       }
     } else {
       foundCR = true;
     }
   }
   if (!foundCR) {
-    EditorRawDOMPoint pointToInsert;
+    EditorDOMPoint pointToInsert;
     if (!aStarts) {
       pointToInsert.SetToEndOf(&aNode);
     } else {
       pointToInsert.Set(&aNode, 0);
     }
     RefPtr<Element> brElement =
         MOZ_KnownLive(HTMLEditorRef())
             .InsertBrElementWithTransaction(pointToInsert);
@@ -10586,18 +10614,20 @@ nsresult HTMLEditRules::PrepareToMakeEle
 
     // Make sure we can put a block here.
     SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                     atStartOfSelection);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
-    RefPtr<Element> positionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
-        *nsGkAtoms::div, splitNodeResult.SplitPoint());
+    RefPtr<Element> positionedDiv =
+        MOZ_KnownLive(HTMLEditorRef())
+            .CreateNodeWithTransaction(*nsGkAtoms::div,
+                                       splitNodeResult.SplitPoint());
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!positionedDiv)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     *aTargetElement = positionedDiv;
@@ -10660,30 +10690,33 @@ nsresult HTMLEditRules::PrepareToMakeEle
         // Create a new nested list of correct type.
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(
                 MOZ_KnownLive(*containerName), atCurNode);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
         if (!curPositionedDiv) {
-          curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
-              *nsGkAtoms::div, splitNodeResult.SplitPoint());
+          curPositionedDiv =
+              MOZ_KnownLive(HTMLEditorRef())
+                  .CreateNodeWithTransaction(*nsGkAtoms::div,
+                                             splitNodeResult.SplitPoint());
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           NS_WARNING_ASSERTION(
               curPositionedDiv,
               "Failed to create current positioned div element");
           *aTargetElement = curPositionedDiv;
         }
-        EditorRawDOMPoint atEndOfCurPositionedDiv;
+        EditorDOMPoint atEndOfCurPositionedDiv;
         atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
-        curList = HTMLEditorRef().CreateNodeWithTransaction(
-            *containerName, atEndOfCurPositionedDiv);
+        curList = MOZ_KnownLive(HTMLEditorRef())
+                      .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                                 atEndOfCurPositionedDiv);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in.  Remember our
         // new block for postprocessing.
@@ -10729,31 +10762,33 @@ nsresult HTMLEditRules::PrepareToMakeEle
         // Create a new nested list of correct type
         SplitNodeResult splitNodeResult =
             MaybeSplitAncestorsForInsertWithTransaction(
                 MOZ_KnownLive(*containerName), atListItem);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
         if (!curPositionedDiv) {
-          EditorRawDOMPoint atListItemParent(atListItem.GetContainer());
-          curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
-              *nsGkAtoms::div, atListItemParent);
+          curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
+                                 .CreateNodeWithTransaction(
+                                     *nsGkAtoms::div,
+                                     EditorDOMPoint(atListItem.GetContainer()));
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           NS_WARNING_ASSERTION(
               curPositionedDiv,
               "Failed to create current positioned div element");
           *aTargetElement = curPositionedDiv;
         }
-        EditorRawDOMPoint atEndOfCurPositionedDiv;
+        EditorDOMPoint atEndOfCurPositionedDiv;
         atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
-        curList = HTMLEditorRef().CreateNodeWithTransaction(
-            *containerName, atEndOfCurPositionedDiv);
+        curList = MOZ_KnownLive(HTMLEditorRef())
+                      .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                                 atEndOfCurPositionedDiv);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
       rv = MOZ_KnownLive(HTMLEditorRef())
@@ -10778,18 +10813,19 @@ nsresult HTMLEditRules::PrepareToMakeEle
         continue;
       }
       SplitNodeResult splitNodeResult =
           MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                       atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
-      curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
-          *nsGkAtoms::div, splitNodeResult.SplitPoint());
+      curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
+                             .CreateNodeWithTransaction(
+                                 *nsGkAtoms::div, splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!curPositionedDiv)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       *aTargetElement = curPositionedDiv;
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -1082,20 +1082,19 @@ class HTMLEditRules : public TextEditRul
    * @param aTag                        The name of element to be inserted
    *                                    after calling this method.
    * @param aStartOfDeepestRightNode    The start point of deepest right node.
    *                                    This point must be descendant of
    *                                    active editing host.
    * @return                            When succeeded, SplitPoint() returns
    *                                    the point to insert the element.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult
   MaybeSplitAncestorsForInsertWithTransaction(
-      nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode);
+      nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode);
 
   /**
    * JoinNearestEditableNodesWithTransaction() joins two editable nodes which
    * are themselves or the nearest editable node of aLeftNode and aRightNode.
    * XXX This method's behavior is odd.  For example, if user types Backspace
    *     key at the second editable paragraph in this case:
    *     <div contenteditable>
    *       <p>first editable paragraph</p>
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -128,23 +128,16 @@ class HTMLEditorPrefs final {
     sInitialized = true;
   }
 };
 
 bool HTMLEditorPrefs::sUserWantsToEnableResizingUIByDefault = false;
 bool HTMLEditorPrefs::sUserWantsToEnableInlineTableEditingUIByDefault = false;
 bool HTMLEditorPrefs::sUserWantsToEnableAbsolutePositioningUIByDefault = false;
 
-template EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
-    nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
-    SplitAtEdges aSplitAtEdges);
-template EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
-    nsIContent& aNode, const EditorRawDOMPoint& aPointToInsert,
-    SplitAtEdges aSplitAtEdges);
-
 HTMLEditor::HTMLEditor()
     : mCRInParagraphCreatesParagraph(false),
       mCSSAware(false),
       mSelectedCellIndex(0),
       mIsObjectResizingEnabled(HTMLEditorPrefs::IsResizingUIEnabledByDefault()),
       mIsResizing(false),
       mPreserveRatio(false),
       mResizedObjectIsAnImage(false),
@@ -1179,17 +1172,17 @@ nsresult HTMLEditor::InsertBrElementAtSe
 
   if (!SelectionRefPtr()->IsCollapsed()) {
     nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  EditorRawDOMPoint atStartOfSelection(
+  EditorDOMPoint atStartOfSelection(
       EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // InsertBrElementWithTransaction() will set selection after the new <br>
   // element.
   RefPtr<Element> newBrElement =
@@ -1293,17 +1286,17 @@ nsresult HTMLEditor::ReplaceHeadContents
   }
 
   // Now insert the new nodes
   int32_t offsetOfNewNode = 0;
 
   // Loop over the contents of the fragment and move into the document
   while (nsCOMPtr<nsIContent> child = documentFragment->GetFirstChild()) {
     nsresult rv = InsertNodeWithTransaction(
-        *child, EditorRawDOMPoint(headNode, offsetOfNewNode++));
+        *child, EditorDOMPoint(headNode, offsetOfNewNode++));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
@@ -1606,17 +1599,17 @@ HTMLEditor::InsertElementAtSelection(Ele
       } else {
         SelectionRefPtr()->CollapseToEnd(IgnoreErrors());
       }
     }
 
     if (SelectionRefPtr()->GetAnchorNode()) {
       EditorRawDOMPoint atAnchor(SelectionRefPtr()->AnchorRef());
       // Adjust position based on the node we are going to insert.
-      EditorRawDOMPoint pointToInsert =
+      EditorDOMPoint pointToInsert =
           GetBetterInsertionPointFor(*aElement, atAnchor);
       if (NS_WARN_IF(!pointToInsert.IsSet())) {
         return NS_ERROR_FAILURE;
       }
 
       EditorDOMPoint insertedPoint =
           InsertNodeIntoProperAncestorWithTransaction(
               *aElement, pointToInsert,
@@ -1649,19 +1642,18 @@ HTMLEditor::InsertElementAtSelection(Ele
   }
   rv = rules->DidDoAction(subActionInfo, rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
-    nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert,
+    nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
     SplitAtEdges aSplitAtEdges) {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return EditorDOMPoint();
   }
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   // Search up the parent chain to find a suitable container.
   EditorDOMPoint pointToInsert(aPointToInsert);
@@ -2115,17 +2107,17 @@ HTMLEditor::MakeOrChangeList(const nsASt
   }
 
   if (!handled && SelectionRefPtr()->IsCollapsed()) {
     nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
 
-    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
     if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
         NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
       return NS_ERROR_FAILURE;
     }
 
     // Have to find a place to put the list.
     EditorDOMPoint pointToInsertList(atStartOfSelection);
 
@@ -2155,19 +2147,18 @@ HTMLEditor::MakeOrChangeList(const nsASt
     // parents of start of selection above, or just start of selection
     // otherwise.
     RefPtr<Element> newList =
         CreateNodeWithTransaction(*listAtom, pointToInsertList);
     if (NS_WARN_IF(!newList)) {
       return NS_ERROR_FAILURE;
     }
     // make a list item
-    EditorRawDOMPoint atStartOfNewList(newList, 0);
     RefPtr<Element> newItem =
-        CreateNodeWithTransaction(*nsGkAtoms::li, atStartOfNewList);
+        CreateNodeWithTransaction(*nsGkAtoms::li, EditorDOMPoint(newList, 0));
     if (NS_WARN_IF(!newItem)) {
       return NS_ERROR_FAILURE;
     }
     ErrorResult error;
     SelectionRefPtr()->Collapse(RawRangeBoundary(newItem, 0), error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
@@ -2291,17 +2282,17 @@ nsresult HTMLEditor::InsertBasicBlockWit
   }
 
   if (!handled && SelectionRefPtr()->IsCollapsed()) {
     nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
 
-    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
     if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
         NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
       return NS_ERROR_FAILURE;
     }
 
     // Have to find a place to put the block.
     EditorDOMPoint pointToInsertBlock(atStartOfSelection);
 
@@ -2431,17 +2422,17 @@ nsresult HTMLEditor::IndentOrOutdentAsSu
 
   if (!handled && SelectionRefPtr()->IsCollapsed() &&
       aIndentOrOutdent == EditSubAction::eIndent) {
     nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
 
-    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    EditorDOMPoint atStartOfSelection(firstRange->StartRef());
     if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
         NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
       return NS_ERROR_FAILURE;
     }
 
     // Have to find a place to put the blockquote.
     EditorDOMPoint pointToInsertBlockquote(atStartOfSelection);
 
@@ -3829,17 +3820,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     // 3) first child of aNode is a block OR
     // 4) either is null
 
     nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling) &&
         !sibling->IsHTMLElement(nsGkAtoms::br) && !IsBlockNode(child)) {
       // Insert br node
       RefPtr<Element> brElement =
-          InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0));
+          InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
     }
 
     // We need a br at end unless:
     // 1) following sibling of aNode is a block, OR
     // 2) last child of aNode is a block, OR
@@ -3847,17 +3838,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     // 4) either is null
 
     sibling = GetNextHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling)) {
       child = GetLastEditableChild(aElement);
       MOZ_ASSERT(child, "aNode has first editable child but not last?");
       if (!IsBlockNode(child) && !child->IsHTMLElement(nsGkAtoms::br)) {
         // Insert br node
-        EditorRawDOMPoint endOfNode;
+        EditorDOMPoint endOfNode;
         endOfNode.SetToEndOf(&aElement);
         RefPtr<Element> brElement = InsertBrElementWithTransaction(endOfNode);
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   } else {
@@ -3870,17 +3861,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling) &&
         !sibling->IsHTMLElement(nsGkAtoms::br)) {
       sibling = GetNextHTMLSibling(&aElement);
       if (sibling && !IsBlockNode(sibling) &&
           !sibling->IsHTMLElement(nsGkAtoms::br)) {
         // Insert br node
         RefPtr<Element> brElement =
-            InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0));
+            InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
 
   // Now remove container
@@ -4557,19 +4548,18 @@ nsresult HTMLEditor::CopyLastEditableChi
        elementInPreviousBlock = elementInPreviousBlock->GetParentElement()) {
     if (!HTMLEditUtils::IsInlineStyle(elementInPreviousBlock) &&
         !elementInPreviousBlock->IsHTMLElement(nsGkAtoms::span)) {
       continue;
     }
     nsAtom* tagName = elementInPreviousBlock->NodeInfo()->NameAtom();
     // At first time, just create the most descendant inline container element.
     if (!firstClonsedElement) {
-      EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
-      firstClonsedElement = lastClonedElement =
-          CreateNodeWithTransaction(*tagName, atStartOfNewBlock);
+      firstClonsedElement = lastClonedElement = CreateNodeWithTransaction(
+          MOZ_KnownLive(*tagName), EditorDOMPoint(newBlock, 0));
       if (NS_WARN_IF(!firstClonsedElement)) {
         return NS_ERROR_FAILURE;
       }
       // Clone all attributes.
       // XXX Looks like that this clones id attribute too.
       CloneAttributesWithTransaction(*lastClonedElement,
                                      *elementInPreviousBlock);
       continue;
@@ -4586,17 +4576,17 @@ nsresult HTMLEditor::CopyLastEditableChi
 
   if (!firstClonsedElement) {
     // XXX Even if no inline elements are cloned, shouldn't we create new
     //     <br> element for aNewBlock?
     return NS_OK;
   }
 
   RefPtr<Element> brElement =
-      InsertBrElementWithTransaction(EditorRawDOMPoint(firstClonsedElement, 0));
+      InsertBrElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
   if (NS_WARN_IF(!brElement)) {
     return NS_ERROR_FAILURE;
   }
   *aNewBrElement = brElement.forget();
   return NS_OK;
 }
 
 nsresult HTMLEditor::GetElementOrigin(Element& aElement, int32_t& aX,
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -520,16 +520,17 @@ class HTMLEditor final : public TextEdit
    * DeleteTextWithTransaction() removes text in the range from aCharData if
    * it's modifiable.  Note that this not an override of same method of
    * EditorBase.
    *
    * @param aCharData           The data node which should be modified.
    * @param aOffset             Start offset of removing text in aCharData.
    * @param aLength             Length of removing text.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult DeleteTextWithTransaction(dom::CharacterData& aTextNode,
                                      uint32_t aOffset, uint32_t aLength);
 
   /**
    * InsertTextWithTransaction() inserts aStringToInsert at aPointToInsert.
    */
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult InsertTextWithTransaction(
@@ -1473,19 +1474,18 @@ class HTMLEditor final : public TextEdit
    * is allowed to result in empty nodes.
    *
    * @param aNode             Node to insert.
    * @param aPointToInsert    Insertion point.
    * @param aSplitAtEdges     Splitting can result in empty nodes?
    * @return                  Returns inserted point if succeeded.
    *                          Otherwise, the result is not set.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
-      nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert,
+      nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
       SplitAtEdges aSplitAtEdges);
 
   /**
    * InsertBrElementAtSelectionWithTransaction() inserts a new <br> element at
    * selection.  If there is non-collapsed selection ranges, the selected
    * ranges is deleted first.
    */
   MOZ_CAN_RUN_SCRIPT
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -641,17 +641,17 @@ nsresult HTMLEditor::DoInsertHTMLWithCon
       // if we just pasted a link, discontinue link style
       nsCOMPtr<nsIContent> linkContent;
       if (!bStartedInLink && (linkContent = GetLinkElement(selNode))) {
         // so, if we just pasted a link, I split it.  Why do that instead of
         // just nudging selection point beyond it?  Because it might have ended
         // in a BR that is not visible.  If so, the code above just placed
         // selection inside that.  So I split it instead.
         SplitNodeResult splitLinkResult = SplitNodeDeepWithTransaction(
-            *linkContent, EditorRawDOMPoint(selNode, selOffset),
+            *linkContent, EditorDOMPoint(selNode, selOffset),
             SplitAtEdges::eDoNotCreateEmptyContainer);
         NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
                              "Failed to split the link");
         if (splitLinkResult.GetPreviousNode()) {
           EditorRawDOMPoint afterLeftLink(splitLinkResult.GetPreviousNode());
           if (afterLeftLink.AdvanceOffset()) {
             DebugOnly<nsresult> rv = SelectionRefPtr()->Collapse(afterLeftLink);
             NS_WARNING_ASSERTION(
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -359,28 +359,28 @@ nsresult HTMLEditor::SetInlinePropertyOn
                                         &aValue)) {
     return NS_OK;
   }
 
   // Make the range an independent node.
   nsCOMPtr<nsIContent> textNodeForTheRange = &aText;
 
   // Split at the end of the range.
-  EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
+  EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
   if (!atEnd.IsEndOfContainer()) {
     // We need to split off back of text node
     ErrorResult error;
     textNodeForTheRange = SplitNodeWithTransaction(atEnd, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
   // Split at the start of the range.
-  EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
+  EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
   if (!atStart.IsStartOfContainer()) {
     // We need to split off front of text node
     ErrorResult error;
     nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atStart, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     Unused << newLeftNode;
@@ -392,17 +392,17 @@ nsresult HTMLEditor::SetInlinePropertyOn
     if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
       // Previous sib is already right kind of inline node; slide this over
       return MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
     }
     sibling = GetNextHTMLSibling(textNodeForTheRange);
     if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
       // Following sib is already right kind of inline node; slide this over
       return MoveNodeWithTransaction(*textNodeForTheRange,
-                                     EditorRawDOMPoint(sibling, 0));
+                                     EditorDOMPoint(sibling, 0));
     }
   }
 
   // Reparent the node inside inline node with appropriate {attribute,value}
   return SetInlinePropertyOnNode(*textNodeForTheRange, aProperty, aAttribute,
                                  aValue);
 }
 
@@ -448,17 +448,17 @@ nsresult HTMLEditor::SetInlinePropertyOn
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
     return NS_OK;
   }
   if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
     nsresult rv =
-        MoveNodeWithTransaction(aNode, EditorRawDOMPoint(nextSibling, 0));
+        MoveNodeWithTransaction(aNode, EditorDOMPoint(nextSibling, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // Don't need to do anything if property already set on node
   if (CSSEditUtils::IsCSSEditableProperty(&aNode, &aProperty, aAttribute)) {
@@ -636,17 +636,17 @@ nsresult HTMLEditor::SplitStyleAbovePoin
         (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
         // or node is any prop, and we asked to split them all
         (!aProperty && node->IsElement() && IsEditable(node) &&
          HTMLEditUtils::IsRemovableInlineStyleElement(*node->AsElement())) ||
         // or the style is specified in the style attribute
         isSet) {
       // Found a style node we need to split
       SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
-          *node, EditorRawDOMPoint(*aNode, *aOffset),
+          *node, EditorDOMPoint(*aNode, *aOffset),
           SplitAtEdges::eAllowToCreateEmptyContainer);
       NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
                            "Failed to split the node");
 
       EditorRawDOMPoint atRightNode(splitNodeResult.SplitPoint());
       *aNode = atRightNode.GetContainer();
       *aOffset = atRightNode.Offset();
       if (aOutLeftNode) {
@@ -727,18 +727,17 @@ nsresult HTMLEditor::ClearStyle(nsCOMPtr
     nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
     if (!newSelParent) {
       newSelParent = leftNode;
     }
     // If rightNode starts with a br, suck it out of right node and into
     // leftNode.  This is so we you don't revert back to the previous style
     // if you happen to click at the end of a line.
     if (savedBR) {
-      rv =
-          MoveNodeWithTransaction(*savedBR, EditorRawDOMPoint(newSelParent, 0));
+      rv = MoveNodeWithTransaction(*savedBR, EditorDOMPoint(newSelParent, 0));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
     // remove the style on this new hierarchy
     int32_t newSelOffset = 0;
     {
       // Track the point at the new hierarchy.  This is so we can know where
@@ -1636,28 +1635,28 @@ nsresult HTMLEditor::RelativeFontChangeO
   if (aEndOffset == -1) {
     aEndOffset = aTextNode.Length();
   }
 
   // Make the range an independent node.
   nsCOMPtr<nsIContent> textNodeForTheRange = &aTextNode;
 
   // Split at the end of the range.
-  EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
+  EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
   if (!atEnd.IsEndOfContainer()) {
     // We need to split off back of text node
     ErrorResult error;
     textNodeForTheRange = SplitNodeWithTransaction(atEnd, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
   // Split at the start of the range.
-  EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
+  EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
   if (!atStart.IsStartOfContainer()) {
     // We need to split off front of text node
     ErrorResult error;
     nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atStart, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     Unused << newLeftNode;
@@ -1673,17 +1672,17 @@ nsresult HTMLEditor::RelativeFontChangeO
       return rv;
     }
     return NS_OK;
   }
   sibling = GetNextHTMLSibling(textNodeForTheRange);
   if (sibling && sibling->IsHTMLElement(nodeType)) {
     // Following sib is already right kind of inline node; slide this over
     nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange,
-                                          EditorRawDOMPoint(sibling, 0));
+                                          EditorDOMPoint(sibling, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // Else reparent the node inside font node with appropriate relative size
   RefPtr<Element> newElement = InsertContainerWithTransaction(
@@ -1784,17 +1783,17 @@ nsresult HTMLEditor::RelativeFontChangeO
       // it
       return MoveNodeToEndWithTransaction(*aNode, *sibling);
     }
 
     sibling = GetNextHTMLSibling(aNode);
     if (sibling && sibling->IsHTMLElement(atom)) {
       // following sib is already right kind of inline node; slide this over
       // into it
-      return MoveNodeWithTransaction(*aNode, EditorRawDOMPoint(sibling, 0));
+      return MoveNodeWithTransaction(*aNode, EditorDOMPoint(sibling, 0));
     }
 
     // else insert it above aNode
     RefPtr<Element> newElement =
         InsertContainerWithTransaction(*aNode, MOZ_KnownLive(*atom));
     if (NS_WARN_IF(!newElement)) {
       return NS_ERROR_FAILURE;
     }
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -2794,18 +2794,18 @@ nsresult HTMLEditor::MergeCells(RefPtr<E
       nsCOMPtr<nsIContent> cellChild = aCellToMerge->GetLastChild();
       if (NS_WARN_IF(!cellChild)) {
         return NS_ERROR_FAILURE;
       }
       nsresult rv = DeleteNodeWithTransaction(*cellChild);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-      rv = InsertNodeWithTransaction(
-          *cellChild, EditorRawDOMPoint(aTargetCell, insertIndex));
+      rv = InsertNodeWithTransaction(*cellChild,
+                                     EditorDOMPoint(aTargetCell, insertIndex));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   if (!aDeleteCellToMerge) {
     return NS_OK;
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -39,21 +39,16 @@
 #include "nsUnicharUtils.h"
 #include "nsIHTMLCollection.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
-template CreateElementResult TextEditRules::CreateBRInternal(
-    const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
-template CreateElementResult TextEditRules::CreateBRInternal(
-    const EditorRawDOMPoint& aPointToInsert, bool aCreateMozBR);
-
 #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
   if (IsReadonly() || IsDisabled()) {            \
     *aCancel = true;                             \
     return NS_OK;                                \
   }
 
 #define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
   if (IsReadonly() || IsDisabled()) {                                          \
@@ -977,17 +972,17 @@ nsresult TextEditRules::WillSetText(bool
       return NS_OK;
     }
     RefPtr<nsTextNode> newNode = EditorBase::CreateTextNode(*doc, tString);
     if (NS_WARN_IF(!newNode)) {
       return NS_OK;
     }
     nsresult rv = MOZ_KnownLive(TextEditorRef())
                       .InsertNodeWithTransaction(
-                          *newNode, EditorRawDOMPoint(rootElement, 0));
+                          *newNode, EditorDOMPoint(rootElement, 0));
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     *aHandled = true;
 
@@ -1468,17 +1463,17 @@ nsresult TextEditRules::CreateTrailingBR
   nsCOMPtr<nsIContent> lastChild = rootElement->GetLastChild();
   // assuming CreateBogusNodeIfNeeded() has been called first
   if (NS_WARN_IF(!lastChild)) {
     return NS_ERROR_FAILURE;
   }
 
   if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
     AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
-    EditorRawDOMPoint endOfRoot;
+    EditorDOMPoint endOfRoot;
     endOfRoot.SetToEndOf(rootElement);
     CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
     if (NS_WARN_IF(createMozBrResult.Failed())) {
       return createMozBrResult.Rv();
     }
     return NS_OK;
   }
 
@@ -1553,18 +1548,18 @@ nsresult TextEditRules::CreateBogusNodeI
   newBrElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
                         kMOZEditorBogusNodeValue, false);
   if (NS_WARN_IF(mBogusNode != newBrElement)) {
     return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
   }
 
   // Put the node in the document.
   nsresult rv = MOZ_KnownLive(TextEditorRef())
-                    .InsertNodeWithTransaction(
-                        *newBrElement, EditorRawDOMPoint(rootElement, 0));
+                    .InsertNodeWithTransaction(*newBrElement,
+                                               EditorDOMPoint(rootElement, 0));
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Set selection.
@@ -1765,19 +1760,18 @@ void TextEditRules::FillBufWithPWChars(n
   char16_t passwordChar = LookAndFeel::GetPasswordCharacter();
 
   aOutString->Truncate();
   for (int32_t i = 0; i < aLength; i++) {
     aOutString->Append(passwordChar);
   }
 }
 
-template <typename PT, typename CT>
 CreateElementResult TextEditRules::CreateBRInternal(
-    const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR) {
+    const EditorDOMPoint& aPointToInsert, bool aCreateMozBR) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return CreateElementResult(NS_ERROR_FAILURE);
   }
 
   RefPtr<Element> brElement =
       MOZ_KnownLive(TextEditorRef())
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -307,19 +307,18 @@ class TextEditRules : public nsITimerCal
   /**
    * Create a normal <br> element and insert it to aPointToInsert.
    *
    * @param aPointToInsert  The point where the new <br> element will be
    *                        inserted.
    * @return                Returns created <br> element or an error code
    *                        if couldn't create new <br> element.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT CreateElementResult
-  CreateBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) {
+  CreateBR(const EditorDOMPoint& aPointToInsert) {
     CreateElementResult ret = CreateBRInternal(aPointToInsert, false);
 #ifdef DEBUG
     // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
     if (!CanHandleEditAction()) {
       MOZ_ASSERT(ret.Rv() == NS_ERROR_EDITOR_DESTROYED);
     }
 #endif  // #ifdef DEBUG
     return ret;
@@ -328,19 +327,18 @@ class TextEditRules : public nsITimerCal
   /**
    * Create a moz-<br> element and insert it to aPointToInsert.
    *
    * @param aPointToInsert  The point where the new moz-<br> element will be
    *                        inserted.
    * @return                Returns created <br> element or an error code
    *                        if couldn't create new <br> element.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT CreateElementResult
-  CreateMozBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) {
+  CreateMozBR(const EditorDOMPoint& aPointToInsert) {
     CreateElementResult ret = CreateBRInternal(aPointToInsert, true);
 #ifdef DEBUG
     // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
     if (!CanHandleEditAction()) {
       MOZ_ASSERT(ret.Rv() == NS_ERROR_EDITOR_DESTROYED);
     }
 #endif  // #ifdef DEBUG
     return ret;
@@ -386,19 +384,18 @@ class TextEditRules : public nsITimerCal
    *
    * @param aParentToInsert     The point where the new <br> element will be
    *                            inserted.
    * @param aCreateMozBR        true if the caller wants to create a moz-<br>
    *                            element.  Otherwise, false.
    * @return                    Returns created <br> element and error code.
    *                            If it succeeded, never returns nullptr.
    */
-  template <typename PT, typename CT>
-  MOZ_CAN_RUN_SCRIPT CreateElementResult CreateBRInternal(
-      const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR);
+  MOZ_CAN_RUN_SCRIPT CreateElementResult
+  CreateBRInternal(const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
 
  protected:
   /**
    * AutoSafeEditorData grabs editor instance and related instances during
    * handling an edit action.  When this is created, its pointer is set to
    * the mSafeData, and this guarantees the lifetime of grabbing objects
    * until it's destroyed.
    */
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -59,21 +59,16 @@
 
 class nsIOutputStream;
 class nsISupports;
 
 namespace mozilla {
 
 using namespace dom;
 
-template already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
-    const EditorDOMPoint& aPointToInsert, EDirection aSelect);
-template already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
-    const EditorRawDOMPoint& aPointToInsert, EDirection aSelect);
-
 TextEditor::TextEditor()
     : mWrapColumn(0),
       mMaxTextLength(-1),
       mInitTriggerCounter(0),
       mNewlineHandling(nsIPlaintextEditor::eNewlinesPasteToFirst)
 #ifdef XP_WIN
       ,
       mCaretStyle(1)
@@ -237,19 +232,18 @@ TextEditor::SetDocumentCharacterSet(cons
   }
 
   nsCOMPtr<nsIContent> headNode = headList->Item(0);
   if (NS_WARN_IF(!headNode)) {
     return NS_OK;
   }
 
   // Create a new meta charset tag
-  EditorRawDOMPoint atStartOfHeadNode(headNode, 0);
   RefPtr<Element> metaElement =
-      CreateNodeWithTransaction(*nsGkAtoms::meta, atStartOfHeadNode);
+      CreateNodeWithTransaction(*nsGkAtoms::meta, EditorDOMPoint(headNode, 0));
   if (NS_WARN_IF(!metaElement)) {
     return NS_OK;
   }
 
   // Set attributes to the created element
   if (characterSet.IsEmpty()) {
     return NS_OK;
   }
@@ -431,20 +425,18 @@ nsresult TextEditor::InsertLineBreakAsAc
   AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertLineBreakAsSubAction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
-    const EditorDOMPointBase<PT, CT>& aPointToInsert,
-    EDirection aSelect /* = eNone */) {
+    const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return nullptr;
   }
 
   // We need to insert a <br> node.
   RefPtr<Element> newBRElement;
@@ -864,17 +856,17 @@ already_AddRefed<Element> TextEditor::De
     nsAtom& aTag) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
-  EditorRawDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
+  EditorDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
   if (!pointToInsert.IsSet()) {
     return nullptr;
   }
   RefPtr<Element> newElement = CreateNodeWithTransaction(aTag, pointToInsert);
 
   // We want the selection to be just after the new node
   EditorRawDOMPoint afterNewElement(newElement);
   MOZ_ASSERT(afterNewElement.IsSetAndValid());
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -349,20 +349,18 @@ class TextEditor : public EditorBase, pu
    * @param aSelect             If eNone, this won't change selection.
    *                            If eNext, selection will be collapsed after
    *                            the <br> element.
    *                            If ePrevious, selection will be collapsed at
    *                            the <br> element.
    * @return                    The new <br> node.  If failed to create new
    *                            <br> node, returns nullptr.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBrElementWithTransaction(
-      const EditorDOMPointBase<PT, CT>& aPointToInsert,
-      EDirection aSelect = eNone);
+      const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
 
   /**
    * Extends the selection for given deletion operation
    * If done, also update aAction to what's actually left to do after the
    * extension.
    */
   nsresult ExtendSelectionForDelete(nsIEditor::EDirection* aAction);
 
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -50,22 +50,16 @@ template void WSRunObject::PriorVisibleN
 template void WSRunObject::NextVisibleNode(const EditorDOMPoint& aPoint,
                                            nsCOMPtr<nsINode>* outVisNode,
                                            int32_t* outVisOffset,
                                            WSType* outType) const;
 template void WSRunObject::NextVisibleNode(const EditorRawDOMPoint& aPoint,
                                            nsCOMPtr<nsINode>* outVisNode,
                                            int32_t* outVisOffset,
                                            WSType* outType) const;
-template already_AddRefed<Element> WSRunObject::InsertBreak(
-    Selection& aSelection, const EditorDOMPoint& aPointToInsert,
-    nsIEditor::EDirection aSelect);
-template already_AddRefed<Element> WSRunObject::InsertBreak(
-    Selection& aSelection, const EditorRawDOMPoint& aPointToInsert,
-    nsIEditor::EDirection aSelect);
 template void WSRunObject::GetASCIIWhitespacesBounds(
     int16_t aDir, const EditorDOMPoint& aPoint, dom::Text** outStartNode,
     int32_t* outStartOffset, dom::Text** outEndNode, int32_t* outEndOffset);
 template void WSRunObject::GetASCIIWhitespacesBounds(
     int16_t aDir, const EditorRawDOMPoint& aPoint, dom::Text** outStartNode,
     int32_t* outStartOffset, dom::Text** outEndNode, int32_t* outEndOffset);
 
 template <typename PT, typename CT>
@@ -166,19 +160,18 @@ nsresult WSRunObject::PrepareToSplitAcro
   AutoTrackDOMPoint tracker(aHTMLEditor->RangeUpdaterRef(), aSplitNode,
                             aSplitOffset);
 
   WSRunObject wsObj(aHTMLEditor, *aSplitNode, *aSplitOffset);
 
   return wsObj.PrepareToSplitAcrossBlocksPriv();
 }
 
-template <typename PT, typename CT>
 already_AddRefed<Element> WSRunObject::InsertBreak(
-    Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert,
+    Selection& aSelection, const EditorDOMPoint& aPointToInsert,
     nsIEditor::EDirection aSelect) {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return nullptr;
   }
 
   // MOOSE: for now, we always assume non-PRE formatting.  Fix this later.
   // meanwhile, the pre case is handled in WillInsertText in
   // HTMLEditRules.cpp
@@ -388,18 +381,18 @@ nsresult WSRunObject::InsertText(Documen
 
 nsresult WSRunObject::DeleteWSBackward() {
   WSPoint point = GetPreviousCharPoint(mScanStartPoint);
   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
     nsresult rv =
-        DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
-                    EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
+        DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
+                    EditorDOMPoint(point.mTextNode, point.mOffset + 1));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // Caller's job to ensure that previous char is really ws.  If it is normal
   // ws, we need to delete the whole run.
@@ -415,18 +408,18 @@ nsresult WSRunObject::DeleteWSBackward()
     nsCOMPtr<nsINode> startNode = startNodeText.get();
     nsCOMPtr<nsINode> endNode = endNodeText.get();
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), address_of(startNode), &startOffset,
         address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
-    rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
-                     EditorRawDOMPoint(endNode, endOffset));
+    rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
+                     EditorDOMPoint(endNode, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   if (point.mChar == kNBSP) {
     nsCOMPtr<nsINode> node(point.mTextNode);
@@ -434,36 +427,36 @@ nsresult WSRunObject::DeleteWSBackward()
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset + 1;
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), address_of(node), &startOffset,
         address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // finally, delete that ws
-    rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
-                     EditorRawDOMPoint(node, endOffset));
+    rv = DeleteRange(EditorDOMPoint(node, startOffset),
+                     EditorDOMPoint(node, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   return NS_OK;
 }
 
 nsresult WSRunObject::DeleteWSForward() {
   WSPoint point = GetNextCharPoint(mScanStartPoint);
   NS_ENSURE_TRUE(point.mTextNode, NS_OK);  // nothing to delete
 
   // Easy case, preformatted ws.
   if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
     nsresult rv =
-        DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
-                    EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
+        DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
+                    EditorDOMPoint(point.mTextNode, point.mOffset + 1));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // Caller's job to ensure that next char is really ws.  If it is normal ws,
   // we need to delete the whole run.
@@ -478,18 +471,18 @@ nsresult WSRunObject::DeleteWSForward() 
     // Adjust surrounding ws
     nsCOMPtr<nsINode> startNode(startNodeText), endNode(endNodeText);
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), address_of(startNode), &startOffset,
         address_of(endNode), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
-    rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
-                     EditorRawDOMPoint(endNode, endOffset));
+    rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
+                     EditorDOMPoint(endNode, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   if (point.mChar == kNBSP) {
     nsCOMPtr<nsINode> node(point.mTextNode);
@@ -497,18 +490,18 @@ nsresult WSRunObject::DeleteWSForward() 
     int32_t startOffset = point.mOffset;
     int32_t endOffset = point.mOffset + 1;
     nsresult rv = WSRunObject::PrepareToDeleteRange(
         MOZ_KnownLive(mHTMLEditor), address_of(node), &startOffset,
         address_of(node), &endOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Finally, delete that ws
-    rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
-                     EditorRawDOMPoint(node, endOffset));
+    rv = DeleteRange(EditorDOMPoint(node, startOffset),
+                     EditorDOMPoint(node, endOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   return NS_OK;
 }
@@ -1297,20 +1290,18 @@ nsresult WSRunObject::PrepareToSplitAcro
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
-template <typename PT1, typename CT1, typename PT2, typename CT2>
-nsresult WSRunObject::DeleteRange(
-    const EditorDOMPointBase<PT1, CT1>& aStartPoint,
-    const EditorDOMPointBase<PT2, CT2>& aEndPoint) {
+nsresult WSRunObject::DeleteRange(const EditorDOMPoint& aStartPoint,
+                                  const EditorDOMPoint& aEndPoint) {
   if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   MOZ_ASSERT(aStartPoint.IsSetAndValid());
   MOZ_ASSERT(aEndPoint.IsSetAndValid());
 
   // MOOSE: this routine needs to be modified to preserve the integrity of the
   // wsFragment info.
@@ -1320,18 +1311,19 @@ nsresult WSRunObject::DeleteRange(
     return NS_OK;
   }
 
   MOZ_ASSERT(mHTMLEditor);
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
       aStartPoint.IsInTextNode()) {
+    RefPtr<Text> textNode = aStartPoint.GetContainerAsText();
     return htmlEditor->DeleteTextWithTransaction(
-        *aStartPoint.GetContainerAsText(), aStartPoint.Offset(),
+        *textNode, aStartPoint.Offset(),
         aEndPoint.Offset() - aStartPoint.Offset());
   }
 
   RefPtr<nsRange> range;
   int32_t count = mNodeArray.Length();
   int32_t idx = mNodeArray.IndexOf(aStartPoint.GetContainer());
   if (idx == -1) {
     // If our starting point wasn't one of our ws text nodes, then just go
@@ -1878,19 +1870,18 @@ nsresult WSRunObject::CheckTrailingNBSPO
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
   return NS_OK;
 }
 
-template <typename PT, typename CT>
 nsresult WSRunObject::ReplacePreviousNBSPIfUnncessary(
-    WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint) {
+    WSFragment* aRun, const EditorDOMPoint& aPoint) {
   if (NS_WARN_IF(!aRun) || NS_WARN_IF(!aPoint.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   MOZ_ASSERT(aPoint.IsSetAndValid());
 
   // Try to change an NBSP to a space, if possible, just to prevent NBSP
   // proliferation.  This routine is called when we are about to make this
   // point in the ws abut an inserted break or text, so we don't have to worry
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -235,19 +235,18 @@ class MOZ_STACK_CLASS WSRunObject final 
    * @param aSelect         If eNone, this won't change selection.
    *                        If eNext, selection will be collapsed after the
    *                        <br> element.
    *                        If ePrevious, selection will be collapsed at the
    *                        <br> element.
    * @return                The new <br> node.  If failed to create new <br>
    *                        node, returns nullptr.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT already_AddRefed<dom::Element> InsertBreak(
-      Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert,
+      Selection& aSelection, const EditorDOMPoint& aPointToInsert,
       nsIEditor::EDirection aSelect);
 
   /**
    * InsertText() inserts aStringToInsert to mScanStartPoint and makes any
    * needed adjustments to white spaces around both mScanStartPoint and
    * mScanEndPoint. E.g., trailing white spaces before mScanStartPoint needs to
    * be removed.  This calls EditorBase::InsertTextWithTransaction() after
    * adjusting white spaces.  So, please refer the method's explanation to know
@@ -382,20 +381,18 @@ class MOZ_STACK_CLASS WSRunObject final 
    * DeleteRange() removes the range between aStartPoint and aEndPoint.
    * When aStartPoint and aEndPoint are same point, does nothing.
    * When aStartPoint and aEndPoint are in same text node, removes characters
    * between them.
    * When aStartPoint is in a text node, removes the text data after the point.
    * When aEndPoint is in a text node, removes the text data before the point.
    * Removes any nodes between them.
    */
-  template <typename PT1, typename CT1, typename PT2, typename CT2>
-  MOZ_CAN_RUN_SCRIPT nsresult
-  DeleteRange(const EditorDOMPointBase<PT1, CT1>& aStartPoint,
-              const EditorDOMPointBase<PT2, CT2>& aEndPoint);
+  MOZ_CAN_RUN_SCRIPT nsresult DeleteRange(const EditorDOMPoint& aStartPoint,
+                                          const EditorDOMPoint& aEndPoint);
 
   /**
    * GetNextCharPoint() returns next character's point of aPoint.  If there is
    * no character after aPoint, mTextNode is set to nullptr.
    */
   template <typename PT, typename CT>
   WSPoint GetNextCharPoint(const EditorDOMPointBase<PT, CT>& aPoint) const;
   WSPoint GetNextCharPoint(const WSPoint& aPoint) const;
@@ -487,31 +484,30 @@ class MOZ_STACK_CLASS WSRunObject final 
   /**
    * ReplacePreviousNBSPIfUnncessary() replaces previous character of aPoint
    * if it's a NBSP and it's unnecessary.
    *
    * @param aRun        Current text run.  aPoint must be in this run.
    * @param aPoint      Current insertion point.  Its previous character is
    *                    unnecessary NBSP will be checked.
    */
-  template <typename PT, typename CT>
   MOZ_CAN_RUN_SCRIPT nsresult ReplacePreviousNBSPIfUnncessary(
-      WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint);
+      WSFragment* aRun, const EditorDOMPoint& aPoint);
 
   MOZ_CAN_RUN_SCRIPT
   nsresult CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode, int32_t aOffset);
 
   MOZ_CAN_RUN_SCRIPT nsresult Scrub();
   bool IsBlockNode(nsINode* aNode);
 
-  EditorRawDOMPoint StartPoint() const {
-    return EditorRawDOMPoint(mStartNode, mStartOffset);
+  EditorDOMPoint StartPoint() const {
+    return EditorDOMPoint(mStartNode, mStartOffset);
   }
-  EditorRawDOMPoint EndPoint() const {
-    return EditorRawDOMPoint(mEndNode, mEndOffset);
+  EditorDOMPoint EndPoint() const {
+    return EditorDOMPoint(mEndNode, mEndOffset);
   }
 
   // The node passed to our constructor.
   EditorDOMPoint mScanStartPoint;
   EditorDOMPoint mScanEndPoint;
 
   // Together, the above represent the point at which we are building up ws
   // info.
--- a/editor/spellchecker/tests/mochitest.ini
+++ b/editor/spellchecker/tests/mochitest.ini
@@ -7,20 +7,22 @@ support-files =
   bug1204147_subframe.html
   bug1204147_subframe2.html
   en-GB/en_GB.dic
   en-GB/en_GB.aff
   en-AU/en_AU.dic
   en-AU/en_AU.aff
   de-DE/de_DE.dic
   de-DE/de_DE.aff
+  !/editor/libeditor/tests/spellcheck.js
 
 [test_async_UpdateCurrentDictionary.html]
 [test_bug678842.html]
 [test_bug697981.html]
 [test_bug717433.html]
 [test_bug1200533.html]
 [test_bug1204147.html]
 [test_bug1205983.html]
 [test_bug1209414.html]
 [test_bug1219928.html]
 skip-if = e10s
 [test_bug1365383.html]
+[test_bug1418629.html]
new file mode 100644
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1418629.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Mozilla bug 1418629</title>
+  <link rel=stylesheet href="/tests/SimpleTest/test.css">
+  <script src="/tests/SimpleTest/EventUtils.js"></script>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/AddTask.js"></script>
+  <script src="/tests/editor/libeditor/tests/spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1418629">Mozilla Bug 1418629</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<input id="input1" autofocus spellcheck="true">
+
+<script>
+const {onSpellCheck} = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
+
+SimpleTest.waitForExplicitFinish();
+
+add_task(async function() {
+  await new Promise((resolve) => {
+    SimpleTest.waitForFocus(() => {
+      SimpleTest.executeSoon(resolve);
+    }, window);
+  });
+
+  let misspeltWords = [];
+  let input = document.getElementById("input1");
+
+  input.focus();
+  input.value = "";
+  synthesizeKey("d");
+  synthesizeKey("o");
+  synthesizeKey("e");
+  synthesizeKey("s");
+
+  await new Promise((resolve) => { onSpellCheck(input, resolve); });
+  // isSpellingCheckOk is defined in spellcheck.js
+  // eslint-disable-next-line no-undef
+  ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
+     "no misspelt words");
+
+  synthesizeKey("n");
+  synthesizeKey("\'");
+  is(input.value, "doesn\'", "");
+
+  await new Promise((resolve) => { onSpellCheck(input, resolve); });
+  // isSpellingCheckOk is defined in spellcheck.js
+  // eslint-disable-next-line no-undef
+  ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
+     "don't run spellchecker during inputting word");
+
+  synthesizeKey(" ");
+  is(input.value, "doesn\' ", "");
+
+  await new Promise((resolve) => { onSpellCheck(input, resolve); });
+  misspeltWords.push("doesn\'");
+  // isSpellingCheckOk is defined in spellcheck.js
+  // eslint-disable-next-line no-undef
+  ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
+     "should run spellchecker");
+});
+
+async function test_with_twice_characters(ch) {
+  let misspeltWords = [];
+  let input = document.getElementById("input1");
+
+  input.focus();
+  input.value = "";
+  synthesizeKey("d");
+  synthesizeKey("o");
+  synthesizeKey("e");
+  synthesizeKey("s");
+  synthesizeKey("n");
+  synthesizeKey(ch);
+  synthesizeKey(ch);
+  is(input.value, "doesn" + ch + ch, "");
+
+  await new Promise((resolve) => { onSpellCheck(input, resolve); });
+  misspeltWords.push("doesn");
+  // isSpellingCheckOk is defined in spellcheck.js
+  // eslint-disable-next-line no-undef
+  ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
+     "should run spellchecker");
+}
+
+add_task(test_with_twice_characters.bind(null, "\'"));
+add_task(test_with_twice_characters.bind(null, String.fromCharCode(0x2019)));
+</script>
+</body>
+</html>
--- a/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellWordUtil.cpp
@@ -413,18 +413,29 @@ CharClass WordSplitState::ClassifyCharac
     if (aIndex == 0) return CHAR_CLASS_SEPARATOR;
     if (ClassifyCharacter(aIndex - 1, false) != CHAR_CLASS_WORD)
       return CHAR_CLASS_SEPARATOR;
     // If the previous charatcer is a word-char, make sure that it's not a
     // special dot character.
     if (mDOMWordText[aIndex - 1] == '.') return CHAR_CLASS_SEPARATOR;
 
     // now we know left char is a word-char, check the right-hand character
-    if (aIndex == int32_t(mDOMWordText.Length()) - 1)
+    if (aIndex == int32_t(mDOMWordText.Length() - 1)) {
+      if (mDOMWordText[aIndex] == '\'' || mDOMWordText[aIndex] == 0x2019) {
+        nsUGenCategory prevCategory =
+            mozilla::unicode::GetGenCategory(mDOMWordText[aIndex - 1]);
+        if (prevCategory == nsUGenCategory::kLetter ||
+            prevCategory == nsUGenCategory::kNumber) {
+          // If single quotation mark is last, we don't return separator yet.
+          return CHAR_CLASS_WORD;
+        }
+      }
       return CHAR_CLASS_SEPARATOR;
+    }
+
     if (ClassifyCharacter(aIndex + 1, false) != CHAR_CLASS_WORD)
       return CHAR_CLASS_SEPARATOR;
     // If the next charatcer is a word-char, make sure that it's not a
     // special dot character.
     if (mDOMWordText[aIndex + 1] == '.') return CHAR_CLASS_SEPARATOR;
 
     // char on either side is a word, this counts as a word
     return CHAR_CLASS_WORD;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -10,17 +10,16 @@
 
 #include "frontend/BytecodeEmitter.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
-#include "mozilla/ReverseIterator.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Variant.h"
 
 #include <string.h>
 
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
@@ -86,34 +85,16 @@ static bool ParseNodeRequiresSpecialLine
   // handling to avoid strange stepping behavior.
   // Functions usually shouldn't have location information (bug 1431202).
 
   ParseNodeKind kind = pn->getKind();
   return kind == ParseNodeKind::WhileStmt || kind == ParseNodeKind::ForStmt ||
          kind == ParseNodeKind::Function;
 }
 
-BytecodeEmitter::BytecodeSection::BytecodeSection(JSContext* cx,
-                                                  uint32_t lineNum)
-    : code_(cx),
-      notes_(cx),
-      tryNoteList_(cx),
-      scopeNoteList_(cx),
-      resumeOffsetList_(cx),
-      currentLine_(lineNum) {}
-
-BytecodeEmitter::PerScriptData::PerScriptData(JSContext* cx)
-    : scopeList_(cx),
-      numberList_(cx),
-      atomIndices_(cx->frontendCollectionPool()) {}
-
-bool BytecodeEmitter::PerScriptData::init(JSContext* cx) {
-  return atomIndices_.acquire(cx);
-}
-
 BytecodeEmitter::BytecodeEmitter(
     BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
     Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode,
     FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
     : sc(sc),
       cx(sc->cx_),
       parent(parent),
       script(cx, script),
@@ -255,31 +236,16 @@ bool BytecodeEmitter::emitCheck(JSOp op,
     static_assert(MaxBytecodeLength + 1 /* this */ + ARGC_LIMIT <= UINT32_MAX,
                   "numICEntries must not overflow");
     bytecodeSection().incrementNumICEntries();
   }
 
   return true;
 }
 
-void BytecodeEmitter::BytecodeSection::updateDepth(ptrdiff_t target) {
-  jsbytecode* pc = code(target);
-
-  int nuses = StackUses(pc);
-  int ndefs = StackDefs(pc);
-
-  stackDepth_ -= nuses;
-  MOZ_ASSERT(stackDepth_ >= 0);
-  stackDepth_ += ndefs;
-
-  if ((uint32_t)stackDepth_ > maxStackDepth_) {
-    maxStackDepth_ = stackDepth_;
-  }
-}
-
 #ifdef DEBUG
 bool BytecodeEmitter::checkStrictOrSloppy(JSOp op) {
   if (IsCheckStrictOp(op) && !sc->strict()) {
     return false;
   }
   if (IsCheckSloppyOp(op) && sc->strict()) {
     return false;
   }
@@ -9557,137 +9523,16 @@ bool BytecodeEmitter::setSrcNoteOffset(u
 void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) {
   unsigned count = bytecodeSection().notes().length();
   // nsrcnotes includes SN_MAKE_TERMINATOR in addition to the srcnotes.
   MOZ_ASSERT(nsrcnotes == count + 1);
   PodCopy(destination, bytecodeSection().notes().begin(), count);
   SN_MAKE_TERMINATOR(&destination[count]);
 }
 
-void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i].init(vector[i]);
-  }
-}
-
-/*
- * Find the index of the given object for code generator.
- *
- * Since the emitter refers to each parsed object only once, for the index we
- * use the number of already indexed objects. We also add the object to a list
- * to convert the list to a fixed-size array when we complete code generation,
- * see js::CGObjectList::finish below.
- */
-unsigned CGObjectList::add(ObjectBox* objbox) {
-  MOZ_ASSERT(objbox->isObjectBox());
-  MOZ_ASSERT(!objbox->emitLink);
-  objbox->emitLink = lastbox;
-  lastbox = objbox;
-  return length++;
-}
-
-void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
-  MOZ_ASSERT(length <= INDEX_LIMIT);
-  MOZ_ASSERT(length == array.size());
-
-  ObjectBox* objbox = lastbox;
-  for (GCPtrObject& obj : mozilla::Reversed(array)) {
-    MOZ_ASSERT(obj == nullptr);
-    MOZ_ASSERT(objbox->object()->isTenured());
-    obj.init(objbox->object());
-    objbox = objbox->emitLink;
-  }
-}
-
-void CGObjectList::finishInnerFunctions() {
-  ObjectBox* objbox = lastbox;
-  while (objbox) {
-    if (objbox->isFunctionBox()) {
-      objbox->asFunctionBox()->finish();
-    }
-    objbox = objbox->emitLink;
-  }
-}
-
-void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
-  MOZ_ASSERT(length() <= INDEX_LIMIT);
-  MOZ_ASSERT(length() == array.size());
-
-  for (uint32_t i = 0; i < length(); i++) {
-    array[i].init(vector[i]);
-  }
-}
-
-bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
-                           size_t start, size_t end) {
-  MOZ_ASSERT(start <= end);
-  MOZ_ASSERT(size_t(uint32_t(start)) == start);
-  MOZ_ASSERT(size_t(uint32_t(end)) == end);
-
-  // Offsets are given relative to sections, but we only expect main-section
-  // to have TryNotes. In finish() we will fixup base offset.
-
-  JSTryNote note;
-  note.kind = kind;
-  note.stackDepth = stackDepth;
-  note.start = uint32_t(start);
-  note.length = uint32_t(end - start);
-
-  return list.append(note);
-}
-
-void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i] = list[i];
-  }
-}
-
-bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
-                             uint32_t parent) {
-  CGScopeNote note;
-  mozilla::PodZero(&note);
-
-  // Offsets are given relative to sections. In finish() we will fixup base
-  // offset if needed.
-
-  note.index = scopeIndex;
-  note.start = offset;
-  note.parent = parent;
-
-  return list.append(note);
-}
-
-void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
-  MOZ_ASSERT(index < length());
-  MOZ_ASSERT(list[index].length == 0);
-  list[index].end = offset;
-}
-
-void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    MOZ_ASSERT(list[i].end >= list[i].start);
-    list[i].length = list[i].end - list[i].start;
-    array[i] = list[i];
-  }
-}
-
-void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
-  MOZ_ASSERT(length() == array.size());
-
-  for (unsigned i = 0; i < length(); i++) {
-    array[i] = list[i];
-  }
-}
-
 const JSSrcNoteSpec js_SrcNoteSpec[] = {
 #define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity},
     FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC)
 #undef DEFINE_SRC_NOTE_SPEC
 };
 
 static int SrcNoteArity(jssrcnote* sn) {
   MOZ_ASSERT(SN_TYPE(sn) < SRC_LAST);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -9,105 +9,32 @@
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Span.h"
 
 #include "ds/InlineTable.h"
 #include "frontend/BCEParserHandle.h"
+#include "frontend/BytecodeSection.h"  // BytecodeSection, PerScriptData
 #include "frontend/DestructuringFlavor.h"
 #include "frontend/EitherParser.h"
 #include "frontend/JumpList.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "frontend/ValueUsage.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 
 namespace js {
 namespace frontend {
 
-class CGNumberList {
-  Rooted<ValueVector> vector;
-
- public:
-  explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
-  MOZ_MUST_USE bool append(const Value& v) { return vector.append(v); }
-  size_t length() const { return vector.length(); }
-  void finish(mozilla::Span<GCPtrValue> array);
-};
-
-struct CGObjectList {
-  uint32_t length;    /* number of emitted so far objects */
-  ObjectBox* lastbox; /* last emitted object */
-
-  CGObjectList() : length(0), lastbox(nullptr) {}
-
-  unsigned add(ObjectBox* objbox);
-  void finish(mozilla::Span<GCPtrObject> array);
-  void finishInnerFunctions();
-};
-
-struct MOZ_STACK_CLASS CGScopeList {
-  Rooted<GCVector<Scope*>> vector;
-
-  explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
-
-  bool append(Scope* scope) { return vector.append(scope); }
-  uint32_t length() const { return vector.length(); }
-  void finish(mozilla::Span<GCPtrScope> array);
-};
-
-struct CGTryNoteList {
-  Vector<JSTryNote> list;
-  explicit CGTryNoteList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
-                           size_t start, size_t end);
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<JSTryNote> array);
-};
-
-struct CGScopeNote : public ScopeNote {
-  // The end offset. Used to compute the length.
-  uint32_t end;
-};
-
-struct CGScopeNoteList {
-  Vector<CGScopeNote> list;
-  explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
-                           uint32_t parent);
-  void recordEnd(uint32_t index, uint32_t offse);
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<ScopeNote> array);
-};
-
-struct CGResumeOffsetList {
-  Vector<uint32_t> list;
-  explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
-
-  MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
-  size_t length() const { return list.length(); }
-  void finish(mozilla::Span<uint32_t> array);
-};
-
-static constexpr size_t MaxBytecodeLength = INT32_MAX;
-static constexpr size_t MaxSrcNotesLength = INT32_MAX;
-
-// Have a few inline elements, so as to avoid heap allocation for tiny
-// sequences.  See bug 1390526.
-typedef Vector<jsbytecode, 64> BytecodeVector;
-typedef Vector<jssrcnote, 64> SrcNotesVector;
-
 class CallOrNewEmitter;
 class ElemOpEmitter;
 class EmitterScope;
 class NestableControl;
 class PropertyEmitter;
 class TDZCheckCache;
 class TryEmitter;
 
@@ -122,259 +49,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
 
   // The JSScript we're ultimately producing.
   Rooted<JSScript*> script;
 
   // The lazy script if mode is LazyFunction, nullptr otherwise.
   Rooted<LazyScript*> lazyScript;
 
  private:
-  // Bytecode and all data directly associated with specific opcode/index inside
-  // bytecode is stored in this class.
-  class BytecodeSection {
-   public:
-    BytecodeSection(JSContext* cx, uint32_t lineNum);
-
-    // ---- Bytecode ----
-
-    BytecodeVector& code() { return code_; }
-    const BytecodeVector& code() const { return code_; }
-
-    jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
-    ptrdiff_t offset() const { return code_.end() - code_.begin(); }
-
-    // ---- Source notes ----
-
-    SrcNotesVector& notes() { return notes_; }
-    const SrcNotesVector& notes() const { return notes_; }
-
-    ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
-    void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
-
-    // ---- Jump ----
-
-    ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
-    void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
-
-    // Check if the last emitted opcode is a jump target.
-    bool lastOpcodeIsJumpTarget() const {
-      return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
-    }
-
-    // JumpTarget should not be part of the emitted statement, as they can be
-    // aliased by multiple statements. If we included the jump target as part of
-    // the statement we might have issues where the enclosing statement might
-    // not contain all the opcodes of the enclosed statements.
-    ptrdiff_t lastNonJumpTargetOffset() const {
-      return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
-    }
-
-    // ---- Stack ----
-
-    int32_t stackDepth() const { return stackDepth_; }
-    void setStackDepth(int32_t depth) { stackDepth_ = depth; }
-
-    uint32_t maxStackDepth() const { return maxStackDepth_; }
-
-    void updateDepth(ptrdiff_t target);
-
-    // ---- Try notes ----
-
-    CGTryNoteList& tryNoteList() { return tryNoteList_; };
-    const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
-
-    // ---- Scope ----
-
-    CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
-    const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
-
-    // ---- Generator ----
-
-    CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
-    const CGResumeOffsetList& resumeOffsetList() const {
-      return resumeOffsetList_;
-    }
-
-    uint32_t numYields() const { return numYields_; }
-    void addNumYields() { numYields_++; }
-
-    // ---- Line and column ----
-
-    uint32_t currentLine() const { return currentLine_; }
-    uint32_t lastColumn() const { return lastColumn_; }
-    void setCurrentLine(uint32_t line) {
-      currentLine_ = line;
-      lastColumn_ = 0;
-    }
-    void setLastColumn(uint32_t column) { lastColumn_ = column; }
-
-    void updateSeparatorPosition() {
-      lastSeparatorOffet_ = code().length();
-      lastSeparatorLine_ = currentLine_;
-      lastSeparatorColumn_ = lastColumn_;
-    }
-
-    void updateSeparatorPositionIfPresent() {
-      if (lastSeparatorOffet_ == code().length()) {
-        lastSeparatorLine_ = currentLine_;
-        lastSeparatorColumn_ = lastColumn_;
-      }
-    }
-
-    bool isDuplicateLocation() const {
-      return lastSeparatorLine_ == currentLine_ &&
-             lastSeparatorColumn_ == lastColumn_;
-    }
-
-    // ---- JIT ----
-
-    uint32_t numICEntries() const { return numICEntries_; }
-    void incrementNumICEntries() {
-      MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
-      numICEntries_++;
-    }
-    void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
-
-    uint32_t numTypeSets() const { return numTypeSets_; }
-    void incrementNumTypeSets() {
-      MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
-      numTypeSets_++;
-    }
-
-   private:
-    // ---- Bytecode ----
-
-    // Bytecode.
-    BytecodeVector code_;
-
-    // ---- Source notes ----
-
-    // Source notes
-    SrcNotesVector notes_;
-
-    // Code offset for last source note
-    ptrdiff_t lastNoteOffset_ = 0;
-
-    // ---- Jump ----
-
-    // Last jump target emitted.
-    JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
-
-    // ---- Stack ----
-
-    // Maximum number of expression stack slots so far.
-    uint32_t maxStackDepth_ = 0;
-
-    // Current stack depth in script frame.
-    int32_t stackDepth_ = 0;
-
-    // ---- Try notes ----
-
-    // List of emitted try notes.
-    CGTryNoteList tryNoteList_;
-
-    // ---- Scope ----
-
-    // List of emitted block scope notes.
-    CGScopeNoteList scopeNoteList_;
-
-    // ---- Generator ----
-
-    // Certain ops (yield, await, gosub) have an entry in the script's
-    // resumeOffsets list. This can be used to map from the op's resumeIndex to
-    // the bytecode offset of the next pc. This indirection makes it easy to
-    // resume in the JIT (because BaselineScript stores a resumeIndex => native
-    // code array).
-    CGResumeOffsetList resumeOffsetList_;
-
-    // Number of yield instructions emitted. Does not include JSOP_AWAIT.
-    uint32_t numYields_ = 0;
-
-    // ---- Line and column ----
-
-    // Line number for srcnotes.
-    //
-    // WARNING: If this becomes out of sync with already-emitted srcnotes,
-    // we can get undefined behavior.
-    uint32_t currentLine_;
-
-    // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
-    // opcode.
-    //
-    // WARNING: If this becomes out of sync with already-emitted srcnotes,
-    // we can get undefined behavior.
-    uint32_t lastColumn_ = 0;
-
-    // The offset, line and column numbers of the last opcode for the
-    // breakpoint for step execution.
-    uint32_t lastSeparatorOffet_ = 0;
-    uint32_t lastSeparatorLine_ = 0;
-    uint32_t lastSeparatorColumn_ = 0;
-
-    // ---- JIT ----
-
-    // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
-    // and, if the script is a function, for |this| and each formal argument.
-    uint32_t numICEntries_ = 0;
-
-    // Number of JOF_TYPESET opcodes generated.
-    uint32_t numTypeSets_ = 0;
-  };
-
   BytecodeSection bytecodeSection_;
 
  public:
   BytecodeSection& bytecodeSection() { return bytecodeSection_; }
   const BytecodeSection& bytecodeSection() const { return bytecodeSection_; }
 
  private:
-  // Data that is not directly associated with specific opcode/index inside
-  // bytecode, but referred from bytecode is stored in this class.
-  class PerScriptData {
-   public:
-    explicit PerScriptData(JSContext* cx);
-
-    MOZ_MUST_USE bool init(JSContext* cx);
-
-    // ---- Scope ----
-
-    CGScopeList& scopeList() { return scopeList_; }
-    const CGScopeList& scopeList() const { return scopeList_; }
-
-    // ---- Literals ----
-
-    CGNumberList& numberList() { return numberList_; }
-    const CGNumberList& numberList() const { return numberList_; }
-
-    CGObjectList& objectList() { return objectList_; }
-    const CGObjectList& objectList() const { return objectList_; }
-
-    PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
-    const PooledMapPtr<AtomIndexMap>& atomIndices() const {
-      return atomIndices_;
-    }
-
-   private:
-    // ---- Scope ----
-
-    // List of emitted scopes.
-    CGScopeList scopeList_;
-
-    // ---- Literals ----
-
-    // List of double and bigint values used by script.
-    CGNumberList numberList_;
-
-    // List of emitted objects.
-    CGObjectList objectList_;
-
-    // Map from atom to index.
-    PooledMapPtr<AtomIndexMap> atomIndices_;
-  };
-
   PerScriptData perScriptData_;
 
  public:
   PerScriptData& perScriptData() { return perScriptData_; }
   const PerScriptData& perScriptData() const { return perScriptData_; }
 
  private:
   // switchToMain sets this to the bytecode offset of the main section.
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BytecodeSection.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 "frontend/BytecodeSection.h"
+
+#include "mozilla/Assertions.h"       // MOZ_ASSERT
+#include "mozilla/PodOperations.h"    // PodZero
+#include "mozilla/ReverseIterator.h"  // mozilla::Reversed
+
+#include "frontend/ParseNode.h"      // ObjectBox
+#include "frontend/SharedContext.h"  // FunctionBox
+#include "vm/BytecodeUtil.h"         // INDEX_LIMIT, StackUses, StackDefs
+#include "vm/JSContext.h"            // JSContext
+
+using namespace js;
+using namespace js::frontend;
+
+void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i].init(vector[i]);
+  }
+}
+
+/*
+ * Find the index of the given object for code generator.
+ *
+ * Since the emitter refers to each parsed object only once, for the index we
+ * use the number of already indexed objects. We also add the object to a list
+ * to convert the list to a fixed-size array when we complete code generation,
+ * see js::CGObjectList::finish below.
+ */
+unsigned CGObjectList::add(ObjectBox* objbox) {
+  MOZ_ASSERT(objbox->isObjectBox());
+  MOZ_ASSERT(!objbox->emitLink);
+  objbox->emitLink = lastbox;
+  lastbox = objbox;
+  return length++;
+}
+
+void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
+  MOZ_ASSERT(length <= INDEX_LIMIT);
+  MOZ_ASSERT(length == array.size());
+
+  ObjectBox* objbox = lastbox;
+  for (GCPtrObject& obj : mozilla::Reversed(array)) {
+    MOZ_ASSERT(obj == nullptr);
+    MOZ_ASSERT(objbox->object()->isTenured());
+    obj.init(objbox->object());
+    objbox = objbox->emitLink;
+  }
+}
+
+void CGObjectList::finishInnerFunctions() {
+  ObjectBox* objbox = lastbox;
+  while (objbox) {
+    if (objbox->isFunctionBox()) {
+      objbox->asFunctionBox()->finish();
+    }
+    objbox = objbox->emitLink;
+  }
+}
+
+void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
+  MOZ_ASSERT(length() <= INDEX_LIMIT);
+  MOZ_ASSERT(length() == array.size());
+
+  for (uint32_t i = 0; i < length(); i++) {
+    array[i].init(vector[i]);
+  }
+}
+
+bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
+                           size_t start, size_t end) {
+  MOZ_ASSERT(start <= end);
+  MOZ_ASSERT(size_t(uint32_t(start)) == start);
+  MOZ_ASSERT(size_t(uint32_t(end)) == end);
+
+  // Offsets are given relative to sections, but we only expect main-section
+  // to have TryNotes. In finish() we will fixup base offset.
+
+  JSTryNote note;
+  note.kind = kind;
+  note.stackDepth = stackDepth;
+  note.start = uint32_t(start);
+  note.length = uint32_t(end - start);
+
+  return list.append(note);
+}
+
+void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i] = list[i];
+  }
+}
+
+bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
+                             uint32_t parent) {
+  CGScopeNote note;
+  mozilla::PodZero(&note);
+
+  // Offsets are given relative to sections. In finish() we will fixup base
+  // offset if needed.
+
+  note.index = scopeIndex;
+  note.start = offset;
+  note.parent = parent;
+
+  return list.append(note);
+}
+
+void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
+  MOZ_ASSERT(index < length());
+  MOZ_ASSERT(list[index].length == 0);
+  list[index].end = offset;
+}
+
+void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    MOZ_ASSERT(list[i].end >= list[i].start);
+    list[i].length = list[i].end - list[i].start;
+    array[i] = list[i];
+  }
+}
+
+void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
+  MOZ_ASSERT(length() == array.size());
+
+  for (unsigned i = 0; i < length(); i++) {
+    array[i] = list[i];
+  }
+}
+
+BytecodeSection::BytecodeSection(JSContext* cx, uint32_t lineNum)
+    : code_(cx),
+      notes_(cx),
+      tryNoteList_(cx),
+      scopeNoteList_(cx),
+      resumeOffsetList_(cx),
+      currentLine_(lineNum) {}
+
+void BytecodeSection::updateDepth(ptrdiff_t target) {
+  jsbytecode* pc = code(target);
+
+  int nuses = StackUses(pc);
+  int ndefs = StackDefs(pc);
+
+  stackDepth_ -= nuses;
+  MOZ_ASSERT(stackDepth_ >= 0);
+  stackDepth_ += ndefs;
+
+  if (uint32_t(stackDepth_) > maxStackDepth_) {
+    maxStackDepth_ = stackDepth_;
+  }
+}
+
+PerScriptData::PerScriptData(JSContext* cx)
+    : scopeList_(cx),
+      numberList_(cx),
+      atomIndices_(cx->frontendCollectionPool()) {}
+
+bool PerScriptData::init(JSContext* cx) { return atomIndices_.acquire(cx); }
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BytecodeSection.h
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_BytecodeSection_h
+#define frontend_BytecodeSection_h
+
+#include "mozilla/Attributes.h"  // MOZ_MUST_USE, MOZ_STACK_CLASS
+#include "mozilla/Span.h"        // mozilla::Span
+
+#include <stddef.h>  // ptrdiff_t, size_t
+#include <stdint.h>  // uint16_t, int32_t, uint32_t
+
+#include "NamespaceImports.h"          // ValueVector
+#include "frontend/JumpList.h"         // JumpTarget
+#include "frontend/NameCollections.h"  // AtomIndexMap, PooledMapPtr
+#include "frontend/SourceNotes.h"      // jssrcnote
+#include "gc/Barrier.h"                // GCPtrObject, GCPtrScope, GCPtrValue
+#include "gc/Rooting.h"                // JS::Rooted
+#include "js/GCVector.h"               // GCVector
+#include "js/TypeDecls.h"              // jsbytecode
+#include "js/Value.h"                  // JS::Vector
+#include "js/Vector.h"                 // Vector
+#include "vm/JSScript.h"               // JSTryNote, JSTryNoteKind, ScopeNote
+#include "vm/Opcodes.h"                // JSOP_*
+
+struct JSContext;
+
+namespace js {
+
+class Scope;
+
+namespace frontend {
+
+class ObjectBox;
+
+class CGNumberList {
+  JS::Rooted<ValueVector> vector;
+
+ public:
+  explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
+  MOZ_MUST_USE bool append(const JS::Value& v) { return vector.append(v); }
+  size_t length() const { return vector.length(); }
+  void finish(mozilla::Span<GCPtrValue> array);
+};
+
+struct CGObjectList {
+  // Number of emitted so far objects.
+  uint32_t length;
+  // Last emitted object.
+  ObjectBox* lastbox;
+
+  CGObjectList() : length(0), lastbox(nullptr) {}
+
+  unsigned add(ObjectBox* objbox);
+  void finish(mozilla::Span<GCPtrObject> array);
+  void finishInnerFunctions();
+};
+
+struct MOZ_STACK_CLASS CGScopeList {
+  JS::Rooted<GCVector<Scope*>> vector;
+
+  explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
+
+  bool append(Scope* scope) { return vector.append(scope); }
+  uint32_t length() const { return vector.length(); }
+  void finish(mozilla::Span<GCPtrScope> array);
+};
+
+struct CGTryNoteList {
+  Vector<JSTryNote> list;
+  explicit CGTryNoteList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
+                           size_t start, size_t end);
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<JSTryNote> array);
+};
+
+struct CGScopeNote : public ScopeNote {
+  // The end offset. Used to compute the length.
+  uint32_t end;
+};
+
+struct CGScopeNoteList {
+  Vector<CGScopeNote> list;
+  explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
+                           uint32_t parent);
+  void recordEnd(uint32_t index, uint32_t offse);
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<ScopeNote> array);
+};
+
+struct CGResumeOffsetList {
+  Vector<uint32_t> list;
+  explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
+
+  MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
+  size_t length() const { return list.length(); }
+  void finish(mozilla::Span<uint32_t> array);
+};
+
+
+static constexpr size_t MaxBytecodeLength = INT32_MAX;
+static constexpr size_t MaxSrcNotesLength = INT32_MAX;
+
+// Have a few inline elements, so as to avoid heap allocation for tiny
+// sequences.  See bug 1390526.
+typedef Vector<jsbytecode, 64> BytecodeVector;
+typedef Vector<jssrcnote, 64> SrcNotesVector;
+
+// Bytecode and all data directly associated with specific opcode/index inside
+// bytecode is stored in this class.
+class BytecodeSection {
+ public:
+  BytecodeSection(JSContext* cx, uint32_t lineNum);
+
+  // ---- Bytecode ----
+
+  BytecodeVector& code() { return code_; }
+  const BytecodeVector& code() const { return code_; }
+
+  jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
+  ptrdiff_t offset() const { return code_.end() - code_.begin(); }
+
+  // ---- Source notes ----
+
+  SrcNotesVector& notes() { return notes_; }
+  const SrcNotesVector& notes() const { return notes_; }
+
+  ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
+  void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
+
+  // ---- Jump ----
+
+  ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
+  void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
+
+  // Check if the last emitted opcode is a jump target.
+  bool lastOpcodeIsJumpTarget() const {
+    return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
+  }
+
+  // JumpTarget should not be part of the emitted statement, as they can be
+  // aliased by multiple statements. If we included the jump target as part of
+  // the statement we might have issues where the enclosing statement might
+  // not contain all the opcodes of the enclosed statements.
+  ptrdiff_t lastNonJumpTargetOffset() const {
+    return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
+  }
+
+  // ---- Stack ----
+
+  int32_t stackDepth() const { return stackDepth_; }
+  void setStackDepth(int32_t depth) { stackDepth_ = depth; }
+
+  uint32_t maxStackDepth() const { return maxStackDepth_; }
+
+  void updateDepth(ptrdiff_t target);
+
+  // ---- Try notes ----
+
+  CGTryNoteList& tryNoteList() { return tryNoteList_; };
+  const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
+
+  // ---- Scope ----
+
+  CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
+  const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
+
+  // ---- Generator ----
+
+  CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
+  const CGResumeOffsetList& resumeOffsetList() const {
+    return resumeOffsetList_;
+  }
+
+  uint32_t numYields() const { return numYields_; }
+  void addNumYields() { numYields_++; }
+
+  // ---- Line and column ----
+
+  uint32_t currentLine() const { return currentLine_; }
+  uint32_t lastColumn() const { return lastColumn_; }
+  void setCurrentLine(uint32_t line) {
+    currentLine_ = line;
+    lastColumn_ = 0;
+  }
+  void setLastColumn(uint32_t column) { lastColumn_ = column; }
+
+  void updateSeparatorPosition() {
+    lastSeparatorOffet_ = code().length();
+    lastSeparatorLine_ = currentLine_;
+    lastSeparatorColumn_ = lastColumn_;
+  }
+
+  void updateSeparatorPositionIfPresent() {
+    if (lastSeparatorOffet_ == code().length()) {
+      lastSeparatorLine_ = currentLine_;
+      lastSeparatorColumn_ = lastColumn_;
+    }
+  }
+
+  bool isDuplicateLocation() const {
+    return lastSeparatorLine_ == currentLine_ &&
+           lastSeparatorColumn_ == lastColumn_;
+  }
+
+  // ---- JIT ----
+
+  uint32_t numICEntries() const { return numICEntries_; }
+  void incrementNumICEntries() {
+    MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
+    numICEntries_++;
+  }
+  void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
+
+  uint32_t numTypeSets() const { return numTypeSets_; }
+  void incrementNumTypeSets() {
+    MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
+    numTypeSets_++;
+  }
+
+ private:
+  // ---- Bytecode ----
+
+  // Bytecode.
+  BytecodeVector code_;
+
+  // ---- Source notes ----
+
+  // Source notes
+  SrcNotesVector notes_;
+
+  // Code offset for last source note
+  ptrdiff_t lastNoteOffset_ = 0;
+
+  // ---- Jump ----
+
+  // Last jump target emitted.
+  JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
+
+  // ---- Stack ----
+
+  // Maximum number of expression stack slots so far.
+  uint32_t maxStackDepth_ = 0;
+
+  // Current stack depth in script frame.
+  int32_t stackDepth_ = 0;
+
+  // ---- Try notes ----
+
+  // List of emitted try notes.
+  CGTryNoteList tryNoteList_;
+
+  // ---- Scope ----
+
+  // List of emitted block scope notes.
+  CGScopeNoteList scopeNoteList_;
+
+  // ---- Generator ----
+
+  // Certain ops (yield, await, gosub) have an entry in the script's
+  // resumeOffsets list. This can be used to map from the op's resumeIndex to
+  // the bytecode offset of the next pc. This indirection makes it easy to
+  // resume in the JIT (because BaselineScript stores a resumeIndex => native
+  // code array).
+  CGResumeOffsetList resumeOffsetList_;
+
+  // Number of yield instructions emitted. Does not include JSOP_AWAIT.
+  uint32_t numYields_ = 0;
+
+  // ---- Line and column ----
+
+  // Line number for srcnotes.
+  //
+  // WARNING: If this becomes out of sync with already-emitted srcnotes,
+  // we can get undefined behavior.
+  uint32_t currentLine_;
+
+  // Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
+  // opcode.
+  //
+  // WARNING: If this becomes out of sync with already-emitted srcnotes,
+  // we can get undefined behavior.
+  uint32_t lastColumn_ = 0;
+
+  // The offset, line and column numbers of the last opcode for the
+  // breakpoint for step execution.
+  uint32_t lastSeparatorOffet_ = 0;
+  uint32_t lastSeparatorLine_ = 0;
+  uint32_t lastSeparatorColumn_ = 0;
+
+  // ---- JIT ----
+
+  // Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
+  // and, if the script is a function, for |this| and each formal argument.
+  uint32_t numICEntries_ = 0;
+
+  // Number of JOF_TYPESET opcodes generated.
+  uint32_t numTypeSets_ = 0;
+};
+
+// Data that is not directly associated with specific opcode/index inside
+// bytecode, but referred from bytecode is stored in this class.
+class PerScriptData {
+ public:
+  explicit PerScriptData(JSContext* cx);
+
+  MOZ_MUST_USE bool init(JSContext* cx);
+
+  // ---- Scope ----
+
+  CGScopeList& scopeList() { return scopeList_; }
+  const CGScopeList& scopeList() const { return scopeList_; }
+
+  // ---- Literals ----
+
+  CGNumberList& numberList() { return numberList_; }
+  const CGNumberList& numberList() const { return numberList_; }
+
+  CGObjectList& objectList() { return objectList_; }
+  const CGObjectList& objectList() const { return objectList_; }
+
+  PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
+  const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; }
+
+ private:
+  // ---- Scope ----
+
+  // List of emitted scopes.
+  CGScopeList scopeList_;
+
+  // ---- Literals ----
+
+  // List of double and bigint values used by script.
+  CGNumberList numberList_;
+
+  // List of emitted objects.
+  CGObjectList objectList_;
+
+  // Map from atom to index.
+  PooledMapPtr<AtomIndexMap> atomIndices_;
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_BytecodeSection_h */
--- a/js/src/frontend/moz.build
+++ b/js/src/frontend/moz.build
@@ -22,16 +22,17 @@ ReservedWordsGenerated = GENERATED_FILES
 ReservedWordsGenerated.script = 'GenerateReservedWords.py'
 ReservedWordsGenerated.inputs += ['ReservedWords.h']
 
 
 UNIFIED_SOURCES += [
     'BytecodeCompiler.cpp',
     'BytecodeControlStructures.cpp',
     'BytecodeEmitter.cpp',
+    'BytecodeSection.cpp',
     'CallOrNewEmitter.cpp',
     'CForEmitter.cpp',
     'DefaultEmitter.cpp',
     'DoWhileEmitter.cpp',
     'ElemOpEmitter.cpp',
     'EmitterScope.cpp',
     'ExpressionStatementEmitter.cpp',
     'FoldConstants.cpp',
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -236,17 +236,17 @@ void Zone::discardJitCode(FreeOp* fop,
       } else {
         jit::FinishDiscardBaselineScript(fop, script);
       }
     }
 
     // Warm-up counter for scripts are reset on GC. After discarding code we
     // need to let it warm back up to get information such as which
     // opcodes are setting array holes or accessing getter properties.
-    script->resetWarmUpCounter();
+    script->resetWarmUpCounterForGC();
 
     // Clear the BaselineScript's control flow graph. The LifoAlloc is purged
     // below.
     if (script->hasBaselineScript()) {
       script->baselineScript()->setControlFlowGraph(nullptr);
     }
 
     // Try to release the script's TypeScript. This should happen after
--- a/js/src/jit-test/lib/jitopts.js
+++ b/js/src/jit-test/lib/jitopts.js
@@ -51,18 +51,18 @@ var Opts_IonEagerNoOffthreadCompilation 
       'baseline.enable': 1,
       'baseline.warmup.trigger': 0,
       'offthread-compilation.enable': 0,
     };
 
 var Opts_Ion2NoOffthreadCompilation =
     {
       'ion.enable': 1,
-      'ion.warmup.trigger': 2,
-      'ion.full.warmup.trigger': 2,
+      'ion.warmup.trigger': 3,
+      'ion.full.warmup.trigger': 3,
       'baseline.enable': 1,
       'baseline.warmup.trigger': 1,
       'offthread-compilation.enable': 0
     };
 
 var Opts_NoJits =
     {
       'ion.enable': 0,
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -513,8 +513,32 @@ wasmAssert(`(module
     // TEST THIS LAST
 
     // "value" is deletable
     assertEq(delete Global.prototype.value, true);
     assertEq("value" in Global.prototype, false);
 
     // ADD NO MORE TESTS HERE!
 }
+
+// Standard wat syntax: the parens around the initializer expression are
+// optional.
+{
+    let i1 = wasmEvalText(
+        `(module
+           (global $g i32 i32.const 37)
+           (func (export "f") (result i32) (global.get $g)))`);
+    assertEq(i1.exports.f(), 37);
+
+    let i2 = wasmEvalText(
+        `(module
+           (global $g (mut f64) f64.const 42.0)
+           (func (export "f") (result f64) (global.get $g)))`);
+    assertEq(i2.exports.f(), 42);
+
+    let i3 = wasmEvalText(
+        `(module
+           (global $x (import "m" "x") i32)
+           (global $g i32 global.get $x)
+           (func (export "f") (result i32) (global.get $g)))`,
+        {m:{x:86}});
+    assertEq(i3.exports.f(), 86);
+}
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -761,31 +761,45 @@ bool BaselineCodeGen<Handler>::emitStack
   }
 
   handler.markLastRetAddrEntryKind(RetAddrEntry::Kind::StackCheck);
 
   masm.bind(&skipCall);
   return true;
 }
 
+static void EmitCallFrameIsDebuggeeCheck(MacroAssembler& masm) {
+  masm.Push(BaselineFrameReg);
+  masm.setupUnalignedABICall(R0.scratchReg());
+  masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
+  masm.passABIArg(R0.scratchReg());
+  masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
+  masm.Pop(BaselineFrameReg);
+}
+
 template <>
 void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
   if (handler.compileDebugInstrumentation()) {
-    masm.Push(BaselineFrameReg);
-    masm.setupUnalignedABICall(R0.scratchReg());
-    masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
-    masm.passABIArg(R0.scratchReg());
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
-    masm.Pop(BaselineFrameReg);
+    EmitCallFrameIsDebuggeeCheck(masm);
   }
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitIsDebuggeeCheck() {
-  MOZ_CRASH("NYI: interpreter emitIsDebuggeeCheck");
+  // Use a toggled jump to call FrameIsDebuggeeCheck only if the debugger is
+  // enabled.
+  //
+  // TODO(bug 1522394): consider having a cx->realm->isDebuggee guard before the
+  // call. Consider moving the callWithABI out-of-line.
+
+  Label skipCheck;
+  CodeOffset toggleOffset = masm.toggledJump(&skipCheck);
+  EmitCallFrameIsDebuggeeCheck(masm);
+  masm.bind(&skipCheck);
+  handler.setDebuggeeCheckOffset(toggleOffset);
 }
 
 template <>
 void BaselineCompilerCodeGen::subtractScriptSlotsSize(Register reg,
                                                       Register scratch) {
   uint32_t slotsSize = handler.script()->nslots() * sizeof(Value);
   masm.subPtr(Imm32(slotsSize), reg);
 }
@@ -1045,17 +1059,54 @@ void BaselineInterpreterCodeGen::emitPre
 
 template <>
 void BaselineCompilerCodeGen::emitInitFrameFields() {
   masm.store32(Imm32(0), frame.addressOfFlags());
 }
 
 template <>
 void BaselineInterpreterCodeGen::emitInitFrameFields() {
-  MOZ_CRASH("NYI: interpreter emitInitFrameFields");
+  Register scratch1 = R0.scratchReg();
+  Register scratch2 = R2.scratchReg();
+
+  masm.store32(Imm32(BaselineFrame::RUNNING_IN_INTERPRETER),
+               frame.addressOfFlags());
+
+  // Initialize interpreterScript.
+  Label notFunction, done;
+  masm.loadPtr(frame.addressOfCalleeToken(), scratch1);
+  masm.branchTestPtr(Assembler::NonZero, scratch1, Imm32(CalleeTokenScriptBit),
+                     &notFunction);
+  {
+    // CalleeToken_Function or CalleeToken_FunctionConstructing.
+    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch1);
+    masm.loadPtr(Address(scratch1, JSFunction::offsetOfScript()), scratch1);
+    masm.jump(&done);
+  }
+  masm.bind(&notFunction);
+  {
+    // CalleeToken_Script.
+    masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch1);
+  }
+  masm.bind(&done);
+  masm.storePtr(scratch1, frame.addressOfInterpreterScript());
+
+  // Initialize interpreterICEntry.
+  masm.loadPtr(Address(scratch1, JSScript::offsetOfTypes()), scratch2);
+  masm.loadPtr(Address(scratch2, TypeScript::offsetOfICScript()), scratch2);
+  masm.computeEffectiveAddress(Address(scratch2, ICScript::offsetOfICEntries()),
+                               scratch2);
+  masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
+
+  // Initialize interpreterPC.
+  masm.loadPtr(Address(scratch1, JSScript::offsetOfScriptData()), scratch1);
+  masm.load32(Address(scratch1, SharedScriptData::offsetOfCodeOffset()),
+              scratch2);
+  masm.addPtr(scratch2, scratch1);
+  masm.storePtr(scratch1, frame.addressOfInterpreterPC());
 }
 
 template <>
 template <typename F1, typename F2>
 bool BaselineCompilerCodeGen::initEnvironmentChainHelper(
     const F1& initFunctionEnv, const F2& initGlobalOrEvalEnv,
     Register scratch) {
   if (handler.function()) {
@@ -6066,17 +6117,37 @@ bool BaselineCompilerCodeGen::emit_JSOP_
   PCCounts* counts = script->maybeGetPCCounts(handler.pc());
   uint64_t* counterAddr = &counts->numExec();
   masm.inc64(AbsoluteAddress(counterAddr));
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_JSOP_JUMPTARGET() {
-  MOZ_CRASH("NYI: interpreter JSOP_JUMPTARGET");
+  Register scratch1 = R0.scratchReg();
+  Register scratch2 = R1.scratchReg();
+
+  // Load icIndex in scratch1.
+  LoadInt32Operand(masm, PCRegAtStart, scratch1);
+
+  // scratch1 := scratch1 * sizeof(ICEntry)
+  static_assert(sizeof(ICEntry) == 8 || sizeof(ICEntry) == 16,
+                "shift below depends on ICEntry size");
+  uint32_t shift = (sizeof(ICEntry) == 16) ? 4 : 3;
+  masm.lshiftPtr(Imm32(shift), scratch1);
+
+  // Compute ICEntry* and store to frame->interpreterICEntry.
+  loadScript(scratch2);
+  masm.loadPtr(Address(scratch2, JSScript::offsetOfTypes()), scratch2);
+  masm.loadPtr(Address(scratch2, TypeScript::offsetOfICScript()), scratch2);
+  masm.computeEffectiveAddress(
+      BaseIndex(scratch2, scratch1, TimesOne, ICScript::offsetOfICEntries()),
+      scratch2);
+  masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
+  return true;
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_CHECKCLASSHERITAGE() {
   frame.syncStack(0);
 
   // Leave the heritage value on the stack.
   masm.loadValue(frame.addressOfStackValue(-1), R0);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -650,32 +650,38 @@ class BaselineCompiler final : private B
 
   MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
 };
 
 // Interface used by BaselineCodeGen for BaselineInterpreterGenerator.
 class BaselineInterpreterHandler {
   InterpreterFrameInfo frame_;
   Label interpretOp_;
+  CodeOffset debuggeeCheckOffset_;
 
  public:
   using FrameInfoT = InterpreterFrameInfo;
 
   explicit BaselineInterpreterHandler(JSContext* cx, MacroAssembler& masm);
 
   InterpreterFrameInfo& frame() { return frame_; }
 
   Label* interpretOpLabel() { return &interpretOp_; }
 
   // Interpreter doesn't know the script and pc statically.
   jsbytecode* maybePC() const { return nullptr; }
   bool isDefinitelyLastOp() const { return false; }
   JSScript* maybeScript() const { return nullptr; }
   JSFunction* maybeFunction() const { return nullptr; }
 
+  void setDebuggeeCheckOffset(CodeOffset offset) {
+    debuggeeCheckOffset_ = offset;
+  }
+  CodeOffset debuggeeCheckOffset() const { return debuggeeCheckOffset_; }
+
   // Interpreter doesn't need to keep track of RetAddrEntries, so these methods
   // are no-ops.
   MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
                                        uint32_t retOffset) {
     return true;
   }
   void markLastRetAddrEntryKind(RetAddrEntry::Kind) {}
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -356,16 +356,18 @@ class ICScript {
   ICEntry* interpreterICEntryFromPCOffset(uint32_t pcOffset);
 
   ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
   ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
                                     ICEntry* prevLookedUpEntry);
 
   ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
   ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
+
+  static constexpr size_t offsetOfICEntries() { return sizeof(ICScript); }
 };
 
 class ICMonitoredStub;
 class ICMonitoredFallbackStub;
 
 // Constant iterator that traverses arbitrary chains of ICStubs.
 // No requirements are made of the ICStub used to construct this
 // iterator, aside from that the stub be part of a nullptr-terminated
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2247,17 +2247,17 @@ static MethodStatus Compile(JSContext* c
 
   bool recompile = false;
   OptimizationLevel optimizationLevel = GetOptimizationLevel(script, osrPc);
   if (optimizationLevel == OptimizationLevel::DontCompile) {
     return Method_Skipped;
   }
 
   if (!CanLikelyAllocateMoreExecutableMemory()) {
-    script->resetWarmUpCounter();
+    script->resetWarmUpCounterToDelayIonCompilation();
     return Method_Skipped;
   }
 
   if (script->baselineScript()->hasPendingIonBuilder()) {
     LinkIonScript(cx, script);
   }
 
   if (script->hasIonScript()) {
@@ -2508,17 +2508,17 @@ bool jit::IonCompileScriptForBaseline(JS
   bool isLoopEntry = JSOp(*pc) == JSOP_LOOPENTRY;
 
   MOZ_ASSERT(!isLoopEntry || LoopEntryCanIonOsr(pc));
 
   if (!script->canIonCompile()) {
     // TODO: ASSERT that ion-compilation-disabled checker stub doesn't exist.
     // TODO: Clear all optimized stubs.
     // TODO: Add a ion-compilation-disabled checker IC stub
-    script->resetWarmUpCounter();
+    script->resetWarmUpCounterToDelayIonCompilation();
     return true;
   }
 
   MOZ_ASSERT(!script->isIonCompilingOffThread());
 
   // If Ion script exists, but PC is not at a loop entry, then Ion will be
   // entered for this script at an appropriate LOOPENTRY or the next time this
   // function is called.
@@ -2571,17 +2571,17 @@ bool jit::IonCompileScriptForBaseline(JS
     // warm-up counter entirely, instead of resetting it.
     bool bailoutExpected =
         script->hasIonScript() && script->ionScript()->bailoutExpected();
     if (stat == Method_CantCompile || bailoutExpected) {
       JitSpew(JitSpew_BaselineOSR,
               "  Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!",
               stat == Method_CantCompile ? "yes" : "no",
               bailoutExpected ? "yes" : "no");
-      script->resetWarmUpCounter();
+      script->resetWarmUpCounterToDelayIonCompilation();
     }
     return true;
   }
 
   return true;
 }
 
 MethodStatus jit::Recompile(JSContext* cx, HandleScript script,
@@ -2794,17 +2794,17 @@ void jit::InvalidateAll(FreeOp* fop, Zon
 static void ClearIonScriptAfterInvalidation(JSContext* cx, JSScript* script,
                                             bool resetUses) {
   script->setIonScript(cx->runtime(), nullptr);
 
   // Wait for the scripts to get warm again before doing another
   // compile, unless we are recompiling *because* a script got hot
   // (resetUses is false).
   if (resetUses) {
-    script->resetWarmUpCounter();
+    script->resetWarmUpCounterToDelayIonCompilation();
   }
 }
 
 void jit::Invalidate(TypeZone& types, FreeOp* fop,
                      const RecompileInfoVector& invalid, bool resetUses,
                      bool cancelOffThread) {
   JitSpew(JitSpew_IonInvalidate, "Start invalidation.");
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -210,17 +210,17 @@ static void HandleExceptionIon(JSContext
         CloseLiveIteratorIon(cx, frame, tn);
         break;
 
       case JSTRY_CATCH:
         if (cx->isExceptionPending()) {
           // Ion can compile try-catch, but bailing out to catch
           // exceptions is slow. Reset the warm-up counter so that if we
           // catch many exceptions we won't Ion-compile the script.
-          script->resetWarmUpCounter();
+          script->resetWarmUpCounterToDelayIonCompilation();
 
           if (*hitBailoutException) {
             break;
           }
 
           // Bailout at the start of the catch block.
           jsbytecode* catchPC = script->offsetToPC(tn->start + tn->length);
           ExceptionBailoutInfo excInfo(frame.frameNo(), catchPC,
@@ -368,17 +368,17 @@ static bool ProcessTryNotesBaseline(JSCo
           break;
         }
 
         SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
 
         // Ion can compile try-catch, but bailing out to catch
         // exceptions is slow. Reset the warm-up counter so that if we
         // catch many exceptions we won't Ion-compile the script.
-        script->resetWarmUpCounter();
+        script->resetWarmUpCounterToDelayIonCompilation();
 
         // Resume at the start of the catch block.
         rfe->kind = ResumeFromException::RESUME_CATCH;
         if (frame.baselineFrame()->runningInInterpreter()) {
           const BaselineInterpreter& interp =
               cx->runtime()->jitRuntime()->baselineInterpreter();
           frame.baselineFrame()->setInterpreterPC(*pc);
           rfe->target = interp.interpretOpAddr().value;
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1631,21 +1631,21 @@ class MacroAssembler : public MacroAssem
       DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
   inline void cmp32Move32(Condition cond, Register lhs, const Address& rhs,
                           Register src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
   inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs,
                           const Address& src, Register dest)
-      DEFINED_ON(arm, arm64, x86_shared);
+      DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
   inline void cmp32Load32(Condition cond, Register lhs, Register rhs,
                           const Address& src, Register dest)
-      DEFINED_ON(arm, arm64, x86_shared);
+      DEFINED_ON(arm, arm64, mips_shared, x86_shared);
 
   inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
                            Register src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86, x64);
 
   inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask,
                             const Address& src, Register dest)
       DEFINED_ON(arm, arm64, mips_shared, x86, x64);
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -430,33 +430,16 @@ void LIRGenerator::visitWasmStore(MWasmS
   auto* lir = new (alloc()) LWasmStore(baseAlloc, valueAlloc);
   if (ins->access().offset()) {
     lir->setTemp(0, tempCopy(base, 0));
   }
 
   add(lir, ins);
 }
 
-void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
-  if (ins->type() == MIRType::Int64) {
-    auto* lir = new (alloc()) LWasmSelectI64(
-        useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
-        useRegister(ins->condExpr()));
-
-    defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
-    return;
-  }
-
-  auto* lir = new (alloc())
-      LWasmSelect(useRegisterAtStart(ins->trueExpr()), use(ins->falseExpr()),
-                  useRegister(ins->condExpr()));
-
-  defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
-}
-
 void LIRGeneratorMIPSShared::lowerUDiv(MDiv* div) {
   MDefinition* lhs = div->getOperand(0);
   MDefinition* rhs = div->getOperand(1);
 
   LUDivOrMod* lir = new (alloc()) LUDivOrMod;
   lir->setOperand(0, useRegister(lhs));
   lir->setOperand(1, useRegister(rhs));
   if (div->fallible()) {
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -25,16 +25,20 @@ void MacroAssembler::moveGPRToFloat32(Re
 void MacroAssembler::move8SignExtend(Register src, Register dest) {
   ma_seb(dest, src);
 }
 
 void MacroAssembler::move16SignExtend(Register src, Register dest) {
   ma_seh(dest, src);
 }
 
+void MacroAssembler::loadAbiReturnAddress(Register dest) {
+  movePtr(ra, dest);
+}
+
 // ===============================================================
 // Logical instructions
 
 void MacroAssembler::not32(Register reg) { ma_not(reg, reg); }
 
 void MacroAssembler::and32(Register src, Register dest) {
   as_and(dest, dest, src);
 }
@@ -792,16 +796,29 @@ void MacroAssembler::cmp32MovePtr(Condit
 }
 
 void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
                                  const Address& rhs, Register src,
                                  Register dest) {
   MOZ_CRASH();
 }
 
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
+                                 const Address& rhs, const Address& src,
+                                 Register dest) {
+  // This is never used, but must be present to facilitate linking on mips(64).
+  MOZ_CRASH("No known use cases");
+}
+
+void MacroAssembler::cmp32Load32(Condition cond, Register lhs, Register rhs,
+                                 const Address& src, Register dest) {
+  // This is never used, but must be present to facilitate linking on mips(64).
+  MOZ_CRASH("No known use cases");
+}
+
 void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr,
                                    Imm32 mask, const Address& src,
                                    Register dest) {
   MOZ_RELEASE_ASSERT(!JitOptions.spectreStringMitigations);
   Label skip;
   branchTest32(Assembler::InvertCondition(cond), addr, mask, &skip);
   loadPtr(src, dest);
   bind(&skip);
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -31,16 +31,17 @@
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/SharedContext.h"
 #include "gc/FreeOp.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonCode.h"
+#include "jit/JitOptions.h"
 #include "jit/JitRealm.h"
 #include "js/CompileOptions.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/SourceText.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
@@ -5460,16 +5461,28 @@ bool JSScript::hasLoops() {
   }
   return false;
 }
 
 bool JSScript::mayReadFrameArgsDirectly() {
   return argumentsHasVarBinding() || hasRest();
 }
 
+void JSScript::resetWarmUpCounterToDelayIonCompilation() {
+  // Reset the warm-up count only if it's greater than the BaselineCompiler
+  // threshold. We do this to ensure this has no effect on Baseline compilation
+  // because we don't want scripts to get stuck in the (Baseline) interpreter in
+  // pathological cases.
+
+  if (warmUpCount > jit::JitOptions.baselineWarmUpThreshold) {
+    incWarmUpResetCounter();
+    warmUpCount = jit::JitOptions.baselineWarmUpThreshold;
+  }
+}
+
 void JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) {
   if (fun) {
     if (fun->realm()->isSelfHostingRealm()) {
       // The self-hosting realm is shared across runtimes, so we can't use
       // JSAutoRealm: it could cause races. Functions in the self-hosting
       // realm will never be lazy, so we can safely assume we don't have
       // to delazify.
       script_ = fun->nonLazyScript();
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -2657,21 +2657,23 @@ class JSScript : public js::gc::TenuredC
     return warmUpCount += amount;
   }
   uint32_t* addressOfWarmUpCounter() {
     return reinterpret_cast<uint32_t*>(&warmUpCount);
   }
   static size_t offsetOfWarmUpCounter() {
     return offsetof(JSScript, warmUpCount);
   }
-  void resetWarmUpCounter() {
+  void resetWarmUpCounterForGC() {
     incWarmUpResetCounter();
     warmUpCount = 0;
   }
 
+  void resetWarmUpCounterToDelayIonCompilation();
+
   unsigned getWarmUpResetCount() const {
     constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
     return mutableFlags_ & MASK;
   }
   void incWarmUpResetCounter() {
     constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
     uint32_t newCount = getWarmUpResetCount() + 1;
     if (newCount <= MASK) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1564,17 +1564,17 @@ bool js::FinishCompilation(JSContext* cx
     }
 
     if (succeeded) {
       types->setHasFreezeConstraints(sweep);
     }
   }
 
   if (!succeeded) {
-    script->resetWarmUpCounter();
+    script->resetWarmUpCounterToDelayIonCompilation();
     *isValidOut = false;
     return true;
   }
 
   *isValidOut = true;
   return true;
 }
 
@@ -2674,19 +2674,17 @@ void TypeZone::addPendingRecompile(JSCon
 }
 
 void TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) {
   MOZ_ASSERT(script);
 
   CancelOffThreadIonCompile(script);
 
   // Let the script warm up again before attempting another compile.
-  if (jit::IsBaselineEnabled(cx)) {
-    script->resetWarmUpCounter();
-  }
+  script->resetWarmUpCounterToDelayIonCompilation();
 
   if (script->hasIonScript()) {
     addPendingRecompile(
         cx, RecompileInfo(script, script->ionScript()->compilationId()));
   }
 
   // Trigger recompilation of any callers inlining this script.
   if (TypeScript* types = script->types()) {
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -4449,47 +4449,58 @@ static bool MaybeParseOwnerIndex(WasmPar
       c.ts.generateError(elemIndex, "can't handle non-default memory/table yet",
                          c.error);
       return false;
     }
   }
   return true;
 }
 
-static AstExpr* ParseInitializerExpression(WasmParseContext& c) {
-  if (!c.ts.match(WasmToken::OpenParen, c.error)) {
-    return nullptr;
+static AstExpr* ParseInitializerConstExpression(WasmParseContext& c) {
+  bool need_rparen = false;
+
+  // For const initializer expressions, the parens are optional.
+  if (c.ts.getIf(WasmToken::OpenParen)) {
+    need_rparen = true;
   }
 
   AstExpr* initExpr = ParseExprInsideParens(c);
   if (!initExpr) {
     return nullptr;
   }
 
-  if (!c.ts.match(WasmToken::CloseParen, c.error)) {
+  if (need_rparen && !c.ts.match(WasmToken::CloseParen, c.error)) {
     return nullptr;
   }
 
   return initExpr;
 }
 
 static bool ParseInitializerExpressionOrPassive(WasmParseContext& c,
                                                 AstExpr** maybeInitExpr) {
 #ifdef ENABLE_WASM_BULKMEM_OPS
   if (c.ts.getIf(WasmToken::Passive)) {
     *maybeInitExpr = nullptr;
     return true;
   }
 #endif
 
-  AstExpr* initExpr = ParseInitializerExpression(c);
+  if (!c.ts.match(WasmToken::OpenParen, c.error)) {
+    return false;
+  }
+
+  AstExpr* initExpr = ParseExprInsideParens(c);
   if (!initExpr) {
     return false;
   }
 
+  if (!c.ts.match(WasmToken::CloseParen, c.error)) {
+    return false;
+  }
+
   *maybeInitExpr = initExpr;
   return true;
 }
 
 static AstDataSegment* ParseDataSegment(WasmParseContext& c) {
   if (!MaybeParseOwnerIndex(c)) {
     return nullptr;
   }
@@ -5114,17 +5125,17 @@ static bool ParseGlobal(WasmParseContext
       c.ts.unget(openParen);
     }
   }
 
   if (!ParseGlobalType(c, &type, &isMutable)) {
     return false;
   }
 
-  AstExpr* init = ParseInitializerExpression(c);
+  AstExpr* init = ParseInitializerConstExpression(c);
   if (!init) {
     return false;
   }
 
   auto* glob = new (c.lifo) AstGlobal(name, type, isMutable, Some(init));
   return glob && module->append(glob);
 }
 
--- a/security/manager/ssl/tests/unit/test_cert_storage.js
+++ b/security/manager/ssl/tests/unit/test_cert_storage.js
@@ -181,21 +181,20 @@ async function verify_non_tls_usage_succ
 
 function load_cert(cert, trust) {
   let file = "bad_certs/" + cert + ".pem";
   addCertFromFile(certDB, file, trust);
 }
 
 function fetch_blocklist() {
   Services.prefs.setBoolPref("services.settings.load_dump", false);
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
   Services.prefs.setCharPref("services.settings.server",
                              `http://localhost:${port}/v1`);
 
-  BlocklistClients.initialize();
+  BlocklistClients.initialize({ verifySignature: false });
 
   return RemoteSettings.pollChanges();
 }
 
 function* generate_revocations_txt_lines() {
   let profile = do_get_profile();
   let revocations = profile.clone();
   revocations.append("revocations.txt");
--- a/security/manager/ssl/tests/unit/test_intermediate_preloads.js
+++ b/security/manager/ssl/tests/unit/test_intermediate_preloads.js
@@ -10,16 +10,17 @@ do_get_profile(); // must be called befo
 const {RemoteSettings} = ChromeUtils.import("resource://services-settings/remote-settings.js");
 const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 const {TelemetryTestUtils} = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm");
 
 let remoteSecSetting;
 if (AppConstants.MOZ_NEW_CERT_STORAGE) {
   const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
   remoteSecSetting = new RemoteSecuritySettings();
+  remoteSecSetting.client.verifySignature = false;
 }
 
 let server;
 
 let intermediate1Data;
 let intermediate2Data;
 
 const INTERMEDIATES_DL_PER_POLL_PREF     = "security.remote_settings.intermediates.downloads_per_poll";
@@ -84,17 +85,16 @@ function syncAndPromiseUpdate() {
 
 function setupKintoPreloadServer(certGenerator, options = {
   attachmentCB: null,
   hashFunc: null,
   lengthFunc: null,
 }) {
   const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
   Services.prefs.setCharPref("services.settings.server", dummyServerURL);
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
 
   const configPath = "/v1/";
   const recordsPath = "/v1/buckets/security-state/collections/intermediates/records";
   const attachmentsPath = "/attachments/";
 
   if (options.hashFunc == null) {
     options.hashFunc = getHash;
   }
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -105,16 +105,17 @@ run-sequentially = hardcoded ports
 skip-if = toolkit == 'android'
 [test_hash_algorithms.js]
 [test_hash_algorithms_wrap.js]
 # bug 1124289 - run_test_in_child violates the sandbox on android
 skip-if = toolkit == 'android'
 [test_hmac.js]
 [test_intermediate_basic_usage_constraints.js]
 [test_intermediate_preloads.js]
+tags = blocklist
 # Bug 1520297 - do something to handle tighter resource constraints on Android
 skip-if = toolkit == 'android'
 [test_imminent_distrust.js]
 run-sequentially = hardcoded ports
 [test_js_cert_override_service.js]
 run-sequentially = hardcoded ports
 [test_keysize.js]
 [test_keysize_ev.js]
--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -246,37 +246,42 @@ async function targetAppFilter(entry, en
   // Skip this entry.
   return null;
 }
 
 var OneCRLBlocklistClient;
 var PinningBlocklistClient;
 var RemoteSecuritySettingsClient;
 
-function initialize() {
+function initialize(options = {}) {
+  const { verifySignature = true } = options;
+
   OneCRLBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_COLLECTION), {
     bucketNamePref: PREF_SECURITY_SETTINGS_ONECRL_BUCKET,
     lastCheckTimePref: PREF_SECURITY_SETTINGS_ONECRL_CHECKED,
     signerName: Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_SIGNER),
   });
+  OneCRLBlocklistClient.verifySignature = verifySignature;
   OneCRLBlocklistClient.on("sync", updateCertBlocklist);
 
   PinningBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_COLLECTION), {
     bucketNamePref: PREF_BLOCKLIST_PINNING_BUCKET,
     lastCheckTimePref: PREF_BLOCKLIST_PINNING_CHECKED_SECONDS,
     signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_SIGNER),
   });
+  PinningBlocklistClient.verifySignature = verifySignature;
   PinningBlocklistClient.on("sync", updatePinningList);
 
   if (AppConstants.MOZ_NEW_CERT_STORAGE) {
     const { RemoteSecuritySettings } = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
 
     // In Bug 1526018 this will move into its own service, as it's not quite like
     // the others.
     RemoteSecuritySettingsClient = new RemoteSecuritySettings();
+    RemoteSecuritySettingsClient.verifySignature = verifySignature;
 
     return {
       OneCRLBlocklistClient,
       PinningBlocklistClient,
       RemoteSecuritySettingsClient,
     };
   }
 
--- a/services/common/tests/unit/test_blocklist_onecrl.js
+++ b/services/common/tests/unit/test_blocklist_onecrl.js
@@ -14,17 +14,17 @@ let server;
 // xpcshell tests under /services/common
 add_task(async function test_something() {
   const configPath = "/v1/";
   const recordsPath = "/v1/buckets/security-state/collections/onecrl/records";
 
   const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
   Services.prefs.setCharPref("services.settings.server", dummyServerURL);
 
-  const {OneCRLBlocklistClient} = BlocklistClients.initialize();
+  const {OneCRLBlocklistClient} = BlocklistClients.initialize({verifySignature: false});
 
   // register a handler
   function handleResponse(request, response) {
     try {
       const sample = getSampleResponse(request, server.identity.primaryPort);
       if (!sample) {
         do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`);
       }
@@ -99,20 +99,16 @@ add_task(async function test_something()
   // Check that a sync completes even when there's bad data in the
   // collection. This will throw on fail, so just calling maybeSync is an
   // acceptible test.
   Services.prefs.setCharPref("services.settings.server", dummyServerURL);
   await OneCRLBlocklistClient.maybeSync(5000);
 });
 
 function run_test() {
-  // Ensure that signature verification is disabled to prevent interference
-  // with basic certificate sync tests
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
-
   // Set up an HTTP Server
   server = new HttpServer();
   server.start(-1);
 
   run_next_test();
 
   registerCleanupFunction(function() {
     server.stop(() => { });
--- a/services/common/tests/unit/test_blocklist_pinning.js
+++ b/services/common/tests/unit/test_blocklist_pinning.js
@@ -22,17 +22,19 @@ updateAppInfo({
   crashReporter: true,
 });
 
 let server;
 
 // Some simple tests to demonstrate that the core preload sync operations work
 // correctly and that simple kinto operations are working as expected.
 add_task(async function test_something() {
-  const PinningPreloadClient = BlocklistClients.initialize().PinningBlocklistClient;
+  const {
+    PinningBlocklistClient: PinningPreloadClient,
+  } = BlocklistClients.initialize({ verifySignature: false });
 
   const configPath = "/v1/";
   const recordsPath = "/v1/buckets/pinning/collections/pins/records";
 
   Services.prefs.setCharPref("services.settings.server",
                              `http://localhost:${server.identity.primaryPort}/v1`);
 
   // register a handler
@@ -133,20 +135,16 @@ add_task(async function test_something()
   // The STS entry for five.example.com now has includeSubdomains set;
   // ensure that the new includeSubdomains value is honored.
   ok(sss.isSecureURI(sss.HEADER_HSTS,
                      Services.io.newURI("https://subdomain.five.example.com"),
                      0));
 });
 
 function run_test() {
-  // Ensure that signature verification is disabled to prevent interference
-  // with basic certificate sync tests
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
-
   // Set up an HTTP Server
   server = new HttpServer();
   server.start(-1);
 
   run_next_test();
 
   registerCleanupFunction(function() {
     server.stop(() => { });
--- a/services/common/tests/unit/test_blocklist_signatures.js
+++ b/services/common/tests/unit/test_blocklist_signatures.js
@@ -3,17 +3,16 @@
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 const { BlocklistClients } = ChromeUtils.import("resource://services-common/blocklist-clients.js");
 const { UptakeTelemetry } = ChromeUtils.import("resource://services-common/uptake-telemetry.js");
 
 let server;
 
-const PREF_SETTINGS_VERIFY_SIGNATURE   = "services.settings.verify_signature";
 const PREF_SETTINGS_SERVER             = "services.settings.server";
 const PREF_SIGNATURE_ROOT              = "security.content.signature.root_hash";
 
 const CERT_DIR = "test_blocklist_signatures/";
 const CHAIN_FILES =
     ["collection_signing_ee.pem",
      "collection_signing_int.pem",
      "collection_signing_root.pem"];
@@ -586,20 +585,18 @@ add_task(async function test_check_signa
   expectedIncrements = {
     [UptakeTelemetry.STATUS.SIGNATURE_ERROR]: 1,
     [UptakeTelemetry.STATUS.SIGNATURE_RETRY_ERROR]: 0,  // Not retried since missing.
   };
   checkUptakeTelemetry(startHistogram, endHistogram, expectedIncrements);
 });
 
 function run_test() {
-  OneCRLBlocklistClient = BlocklistClients.initialize().OneCRLBlocklistClient;
-
-  // ensure signatures are enforced
-  Services.prefs.setBoolPref(PREF_SETTINGS_VERIFY_SIGNATURE, true);
+  // Signature verification is evabled by default.
+  ({OneCRLBlocklistClient} = BlocklistClients.initialize());
 
   // get a signature verifier to ensure nsNSSComponent is initialized
   Cc["@mozilla.org/security/contentsignatureverifier;1"]
     .createInstance(Ci.nsIContentSignatureVerifier);
 
   // set the content signing root to our test root
   setRoot();
 
--- a/services/settings/RemoteSettingsClient.jsm
+++ b/services/settings/RemoteSettingsClient.jsm
@@ -30,18 +30,16 @@ XPCOMUtils.defineLazyGlobalGetters(this,
 const DB_NAME = "remote-settings";
 
 const TELEMETRY_COMPONENT = "remotesettings";
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "gServerURL",
                                       "services.settings.server");
 XPCOMUtils.defineLazyPreferenceGetter(this, "gChangesPath",
                                       "services.settings.changes.path");
-XPCOMUtils.defineLazyPreferenceGetter(this, "gVerifySignature",
-                                      "services.settings.verify_signature", true);
 
 /**
  * cacheProxy returns an object Proxy that will memoize properties of the target.
  * @param {Object} target the object to wrap.
  * @returns {Proxy}
  */
 function cacheProxy(target) {
   const cache = new Map();
@@ -184,16 +182,20 @@ class RemoteSettingsClient extends Event
     super(["sync"]); // emitted events
 
     this.collectionName = collectionName;
     this.signerName = signerName;
     this.filterFunc = filterFunc;
     this.localFields = localFields;
     this._lastCheckTimePref = lastCheckTimePref;
 
+    // This attribute allows signature verification to be disabled, when running tests
+    // or when pulling data from a dev server.
+    this.verifySignature = true;
+
     // The bucket preference value can be changed (eg. `main` to `main-preview`) in order
     // to preview the changes to be approved in a real client.
     this.bucketNamePref = bucketNamePref;
     XPCOMUtils.defineLazyPreferenceGetter(this, "bucketName", this.bucketNamePref);
 
     XPCOMUtils.defineLazyGetter(this, "_kinto", () => new Kinto({
       bucket: this.bucketName,
       adapter: Kinto.adapters.IDB,
@@ -323,17 +325,17 @@ class RemoteSettingsClient extends Event
       // to record the fact that a check happened.
       if (expectedTimestamp <= collectionLastModified) {
         reportStatus = UptakeTelemetry.STATUS.UP_TO_DATE;
         return;
       }
 
       // If signature verification is enabled, then add a synchronization hook
       // for incoming changes that validates the signature.
-      if (this.signerName && gVerifySignature) {
+      if (this.verifySignature) {
         kintoCollection.hooks["incoming-changes"] = [async (payload, collection) => {
           await this._validateCollectionSignature(payload.changes,
                                                   payload.lastModified,
                                                   collection,
                                                   { expectedTimestamp });
           // In case the signature is valid, apply the changes locally.
           return payload;
         }];
--- a/services/settings/test/unit/test_remote_settings.js
+++ b/services/settings/test/unit/test_remote_settings.js
@@ -39,22 +39,22 @@ async function clear_state() {
 function run_test() {
   // Set up an HTTP Server
   server = new HttpServer();
   server.start(-1);
 
   // Point the blocklist clients to use this local HTTP server.
   Services.prefs.setCharPref("services.settings.server",
                              `http://localhost:${server.identity.primaryPort}/v1`);
-  // Ensure that signature verification is disabled to prevent interference
-  // with basic certificate sync tests
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
 
   client = RemoteSettings("password-fields");
+  client.verifySignature = false;
+
   clientWithDump = RemoteSettings("language-dictionaries");
+  clientWithDump.verifySignature = false;
 
   // Setup server fake responses.
   function handleResponse(request, response) {
     try {
       const sample = getSampleResponse(request, server.identity.primaryPort);
       if (!sample) {
         do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`);
       }
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -764,16 +764,26 @@ def build_docker_worker_payload(config, 
 def build_generic_worker_payload(config, task, task_def):
     worker = task['worker']
 
     task_def['payload'] = {
         'command': worker['command'],
         'maxRunTime': worker['max-run-time'],
     }
 
+    if worker['os'] == 'windows':
+        task_def['payload']['onExitStatus'] = {
+            'retry': [
+                # These codes (on windows) indicate a process interruption,
+                # rather than a task run failure. See bug 1544403.
+                1073807364,  # process force-killed due to system shutdown
+                3221225786,  # sigint (any interrupt)
+            ]
+        }
+
     env = worker.get('env', {})
 
     if task.get('needs-sccache'):
         env['USE_SCCACHE'] = '1'
         # Disable sccache idle shutdown.
         env['SCCACHE_IDLE_TIMEOUT'] = '0'
     else:
         env['SCCACHE_DISABLE'] = '1'
--- a/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini
@@ -1,11 +1,11 @@
 [payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
   disabled:
-    if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301
+    if (os == "android") and not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [Feature-Policy allow="payment" allows same-origin relocation.]
     expected:
       if not e10s: FAIL
 
   [Feature-Policy allow="payment" disallows cross-origin relocation.]
     expected:
       if not e10s: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy-attribute.https.sub.html.ini
@@ -1,10 +1,10 @@
 [payment-allowed-by-feature-policy-attribute.https.sub.html]
   disabled:
-    if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301
+    if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [Feature policy "payment" can be enabled in same-origin iframe using allow="payment" attribute]
     expected:
       if not e10s: FAIL
 
   [Feature policy "payment" can be enabled in cross-origin iframe using allow="payment" attribute]
     expected: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-allowed-by-feature-policy.https.sub.html.ini
@@ -1,11 +1,11 @@
 [payment-allowed-by-feature-policy.https.sub.html]
   disabled:
-    if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301
+    if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [Feature-Policy header {"payment" : ["*"\]} allows the top-level document.]
     expected:
       if not e10s: FAIL
 
   [Feature-Policy header {"payment" : ["*"\]} allows same-origin iframes.]
     expected:
       if not e10s: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-default-feature-policy.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-default-feature-policy.https.sub.html.ini
@@ -1,11 +1,11 @@
 [payment-default-feature-policy.https.sub.html]
   disabled:
-    if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301
+    if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [Default "payment" feature policy ["self"\] allows the top-level document.]
     expected:
       if not e10s: FAIL
 
   [Default "payment" feature policy ["self"\] allows same-origin iframes.]
     expected:
       if not e10s: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-disabled-by-feature-policy.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-disabled-by-feature-policy.https.sub.html.ini
@@ -1,7 +1,7 @@
 [payment-disabled-by-feature-policy.https.sub.html]
   disabled:
-    if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301
+    if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [Feature-Policy header {"payment" : [\]} disallows the top-level document.]
     expected:
       if not e10s: FAIL
 
--- a/testing/web-platform/meta/feature-policy/payment-supported-by-feature-policy.tentative.html.ini
+++ b/testing/web-platform/meta/feature-policy/payment-supported-by-feature-policy.tentative.html.ini
@@ -1,4 +1,6 @@
 [payment-supported-by-feature-policy.tentative.html]
+  disabled:
+    if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
   [document.featurePolicy.features should advertise payment.]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-reassign-dynamic-004.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Scoping: Dynamic reassignment of a slot.</title>
+<link rel="author" title="Edgar Chen" href="mailto:echen@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1548848">
+<link rel="match" href="reference/green-box.html"/>
+<div id="host">
+  <div id="green" style="background: green"></div>
+<div>
+<script>
+  let root = host.attachShadow({mode: 'open'});
+  root.innerHTML = `
+    <style>::slotted(div),div { width: 100px; height: 100px }</style>
+    <p>Test passes if you see a single 100px by 100px green box below.</p>
+    <slot id="slot"></slot>
+    <slot>
+      <div style="background: red"></div>
+    </slot>
+  `;
+
+  onload = function () {
+    root.offsetTop; // Update layout
+    root.getElementById('slot').remove();
+  };
+</script>
\ No newline at end of file
--- a/toolkit/mozapps/extensions/test/xpcshell/rs-blocklist/test_blocklist_clients.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/rs-blocklist/test_blocklist_clients.js
@@ -22,37 +22,37 @@ function run_test() {
 
   // Set up an HTTP Server
   server = new HttpServer();
   server.start(-1);
 
   // Point the blocklist clients to use this local HTTP server.
   Services.prefs.setCharPref("services.settings.server",
                              `http://localhost:${server.identity.primaryPort}/v1`);
-  // Ensure that signature verification is disabled to prevent interference
-  // with basic certificate sync tests
-  Services.prefs.setBoolPref("services.settings.verify_signature", false);
 
   // Unfortunately security settings are coupled with blocklists clients,
   // this will be fixed in Bug 1526018
   // We disable intermediate preloading because it runs when polling ends, and
   // interferes with `clear_state()` from this test suite.
   Services.prefs.setBoolPref("security.remote_settings.intermediates.enabled", false);
 
   // This will initialize the remote settings clients for blocklists.
   BlocklistGlobal.ExtensionBlocklistRS._ensureInitialized();
   BlocklistGlobal.PluginBlocklistRS._ensureInitialized();
   BlocklistGlobal.GfxBlocklistRS._ensureInitialized();
 
-
   gBlocklistClients = [
     {client: BlocklistGlobal.ExtensionBlocklistRS._client, testData: ["i808", "i720", "i539"]},
     {client: BlocklistGlobal.PluginBlocklistRS._client, testData: ["p1044", "p32", "p28"]},
     {client: BlocklistGlobal.GfxBlocklistRS._client, testData: ["g204", "g200", "g36"]},
   ];
+  // Disable signature verification in these tests.
+  for (const c of gBlocklistClients) {
+    c.verifySignature = false;
+  }
 
   // Setup server fake responses.
   function handleResponse(request, response) {
     try {
       const sample = getSampleResponse(request, server.identity.primaryPort);
       if (!sample) {
         do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`);
       }
--- a/toolkit/themes/shared/config.css
+++ b/toolkit/themes/shared/config.css
@@ -7,17 +7,16 @@
   padding-top: 0;
   padding-bottom: 0;
   padding-inline-start: calc(48px + 4.6em);
   padding-inline-end: 48px;
 }
 
 .title {
   background-image: url("chrome://global/skin/icons/warning.svg");
-  -moz-context-properties: fill;
   fill: #fcd100;
 }
 
 #warningTitle {
   font-weight: lighter;
   line-height: 1.2;
   color: #333;
   margin: 0;
--- a/toolkit/themes/shared/in-content/info-pages.inc.css
+++ b/toolkit/themes/shared/in-content/info-pages.inc.css
@@ -40,16 +40,18 @@ body.wide-container {
 .title {
   background-image: url("chrome://global/skin/icons/info.svg");
   background-position: left 0;
   background-repeat: no-repeat;
   background-size: 1.6em;
   margin-inline-start: -2.3em;
   padding-inline-start: 2.3em;
   font-size: 2.2em;
+  -moz-context-properties: fill;
+  fill: currentColor;
 }
 
 .title:-moz-locale-dir(rtl),
 .title:dir(rtl) {
   background-position: right 0;
 }
 
 .title-text {