Merge autoland to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Wed, 24 Oct 2018 16:26:39 +0300
changeset 491098 d94d73fcec772472f47a3b29f5bf6addd4315f39
parent 491059 c29f681979ee3d1ec49a3f2563a1030cf5b8ac94 (current diff)
parent 491097 1aa6481a987abc5f49ef0dce223fefbb3ebb890d (diff)
child 491099 034e53d3e7f06e67b30811198f020ad2cfa69998
child 491125 45632b51fa05f02e8910e26e8a00468ac6c9f377
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersmerge
milestone65.0a1
Merge autoland to mozilla-central. a=merge
third_party/rust/plane-split/.travis.yml
third_party/rust/plane-split/LICENSE
third_party/rust/plane-split/README.md
third_party/rust/plane-split/benches/split.rs
third_party/rust/plane-split/src/bsp.rs
third_party/rust/plane-split/src/clip.rs
third_party/rust/plane-split/src/lib.rs
third_party/rust/plane-split/src/polygon.rs
third_party/rust/plane-split/tests/clip.rs
third_party/rust/plane-split/tests/main.rs
third_party/rust/plane-split/tests/split.rs
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1860,17 +1860,17 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "plain"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "plane-split"
-version = "0.13.2"
+version = "0.13.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2943,17 +2943,17 @@ dependencies = [
  "dwrote 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "plane-split 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "plane-split 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.57.2",
 ]
@@ -3284,17 +3284,17 @@ dependencies = [
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
 "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
 "checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
 "checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
 "checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
 "checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
 "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
 "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
-"checksum plane-split 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d252db71f3d2109c4936e87d9f29f3c737e89f9ac239999d78866bdd60b9deda"
+"checksum plane-split 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9b1d9a84aa3bbc2dafd06856bdb1dc333eb1d442ad8987b9d596c7344b3ed969"
 "checksum podio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e5422a1ee1bc57cc47ae717b0137314258138f38fd5f3cea083f43a9725383a0"
 "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 "checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4"
 "checksum proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "cccdc7557a98fe98453030f077df7f3a042052fae465bb61d2c2c41435cfd9b6"
 "checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
 "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4"
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
 "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8"
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -210,17 +210,36 @@ function unwatchRuntime(id) {
       dispatch({ type: UNWATCH_RUNTIME_SUCCESS });
     } catch (e) {
       dispatch({ type: UNWATCH_RUNTIME_FAILURE, error: e.message });
     }
   };
 }
 
 function updateUSBRuntimes(runtimes) {
-  return { type: USB_RUNTIMES_UPDATED, runtimes };
+  return async (dispatch, getState) => {
+    const currentRuntime = getCurrentRuntime(getState().runtimes);
+
+    if (currentRuntime &&
+        currentRuntime.type === RUNTIMES.USB &&
+        !runtimes.find(runtime => currentRuntime.id === runtime.id)) {
+      // Since current USB runtime was invalid, move to this firefox page.
+      // This case is considered as followings and so on:
+      // * Remove ADB addon
+      // * (Physically) Disconnect USB runtime
+      //
+      // The reason why we call selectPage before USB_RUNTIMES_UPDATED was fired is below.
+      // Current runtime can not be retrieved after USB_RUNTIMES_UPDATED action, since
+      // that updates runtime state. So, before that we fire selectPage action so that to
+      // transact unwatchRuntime correctly.
+      await dispatch(Actions.selectPage(RUNTIMES.THIS_FIREFOX, RUNTIMES.THIS_FIREFOX));
+    }
+
+    dispatch({ type: USB_RUNTIMES_UPDATED, runtimes });
+  };
 }
 
 module.exports = {
   connectRuntime,
   disconnectRuntime,
   unwatchRuntime,
   updateConnectionPromptSetting,
   updateUSBRuntimes,
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -23,41 +23,44 @@ class App extends PureComponent {
   static get propTypes() {
     return {
       adbAddonStatus: PropTypes.string,
       // The "dispatch" helper is forwarded to the App component via connect.
       // From that point, components are responsible for forwarding the dispatch
       // property to all components who need to dispatch actions.
       dispatch: PropTypes.func.isRequired,
       fluentBundles: PropTypes.arrayOf(PropTypes.object).isRequired,
+      networkEnabled: PropTypes.bool.isRequired,
       networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
       networkRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
       selectedPage: PropTypes.string,
       usbRuntimes: PropTypes.arrayOf(Types.runtime).isRequired,
     };
   }
 
   getSelectedPageComponent() {
     const {
       adbAddonStatus,
       dispatch,
+      networkEnabled,
       networkLocations,
       selectedPage,
     } = this.props;
 
     if (!selectedPage) {
       // No page selected.
       return null;
     }
 
     switch (selectedPage) {
       case PAGES.CONNECT:
         return ConnectPage({
           adbAddonStatus,
           dispatch,
+          networkEnabled,
           networkLocations,
         });
       default:
         // All pages except for the CONNECT page are RUNTIME pages.
         return RuntimePage({ dispatch });
     }
   }
 
@@ -92,16 +95,17 @@ class App extends PureComponent {
       )
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     adbAddonStatus: state.ui.adbAddonStatus,
+    networkEnabled: state.ui.networkEnabled,
     networkLocations: state.ui.networkLocations,
     networkRuntimes: state.runtimes.networkRuntimes,
     selectedPage: state.ui.selectedPage,
     usbRuntimes: state.runtimes.usbRuntimes,
   };
 };
 
 const mapDispatchToProps = dispatch => ({
--- a/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
@@ -1,7 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .connect-page__usb__toggle-button {
   margin-top: calc(var(--base-distance) * 4);
 }
+
+.connect-page__disabled-section {
+  color: var(--grey-40);
+}
--- a/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
@@ -19,27 +19,30 @@ const Actions = require("../../actions/i
 
 loader.lazyRequireGetter(this, "ADB_ADDON_STATES", "devtools/shared/adb/adb-addon", true);
 
 const ConnectSection = createFactory(require("./ConnectSection"));
 const ConnectSteps = createFactory(require("./ConnectSteps"));
 const NetworkLocationsForm = createFactory(require("./NetworkLocationsForm"));
 const NetworkLocationsList = createFactory(require("./NetworkLocationsList"));
 
+const { PREFERENCES } = require("../../constants");
+
 const USB_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const WIFI_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const GLOBE_ICON_SRC = "chrome://devtools/skin/images/globe.svg";
 
 class ConnectPage extends PureComponent {
   static get propTypes() {
     return {
       adbAddonStatus: PropTypes.string,
       dispatch: PropTypes.func.isRequired,
       // Provided by wrapping the component with FluentReact.withLocalization.
       getString: PropTypes.func.isRequired,
+      networkEnabled: PropTypes.bool.isRequired,
       networkLocations: PropTypes.arrayOf(PropTypes.string).isRequired,
     };
   }
 
   renderWifi() {
     const { getString } = this.props;
     return Localized(
       {
@@ -148,31 +151,53 @@ class ConnectPage extends PureComponent 
           )
         ),
         this.renderUsbToggleButton()
       )
     );
   }
 
   renderNetwork() {
-    const { dispatch, networkLocations } = this.props;
+    const { dispatch, networkEnabled, networkLocations } = this.props;
+
     return Localized(
       {
         id: "about-debugging-connect-network",
         attrs: { title: true },
       },
       ConnectSection(
         {
           className: "connect-page__network",
           icon: GLOBE_ICON_SRC,
           title: "Via Network Location",
         },
-        NetworkLocationsList({ dispatch, networkLocations }),
-        dom.hr({ className: "separator separator--breathe" }),
-        NetworkLocationsForm({ dispatch }),
+        ...(
+          networkEnabled ?
+          [
+            NetworkLocationsList({ dispatch, networkLocations }),
+            dom.hr({ className: "separator separator--breathe" }),
+            NetworkLocationsForm({ dispatch }),
+          ] : [
+            // We are using an array for this single element because of the spread
+            // operator (...). The spread operator avoids React warnings about missing
+            // keys.
+            Localized(
+              {
+                id: "about-debugging-connect-network-disabled",
+                $pref: PREFERENCES.NETWORK_ENABLED,
+              },
+              dom.div(
+                {
+                  className: "connect-page__disabled-section",
+                },
+                "about-debugging-connect-network-disabled"
+              )
+            ),
+          ]
+        )
       )
     );
   }
 
   render() {
     return dom.article(
       {
         className: "page connect-page js-connect-page",
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -57,16 +57,21 @@ const DEBUG_TARGET_PANE = {
   TEMPORARY_EXTENSION: "temporaryExtension",
 };
 
 const PAGES = {
   THIS_FIREFOX: "this-firefox",
   CONNECT: "connect",
 };
 
+const PREFERENCES = {
+  // Temporary preference without any default value until network locations are enabled.
+  NETWORK_ENABLED: "devtools.aboutdebugging.network",
+};
+
 const RUNTIME_PREFERENCE = {
   CONNECTION_PROMPT: "devtools.debugger.prompt-connection",
 };
 
 const RUNTIMES = {
   NETWORK: "network",
   THIS_FIREFOX: "this-firefox",
   USB: "usb",
@@ -89,14 +94,15 @@ const USB_STATES = {
   UPDATING_USB: "UPDATING_USB",
 };
 
 // flatten constants
 module.exports = Object.assign({}, {
   DEBUG_TARGETS,
   DEBUG_TARGET_PANE,
   PAGES,
+  PREFERENCES,
   RUNTIME_PREFERENCE,
   RUNTIMES,
   SERVICE_WORKER_FETCH_STATES,
   SERVICE_WORKER_STATUSES,
   USB_STATES,
 }, actionTypes);
--- a/devtools/client/aboutdebugging-new/src/create-store.js
+++ b/devtools/client/aboutdebugging-new/src/create-store.js
@@ -1,28 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const Services = require("Services");
+
 const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");
 const { thunk } = require("devtools/client/shared/redux/middleware/thunk.js");
 
 const rootReducer = require("./reducers/index");
 const { DebugTargetsState } = require("./reducers/debug-targets-state");
 const { RuntimesState } = require("./reducers/runtimes-state");
 const { UiState } = require("./reducers/ui-state");
 const debugTargetListenerMiddleware = require("./middleware/debug-target-listener");
 const extensionComponentDataMiddleware = require("./middleware/extension-component-data");
 const tabComponentDataMiddleware = require("./middleware/tab-component-data");
 const workerComponentDataMiddleware = require("./middleware/worker-component-data");
 const { getDebugTargetCollapsibilities } = require("./modules/debug-target-collapsibilities");
 const { getNetworkLocations } = require("./modules/network-locations");
 
+const { PREFERENCES } = require("./constants");
+
 function configureStore() {
   const initialState = {
     debugTargets: new DebugTargetsState(),
     runtimes: new RuntimesState(),
     ui: getUiState(),
   };
 
   const middleware = applyMiddleware(thunk,
@@ -32,12 +36,13 @@ function configureStore() {
                                      workerComponentDataMiddleware);
 
   return createStore(rootReducer, initialState, middleware);
 }
 
 function getUiState() {
   const collapsibilities = getDebugTargetCollapsibilities();
   const locations = getNetworkLocations();
-  return new UiState(locations, collapsibilities);
+  const networkEnabled = Services.prefs.getBoolPref(PREFERENCES.NETWORK_ENABLED, false);
+  return new UiState(locations, collapsibilities, networkEnabled);
 }
 
 exports.configureStore = configureStore;
--- a/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/debug-target-listener.js
@@ -2,20 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
 
 const {
+  DEBUG_TARGETS,
   UNWATCH_RUNTIME_START,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 const Actions = require("../actions/index");
+const { isSupportedDebugTarget } = require("../modules/debug-target-support");
 
 function debugTargetListenerMiddleware(store) {
   const onExtensionsUpdated = () => {
     store.dispatch(Actions.requestExtensions());
   };
 
   const onTabsUpdated = () => {
     store.dispatch(Actions.requestTabs());
@@ -49,35 +51,55 @@ function debugTargetListenerMiddleware(s
 
   const onWorkersUpdated = () => {
     store.dispatch(Actions.requestWorkers());
   };
 
   return next => action => {
     switch (action.type) {
       case WATCH_RUNTIME_SUCCESS: {
-        const { client } = action.runtime.runtimeDetails;
-        client.addListener("tabListChanged", onTabsUpdated);
-        AddonManager.addAddonListener(extensionsListener);
-        client.addListener("workerListChanged", onWorkersUpdated);
-        client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
-        client.addListener("processListChanged", onWorkersUpdated);
-        client.addListener("registration-changed", onWorkersUpdated);
-        client.addListener("push-subscription-modified", onWorkersUpdated);
+        const { runtime } = action;
+        const { client } = runtime.runtimeDetails;
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
+          client.addListener("tabListChanged", onTabsUpdated);
+        }
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
+          AddonManager.addAddonListener(extensionsListener);
+        }
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
+          client.addListener("workerListChanged", onWorkersUpdated);
+          client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
+          client.addListener("processListChanged", onWorkersUpdated);
+          client.addListener("registration-changed", onWorkersUpdated);
+          client.addListener("push-subscription-modified", onWorkersUpdated);
+        }
         break;
       }
       case UNWATCH_RUNTIME_START: {
-        const { client } = action.runtime.runtimeDetails;
-        client.removeListener("tabListChanged", onTabsUpdated);
-        AddonManager.removeAddonListener(extensionsListener);
-        client.removeListener("workerListChanged", onWorkersUpdated);
-        client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
-        client.removeListener("processListChanged", onWorkersUpdated);
-        client.removeListener("registration-changed", onWorkersUpdated);
-        client.removeListener("push-subscription-modified", onWorkersUpdated);
+        const { runtime } = action;
+        const { client } = runtime.runtimeDetails;
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
+          client.removeListener("tabListChanged", onTabsUpdated);
+        }
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
+          AddonManager.removeAddonListener(extensionsListener);
+        }
+
+        if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
+          client.removeListener("workerListChanged", onWorkersUpdated);
+          client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
+          client.removeListener("processListChanged", onWorkersUpdated);
+          client.removeListener("registration-changed", onWorkersUpdated);
+          client.removeListener("push-subscription-modified", onWorkersUpdated);
+        }
         break;
       }
     }
 
     return next(action);
   };
 }
 
--- a/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
@@ -106,23 +106,28 @@ function runtimesReducer(state = Runtime
       const runtimeDetails =
         Object.assign({}, runtime.runtimeDetails, { connectionPromptEnabled });
       return _updateRuntimeById(runtimeId, { runtimeDetails }, state);
     }
 
     case USB_RUNTIMES_UPDATED: {
       const { runtimes } = action;
       const usbRuntimes = runtimes.map(runtime => {
+        const existingRuntime = findRuntimeById(runtime.id, state);
+        const existingRuntimeDetails =
+          existingRuntime ? existingRuntime.runtimeDetails : null;
+
         return {
           id: runtime.id,
           extra: {
             connectionParameters: { socketPath: runtime._socketPath },
             deviceName: runtime.deviceName,
           },
           name: runtime.shortName,
+          runtimeDetails: existingRuntimeDetails,
           type: RUNTIMES.USB,
         };
       });
       return Object.assign({}, state, { usbRuntimes });
     }
 
     case WATCH_RUNTIME_SUCCESS: {
       const { id } = action.runtime;
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -6,20 +6,22 @@
 
 const {
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
 } = require("../constants");
 
-function UiState(locations = [], debugTargetCollapsibilities = {}) {
+function UiState(locations = [], debugTargetCollapsibilities = {},
+                 networkEnabled = false) {
   return {
     adbAddonStatus: null,
     debugTargetCollapsibilities,
+    networkEnabled,
     networkLocations: locations,
     selectedPage: null,
   };
 }
 
 function uiReducer(state = UiState(), action) {
   switch (action.type) {
     case ADB_ADDON_STATUS_UPDATED: {
--- a/devtools/client/aboutdebugging-new/test/browser/head.js
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -29,16 +29,17 @@ registerCleanupFunction(async function()
   await ADB.kill();
 });
 
 /**
  * Enable the new about:debugging panel.
  */
 async function enableNewAboutDebugging() {
   await pushPref("devtools.aboutdebugging.new-enabled", true);
+  await pushPref("devtools.aboutdebugging.network", true);
 }
 
 async function openAboutDebugging(page, win) {
   await enableNewAboutDebugging();
 
   info("opening about:debugging");
   const tab = await addTab("about:debugging", { window: win });
   const browser = tab.linkedBrowser;
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -72,16 +72,21 @@ about-debugging-connect-usb-step-enable-
 
 # USB section step by step guide
 about-debugging-connect-usb-step-plug-device = Connect the USB Device to your computer
 
 # Network section of the Connect page
 about-debugging-connect-network
   .title = Via Network Location
 
+# Temporary text displayed when network location support is turned off via preferences.
+# { $pref } is the name of the preference that enables network locations
+# Do not localize
+about-debugging-connect-network-disabled = Network location support currently under development. You can enable it with the preference "{ $pref }".
+
 # Below are the titles for the various categories of debug targets that can be found
 # on "runtime" pages of about:debugging.
 # Title of the temporary extensions category (only available for "This Firefox" runtime).
 about-debugging-runtime-temporary-extensions = Temporary Extensions
 # Title of the extensions category.
 about-debugging-runtime-extensions = Extensions
 # Title of the tabs category.
 about-debugging-runtime-tabs = Tabs
--- a/devtools/client/inspector/animation/test/browser.ini
+++ b/devtools/client/inspector/animation/test/browser.ini
@@ -64,16 +64,17 @@ skip-if = (os == "win" && ccov) # Bug 14
 [browser_animation_keyframes-progress-bar_after-resuming.js]
 [browser_animation_logic_adjust-time.js]
 [browser_animation_logic_adjust-time-with-playback-rate.js]
 [browser_animation_logic_auto-stop.js]
 [browser_animation_logic_avoid-updating-during-hiding.js]
 [browser_animation_logic_created-time.js]
 [browser_animation_logic_mutations.js]
 [browser_animation_logic_mutations_fast.js]
+skip-if= true # Bug 1500046
 [browser_animation_logic_mutations_properties.js]
 [browser_animation_logic_overflowed_delay_end-delay.js]
 skip-if = debug #bug 1480027
 [browser_animation_logic_scroll-amount.js]
 [browser_animation_pause-resume-button.js]
 [browser_animation_pause-resume-button_end-time.js]
 [browser_animation_pause-resume-button_respectively.js]
 [browser_animation_pause-resume-button_spacebar.js]
--- a/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
+++ b/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
@@ -117,18 +117,19 @@ class FlexItemSizingProperties extends P
       // If not and width/height is defined, then that's what defines the base size.
       property = this.renderCssProperty(dimension, dimensionValue);
     } else {
       // Finally, if nothing is set, then the base size is the max-content size.
       reason = this.renderReasons(
         [getStr("flexbox.itemSizing.itemBaseSizeFromContent")]);
     }
 
+    const className = "section base";
     return (
-      dom.li({ className: property ? "section" : "section no-property" },
+      dom.li({ className: className + (property ? "" : " no-property") },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.baseSizeSectionHeader")
         ),
         dom.span({ className: "value theme-fg-color1" },
           this.getRoundedDimension(mainBaseSize)
         ),
         property,
         reason
@@ -145,17 +146,17 @@ class FlexItemSizingProperties extends P
     } = flexItemSizing;
 
     // Don't attempt to display anything useful if everything is 0.
     if (!mainFinalSize && !mainBaseSize && !mainDeltaSize) {
       return null;
     }
 
     const flexGrow = properties["flex-grow"];
-    const flexGrow0 = parseFloat(flexGrow) === 0;
+    const nonZeroFlexGrowDefined = flexGrow && parseFloat(flexGrow) !== 0;
     const flexShrink = properties["flex-shrink"];
     const flexShrink0 = parseFloat(flexShrink) === 0;
     const grew = mainDeltaSize > 0;
     const shrank = mainDeltaSize < 0;
     // TODO: replace this with the new clamping state that the API will return once bug
     // 1498273 is fixed.
     const wasClamped = mainDeltaSize + mainBaseSize !== mainFinalSize;
 
@@ -165,23 +166,23 @@ class FlexItemSizingProperties extends P
     // not on the line.
     if (lineGrowthState === "growing") {
       reasons.push(getStr("flexbox.itemSizing.extraRoomOnLine"));
     } else if (lineGrowthState === "shrinking") {
       reasons.push(getStr("flexbox.itemSizing.notEnoughRoomOnLine"));
     }
 
     // Then tell users whether the item was set to grow, shrink or none of them.
-    if (flexGrow && !flexGrow0 && lineGrowthState !== "shrinking") {
+    if (nonZeroFlexGrowDefined && lineGrowthState !== "shrinking") {
       reasons.push(getStr("flexbox.itemSizing.setToGrow"));
     }
     if (flexShrink && !flexShrink0 && lineGrowthState !== "growing") {
       reasons.push(getStr("flexbox.itemSizing.setToShrink"));
     }
-    if (!grew && !shrank && lineGrowthState === "growing") {
+    if (!nonZeroFlexGrowDefined && !grew && !shrank && lineGrowthState === "growing") {
       reasons.push(getStr("flexbox.itemSizing.notSetToGrow"));
     }
     if (!grew && !shrank && lineGrowthState === "shrinking") {
       reasons.push(getStr("flexbox.itemSizing.notSetToShrink"));
     }
 
     let property = null;
 
@@ -206,21 +207,23 @@ class FlexItemSizingProperties extends P
         property = this.renderCssProperty("flex-shrink", "1", true);
       }
 
       if (wasClamped) {
         // It might have wanted to shrink more (to accomodate all items) but couldn't
         // because it was later min-clamped.
         reasons.push(getStr("flexbox.itemSizing.shrinkAttemptWhenClamped"));
       }
-    } else if (lineGrowthState === "growing" && flexGrow && !flexGrow0) {
-      // The item did not grow or shrink. There was room on the line and flex-grow was
-      // set, other items have likely used up all of the space.
+    } else if (lineGrowthState === "growing" && nonZeroFlexGrowDefined) {
       property = this.renderCssProperty("flex-grow", flexGrow);
-      reasons.push(getStr("flexbox.itemSizing.growthAttemptButSiblings"));
+      if (!wasClamped) {
+        // The item did not grow or shrink. There was room on the line and flex-grow was
+        // set, other items have likely used up all of the space.
+        reasons.push(getStr("flexbox.itemSizing.growthAttemptButSiblings"));
+      }
     } else if (lineGrowthState === "shrinking") {
       // The item did not grow or shrink and there wasn't enough room on the line.
       if (!flexShrink0) {
         // flex-shrink was set (either defined in CSS, or via its default value of 1).
         // but the item didn't shrink.
         if (flexShrink) {
           property = this.renderCssProperty("flex-shrink", flexShrink);
         } else {
@@ -239,18 +242,19 @@ class FlexItemSizingProperties extends P
       }
     }
 
     // Don't display the section at all if there's nothing useful to show users.
     if (!property && !reasons.length) {
       return null;
     }
 
+    const className = "section flexibility";
     return (
-      dom.li({ className: property ? "section" : "section no-property" },
+      dom.li({ className: className + (property ? "" : " no-property") },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.flexibilitySectionHeader")
         ),
         dom.span({ className: "value theme-fg-color1" },
           this.getFlexibilityValueString(grew, mainDeltaSize)
         ),
         property,
         this.renderReasons(reasons)
@@ -266,17 +270,17 @@ class FlexItemSizingProperties extends P
     // TODO: replace this with the new clamping state that the API will return once bug
     // 1498273 is fixed.
     const minDimensionValue = properties[`min-${dimension}`];
     if (mainMinSize !== mainFinalSize || !minDimensionValue) {
       return null;
     }
 
     return (
-      dom.li({ className: "section" },
+      dom.li({ className: "section min" },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.minSizeSectionHeader")
         ),
         dom.span({ className: "value theme-fg-color1" },
           this.getRoundedDimension(mainMinSize)
         ),
         this.renderCssProperty(`min-${dimension}`, minDimensionValue)
       )
@@ -288,31 +292,31 @@ class FlexItemSizingProperties extends P
     // 1498273 is fixed.
     if (mainMaxSize !== mainFinalSize) {
       return null;
     }
 
     const maxDimensionValue = properties[`max-${dimension}`];
 
     return (
-      dom.li({ className: "section" },
+      dom.li({ className: "section max" },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.maxSizeSectionHeader")
         ),
         dom.span({ className: "value theme-fg-color1" },
           this.getRoundedDimension(mainMaxSize)
         ),
         this.renderCssProperty(`max-${dimension}`, maxDimensionValue)
       )
     );
   }
 
   renderFinalSizeSection({ mainFinalSize }) {
     return (
-      dom.li({ className: "section no-property" },
+      dom.li({ className: "section final no-property" },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.finalSizeSectionHeader")
         ),
         dom.span({ className: "value theme-fg-color1" },
           this.getRoundedDimension(mainFinalSize)
         )
       )
     );
--- a/devtools/client/inspector/flexbox/test/browser.ini
+++ b/devtools/client/inspector/flexbox/test/browser.ini
@@ -1,27 +1,30 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
+  doc_flexbox_pseudos.html
   doc_flexbox_simple.html
-  doc_flexbox_pseudos.html
+  doc_flexbox_specific_cases.html
   doc_flexbox_text_nodes.html
   head.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_flexbox_highlighter_color_picker_on_ESC.js]
 [browser_flexbox_highlighter_color_picker_on_RETURN.js]
 [browser_flexbox_item_outline_exists.js]
 [browser_flexbox_item_outline_has_correct_layout.js]
 [browser_flexbox_item_outline_hidden_when_useless.js]
 [browser_flexbox_item_outline_rotates_for_column.js]
 [browser_flexbox_pseudo_elements_are_listed.js]
+[browser_flexbox_sizing_grow_and_not_grow.js]
 [browser_flexbox_sizing_info_exists.js]
 [browser_flexbox_sizing_info_for_pseudos.js]
 [browser_flexbox_sizing_info_for_text_nodes.js]
 [browser_flexbox_sizing_info_has_correct_sections.js]
+[browser_flexbox_sizing_unrelated_to_siblings_when_clamped.js]
 [browser_flexbox_text_nodes_are_listed.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_grow_and_not_grow.js
@@ -0,0 +1,33 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { getStr } = require("devtools/client/inspector/layout/utils/l10n");
+
+// Non-regression for bug 1501207.
+// In this bug, an item that was set to grow, with a flex-basis larger than its max-width
+// was marked both as "set to grow" and "not set to grow" at the same time.
+// So this test checks that this does not happen anymore.
+
+const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  info("Select the test item in the document and wait for the sizing info to render");
+  const onFlexibilityReasonsRendered = waitForDOM(doc,
+    "ul.flex-item-sizing .section.flexibility .reasons");
+  await selectNode("#grow-not-grow div:first-child", inspector);
+  const [reasonsList] = await onFlexibilityReasonsRendered;
+
+  info("Get the list of reasons in the flexibility section");
+  const [...reasons] = reasonsList.querySelectorAll("li");
+  ok(reasons.some(r => r.textContent === getStr("flexbox.itemSizing.setToGrow")),
+     "The 'set to grow' reason was found");
+  ok(reasons.every(r => r.textContent !== getStr("flexbox.itemSizing.notSetToGrow")),
+     "The 'not set to grow' reason was not found");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_unrelated_to_siblings_when_clamped.js
@@ -0,0 +1,33 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { getStr } = require("devtools/client/inspector/layout/utils/l10n");
+
+// Non-regression for bug 1501263.
+// In this bug, an item that was set to grow, with a max-width, and growing siblings
+// was marked as "couldn't grow because siblings have used the extra space". This was
+// wrong because the item was only being clamped by its max-width.
+// This test prevents this from happening again.
+
+const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  info("Select the test item in the document and wait for the sizing info to render");
+  const onFlexibilityReasonsRendered = waitForDOM(doc,
+    "ul.flex-item-sizing .section.flexibility .reasons");
+  await selectNode("#grow-not-grow div:first-child", inspector);
+  const [reasonsList] = await onFlexibilityReasonsRendered;
+
+  info("Get the list of reasons in the flexibility section");
+  const [...reasons] = reasonsList.querySelectorAll("li");
+  const str = getStr("flexbox.itemSizing.growthAttemptButSiblings");
+  ok(reasons.every(r => r.textContent !== str),
+     "The 'could not grow because of siblings' reason was not found");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+#grow-not-grow {
+  display:flex;
+  width:500px;
+  border:1px solid;
+}
+#grow-not-grow div:first-child {
+  flex-grow:1;
+  flex-basis:100px;
+  max-width:50px;
+}
+#grow-not-grow div:last-child {
+  flex-grow: 1;
+}
+</style>
+<div id="grow-not-grow">
+  <div>item 1</div>
+  <div>item 2</div>
+</div>
--- a/devtools/client/inspector/rules/test/browser_rules_add-property-and-reselect.js
+++ b/devtools/client/inspector/rules/test/browser_rules_add-property-and-reselect.js
@@ -10,35 +10,55 @@
 const TEST_URI = URL_ROOT + "doc_content_stylesheet.html";
 
 add_task(async function() {
   await addTab(TEST_URI);
   const {inspector, view} = await openRuleView();
   await selectNode("#target", inspector);
 
   info("Setting a font-weight property on all rules");
-  await setPropertyOnAllRules(view);
+  await setPropertyOnAllRules(view, inspector);
 
   info("Reselecting the element");
   await selectNode("body", inspector);
   await selectNode("#target", inspector);
 
   checkPropertyOnAllRules(view);
 });
 
-async function setPropertyOnAllRules(view) {
-  // Wait for the properties to be properly created on the backend and for the
-  // view to be updated.
-  const onRefreshed = view.once("ruleview-refreshed");
-  for (const rule of view._elementStyle.rules) {
-    rule.editor.addProperty("font-weight", "bold", "", true);
+async function setPropertyOnAllRules(view, inspector) {
+  // Set the inline style rule first independently because it needs to wait for specific
+  // events and the DOM mutation that it causes refreshes the rules view, so we need to
+  // get the list of rules again later.
+  info("Adding font-weight:bold in the inline style rule");
+  const inlineStyleRuleEditor = view._elementStyle.rules[0].editor;
+
+  const onMutation = inspector.once("markupmutation");
+  const onRuleViewRefreshed = view.once("ruleview-refreshed");
+
+  inlineStyleRuleEditor.addProperty("font-weight", "bold", "", true);
+
+  await Promise.all([onMutation, onRuleViewRefreshed]);
+
+  // Now set the other rules after having retrieved the list.
+  const allRules = view._elementStyle.rules;
+
+  for (let i = 1; i < allRules.length; i++) {
+    info(`Adding font-weight:bold in rule ${i}`);
+    const rule = allRules[i];
+    const ruleEditor = rule.editor;
+
+    const onRuleViewChanged = view.once("ruleview-changed");
+
+    ruleEditor.addProperty("font-weight", "bold", "", true);
+
+    await onRuleViewChanged;
   }
-  await onRefreshed;
 }
 
 function checkPropertyOnAllRules(view) {
   for (const rule of view._elementStyle.rules) {
-    const lastRule = rule.textProps[rule.textProps.length - 1];
+    const lastProperty = rule.textProps[rule.textProps.length - 1];
 
-    is(lastRule.name, "font-weight", "Last rule name is font-weight");
-    is(lastRule.value, "bold", "Last rule value is bold");
+    is(lastProperty.name, "font-weight", "Last property name is font-weight");
+    is(lastProperty.value, "bold", "Last property value is bold");
   }
 }
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2506,17 +2506,17 @@ EditorBase::CloneAttributes(Element* aDe
 
   return NS_OK;
 }
 
 void
 EditorBase::CloneAttributesWithTransaction(Element& aDestElement,
                                            Element& aSourceElement)
 {
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // Use transaction system for undo only if destination is already in the
   // document
   Element* rootElement = GetRoot();
   if (NS_WARN_IF(!rootElement)) {
     return;
   }
 
@@ -5177,9 +5177,49 @@ EditorBase::HideCaret(bool aHide)
   mHidingCaret = aHide;
   if (aHide) {
     caret->AddForceHide();
   } else {
     caret->RemoveForceHide();
   }
 }
 
+/******************************************************************************
+ * EditorBase::AutoSelectionRestorer
+ *****************************************************************************/
+
+EditorBase::AutoSelectionRestorer::AutoSelectionRestorer(
+                                     Selection& aSelection,
+                                     EditorBase& aEditorBase
+                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : mEditorBase(nullptr)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (aEditorBase.ArePreservingSelection()) {
+    // We already have initialized mSavedSel, so this must be nested call.
+    return;
+  }
+  mSelection = &aSelection;
+  mEditorBase = &aEditorBase;
+  mEditorBase->PreserveSelectionAcrossActions(mSelection);
+}
+
+EditorBase::AutoSelectionRestorer::~AutoSelectionRestorer()
+{
+  NS_ASSERTION(!mSelection || mEditorBase,
+               "mEditorBase should be non-null when mSelection is");
+  // mSelection will be null if this was nested call.
+  if (mSelection && mEditorBase->ArePreservingSelection()) {
+    mEditorBase->RestorePreservedSelection(mSelection);
+  }
+}
+
+void
+EditorBase::AutoSelectionRestorer::Abort()
+{
+  NS_ASSERTION(!mSelection || mEditorBase,
+               "mEditorBase should be non-null when mSelection is");
+  if (mSelection) {
+    mEditorBase->StopPreservingSelection();
+  }
+}
+
 } // namespace mozilla
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EditorBase_h
 #define mozilla_EditorBase_h
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
+#include "mozilla/EditAction.h"         // for EditSubAction
 #include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/OwningNonNull.h"      // for OwningNonNull
 #include "mozilla/PresShell.h"          // for PresShell
 #include "mozilla/RangeBoundary.h"      // for RawRangeBoundary, RangeBoundary
 #include "mozilla/SelectionState.h"     // for RangeUpdater, etc.
 #include "mozilla/StyleSheet.h"         // for StyleSheet
 #include "mozilla/TransactionManager.h" // for TransactionManager
@@ -78,17 +79,16 @@ class SplitNodeResult;
 class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
 class TextEditRules;
 class TextInputListener;
 class TextServicesDocument;
 class TypeInState;
 class WSRunObject;
-enum class EditSubAction : int32_t;
 
 namespace dom {
 class DataTransfer;
 class DragEvent;
 class Element;
 class EventTarget;
 class Text;
 } // namespace dom
@@ -1905,16 +1905,200 @@ private:
 
 
   /**
    * SetTextDirectionTo() sets text-direction of the root element.
    * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead.
    * This is a helper class of them.
    */
   nsresult SetTextDirectionTo(TextDirection aTextDirection);
+
+protected: // helper classes which may be used by friends
+  /**
+   * Stack based helper class for calling EditorBase::EndTransactionInternal().
+   */
+  class MOZ_RAII AutoTransactionBatch final
+  {
+  public:
+    explicit AutoTransactionBatch(EditorBase& aEditorBase
+                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      mEditorBase->BeginTransactionInternal();
+    }
+
+    ~AutoTransactionBatch()
+    {
+      mEditorBase->EndTransactionInternal();
+    }
+
+  protected:
+    OwningNonNull<EditorBase> mEditorBase;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
+  /**
+   * Stack based helper class for batching a collection of transactions inside
+   * a placeholder transaction.
+   */
+  class MOZ_RAII AutoPlaceholderBatch final
+  {
+  public:
+    explicit AutoPlaceholderBatch(EditorBase& aEditorBase
+                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      mEditorBase->BeginPlaceholderTransaction(nullptr);
+    }
+
+    AutoPlaceholderBatch(EditorBase& aEditorBase,
+                         nsAtom& aTransactionName
+                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      mEditorBase->BeginPlaceholderTransaction(&aTransactionName);
+    }
+
+    ~AutoPlaceholderBatch()
+    {
+      mEditorBase->EndPlaceholderTransaction();
+    }
+
+  protected:
+    OwningNonNull<EditorBase> mEditorBase;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
+  /**
+   * Stack based helper class for saving/restoring selection.  Note that this
+   * assumes that the nodes involved are still around afterwords!
+   */
+  class MOZ_RAII AutoSelectionRestorer final
+  {
+  public:
+    /**
+     * Constructor responsible for remembering all state needed to restore
+     * aSelection.
+     */
+    AutoSelectionRestorer(Selection& aSelection,
+                          EditorBase& aEditorBase
+                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+
+    /**
+     * Destructor restores mSelection to its former state
+     */
+    ~AutoSelectionRestorer();
+
+    /**
+     * Abort() cancels to restore the selection.
+     */
+    void Abort();
+
+  protected:
+    RefPtr<Selection> mSelection;
+    EditorBase* mEditorBase;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
+  /**
+   * AutoTopLevelEditSubActionNotifier notifies editor of start to handle
+   * top level edit sub-action and end handling top level edit sub-action.
+   */
+  class MOZ_RAII AutoTopLevelEditSubActionNotifier final
+  {
+  public:
+    AutoTopLevelEditSubActionNotifier(EditorBase& aEditorBase,
+                                      EditSubAction aEditSubAction,
+                                      nsIEditor::EDirection aDirection
+                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+      , mDoNothing(false)
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      // mTopLevelEditSubAction will already be set if this is nested call
+      // XXX Looks like that this is not aware of unexpected nested edit action
+      //     handling via selectionchange event listener or mutation event
+      //     listener.
+      if (!mEditorBase.mTopLevelEditSubAction) {
+        mEditorBase.OnStartToHandleTopLevelEditSubAction(aEditSubAction,
+                                                         aDirection);
+      } else {
+        mDoNothing = true; // nested calls will end up here
+      }
+    }
+
+    ~AutoTopLevelEditSubActionNotifier()
+    {
+      if (!mDoNothing) {
+        mEditorBase.OnEndHandlingTopLevelEditSubAction();
+      }
+    }
+
+  protected:
+    EditorBase& mEditorBase;
+    bool mDoNothing;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
+  /**
+   * Stack based helper class for turning off active selection adjustment
+   * by low level transactions
+   */
+  class MOZ_RAII AutoTransactionsConserveSelection final
+  {
+  public:
+    explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase
+                                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+      , mAllowedTransactionsToChangeSelection(
+          aEditorBase.AllowsTransactionsToChangeSelection())
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      mEditorBase.MakeThisAllowTransactionsToChangeSelection(false);
+    }
+
+    ~AutoTransactionsConserveSelection()
+    {
+      mEditorBase.MakeThisAllowTransactionsToChangeSelection(
+                    mAllowedTransactionsToChangeSelection);
+    }
+
+  protected:
+    EditorBase& mEditorBase;
+    bool mAllowedTransactionsToChangeSelection;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
+  /***************************************************************************
+   * stack based helper class for batching reflow and paint requests.
+   */
+  class MOZ_RAII AutoUpdateViewBatch final
+  {
+  public:
+    explicit AutoUpdateViewBatch(EditorBase& aEditorBase
+                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mEditorBase(aEditorBase)
+    {
+      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+      mEditorBase.BeginUpdateViewBatch();
+    }
+
+    ~AutoUpdateViewBatch()
+    {
+      mEditorBase.EndUpdateViewBatch();
+    }
+
+  protected:
+    EditorBase& mEditorBase;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  };
+
 protected:
   enum Tristate
   {
     eTriUnset,
     eTriFalse,
     eTriTrue
   };
 
@@ -1996,22 +2180,16 @@ protected:
   bool mIsInEditSubAction;
   // Whether caret is hidden forcibly.
   bool mHidingCaret;
   // Whether spellchecker dictionary is initialized after focused.
   bool mSpellCheckerDictionaryUpdated;
   // Whether we are an HTML editor class.
   bool mIsHTMLEditorClass;
 
-  friend class AutoPlaceholderBatch;
-  friend class AutoSelectionRestorer;
-  friend class AutoTopLevelEditSubActionNotifier;
-  friend class AutoTransactionBatch;
-  friend class AutoTransactionsConserveSelection;
-  friend class AutoUpdateViewBatch;
   friend class CompositionTransaction;
   friend class CreateElementTransaction;
   friend class CSSEditUtils;
   friend class DeleteNodeTransaction;
   friend class DeleteRangeTransaction;
   friend class DeleteTextTransaction;
   friend class HTMLEditRules;
   friend class HTMLEditUtils;
--- a/editor/libeditor/EditorUtils.cpp
+++ b/editor/libeditor/EditorUtils.cpp
@@ -20,59 +20,16 @@
 class nsISupports;
 class nsRange;
 
 namespace mozilla {
 
 using namespace dom;
 
 /******************************************************************************
- * AutoSelectionRestorer
- *****************************************************************************/
-
-AutoSelectionRestorer::AutoSelectionRestorer(
-                         Selection* aSelection,
-                         EditorBase* aEditorBase
-                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : mEditorBase(nullptr)
-{
-  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aEditorBase)) {
-    return;
-  }
-  if (aEditorBase->ArePreservingSelection()) {
-    // We already have initialized mSavedSel, so this must be nested call.
-    return;
-  }
-  mSelection = aSelection;
-  mEditorBase = aEditorBase;
-  mEditorBase->PreserveSelectionAcrossActions(mSelection);
-}
-
-AutoSelectionRestorer::~AutoSelectionRestorer()
-{
-  NS_ASSERTION(!mSelection || mEditorBase,
-               "mEditorBase should be non-null when mSelection is");
-  // mSelection will be null if this was nested call.
-  if (mSelection && mEditorBase->ArePreservingSelection()) {
-    mEditorBase->RestorePreservedSelection(mSelection);
-  }
-}
-
-void
-AutoSelectionRestorer::Abort()
-{
-  NS_ASSERTION(!mSelection || mEditorBase,
-               "mEditorBase should be non-null when mSelection is");
-  if (mSelection) {
-    mEditorBase->StopPreservingSelection();
-  }
-}
-
-/******************************************************************************
  * some helper classes for iterating the dom tree
  *****************************************************************************/
 
 DOMIterator::DOMIterator(nsINode& aNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   mIter = NS_NewContentIterator();
   DebugOnly<nsresult> rv = mIter->Init(&aNode);
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -448,210 +448,40 @@ private:
 
   nsresult mRv;
 
   SplitRangeOffFromNodeResult() = delete;
 };
 
 /***************************************************************************
  * stack based helper class for calling EditorBase::EndTransaction() after
- * EditorBase::BeginTransaction().
+ * EditorBase::BeginTransaction().  This shouldn't be used in editor classes
+ * or helper classes while an edit action is being handled.  Use
+ * AutoTransactionBatch in such cases since it uses non-virtual internal
+ * methods.
  ***************************************************************************/
-class MOZ_RAII AutoTransactionBatch final
+class MOZ_RAII AutoTransactionBatchExternal final
 {
 private:
   OwningNonNull<EditorBase> mEditorBase;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
 public:
-  explicit AutoTransactionBatch(EditorBase& aEditorBase
-                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditorBase(aEditorBase)
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mEditorBase->BeginTransactionInternal();
-  }
-
-  ~AutoTransactionBatch()
-  {
-    mEditorBase->EndTransactionInternal();
-  }
-};
-
-/***************************************************************************
- * stack based helper class for batching a collection of transactions inside a
- * placeholder transaction.
- */
-class MOZ_RAII AutoPlaceholderBatch final
-{
-private:
-  RefPtr<EditorBase> mEditorBase;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-public:
-  explicit AutoPlaceholderBatch(EditorBase* aEditorBase
-                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditorBase(aEditorBase)
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    BeginPlaceholderTransaction(nullptr);
-  }
-  AutoPlaceholderBatch(EditorBase* aEditorBase,
-                       nsAtom* aTransactionName
-                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  explicit AutoTransactionBatchExternal(EditorBase& aEditorBase
+                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mEditorBase(aEditorBase)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    BeginPlaceholderTransaction(aTransactionName);
-  }
-  ~AutoPlaceholderBatch()
-  {
-    if (mEditorBase) {
-      mEditorBase->EndPlaceholderTransaction();
-    }
+    mEditorBase->BeginTransaction();
   }
 
-private:
-  void BeginPlaceholderTransaction(nsAtom* aTransactionName)
+  ~AutoTransactionBatchExternal()
   {
-    if (mEditorBase) {
-      mEditorBase->BeginPlaceholderTransaction(aTransactionName);
-    }
-  }
-};
-
-/***************************************************************************
- * stack based helper class for saving/restoring selection.  Note that this
- * assumes that the nodes involved are still around afterwards!
- */
-class MOZ_RAII AutoSelectionRestorer final
-{
-private:
-  // Ref-counted reference to the selection that we are supposed to restore.
-  RefPtr<dom::Selection> mSelection;
-  EditorBase* mEditorBase;  // Non-owning ref to EditorBase.
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-public:
-  /**
-   * Constructor responsible for remembering all state needed to restore
-   * aSelection.
-   */
-  AutoSelectionRestorer(dom::Selection* aSelection,
-                        EditorBase* aEditorBase
-                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
-
-  /**
-   * Destructor restores mSelection to its former state
-   */
-  ~AutoSelectionRestorer();
-
-  /**
-   * Abort() cancels to restore the selection.
-   */
-  void Abort();
-};
-
-/***************************************************************************
- * AutoTopLevelEditSubActionNotifier notifies editor of start to handle
- * top level edit sub-action and end handling top level edit sub-action.
- */
-class MOZ_RAII AutoTopLevelEditSubActionNotifier final
-{
-public:
-  AutoTopLevelEditSubActionNotifier(EditorBase& aEditorBase,
-                                    EditSubAction aEditSubAction,
-                                    nsIEditor::EDirection aDirection
-                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditorBase(aEditorBase)
-    , mDoNothing(false)
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    // mTopLevelEditSubAction will already be set if this is nested call
-    // XXX Looks like that this is not aware of unexpected nested edit action
-    //     handling via selectionchange event listener or mutation event
-    //     listener.
-    if (!mEditorBase.mTopLevelEditSubAction) {
-      mEditorBase.OnStartToHandleTopLevelEditSubAction(aEditSubAction,
-                                                       aDirection);
-    } else {
-      mDoNothing = true; // nested calls will end up here
-    }
+    mEditorBase->EndTransaction();
   }
-
-  ~AutoTopLevelEditSubActionNotifier()
-  {
-    if (!mDoNothing) {
-      mEditorBase.OnEndHandlingTopLevelEditSubAction();
-    }
-  }
-
-protected:
-  EditorBase& mEditorBase;
-  bool mDoNothing;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
-/***************************************************************************
- * stack based helper class for turning off active selection adjustment
- * by low level transactions
- */
-class MOZ_RAII AutoTransactionsConserveSelection final
-{
-public:
-  explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase
-                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditorBase(aEditorBase)
-    , mAllowedTransactionsToChangeSelection(
-        aEditorBase.AllowsTransactionsToChangeSelection())
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mEditorBase.MakeThisAllowTransactionsToChangeSelection(false);
-  }
-
-  ~AutoTransactionsConserveSelection()
-  {
-    mEditorBase.MakeThisAllowTransactionsToChangeSelection(
-                  mAllowedTransactionsToChangeSelection);
-  }
-
-protected:
-  EditorBase& mEditorBase;
-  bool mAllowedTransactionsToChangeSelection;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
-/***************************************************************************
- * stack based helper class for batching reflow and paint requests.
- */
-class MOZ_RAII AutoUpdateViewBatch final
-{
-public:
-  explicit AutoUpdateViewBatch(EditorBase* aEditorBase
-                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mEditorBase(aEditorBase)
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    NS_ASSERTION(mEditorBase, "null mEditorBase pointer!");
-
-    if (mEditorBase) {
-      mEditorBase->BeginUpdateViewBatch();
-    }
-  }
-
-  ~AutoUpdateViewBatch()
-  {
-    if (mEditorBase) {
-      mEditorBase->EndUpdateViewBatch();
-    }
-  }
-
-protected:
-  EditorBase* mEditorBase;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class MOZ_STACK_CLASS AutoRangeArray final
 {
 public:
   explicit AutoRangeArray(dom::Selection* aSelection)
   {
     if (!aSelection) {
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -6,17 +6,16 @@
 
 #include <math.h>
 
 #include "HTMLEditorObjectResizerUtils.h"
 #include "HTMLEditRules.h"
 #include "HTMLEditUtils.h"
 #include "TextEditUtils.h"
 #include "mozilla/EditAction.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEditRules.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsAlgorithm.h"
@@ -43,17 +42,17 @@
 
 namespace mozilla {
 
 using namespace dom;
 
 nsresult
 HTMLEditor::SetSelectionToAbsoluteOrStatic(bool aEnabled)
 {
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       aEnabled ?
                                         EditSubAction::eSetPositionToAbsolute :
                                         EditSubAction::eSetPositionToStatic,
                                       nsIEditor::eNext);
 
   // the line below does not match the code; should it be removed?
@@ -144,17 +143,17 @@ HTMLEditor::SetZIndex(Element& aElement,
   zIndexStr.AppendInt(aZindex);
 
   mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::z_index, zIndexStr);
 }
 
 nsresult
 HTMLEditor::AddZIndex(int32_t aChange)
 {
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       aChange < 0 ?
                                         EditSubAction::eDecreaseZIndex :
                                         EditSubAction::eIncreaseZIndex,
                                       nsIEditor::eNext);
 
   // brade: can we get rid of this comment?
@@ -475,17 +474,17 @@ HTMLEditor::SetFinalPosition(int32_t aX,
 
   SnapToGrid(newX, newY);
 
   nsAutoString x, y;
   x.AppendInt(newX);
   y.AppendInt(newY);
 
   // we want one transaction only from a user's point of view
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
     return NS_ERROR_FAILURE;
   }
   OwningNonNull<Element> absolutelyPositionedObject =
     *mAbsolutelyPositionedObject;
   mCSSEditUtils->SetCSSPropertyPixels(*absolutelyPositionedObject,
                                       *nsGkAtoms::top, newY);
@@ -533,17 +532,17 @@ HTMLEditor::SetPositionToAbsoluteOrStati
   }
 
   return SetPositionToStatic(aElement);
 }
 
 nsresult
 HTMLEditor::SetPositionToAbsolute(Element& aElement)
 {
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   int32_t x, y;
   GetElementOrigin(aElement, x, y);
 
   mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::position,
                                 NS_LITERAL_STRING("absolute"));
 
   AddPositioningOffset(x, y);
@@ -566,17 +565,17 @@ HTMLEditor::SetPositionToAbsolute(Elemen
     }
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::SetPositionToStatic(Element& aElement)
 {
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::position,
                                    EmptyString());
   mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::top,
                                    EmptyString());
   mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::left,
                                    EmptyString());
   mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::z_index,
@@ -631,17 +630,17 @@ HTMLEditor::GetGridSize(uint32_t* aSize)
 }
 
 // self-explanatory
 void
 HTMLEditor::SetTopAndLeft(Element& aElement,
                           int32_t aX,
                           int32_t aY)
 {
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   mCSSEditUtils->SetCSSPropertyPixels(aElement, *nsGkAtoms::left, aX);
   mCSSEditUtils->SetCSSPropertyPixels(aElement, *nsGkAtoms::top, aY);
 }
 
 nsresult
 HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(Element& aElement,
                                                          nsAString& aReturn)
 {
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -4017,17 +4017,17 @@ HTMLEditRules::WillMakeList(const nsAStr
 
 nsresult
 HTMLEditRules::MakeList(nsAtom& aListType,
                         bool aEntireList,
                         const nsAString* aBulletType,
                         bool* aCancel,
                         nsAtom& aItemType)
 {
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv =
     GetListActionNodes(arrayOfNodes,
                        aEntireList ? EntireList::yes : EntireList::no,
                        TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -4102,17 +4102,17 @@ HTMLEditRules::MakeList(nsAtom& aListTyp
     }
     if (NS_WARN_IF(!theListItem)) {
       return NS_ERROR_FAILURE;
     }
 
     // remember our new block for postprocessing
     mNewBlock = theListItem;
     // Put selection in new list item and don't restore the Selection.
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRef().Collapse(EditorRawDOMPoint(theListItem, 0), error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!error.Failed())) {
       return error.StealNSResult();
@@ -4432,17 +4432,17 @@ HTMLEditRules::WillRemoveList(bool* aCan
   *aCancel = false;
   *aHandled = true;
 
   nsresult rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditSubAction::eCreateOrChangeList);
 
   // use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   rv = GetListActionNodes(arrayOfNodes, EntireList::no, TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -4536,17 +4536,17 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
   AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
 
   // Contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   rv = GetNodesFromSelection(EditSubAction::eCreateOrRemoveBlock, arrayOfNodes,
                              TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -4607,17 +4607,17 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brContent)) {
         return NS_ERROR_FAILURE;
       }
       // Put selection at the split point
       EditorRawDOMPoint atBrNode(brContent);
       // Don't restore the selection
-      selectionRestorer.Abort();
+      restoreSelectionLater.Abort();
       ErrorResult error;
       SelectionRef().Collapse(atBrNode, error);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         error.SuppressException();
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
@@ -4666,17 +4666,17 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       arrayOfNodes.RemoveElementAt(0);
     }
     // Don't restore the selection
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     // Put selection in new block
     rv = SelectionRef().Collapse(block, 0);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -4788,17 +4788,17 @@ HTMLEditRules::WillCSSIndent(bool* aCanc
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::IndentAroundSelectionWithCSS()
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
   nsTArray<OwningNonNull<nsRange>> arrayOfRanges;
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
 
   // short circuit: detect case of collapsed selection inside an <li>.
   // just sublist that <li>.  This prevents bug 97797.
 
   nsCOMPtr<Element> liNode;
   if (SelectionRef().IsCollapsed()) {
@@ -4875,17 +4875,17 @@ HTMLEditRules::IndentAroundSelectionWith
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       arrayOfNodes.RemoveElementAt(0);
     }
     // put selection in new block
     EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
     // Don't restore the selection
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRef().Collapse(atStartOfTheBlock, error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!error.Failed())) {
       return error.StealNSResult();
@@ -5092,17 +5092,17 @@ HTMLEditRules::WillHTMLIndent(bool* aCan
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::IndentAroundSelectionWithHTML()
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditSubAction::eIndent);
@@ -5155,17 +5155,17 @@ HTMLEditRules::IndentAroundSelectionWith
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       arrayOfNodes.RemoveElementAt(0);
     }
     EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
     // Don't restore the selection
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRef().Collapse(atStartOfTheBlock, error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!error.Failed())) {
       return error.StealNSResult();
@@ -5472,17 +5472,17 @@ HTMLEditRules::WillOutdent(bool* aCancel
   return NS_OK;
 }
 
 SplitRangeOffFromNodeResult
 HTMLEditRules::OutdentAroundSelection()
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   bool useCSS = HTMLEditorRef().IsCSSEnabled();
 
   // Convert the selection ranges into "promoted" selection ranges: this
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
@@ -6112,17 +6112,17 @@ HTMLEditRules::WillAlign(const nsAString
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType)
 {
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   // Convert the selection ranges into "promoted" selection ranges: This
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range
   nsTArray<OwningNonNull<nsINode>> nodeArray;
   nsresult rv =
     GetNodesFromSelection(EditSubAction::eSetOrClearAlignment, nodeArray,
@@ -6240,17 +6240,17 @@ HTMLEditRules::AlignContentsAtSelection(
     // Put in a moz-br so that it won't get deleted
     CreateElementResult createMozBrResult =
       CreateMozBR(EditorRawDOMPoint(div, 0));
     if (NS_WARN_IF(createMozBrResult.Failed())) {
       return createMozBrResult.Rv();
     }
     EditorRawDOMPoint atStartOfDiv(div, 0);
     // Don't restore the selection
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRef().Collapse(atStartOfDiv, error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
@@ -10993,17 +10993,17 @@ HTMLEditRules::PrepareToMakeElementAbsol
                  bool* aHandled,
                  RefPtr<Element>* aTargetElement)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   MOZ_ASSERT(aHandled);
   MOZ_ASSERT(aTargetElement);
 
-  AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+  AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
   // Convert the selection ranges into "promoted" selection ranges: this
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range.
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditSubAction::eSetPositionToAbsolute);
@@ -11057,17 +11057,17 @@ HTMLEditRules::PrepareToMakeElementAbsol
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       arrayOfNodes.RemoveElementAt(0);
     }
     // Put selection in new block
     *aHandled = true;
     // Don't restore the selection
-    selectionRestorer.Abort();
+    restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRef().Collapse(RawRangeBoundary(positionedDiv, 0), error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
@@ -11298,17 +11298,17 @@ HTMLEditRules::WillRemoveAbsolutePositio
 
   RefPtr<Element> element =
     HTMLEditorRef().GetAbsolutelyPositionedSelectionContainer();
   if (NS_WARN_IF(!element)) {
     return NS_ERROR_FAILURE;
   }
 
   {
-    AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+    AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
     nsresult rv = HTMLEditorRef().SetPositionToAbsoluteOrStatic(*element, false);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -11344,17 +11344,17 @@ HTMLEditRules::WillRelativeChangeZIndex(
 
   RefPtr<Element> element =
     HTMLEditorRef().GetAbsolutelyPositionedSelectionContainer();
   if (NS_WARN_IF(!element)) {
     return NS_ERROR_FAILURE;
   }
 
   {
-    AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
+    AutoSelectionRestorer restoreSelectionLater(SelectionRef(), HTMLEditorRef());
 
     int32_t zIndex;
     nsresult rv = HTMLEditorRef().RelativeChangeElementZIndex(*element, aChange,
                                                               &zIndex);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1105,17 +1105,17 @@ HTMLEditor::UpdateBaseURL()
     doc->SetBaseURI(doc->GetDocumentURI());
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::OnInputLineBreak()
 {
-  AutoPlaceholderBatch batch(this, nsGkAtoms::TypingTxnName);
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertBrElementAtSelectionWithTransaction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
@@ -1321,17 +1321,17 @@ HTMLEditor::ReplaceHeadContentsWithSourc
   // Windows linebreaks: Map CRLF to LF:
   inputString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
                                NS_LITERAL_STRING("\n"));
 
   // Mac linebreaks: Map any remaining CR to LF:
   inputString.ReplaceSubstring(NS_LITERAL_STRING("\r"),
                                NS_LITERAL_STRING("\n"));
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // Get the first range in the selection, for context:
   RefPtr<nsRange> range = selection->GetRangeAt(0);
   if (NS_WARN_IF(!range)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult err;
@@ -1417,17 +1417,17 @@ HTMLEditor::RebuildDocumentFromSource(co
     foundclosehead = false;
   }
   // a valid close head appears before a found body
   if (foundbody && beginclosehead.get() > beginbody.get()) {
     foundclosehead = false;
   }
 
   // Time to change the document
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   nsReadingIterator<char16_t> endtotal;
   aSourceString.EndReading(endtotal);
 
   if (foundhead) {
     if (foundclosehead) {
       nsresult rv =
         ReplaceHeadContentsWithSourceWithTransaction(
@@ -1627,17 +1627,17 @@ HTMLEditor::InsertElementAtSelection(Ele
                                      bool aDeleteSelection)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
 
   CommitComposition();
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertElement,
                                       nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   if (!selection) {
     return NS_ERROR_FAILURE;
   }
@@ -1819,17 +1819,17 @@ HTMLEditor::SelectContentInternal(Select
   }
 
   nsINode* parent = aContentToSelect.GetParentNode();
   if (NS_WARN_IF(!parent)) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't notify selection change at collapse.
-  AutoUpdateViewBatch notifySelectionChangeOnce(this);
+  AutoUpdateViewBatch notifySelectionChangeOnce(*this);
 
   // XXX Perhaps, Selection should have SelectNode(nsIContent&).
   int32_t offsetInParent = parent->ComputeIndexOf(&aContentToSelect);
 
   // Collapse selection to just before desired element,
   nsresult rv = aSelection.Collapse(parent, offsetInParent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -2126,17 +2126,17 @@ HTMLEditor::MakeOrChangeList(const nsASt
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eCreateOrChangeList,
                                       nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
@@ -2221,17 +2221,17 @@ HTMLEditor::RemoveList(const nsAString&)
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eRemoveList,
                                       nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
@@ -2257,17 +2257,17 @@ HTMLEditor::MakeDefinitionListItemWithTr
   MOZ_ASSERT(&aTagName == nsGkAtoms::dt ||
              &aTagName == nsGkAtoms::dd);
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier
     maybeTopLevelEditSubAction(*this,
                                EditSubAction::eCreateOrChangeDefinitionList,
                                nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
@@ -2299,17 +2299,17 @@ HTMLEditor::InsertBasicBlockWithTransact
   MOZ_ASSERT(&aTagName != nsGkAtoms::dd &&
              &aTagName != nsGkAtoms::dt);
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       EditSubAction::eCreateOrRemoveBlock,
                                       nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
@@ -2403,32 +2403,32 @@ HTMLEditor::Indent(const nsAString& aInd
 
 nsresult
 HTMLEditor::IndentAsAction()
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   nsresult rv = IndentOrOutdentAsSubAction(EditSubAction::eIndent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::OutdentAsAction()
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   nsresult rv = IndentOrOutdentAsSubAction(EditSubAction::eOutdent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
@@ -2540,17 +2540,17 @@ HTMLEditor::IndentOrOutdentAsSubAction(E
 //TODO: IMPLEMENT ALIGNMENT!
 
 NS_IMETHODIMP
 HTMLEditor::Align(const nsAString& aAlignType)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       EditSubAction::eSetOrClearAlignment,
                                       nsIEditor::eNext);
 
   bool cancel, handled;
 
   // Find out if the selection is collapsed:
@@ -2954,17 +2954,17 @@ HTMLEditor::InsertLinkAroundSelection(El
 
   nsAutoString href;
   anchor->GetHref(href);
   if (href.IsEmpty()) {
     return NS_OK;
   }
 
   nsresult rv;
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // Set all attributes found on the supplied anchor element
   RefPtr<nsDOMAttributeMap> attrMap = anchor->Attributes();
   NS_ENSURE_TRUE(attrMap, NS_ERROR_FAILURE);
 
   uint32_t count = attrMap->Length();
   nsAutoString value;
 
@@ -4468,21 +4468,21 @@ HTMLEditor::SetCSSBackgroundColorWithTra
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   bool isCollapsed = selection->IsCollapsed();
 
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertElement,
                                       nsIEditor::eNext);
-  AutoSelectionRestorer selectionRestorer(selection, this);
+  AutoSelectionRestorer restoreSelectionLater(*selection, *this);
   AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
   // XXX Although, this method may set background color of ancestor block
   //     element, using EditSubAction::eSetTextProperty.
   bool cancel, handled;
   EditSubActionInfo subActionInfo(EditSubAction::eSetTextProperty);
   nsresult rv =
     rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -331,26 +331,25 @@ public:
   int32_t GetZIndex(Element& aElement);
 
   /**
    * adds aChange to the z-index of the currently positioned element.
    * @param aChange [IN] relative change to apply to current z-index
    */
   nsresult AddZIndex(int32_t aChange);
 
-  nsresult SetInlineProperty(nsAtom& aProperty,
-                             nsAtom* aAttribute,
-                             const nsAString& aValue)
-  {
-    nsresult rv = SetInlinePropertyInternal(aProperty, aAttribute, aValue);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    return NS_OK;
-  }
+  /**
+   * SetInlinePropertyAsAction() sets a property which changes inline style of
+   * text.  E.g., bold, italic, super and sub.
+   * This automatically removes exclusive style, however, treats all changes
+   * as a transaction.
+   */
+  nsresult SetInlinePropertyAsAction(nsAtom& aProperty,
+                                     nsAtom* aAttribute,
+                                     const nsAString& aValue);
 
   nsresult GetInlineProperty(nsAtom* aProperty,
                              nsAtom* aAttribute,
                              const nsAString& aValue,
                              bool* aFirst,
                              bool* aAny,
                              bool* aAll);
   nsresult GetInlinePropertyWithAttrValue(nsAtom* aProperty,
--- a/editor/libeditor/HTMLEditorCommands.cpp
+++ b/editor/libeditor/HTMLEditorCommands.cpp
@@ -270,33 +270,22 @@ StyleUpdatingCommand::ToggleState(HTMLEd
 
     nsresult rv = aHTMLEditor->RemoveInlineProperty(mTagName, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  // Superscript and Subscript styles are mutually exclusive.
-  AutoTransactionBatch bundleAllTransactions(*aHTMLEditor);
-
-  if (mTagName == nsGkAtoms::sub || mTagName == nsGkAtoms::sup) {
-    nsresult rv = aHTMLEditor->RemoveInlineProperty(mTagName, nullptr);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
   nsresult rv =
-    aHTMLEditor->SetInlineProperty(*mTagName, nullptr, EmptyString());
+    aHTMLEditor->SetInlinePropertyAsAction(*mTagName, nullptr, EmptyString());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
-
-  return rv;
+  return NS_OK;
 }
 
 ListCommand::ListCommand(nsAtom* aTagName)
   : StateUpdatingCommandBase(aTagName)
 {
 }
 
 nsresult
@@ -741,18 +730,19 @@ FontFaceStateCommand::SetState(HTMLEdito
                                const nsString& newState)
 {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (newState.EqualsLiteral("tt")) {
     // The old "teletype" attribute
-    nsresult rv = aHTMLEditor->SetInlineProperty(*nsGkAtoms::tt, nullptr,
-                                                 EmptyString());
+    nsresult rv =
+      aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::tt, nullptr,
+                                             EmptyString());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // Clear existing font face
     rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font, nsGkAtoms::face);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -768,19 +758,18 @@ FontFaceStateCommand::SetState(HTMLEdito
   if (newState.IsEmpty() || newState.EqualsLiteral("normal")) {
     rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font, nsGkAtoms::face);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  rv = aHTMLEditor->SetInlineProperty(*nsGkAtoms::font,
-                                      nsGkAtoms::face,
-                                      newState);
+  rv = aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::font, nsGkAtoms::face,
+                                              newState);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 FontSizeStateCommand::FontSizeStateCommand()
   : MultiStateCommandBase()
@@ -831,19 +820,19 @@ FontSizeStateCommand::SetState(HTMLEdito
 {
   if (NS_WARN_IF(!aHTMLEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (!newState.IsEmpty() &&
       !newState.EqualsLiteral("normal") &&
       !newState.EqualsLiteral("medium")) {
-    nsresult rv = aHTMLEditor->SetInlineProperty(*nsGkAtoms::font,
-                                                 nsGkAtoms::size,
-                                                 newState);
+    nsresult rv =
+      aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::font, nsGkAtoms::size,
+                                             newState);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   // remove any existing font size, big or small
   nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
@@ -904,19 +893,19 @@ FontColorStateCommand::SetState(HTMLEdit
     nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
                                                     nsGkAtoms::color);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  nsresult rv = aHTMLEditor->SetInlineProperty(*nsGkAtoms::font,
-                                               nsGkAtoms::color,
-                                               newState);
+  nsresult rv =
+    aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::font, nsGkAtoms::color,
+                                           newState);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 HighlightColorStateCommand::HighlightColorStateCommand()
   : MultiStateCommandBase()
@@ -956,19 +945,19 @@ HighlightColorStateCommand::SetState(HTM
     nsresult rv = aHTMLEditor->RemoveInlineProperty(nsGkAtoms::font,
                                                     nsGkAtoms::bgcolor);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  nsresult rv = aHTMLEditor->SetInlineProperty(*nsGkAtoms::font,
-                                               nsGkAtoms::bgcolor,
-                                               newState);
+  nsresult rv =
+    aHTMLEditor->SetInlinePropertyAsAction(*nsGkAtoms::font, nsGkAtoms::bgcolor,
+                                           newState);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HighlightColorStateCommand::IsCommandEnabled(const char* aCommandName,
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -91,17 +91,17 @@ static nsresult FindTargetNode(nsINode* 
 
 nsresult
 HTMLEditor::LoadHTML(const nsAString& aInputString)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // force IME commit; set up rules sniffing and batching
   CommitComposition();
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       EditSubAction::eInsertHTMLSource,
                                       nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
@@ -195,17 +195,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // Prevent the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // force IME commit; set up rules sniffing and batching
   CommitComposition();
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::ePasteHTMLContent,
                                       nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
@@ -319,17 +319,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
     }
   } else {
     // Delete whole cells: we will replace with new table content.
 
     // Braces for artificial block to scope AutoSelectionRestorer.
     // Save current selection since DeleteTableCellWithTransaction() perturbs
     // it.
     {
-      AutoSelectionRestorer selectionRestorer(selection, this);
+      AutoSelectionRestorer restoreSelectionLater(*selection, *this);
       rv = DeleteTableCellWithTransaction(1);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
     // collapse selection to beginning of deleted table content
     selection->CollapseToStart(IgnoreErrors());
   }
@@ -1004,17 +1004,17 @@ HTMLEditor::BlobReader::OnResult(const n
   nsString blobType;
   mBlob->GetType(blobType);
 
   NS_ConvertUTF16toUTF8 type(blobType);
   nsAutoString stuffToPaste;
   nsresult rv = ImgFromData(type, aResult, stuffToPaste);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  AutoPlaceholderBatch beginBatching(mHTMLEditor);
+  AutoPlaceholderBatch treatAsOneTransaction(*mHTMLEditor);
   rv = mHTMLEditor->DoInsertHTMLWithContext(stuffToPaste, EmptyString(),
                                             EmptyString(),
                                             NS_LITERAL_STRING(kFileMime),
                                             mSourceDoc,
                                             mDestinationNode, mDestOffset,
                                             mDoDeleteSelection,
                                             mIsSafe, false);
   return rv;
@@ -1190,17 +1190,17 @@ HTMLEditor::InsertObject(const nsACStrin
       rv = imageStream->Close();
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsAutoString stuffToPaste;
     rv = ImgFromData(type, imageData, stuffToPaste);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    AutoPlaceholderBatch beginBatching(this);
+    AutoPlaceholderBatch treatAsOneTransaction(*this);
     rv = DoInsertHTMLWithContext(stuffToPaste, EmptyString(), EmptyString(),
                                  NS_LITERAL_STRING(kFileMime),
                                  aSourceDoc,
                                  aDestinationNode, aDestOffset,
                                  aDoDeleteSelection,
                                  aIsSafe, false);
   }
 
@@ -1242,17 +1242,17 @@ HTMLEditor::InsertFromTransferable(nsITr
       if (textDataObj && len > 0) {
         nsAutoCString cfhtml;
         textDataObj->GetData(cfhtml);
         NS_ASSERTION(cfhtml.Length() <= (len), "Invalid length!");
         nsString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
 
         rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
-          AutoPlaceholderBatch beginBatching(this);
+          AutoPlaceholderBatch treatAsOneTransaction(*this);
           // If we have our private HTML flavor, we will only use the fragment
           // from the CF_HTML. The rest comes from the clipboard.
           if (havePrivateHTMLFlavor) {
             rv = DoInsertHTMLWithContext(cffragment,
                                          aContextStr, aInfoStr, flavor,
                                          aSourceDoc,
                                          nullptr, 0,
                                          aDoDeleteSelection,
@@ -1292,17 +1292,17 @@ HTMLEditor::InsertFromTransferable(nsITr
           nsAutoCString text;
           textDataObj->GetData(text);
           NS_ASSERTION(text.Length() <= len, "Invalid length!");
           stuffToPaste.Assign(NS_ConvertUTF8toUTF16(Substring(text, 0, len)));
         }
       }
 
       if (!stuffToPaste.IsEmpty()) {
-        AutoPlaceholderBatch beginBatching(this);
+        AutoPlaceholderBatch treatAsOneTransaction(*this);
         if (bestFlavor.EqualsLiteral(kHTMLMime)) {
           rv = DoInsertHTMLWithContext(stuffToPaste,
                                        aContextStr, aInfoStr, flavor,
                                        aSourceDoc,
                                        nullptr, 0,
                                        aDoDeleteSelection,
                                        isSafe);
         } else {
@@ -1377,17 +1377,17 @@ HTMLEditor::InsertFromDataTransfer(DataT
         nsAutoString text;
         GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
         NS_ConvertUTF16toUTF8 cfhtml(text);
 
         nsString cfcontext, cffragment, cfselection; // cfselection left emtpy for now
 
         nsresult rv = ParseCFHTML(cfhtml, getter_Copies(cffragment), getter_Copies(cfcontext));
         if (NS_SUCCEEDED(rv) && !cffragment.IsEmpty()) {
-          AutoPlaceholderBatch beginBatching(this);
+          AutoPlaceholderBatch treatAsOneTransaction(*this);
 
           if (hasPrivateHTMLFlavor) {
             // If we have our private HTML flavor, we will only use the fragment
             // from the CF_HTML. The rest comes from the clipboard.
             nsAutoString contextString, infoString;
             GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
             GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
             return DoInsertHTMLWithContext(cffragment,
@@ -1406,34 +1406,34 @@ HTMLEditor::InsertFromDataTransfer(DataT
           }
         }
       } else if (type.EqualsLiteral(kHTMLMime)) {
         nsAutoString text, contextString, infoString;
         GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), aIndex, contextString);
         GetStringFromDataTransfer(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), aIndex, infoString);
 
-        AutoPlaceholderBatch beginBatching(this);
+        AutoPlaceholderBatch treatAsOneTransaction(*this);
         if (type.EqualsLiteral(kHTMLMime)) {
           return DoInsertHTMLWithContext(text,
                                          contextString, infoString, type,
                                          aSourceDoc,
                                          aDestinationNode, aDestOffset,
                                          aDoDeleteSelection,
                                          isSafe);
         }
       }
     }
 
     if (type.EqualsLiteral(kTextMime) ||
         type.EqualsLiteral(kMozTextInternal)) {
       nsAutoString text;
       GetStringFromDataTransfer(aDataTransfer, type, aIndex, text);
 
-      AutoPlaceholderBatch beginBatching(this);
+      AutoPlaceholderBatch treatAsOneTransaction(*this);
       return InsertTextAt(text, aDestinationNode, aDestOffset, aDoDeleteSelection);
     }
   }
 
   return NS_OK;
 }
 
 bool
@@ -1700,17 +1700,17 @@ HTMLEditor::PasteAsQuotationAsAction(int
   if (IsPlaintextEditor()) {
     // XXX In this case, we don't dispatch ePaste event.  Why?
     return PasteAsPlaintextQuotation(aClipboardType);
   }
 
   // If it's not in plain text edit mode, paste text into new
   // <blockquote type="cite"> element after removing selection.
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertQuotation,
                                       nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
@@ -1794,31 +1794,31 @@ HTMLEditor::PasteAsPlaintextQuotation(in
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (flav.EqualsLiteral(kUnicodeMime)) {
     nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
     if (textDataObj && len > 0) {
       nsAutoString stuffToPaste;
       textDataObj->GetData(stuffToPaste);
       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
-      AutoPlaceholderBatch beginBatching(this);
+      AutoPlaceholderBatch treatAsOneTransaction(*this);
       rv = InsertAsPlaintextQuotation(stuffToPaste, true, 0);
     }
   }
 
   return rv;
 }
 
 nsresult
 HTMLEditor::InsertTextWithQuotations(const nsAString& aStringToInsert)
 {
   // The whole operation should be undoable in one transaction:
   // XXX Why isn't enough to use only AutoPlaceholderBatch here?
   AutoTransactionBatch bundleAllTransactions(*this);
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   nsresult rv = InsertTextWithQuotationsInternal(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
@@ -1911,17 +1911,17 @@ HTMLEditor::InsertTextWithQuotationsInte
 
   return rv;
 }
 
 nsresult
 HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText,
                               nsINode** aNodeInserted)
 {
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   if (IsPlaintextEditor()) {
     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
   }
 
   nsAutoString citation;
   return InsertAsCitedQuotation(aQuotedText, citation, false,
                                 aNodeInserted);
 }
@@ -2058,31 +2058,31 @@ HTMLEditor::Rewrap(bool aRespectNewlines
     DebugOnly<nsresult> rv = SelectAllInternal();
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),  "Failed to select all text");
   }
 
   // The whole operation in InsertTextWithQuotationsInternal() should be
   // undoable in one transaction.
   // XXX Why isn't enough to use only AutoPlaceholderBatch here?
   AutoTransactionBatch bundleAllTransactions(*this);
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   rv = InsertTextWithQuotationsInternal(wrapped);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
                                    const nsAString& aCitation,
                                    bool aInsertHTML,
                                    nsINode** aNodeInserted)
 {
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // Don't let anyone insert HTML when we're in plaintext mode.
   if (IsPlaintextEditor()) {
     NS_ASSERTION(!aInsertHTML,
       "InsertAsCitedQuotation: trying to insert html into plaintext editor");
     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
   }
 
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -3,17 +3,16 @@
  * 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 "mozilla/HTMLEditor.h"
 #include "HTMLEditorObjectResizerUtils.h"
 
 #include "HTMLEditUtils.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/MouseEvent.h"
 #include "mozilla/dom/EventTarget.h"
 #include "nsAString.h"
@@ -1073,17 +1072,17 @@ HTMLEditor::SetFinalSize(int32_t aX,
   bool setWidth  = !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth);
   bool setHeight = !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight);
 
   int32_t x, y;
   x = left - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderLeft+mResizedObjectMarginLeft : 0);
   y = top - ((mResizedObjectIsAbsolutelyPositioned) ? mResizedObjectBorderTop+mResizedObjectMarginTop : 0);
 
   // we want one transaction only from a user's point of view
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   if (mResizedObjectIsAbsolutelyPositioned) {
     if (setHeight) {
       mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::top, y);
     }
     if (setWidth) {
       mCSSEditUtils->SetCSSPropertyPixels(*mResizedObject, *nsGkAtoms::left, x);
     }
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -49,16 +49,43 @@ bool
 HTMLEditor::IsEmptyTextNode(nsINode& aNode)
 {
   bool isEmptyTextNode = false;
   return EditorBase::IsTextNode(&aNode) &&
          NS_SUCCEEDED(IsEmptyNode(&aNode, &isEmptyTextNode)) &&
          isEmptyTextNode;
 }
 
+nsresult
+HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
+                                      nsAtom* aAttribute,
+                                      const nsAString& aValue)
+{
+  AutoTransactionBatch treatAsOneTransaction(*this);
+
+  if (&aProperty == nsGkAtoms::sup) {
+    // Superscript and Subscript styles are mutually exclusive.
+    nsresult rv = RemoveInlinePropertyInternal(nsGkAtoms::sub, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else if (&aProperty == nsGkAtoms::sub) {
+    // Superscript and Subscript styles are mutually exclusive.
+    nsresult rv = RemoveInlinePropertyInternal(nsGkAtoms::sup, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+  nsresult rv = SetInlinePropertyInternal(aProperty, aAttribute, aValue);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 HTMLEditor::SetInlineProperty(const nsAString& aProperty,
                               const nsAString& aAttribute,
                               const nsAString& aValue)
 {
   RefPtr<nsAtom> property = NS_Atomize(aProperty);
   if (NS_WARN_IF(!property)) {
     return NS_ERROR_INVALID_ARG;
@@ -86,21 +113,21 @@ HTMLEditor::SetInlinePropertyInternal(ns
 
   if (selection->IsCollapsed()) {
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
     mTypeInState->SetProp(&aProperty, aAttribute, aValue);
     return NS_OK;
   }
 
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertElement,
                                       nsIEditor::eNext);
-  AutoSelectionRestorer selectionRestorer(selection, this);
+  AutoSelectionRestorer restoreSelectionLater(*selection, *this);
   AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
   bool cancel, handled;
   EditSubActionInfo subActionInfo(EditSubAction::eSetTextProperty);
   // Protect the edit rules object from dying
   nsresult rv =
     rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1211,17 +1238,17 @@ HTMLEditor::GetInlinePropertyWithAttrVal
   if (!aValue.IsEmpty())
     val = &aValue;
   return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll, &outValue);
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveAllInlineProperties()
 {
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       EditSubAction::eRemoveAllTextProperties,
                                       nsIEditor::eNext);
 
   nsresult rv = RemoveInlinePropertyInternal(nullptr, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
@@ -1263,21 +1290,21 @@ HTMLEditor::RemoveInlinePropertyInternal
     if (aProperty) {
       mTypeInState->ClearProp(aProperty, aAttribute);
     } else {
       mTypeInState->ClearAllProps();
     }
     return NS_OK;
   }
 
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eRemoveTextProperty,
                                       nsIEditor::eNext);
-  AutoSelectionRestorer selectionRestorer(selection, this);
+  AutoSelectionRestorer restoreSelectionLater(*selection, *this);
   AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
   bool cancel, handled;
   EditSubActionInfo subActionInfo(EditSubAction::eRemoveTextProperty);
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
   nsresult rv =
     rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
@@ -1433,21 +1460,21 @@ HTMLEditor::RelativeFontChange(FontSize 
 
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
     mTypeInState->SetProp(&atom, nullptr, EmptyString());
     return NS_OK;
   }
 
   // Wrap with txn batching, rules sniffing, and selection preservation code
-  AutoPlaceholderBatch batchIt(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eSetTextProperty,
                                       nsIEditor::eNext);
-  AutoSelectionRestorer selectionRestorer(selection, this);
+  AutoSelectionRestorer restoreSelectionLater(*selection, *this);
   AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
   // Loop through the ranges in the selection
   AutoRangeArray arrayOfRanges(selection);
   for (auto& range : arrayOfRanges.mRanges) {
     // Adjust range to include any ancestors with entirely selected children
     nsresult rv = PromoteInlineRange(*range);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -446,17 +446,17 @@ HTMLEditor::InsertTableColumnsWithTransa
   ErrorResult error;
   TableSize tableSize(*this, *table, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
   // Should not be empty since we've already found a cell.
   MOZ_ASSERT(!tableSize.IsEmpty());
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent auto insertion of <br> element in new cell until we're done.
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertNode,
                                       nsIEditor::eNext);
 
   switch (aInsertPosition) {
     case InsertPosition::eBeforeSelectedCell:
       break;
@@ -635,17 +635,17 @@ HTMLEditor::InsertTableRowsWithTransacti
   ErrorResult error;
   TableSize tableSize(*this, *table, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
   // Should not be empty since we've already found a cell.
   MOZ_ASSERT(!tableSize.IsEmpty());
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent auto insertion of BR in new cell until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertNode,
                                       nsIEditor::eNext);
 
   switch (aInsertPosition) {
     case InsertPosition::eBeforeSelectedCell:
       break;
@@ -858,17 +858,17 @@ HTMLEditor::DeleteTable()
                                nullptr, nullptr, nullptr, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (NS_WARN_IF(!selection) || NS_WARN_IF(!table)) {
     return NS_ERROR_FAILURE;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   rv = DeleteTableElementAndChildrenWithTransaction(*selection, *table);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -899,17 +899,17 @@ HTMLEditor::DeleteTableCellWithTransacti
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (NS_WARN_IF(!table) || NS_WARN_IF(!cell)) {
     // Don't fail if we didn't find a table or cell.
     return NS_OK;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eDeleteNode,
                                       nsIEditor::eNext);
 
   ErrorResult error;
   RefPtr<Element> firstSelectedCellElement =
     GetFirstSelectedTableCellElement(*selection, error);
@@ -1174,17 +1174,17 @@ HTMLEditor::DeleteTableCellContentsWithT
     return rv;
   }
   if (NS_WARN_IF(!selection) || NS_WARN_IF(!cell)) {
     // Don't fail if no cell found.
     return NS_OK;
   }
 
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eDeleteNode,
                                       nsIEditor::eNext);
   // Don't let Rules System change the selection
   AutoTransactionsConserveSelection dontChangeSelection(*this);
 
   ErrorResult error;
@@ -1260,17 +1260,17 @@ HTMLEditor::DeleteSelectedTableColumnsWi
   }
 
   ErrorResult error;
   TableSize tableSize(*this, *table, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
 
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eDeleteNode,
                                       nsIEditor::eNext);
 
   // Shortcut the case of deleting all columns in table
   if (!startColIndex && aNumberOfColumnsToDelete >= tableSize.mColumnCount) {
@@ -1490,17 +1490,17 @@ HTMLEditor::DeleteSelectedTableRowsWithT
   }
 
   ErrorResult error;
   TableSize tableSize(*this, *table, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
 
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eDeleteNode,
                                       nsIEditor::eNext);
 
   // Shortcut the case of deleting all rows in table
   if (!startRowIndex && aNumberOfRowsToDelete >= tableSize.mRowCount) {
@@ -2142,17 +2142,17 @@ HTMLEditor::SplitTableCell()
                       actualRowSpan, actualColSpan);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Must have some span to split
   if (actualRowSpan <= 1 && actualColSpan <= 1) {
     return NS_OK;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent auto insertion of BR in new cell until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertNode,
                                       nsIEditor::eNext);
 
   // We reset selection
   AutoSelectionSetterAfterTableEdit setCaret(*this, table, startRowIndex,
                                              startColIndex, ePreviousColumn,
@@ -2396,32 +2396,32 @@ HTMLEditor::SplitCellIntoRows(Element* a
 NS_IMETHODIMP
 HTMLEditor::SwitchTableCellHeaderType(Element* aSourceCell,
                                       Element** aNewCell)
 {
   if (NS_WARN_IF(!aSourceCell)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent auto insertion of BR in new cell created by
   // ReplaceContainerAndCloneAttributesWithTransaction().
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertNode,
                                       nsIEditor::eNext);
 
   // Save current selection to restore when done.
   // This is needed so ReplaceContainerAndCloneAttributesWithTransaction()
   // can monitor selection when replacing nodes.
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
-  AutoSelectionRestorer selectionRestorer(selection, this);
+  AutoSelectionRestorer restoreSelectionLater(*selection, *this);
 
   // Set to the opposite of current type
   nsAtom* newCellName =
     aSourceCell->IsHTMLElement(nsGkAtoms::td) ? nsGkAtoms::th : nsGkAtoms::td;
 
   // This creates new node, moves children, copies attributes (true)
   //   and manages the selection!
   RefPtr<Element> newCell =
@@ -2454,17 +2454,17 @@ HTMLEditor::JoinTableCells(bool aMergeNo
                                &startRowIndex, &startColIndex);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (!table || !targetCell) {
     return NS_OK;
   }
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   //Don't let Rules System change the selection
   AutoTransactionsConserveSelection dontChangeSelection(*this);
 
   // Note: We dont' use AutoSelectionSetterAfterTableEdit here so the selection
   //  is retained after joining. This leaves the target cell selected
   //  as well as the "non-contiguous" cells, so user can see what happened.
 
   RefPtr<Selection> selection = GetSelection();
@@ -3084,19 +3084,19 @@ HTMLEditor::NormalizeTable(Selection& aS
 
   ErrorResult error;
   TableSize tableSize(*this, *tableElement, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   // Save current selection
-  AutoSelectionRestorer selectionRestorer(&aSelection, this);
-
-  AutoPlaceholderBatch beginBatching(this);
+  AutoSelectionRestorer restoreSelectionLater(aSelection, *this);
+
+  AutoPlaceholderBatch treateAsOneTransaction(*this);
   // Prevent auto insertion of BR in new cell until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertNode,
                                       nsIEditor::eNext);
 
   // XXX If there is a cell which has bigger or smaller "rowspan" or "colspan"
   //     values, FixBadRowSpan() will return error.  So, we can do nothing
   //     if the table needs normalization...
--- a/editor/libeditor/PlaceholderTransaction.h
+++ b/editor/libeditor/PlaceholderTransaction.h
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef PlaceholderTransaction_h
 #define PlaceholderTransaction_h
 
 #include "EditAggregateTransaction.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/Maybe.h"
 #include "nsIAbsorbingTransaction.h"
 #include "nsCOMPtr.h"
 #include "nsWeakPtr.h"
 
 namespace mozilla {
 
 class CompositionTransaction;
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_TextEditRules_h
 #define mozilla_TextEditRules_h
 
 #include "mozilla/EditAction.h"
+#include "mozilla/EditorBase.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h" // for nsIEditor::AsHTMLEditor()
 #include "mozilla/TextEditor.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditor.h"
 #include "nsINamed.h"
@@ -52,16 +53,23 @@ class Selection;
  *    false, the method have to return NS_ERROR_EDITOR_DESTROYED.  In other
  *    words, any methods which may change Selection or the DOM tree have to
  *    return nsresult directly or with simple class like EditActionResult.
  *    And such methods should be marked as MOZ_MUST_USE.
  */
 class TextEditRules : public nsITimerCallback
                     , public nsINamed
 {
+protected:
+  typedef EditorBase::AutoSelectionRestorer
+            AutoSelectionRestorer;
+  typedef EditorBase::AutoTopLevelEditSubActionNotifier
+            AutoTopLevelEditSubActionNotifier;
+  typedef EditorBase::AutoTransactionsConserveSelection
+            AutoTransactionsConserveSelection;
 public:
   typedef dom::Element Element;
   typedef dom::Selection Selection;
   typedef dom::Text Text;
   template<typename T> using OwningNonNull = OwningNonNull<T>;
 
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSINAMED
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -8,17 +8,16 @@
 #include "EditAggregateTransaction.h"
 #include "HTMLEditRules.h"
 #include "InternetCiter.h"
 #include "TextEditUtils.h"
 #include "gfxFontUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEditRules.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextServicesDocument.h"
@@ -415,28 +414,28 @@ TextEditor::HandleKeyPressEvent(WidgetKe
   aKeyboardEvent->PreventDefault();
   nsAutoString str(aKeyboardEvent->mCharCode);
   return OnInputText(str);
 }
 
 nsresult
 TextEditor::OnInputText(const nsAString& aStringToInsert)
 {
-  AutoPlaceholderBatch batch(this, nsGkAtoms::TypingTxnName);
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertTextAsSubAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 TextEditor::OnInputParagraphSeparator()
 {
-  AutoPlaceholderBatch batch(this, nsGkAtoms::TypingTxnName);
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertParagraphSeparatorAsAction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 template<typename PT, typename CT>
@@ -658,17 +657,17 @@ TextEditor::DeleteSelectionAsAction(EDir
   // Showing this assertion is fine if this method is called by outside via
   // mutation event listener or something.  Otherwise, this is called by
   // wrong method.
   NS_ASSERTION(!mPlaceholderBatch,
     "Should be called only when this is the only edit action of the operation "
     "unless mutation event listener nests some operations");
 
   // delete placeholder txns merge.
-  AutoPlaceholderBatch batch(this, nsGkAtoms::DeleteTxnName);
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::DeleteTxnName);
   nsresult rv = DeleteSelectionAsSubAction(aDirection, aStripWrappers);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
@@ -963,17 +962,17 @@ TextEditor::InsertTextAsAction(const nsA
 {
   // Showing this assertion is fine if this method is called by outside via
   // mutation event listener or something.  Otherwise, this is called by
   // wrong method.
   NS_ASSERTION(!mPlaceholderBatch,
     "Should be called only when this is the only edit action of the operation "
     "unless mutation event listener nests some operations");
 
-  AutoPlaceholderBatch batch(this, nullptr);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   nsresult rv = InsertTextAsSubAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
@@ -1040,17 +1039,17 @@ TextEditor::InsertParagraphSeparatorAsAc
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
                                       EditSubAction::eInsertParagraphSeparator,
                                       nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
@@ -1131,29 +1130,29 @@ TextEditor::InsertParagraphSeparatorAsAc
   return rv;
 }
 
 nsresult
 TextEditor::SetText(const nsAString& aString)
 {
   MOZ_ASSERT(aString.FindChar(static_cast<char16_t>('\r')) == kNotFound);
 
-  AutoPlaceholderBatch batch(this, nullptr);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
   nsresult rv = SetTextAsSubAction(aString);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 TextEditor::ReplaceTextAsAction(const nsAString& aString,
                                 nsRange* aReplaceRange /* = nullptr */)
 {
-  AutoPlaceholderBatch batch(this, nullptr);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // This should emulates inserting text for better undo/redo behavior.
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertText,
                                       nsIEditor::eNext);
 
   if (!aReplaceRange) {
     nsresult rv = SetTextAsSubAction(aString);
@@ -1165,17 +1164,17 @@ TextEditor::ReplaceTextAsAction(const ns
 
   if (NS_WARN_IF(aString.IsEmpty() && aReplaceRange->Collapsed())) {
     return NS_OK;
   }
 
   // Note that do not notify selectionchange caused by selecting all text
   // because it's preparation of our delete implementation so web apps
   // shouldn't receive such selectionchange before the first mutation.
-  AutoUpdateViewBatch preventSelectionChangeEvent(this);
+  AutoUpdateViewBatch preventSelectionChangeEvent(*this);
 
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   // Select the range but as far as possible, we should not create new range
   // even if it's part of special Selection.
@@ -1230,17 +1229,17 @@ TextEditor::SetTextAsSubAction(const nsA
   }
   if (cancel) {
     return NS_OK;
   }
   if (!handled) {
     // Note that do not notify selectionchange caused by selecting all text
     // because it's preparation of our delete implementation so web apps
     // shouldn't receive such selectionchange before the first mutation.
-    AutoUpdateViewBatch preventSelectionChangeEvent(this);
+    AutoUpdateViewBatch preventSelectionChangeEvent(*this);
 
     // We want to select trailing BR node to remove all nodes to replace all,
     // but TextEditor::SelectEntireDocument doesn't select that BR node.
     if (rules->DocumentIsEmpty()) {
       // if it's empty, don't select entire doc - that would select
       // the bogus node
       Element* rootElement = GetRoot();
       if (NS_WARN_IF(!rootElement)) {
@@ -1349,17 +1348,17 @@ TextEditor::OnCompositionChange(WidgetCo
     "UpdateIMEComposition() must be called without place holder batch");
   TextComposition::CompositionChangeEventHandlingMarker
     compositionChangeEventHandlingMarker(mComposition, &aCompsitionChangeEvent);
 
   RefPtr<nsCaret> caretP = presShell->GetCaret();
 
   nsresult rv;
   {
-    AutoPlaceholderBatch batch(this, nsGkAtoms::IMETxnName);
+    AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::IMETxnName);
 
     MOZ_ASSERT(mIsInEditSubAction,
       "AutoPlaceholderBatch should've notified the observes of before-edit");
     rv = InsertTextAsSubAction(aCompsitionChangeEvent.mData);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert new composition string");
 
     if (caretP) {
@@ -1622,17 +1621,17 @@ TextEditor::Undo(uint32_t aCount)
   // undoes the committing composition.
   if (GetComposition()) {
     return NS_OK;
   }
 
   // Protect the edit rules object from dying.
   RefPtr<TextEditRules> rules(mRules);
 
-  AutoUpdateViewBatch beginViewBatching(this);
+  AutoUpdateViewBatch preventSelectionChangeEvent(*this);
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv;
   {
@@ -1677,17 +1676,17 @@ TextEditor::Redo(uint32_t aCount)
   // the redo queue.  So, it becomes impossible to redo anything.
   if (GetComposition()) {
     return NS_OK;
   }
 
   // Protect the edit rules object from dying.
   RefPtr<TextEditRules> rules(mRules);
 
-  AutoUpdateViewBatch beginViewBatching(this);
+  AutoUpdateViewBatch preventSelectionChangeEvent(*this);
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv;
   {
@@ -1761,17 +1760,18 @@ TextEditor::FireClipboardEvent(EventMess
 
 NS_IMETHODIMP
 TextEditor::Cut()
 {
   bool actionTaken = false;
   if (FireClipboardEvent(eCut, nsIClipboard::kGlobalClipboard, &actionTaken)) {
     // XXX This transaction name is referred by PlaceholderTransaction::Merge()
     //     so that we need to keep using it here.
-    AutoPlaceholderBatch batch(this, nsGkAtoms::DeleteTxnName);
+    AutoPlaceholderBatch treatAsOneTransaction(*this,
+                                               *nsGkAtoms::DeleteTxnName);
     DeleteSelectionAsSubAction(eNone, eStrip);
   }
   return actionTaken ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 TextEditor::CanCut(bool* aCanCut)
 {
@@ -1981,17 +1981,17 @@ TextEditor::PasteAsQuotationAsAction(int
       !flav.EqualsLiteral(kMozTextInternal)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsString> textDataObj = do_QueryInterface(genericDataObj);
   if (textDataObj && len > 0) {
     nsAutoString stuffToPaste;
     textDataObj->GetData ( stuffToPaste );
-    AutoPlaceholderBatch beginBatching(this);
+    AutoPlaceholderBatch treatAsOneTransaction(*this);
     rv = InsertWithQuotationsAsSubAction(stuffToPaste);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   return NS_OK;
 }
 
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "mozilla/TextEditor.h"
 
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/SelectionState.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DragEvent.h"
 #include "mozilla/dom/Selection.h"
 #include "nsAString.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
@@ -120,17 +119,17 @@ TextEditor::InsertTextFromTransferable(n
     if (textDataObj && len > 0) {
       nsAutoString stuffToPaste;
       textDataObj->GetData(stuffToPaste);
       NS_ASSERTION(stuffToPaste.Length() <= (len/2), "Invalid length!");
 
       // Sanitize possible carriage returns in the string to be inserted
       nsContentUtils::PlatformToDOMLineBreaks(stuffToPaste);
 
-      AutoPlaceholderBatch beginBatching(this);
+      AutoPlaceholderBatch treatAsOneTransaction(*this);
       rv = InsertTextAt(stuffToPaste, nullptr, 0, true);
     }
   }
 
   // Try to scroll the selection into view if the paste/drop succeeded
 
   if (NS_SUCCEEDED(rv)) {
     ScrollSelectionIntoView(false);
@@ -150,17 +149,17 @@ TextEditor::InsertFromDataTransfer(DataT
   nsCOMPtr<nsIVariant> data;
   aDataTransfer->GetDataAtNoSecurityCheck(NS_LITERAL_STRING("text/plain"), aIndex,
                                           getter_AddRefs(data));
   if (data) {
     nsAutoString insertText;
     data->GetAsAString(insertText);
     nsContentUtils::PlatformToDOMLineBreaks(insertText);
 
-    AutoPlaceholderBatch beginBatching(this);
+    AutoPlaceholderBatch treatAsOneTransaction(*this);
     return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection);
   }
 
   return NS_OK;
 }
 
 nsresult
 TextEditor::OnDrop(DragEvent* aDropEvent)
@@ -198,17 +197,17 @@ TextEditor::OnDrop(DragEvent* aDropEvent
   }
 
   uint32_t numItems = dataTransfer->MozItemCount();
   if (numItems < 1) {
     return NS_ERROR_FAILURE;  // Nothing to drop?
   }
 
   // Combine any deletion and drop insertion into one transaction
-  AutoPlaceholderBatch beginBatching(this);
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   bool deleteSelection = false;
 
   // We have to figure out whether to delete and relocate caret only once
   // Parent and offset are under the mouse cursor
   nsCOMPtr<nsINode> newSelectionParent = aDropEvent->GetRangeParent();
   NS_ENSURE_TRUE(newSelectionParent, NS_ERROR_FAILURE);
 
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -5,17 +5,16 @@
 
 #include "WSRunObject.h"
 
 #include "TextEditUtils.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/EditorDOMPoint.h"
-#include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/SelectionState.h"
 
 #include "nsAString.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -1,22 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 WSRunObject_h
 #define WSRunObject_h
 
-#include "nsCOMPtr.h"
-#include "nsIEditor.h" // for EDirection
-#include "nsINode.h"
-#include "nscore.h"
-#include "mozilla/Attributes.h"
 #include "mozilla/dom/Text.h"
+#include "mozilla/EditorBase.h"
 #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
 
 namespace mozilla {
 
 class HTMLEditor;
 class HTMLEditRules;
 
 // class WSRunObject represents the entire whitespace situation
@@ -145,16 +141,20 @@ inline const WSType operator&(const WSTy
 inline const WSType operator|(const WSType::Enum& aLeft,
                               const WSType::Enum& aRight)
 {
   return WSType(aLeft) | WSType(aRight);
 }
 
 class MOZ_STACK_CLASS WSRunObject final
 {
+protected:
+  typedef EditorBase::AutoTransactionsConserveSelection
+            AutoTransactionsConserveSelection;
+
 public:
   enum BlockBoundary
   {
     kBeforeBlock,
     kBlockStart,
     kBlockEnd,
     kAfterBlock
   };
--- a/editor/spellchecker/TextServicesDocument.cpp
+++ b/editor/spellchecker/TextServicesDocument.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "TextServicesDocument.h"
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
-#include "mozilla/EditorUtils.h"        // for AutoTransactionBatch
+#include "mozilla/EditorUtils.h"        // for AutoTransactionBatchExternal
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/TextEditor.h"         // for TextEditor
 #include "nsAString.h"                  // for nsAString::Length, etc
 #include "nsContentUtils.h"             // for nsContentUtils
 #include "nsComposeTxtSrvFilter.h"
 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
@@ -1143,22 +1143,19 @@ TextServicesDocument::InsertText(const n
     // Collapse to the start of the current selection
     // for the insert!
 
     nsresult rv = SetSelection(mSelStartOffset, 0);
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // AutoTransactionBatch grabs mTextEditor, so, we don't need to grab the
-  // instance with local variable here.
-  // XXX Well, do we really need to create AutoTransactionBatch here?
-  //     Looks like that after InsertTextAsAction(), this does nothing
-  //     from a point of view of editor.
-  AutoTransactionBatch bundleAllTransactions(*mTextEditor);
+  // AutoTransactionBatchExternal grabs mTextEditor, so, we don't need to grab
+  // the instance with local variable here.
+  AutoTransactionBatchExternal treatAsOneTransaction(*mTextEditor);
 
   nsresult rv = mTextEditor->InsertTextAsAction(*aText);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   int32_t strLength = aText->Length();
 
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -27,17 +27,17 @@ byteorder = "1.0"
 cfg-if = "0.1.2"
 euclid = "0.19"
 fxhash = "0.2.1"
 gleam = "0.6.3"
 image = { optional = true, version = "0.19" }
 lazy_static = "1"
 log = "0.4"
 num-traits = "0.2"
-plane-split = "0.13.2"
+plane-split = "0.13.3"
 png = { optional = true, version = "0.12" }
 rayon = "1"
 ron = { optional = true, version = "0.1.7" }
 serde = { optional = true, version = "1.0", features = ["serde_derive"] }
 serde_json = { optional = true, version = "1.0" }
 smallvec = "0.6"
 thread_profiler = "0.1.1"
 time = "0.1"
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -297,26 +297,26 @@ fn request_render_task_from_pathfinder(g
     // FIXME(pcwalton): Support vertical subpixel offsets.
     // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
     // smoothing" is unchecked in System Preferences.
 
     let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
     let embolden_amount = compute_embolden_amount(size.to_f32_px());
 
     let location = RenderTaskLocation::Dynamic(None, *glyph_size);
-    let glyph_render_task = RenderTask::new_glyph(location,
+    let glyph_render_task = RenderTask::new_glyph(location.clone(),
                                                   mesh,
                                                   &glyph_origin,
                                                   &subpixel_offset,
                                                   font.render_mode,
                                                   &embolden_amount);
 
     let root_task_id = render_tasks.add(glyph_render_task);
     let render_pass = match render_mode {
         FontRenderMode::Mono | FontRenderMode::Alpha => &mut render_passes.alpha_glyph_pass,
         FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
     };
-    render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);
+    render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color, &location);
 
     Ok(root_task_id)
 }
 
 pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -120,17 +120,17 @@ impl RenderTaskTree {
 
         let pass_index = if task.is_global_cached_task() {
             0
         } else {
             pass_index
         };
 
         let pass = &mut passes[pass_index];
-        pass.add_render_task(id, task.get_dynamic_size(), task.target_kind());
+        pass.add_render_task(id, task.get_dynamic_size(), task.target_kind(), &task.location);
     }
 
     pub fn prepare_for_render(&mut self) {
         for task in &mut self.tasks {
             task.prepare_for_render();
         }
     }
 
@@ -168,17 +168,17 @@ impl ops::Index<RenderTaskId> for Render
 impl ops::IndexMut<RenderTaskId> for RenderTaskTree {
     fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask {
         debug_assert_eq!(self.frame_id, id.1);
         &mut self.tasks[id.0 as usize]
     }
 }
 
 /// Identifies the output buffer location for a given `RenderTask`.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub enum RenderTaskLocation {
     /// The `RenderTask` should be drawn to a fixed region in a specific render
     /// target. This is used for the root `RenderTask`, where the main
     /// framebuffer is used as the render target.
     Fixed(DeviceIntRect),
     /// The `RenderTask` should be drawn to a target provided by the atlas
@@ -197,16 +197,26 @@ pub enum RenderTaskLocation {
         texture: CacheTextureId,
         /// The target layer in the above texture.
         layer: LayerIndex,
         /// The target region within the above layer.
         rect: DeviceIntRect,
     },
 }
 
+impl RenderTaskLocation {
+    /// Returns true if this is a dynamic location.
+    pub fn is_dynamic(&self) -> bool {
+        match *self {
+            RenderTaskLocation::Dynamic(..) => true,
+            _ => false,
+        }
+    }
+}
+
 #[derive(Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct CacheMaskTask {
     actual_rect: DeviceIntRect,
     pub root_spatial_node_index: SpatialNodeIndex,
     pub clip_node_range: ClipNodeRange,
 }
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -190,36 +190,37 @@ pub enum RenderTargetKind {
 ///
 /// Note that in some cases (like drop-shadows), we can depend on the output of
 /// a pass earlier than the immediately-preceding pass. See `SavedTargetIndex`.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct RenderTargetList<T> {
     screen_size: DeviceIntSize,
     pub format: ImageFormat,
-    /// The maximum width and height of any single primitive we've encountered.
+    /// The maximum width and height of any single primitive we've encountered
+    /// that will be drawn to a dynamic location.
     ///
     /// We initially create our per-slice allocators with a width and height of
     /// IDEAL_MAX_TEXTURE_DIMENSION. If we encounter a larger primitive, the
-    /// allocation will fail, but we'll bump max_size, which will cause the
+    /// allocation will fail, but we'll bump max_dynamic_size, which will cause the
     /// allocator for the next slice to be just large enough to accomodate it.
-    pub max_size: DeviceUintSize,
+    pub max_dynamic_size: DeviceUintSize,
     pub targets: Vec<T>,
     pub saved_index: Option<SavedTargetIndex>,
 }
 
 impl<T: RenderTarget> RenderTargetList<T> {
     fn new(
         screen_size: DeviceIntSize,
         format: ImageFormat,
     ) -> Self {
         RenderTargetList {
             screen_size,
             format,
-            max_size: DeviceUintSize::new(0, 0),
+            max_dynamic_size: DeviceUintSize::new(0, 0),
             targets: Vec::new(),
             saved_index: None,
         }
     }
 
     fn build(
         &mut self,
         ctx: &mut RenderTargetContext,
@@ -278,18 +279,18 @@ impl<T: RenderTarget> RenderTargetList<T
 
         let origin = match existing_origin {
             Some(origin) => origin,
             None => {
                 // Have the allocator restrict slice sizes to our max ideal
                 // dimensions, unless we've already gone bigger on a previous
                 // slice.
                 let allocator_dimensions = DeviceUintSize::new(
-                    cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, self.max_size.width),
-                    cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, self.max_size.height),
+                    cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, self.max_dynamic_size.width),
+                    cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, self.max_dynamic_size.height),
                 );
                 let mut new_target = T::new(Some(allocator_dimensions), self.screen_size);
                 let origin = new_target.allocate(alloc_size).expect(&format!(
                     "Each render task must allocate <= size of one target! ({})",
                     alloc_size
                 ));
                 self.targets.push(new_target);
                 origin
@@ -300,18 +301,18 @@ impl<T: RenderTarget> RenderTargetList<T
     }
 
     pub fn needs_depth(&self) -> bool {
         self.targets.iter().any(|target| target.needs_depth())
     }
 
     pub fn check_ready(&self, t: &Texture) {
         let dimensions = t.get_dimensions();
-        assert!(dimensions.width >= self.max_size.width);
-        assert!(dimensions.height >= self.max_size.height);
+        assert!(dimensions.width >= self.max_dynamic_size.width);
+        assert!(dimensions.height >= self.max_dynamic_size.height);
         assert_eq!(t.get_format(), self.format);
         assert_eq!(t.get_layer_count() as usize, self.targets.len());
         assert!(t.supports_depth() >= self.needs_depth());
     }
 }
 
 /// Frame output information for a given pipeline ID.
 /// Storing the task ID allows the renderer to find
@@ -888,24 +889,31 @@ impl RenderPass {
     }
 
     /// Adds a task to this pass.
     pub fn add_render_task(
         &mut self,
         task_id: RenderTaskId,
         size: DeviceIntSize,
         target_kind: RenderTargetKind,
+        location: &RenderTaskLocation,
     ) {
         if let RenderPassKind::OffScreen { ref mut color, ref mut alpha, .. } = self.kind {
-            let max_size = match target_kind {
-                RenderTargetKind::Color => &mut color.max_size,
-                RenderTargetKind::Alpha => &mut alpha.max_size,
-            };
-            max_size.width = cmp::max(max_size.width, size.width as u32);
-            max_size.height = cmp::max(max_size.height, size.height as u32);
+            // If this will be rendered to a dynamically-allocated region on an
+            // off-screen render target, update the max-encountered size. We don't
+            // need to do this for things drawn to the texture cache, since those
+            // don't affect our render target allocation.
+            if location.is_dynamic() {
+                let max_size = match target_kind {
+                    RenderTargetKind::Color => &mut color.max_dynamic_size,
+                    RenderTargetKind::Alpha => &mut alpha.max_dynamic_size,
+                };
+                max_size.width = cmp::max(max_size.width, size.width as u32);
+                max_size.height = cmp::max(max_size.height, size.height as u32);
+            }
         }
 
         self.tasks.push(task_id);
     }
 
     /// Processes this pass to prepare it for rendering.
     ///
     /// Among other things, this allocates output regions for each of our tasks
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-e7d340b0f39bbd0046e983a75245bdde54013cdb
+4e9ed699f3be741102120ba2d499e91c0adba6ab
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1188,20 +1188,23 @@ InitFromBailout(JSContext* cx, size_t fr
                 blFrame->setFrameSize(frameSize);
                 JitSpew(JitSpew_BaselineBailouts, "      Adjusted framesize += %d: %d",
                                 (int) (numUses * sizeof(Value)),
                                 (int) frameSize);
             }
 
             // Set the resume address to the return point from the IC, and set
             // the monitor stub addr.
-            builder.setResumeAddr(baselineScript->returnAddressForIC(icEntry));
+            RetAddrEntry& retAddrEntry =
+                baselineScript->retAddrEntryFromPCOffset(pcOff, RetAddrEntry::Kind::IC);
+            uint8_t* retAddr = baselineScript->returnAddressForEntry(retAddrEntry);
+            builder.setResumeAddr(retAddr);
             builder.setMonitorStub(firstMonStub);
             JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p monitorStub=%p",
-                    baselineScript->returnAddressForIC(icEntry), firstMonStub);
+                    retAddr, firstMonStub);
 
         } else {
             // If needed, initialize BaselineBailoutInfo's valueR0 and/or valueR1 with the
             // top stack values.
             //
             // Note that we use the 'maybe' variant of nativeCodeForPC because
             // of exception propagation for debug mode. See note below.
             PCMappingSlotInfo slotInfo;
@@ -1304,17 +1307,20 @@ InitFromBailout(JSContext* cx, size_t fr
     if (!builder.writeWord(baselineFrameDescr, "Descriptor")) {
         return false;
     }
 
     // Calculate and write out return address.
     // The icEntry in question MUST have an inlinable fallback stub.
     ICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
     MOZ_ASSERT(IsInlinableFallback(icEntry.firstStub()->getChainFallback()));
-    if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr")) {
+
+    RetAddrEntry& retAddrEntry =
+        baselineScript->retAddrEntryFromPCOffset(pcOff, RetAddrEntry::Kind::IC);
+    if (!builder.writePtr(baselineScript->returnAddressForEntry(retAddrEntry), "ReturnAddr")) {
         return false;
     }
 
     // Build baseline stub frame:
     // +===============+
     // |    StubPtr    |
     // +---------------+
     // |   FramePtr    |
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -240,16 +240,17 @@ BaselineCompiler::compile()
     size_t bytecodeTypeMapEntries = script->nTypeSets() + 1;
     UniquePtr<BaselineScript> baselineScript(
         BaselineScript::New(script, prologueOffset_.offset(),
                             epilogueOffset_.offset(),
                             profilerEnterFrameToggleOffset_.offset(),
                             profilerExitFrameToggleOffset_.offset(),
                             postDebugPrologueOffset_.offset(),
                             icEntries_.length(),
+                            retAddrEntries_.length(),
                             pcMappingIndexEntries.length(),
                             pcEntries.length(),
                             bytecodeTypeMapEntries,
                             yieldAndAwaitOffsets_.length(),
                             traceLoggerToggleOffsets_.length()),
         JS::DeletePolicy<BaselineScript>(cx->runtime()));
     if (!baselineScript) {
         ReportOutOfMemory(cx);
@@ -264,20 +265,23 @@ BaselineCompiler::compile()
             script->filename(), script->lineno(), script->column());
 
     MOZ_ASSERT(pcMappingIndexEntries.length() > 0);
     baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
 
     MOZ_ASSERT(pcEntries.length() > 0);
     baselineScript->copyPCMappingEntries(pcEntries);
 
-    // Copy IC entries
-    if (icEntries_.length()) {
+    // Copy ICEntries and RetAddrEntries.
+    if (icEntries_.length() > 0) {
         baselineScript->copyICEntries(script, &icEntries_[0]);
     }
+    if (retAddrEntries_.length() > 0) {
+        baselineScript->copyRetAddrEntries(script, &retAddrEntries_[0]);
+    }
 
     // Adopt fallback stubs from the compiler into the baseline script.
     baselineScript->adoptFallbackStubs(&stubSpace_);
 
     // If profiler instrumentation is enabled, toggle instrumentation on.
     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) {
         baselineScript->toggleProfilerInstrumentation(true);
     }
@@ -573,26 +577,38 @@ BaselineCompiler::emitOutOfLinePostBarri
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
 
     masm.popValue(R0);
     masm.ret();
     return true;
 }
 
 bool
-BaselineCompiler::emitIC(ICStub* stub, ICEntry::Kind kind)
-{
-    ICEntry* entry = allocateICEntry(stub, kind);
-    if (!entry) {
+BaselineCompiler::emitIC(ICStub* stub, bool isForOp)
+{
+    if (!stub) {
         return false;
     }
 
-    CodeOffset patchOffset;
-    EmitCallIC(&patchOffset, masm);
-    entry->setReturnOffset(CodeOffset(masm.currentOffset()));
+    CodeOffset patchOffset, callOffset;
+    EmitCallIC(masm, &patchOffset, &callOffset);
+
+    // ICs need both an ICEntry and a RetAddrEntry.
+
+    RetAddrEntry::Kind kind = isForOp ? RetAddrEntry::Kind::IC : RetAddrEntry::Kind::NonOpIC;
+    if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind, callOffset)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    if (!icEntries_.emplaceBack(stub, script->pcToOffset(pc), isForOp)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     if (!addICLoadLabel(patchOffset)) {
         return false;
     }
 
     return true;
 }
 
 void
@@ -681,19 +697,17 @@ BaselineCompiler::callVM(const VMFunctio
         Label ok;
         masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
                           Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
         masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call");
         masm.bind(&ok);
     }
 #endif
 
-    // Add a fake ICEntry (without stubs), so that the return offset to
-    // pc mapping works.
-    return appendICEntry(ICEntry::Kind_CallVM, callOffset);
+    return appendRetAddrEntry(RetAddrEntry::Kind::CallVM, callOffset);
 }
 
 typedef bool (*CheckOverRecursedBaselineFn)(JSContext*, BaselineFrame*);
 static const VMFunction CheckOverRecursedBaselineInfo =
     FunctionInfo<CheckOverRecursedBaselineFn>(CheckOverRecursedBaseline,
                                               "CheckOverRecursedBaseline");
 
 bool
@@ -730,17 +744,17 @@ BaselineCompiler::emitStackCheck()
     if (needsEarlyStackCheck()) {
         phase = CHECK_OVER_RECURSED;
     }
 
     if (!callVMNonOp(CheckOverRecursedBaselineInfo, phase)) {
         return false;
     }
 
-    icEntries_.back().setFakeKind(ICEntry::Kind_StackCheck);
+    retAddrEntries_.back().setKind(RetAddrEntry::Kind::StackCheck);
 
     masm.bind(&skipCall);
     return true;
 }
 
 void
 BaselineCompiler::emitIsDebuggeeCheck()
 {
@@ -767,18 +781,18 @@ BaselineCompiler::emitDebugPrologue()
 
         prepareVMCall();
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
         if (!callVM(DebugPrologueInfo)) {
             return false;
         }
 
-        // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
-        icEntries_.back().setFakeKind(ICEntry::Kind_DebugPrologue);
+        // Fix up the RetAddrEntry appended by callVM for on-stack recompilation.
+        retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugPrologue);
 
         // If the stub returns |true|, we have to return the value stored in the
         // frame's return value slot.
         Label done;
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
         {
             masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
             masm.jump(&return_);
@@ -940,18 +954,18 @@ BaselineCompiler::emitWarmUpCounterIncre
 
         masm.Push(ImmPtr(pc));
         masm.PushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
         if (!callVM(IonCompileScriptForBaselineInfo)) {
             return false;
         }
 
-        // Annotate the ICEntry as warmup counter.
-        icEntries_.back().setFakeKind(ICEntry::Kind_WarmupCounter);
+        // Annotate the RetAddrEntry as warmup counter.
+        retAddrEntries_.back().setKind(RetAddrEntry::Kind::WarmupCounter);
     }
     masm.bind(&skipCall);
 
     return true;
 }
 
 bool
 BaselineCompiler::emitArgumentTypeChecks()
@@ -1005,18 +1019,18 @@ BaselineCompiler::emitDebugTrap()
     mozilla::DebugOnly<CodeOffset> offset = masm.toggledCall(handler, enabled);
 
 #ifdef DEBUG
     // Patchable call offset has to match the pc mapping offset.
     PCMappingEntry& entry = pcMappingEntries_.back();
     MOZ_ASSERT((&offset)->offset() == entry.nativeOffset);
 #endif
 
-    // Add an IC entry for the return offset -> pc mapping.
-    return appendICEntry(ICEntry::Kind_DebugTrap, masm.currentOffset());
+    // Add a RetAddrEntry for the return offset -> pc mapping.
+    return appendRetAddrEntry(RetAddrEntry::Kind::DebugTrap, masm.currentOffset());
 }
 
 #ifdef JS_TRACE_LOGGING
 bool
 BaselineCompiler::emitTraceLoggerEnter()
 {
     AllocatableRegisterSet regs(RegisterSet::Volatile());
     Register loggerReg = regs.takeAnyGeneral();
@@ -4202,18 +4216,18 @@ BaselineCompiler::emitReturn()
 
         prepareVMCall();
         pushArg(ImmPtr(pc));
         pushArg(R0.scratchReg());
         if (!callVM(DebugEpilogueInfo)) {
             return false;
         }
 
-        // Fix up the fake ICEntry appended by callVM for on-stack recompilation.
-        icEntries_.back().setFakeKind(ICEntry::Kind_DebugEpilogue);
+        // Fix up the RetAddrEntry appended by callVM for on-stack recompilation.
+        retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugEpilogue);
 
         masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     }
 
     // Only emit the jump if this JSOP_RETRVAL is not the last instruction.
     // Not needed for last instruction, because last instruction flows
     // into return label.
     if (pc + GetBytecodeLength(pc) < script->codeEnd()) {
@@ -4941,17 +4955,17 @@ BaselineCompiler::emit_JSOP_DEBUGAFTERYI
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     prepareVMCall();
     pushArg(ImmPtr(pc));
     pushArg(R0.scratchReg());
     if (!callVM(DebugAfterYieldInfo)) {
         return false;
     }
 
-    icEntries_.back().setFakeKind(ICEntry::Kind_DebugAfterYield);
+    retAddrEntries_.back().setKind(RetAddrEntry::Kind::DebugAfterYield);
 
     Label done;
     masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &done);
     {
         masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
         masm.jump(&return_);
     }
     masm.bind(&done);
@@ -5063,18 +5077,18 @@ BaselineCompiler::emit_JSOP_RESUME()
     // generator returns.
     Label genStart, returnTarget;
 #ifdef JS_USE_LINK_REGISTER
     masm.call(&genStart);
 #else
     masm.callAndPushReturnAddress(&genStart);
 #endif
 
-    // Add an IC entry so the return offset -> pc mapping works.
-    if (!appendICEntry(ICEntry::Kind_Op, masm.currentOffset())) {
+    // Add a RetAddrEntry so the return offset -> pc mapping works.
+    if (!appendRetAddrEntry(RetAddrEntry::Kind::IC, masm.currentOffset())) {
         return false;
     }
 
     masm.jump(&returnTarget);
     masm.bind(&genStart);
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -258,16 +258,17 @@ class BaselineCompiler final
     bool compileDebugInstrumentation_;
 
     TempAllocator& alloc_;
     BytecodeAnalysis analysis_;
     FrameInfo frame;
 
     FallbackICStubSpace stubSpace_;
     js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
+    js::Vector<RetAddrEntry, 16, SystemAllocPolicy> retAddrEntries_;
 
     // Stores the native code offset for a bytecode pc.
     struct PCMappingEntry
     {
         uint32_t pcOffset;
         uint32_t nativeOffset;
         PCMappingSlotInfo slotInfo;
 
@@ -339,40 +340,18 @@ class BaselineCompiler final
 
     MethodStatus compile();
 
     void setCompileDebugInstrumentation() {
         compileDebugInstrumentation_ = true;
     }
 
   private:
-    ICEntry* allocateICEntry(ICStub* stub, ICEntry::Kind kind) {
-        if (!stub) {
-            return nullptr;
-        }
-
-        // Create the entry and add it to the vector.
-        if (!icEntries_.append(ICEntry(script->pcToOffset(pc), kind))) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
-        ICEntry& vecEntry = icEntries_.back();
-
-        // Set the first stub for the IC entry to the fallback stub
-        vecEntry.setFirstStub(stub);
-
-        // Return pointer to the IC entry
-        return &vecEntry;
-    }
-
-    // Append an ICEntry without a stub.
-    bool appendICEntry(ICEntry::Kind kind, uint32_t returnOffset) {
-        ICEntry entry(script->pcToOffset(pc), kind);
-        entry.setReturnOffset(CodeOffset(returnOffset));
-        if (!icEntries_.append(entry)) {
+    MOZ_MUST_USE bool appendRetAddrEntry(RetAddrEntry::Kind kind, uint32_t retOffset) {
+        if (!retAddrEntries_.emplaceBack(script->pcToOffset(pc), kind, CodeOffset(retOffset))) {
             ReportOutOfMemory(cx);
             return false;
         }
         return true;
     }
 
     bool addICLoadLabel(CodeOffset label) {
         MOZ_ASSERT(!icEntries_.empty());
@@ -421,39 +400,39 @@ class BaselineCompiler final
         CHECK_OVER_RECURSED
     };
     bool callVM(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE);
 
     bool callVMNonOp(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE) {
         if (!callVM(fun, phase)) {
             return false;
         }
-        icEntries_.back().setFakeKind(ICEntry::Kind_NonOpCallVM);
+        retAddrEntries_.back().setKind(RetAddrEntry::Kind::NonOpCallVM);
         return true;
     }
 
     BytecodeAnalysis& analysis() {
         return analysis_;
     }
 
     MethodStatus emitBody();
 
     MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit=false);
     void emitLoadReturnValue(ValueOperand val);
 
     void emitInitializeLocals();
     MOZ_MUST_USE bool emitPrologue();
     MOZ_MUST_USE bool emitEpilogue();
     MOZ_MUST_USE bool emitOutOfLinePostBarrierSlot();
-    MOZ_MUST_USE bool emitIC(ICStub* stub, ICEntry::Kind kind);
+    MOZ_MUST_USE bool emitIC(ICStub* stub, bool isForOp);
     MOZ_MUST_USE bool emitOpIC(ICStub* stub) {
-        return emitIC(stub, ICEntry::Kind_Op);
+        return emitIC(stub, true);
     }
     MOZ_MUST_USE bool emitNonOpIC(ICStub* stub) {
-        return emitIC(stub, ICEntry::Kind_NonOp);
+        return emitIC(stub, false);
     }
 
     MOZ_MUST_USE bool emitStackCheck();
     MOZ_MUST_USE bool emitInterruptCheck();
     MOZ_MUST_USE bool emitWarmUpCounterIncrement(bool allowOsr=true);
     MOZ_MUST_USE bool emitArgumentTypeChecks();
     void emitIsDebuggeeCheck();
     MOZ_MUST_USE bool emitDebugPrologue();
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -22,50 +22,50 @@ using namespace js::jit;
 struct DebugModeOSREntry
 {
     JSScript* script;
     BaselineScript* oldBaselineScript;
     ICStub* oldStub;
     ICStub* newStub;
     BaselineDebugModeOSRInfo* recompInfo;
     uint32_t pcOffset;
-    ICEntry::Kind frameKind;
+    RetAddrEntry::Kind frameKind;
 
     explicit DebugModeOSREntry(JSScript* script)
       : script(script),
         oldBaselineScript(script->baselineScript()),
         oldStub(nullptr),
         newStub(nullptr),
         recompInfo(nullptr),
         pcOffset(uint32_t(-1)),
-        frameKind(ICEntry::Kind_Invalid)
+        frameKind(RetAddrEntry::Kind::Invalid)
     { }
 
     DebugModeOSREntry(JSScript* script, uint32_t pcOffset)
       : script(script),
         oldBaselineScript(script->baselineScript()),
         oldStub(nullptr),
         newStub(nullptr),
         recompInfo(nullptr),
         pcOffset(pcOffset),
-        frameKind(ICEntry::Kind_Invalid)
+        frameKind(RetAddrEntry::Kind::Invalid)
     { }
 
-    DebugModeOSREntry(JSScript* script, const ICEntry& icEntry)
+    DebugModeOSREntry(JSScript* script, const RetAddrEntry& retAddrEntry)
       : script(script),
         oldBaselineScript(script->baselineScript()),
         oldStub(nullptr),
         newStub(nullptr),
         recompInfo(nullptr),
-        pcOffset(icEntry.pcOffset()),
-        frameKind(icEntry.kind())
+        pcOffset(retAddrEntry.pcOffset()),
+        frameKind(retAddrEntry.kind())
     {
 #ifdef DEBUG
-        MOZ_ASSERT(pcOffset == icEntry.pcOffset());
-        MOZ_ASSERT(frameKind == icEntry.kind());
+        MOZ_ASSERT(pcOffset == retAddrEntry.pcOffset());
+        MOZ_ASSERT(frameKind == retAddrEntry.kind());
 #endif
     }
 
     DebugModeOSREntry(JSScript* script, BaselineDebugModeOSRInfo* info)
       : script(script),
         oldBaselineScript(script->baselineScript()),
         oldStub(nullptr),
         newStub(nullptr),
@@ -92,23 +92,23 @@ struct DebugModeOSREntry
     ~DebugModeOSREntry() {
         // Note that this is nulled out when the recompInfo is taken by the
         // frame. The frame then has the responsibility of freeing the
         // recompInfo.
         js_delete(recompInfo);
     }
 
     bool needsRecompileInfo() const {
-        return frameKind == ICEntry::Kind_CallVM ||
-               frameKind == ICEntry::Kind_WarmupCounter ||
-               frameKind == ICEntry::Kind_StackCheck ||
-               frameKind == ICEntry::Kind_DebugTrap ||
-               frameKind == ICEntry::Kind_DebugPrologue ||
-               frameKind == ICEntry::Kind_DebugAfterYield ||
-               frameKind == ICEntry::Kind_DebugEpilogue;
+        return frameKind == RetAddrEntry::Kind::CallVM ||
+               frameKind == RetAddrEntry::Kind::WarmupCounter ||
+               frameKind == RetAddrEntry::Kind::StackCheck ||
+               frameKind == RetAddrEntry::Kind::DebugTrap ||
+               frameKind == RetAddrEntry::Kind::DebugPrologue ||
+               frameKind == RetAddrEntry::Kind::DebugAfterYield ||
+               frameKind == RetAddrEntry::Kind::DebugEpilogue;
     }
 
     bool recompiled() const {
         return oldBaselineScript != script->baselineScript();
     }
 
     BaselineDebugModeOSRInfo* takeRecompInfo() {
         MOZ_ASSERT(needsRecompileInfo() && recompInfo);
@@ -123,17 +123,17 @@ struct DebugModeOSREntry
 
         // If we are returning to a frame which needs a continuation fixer,
         // allocate the recompile info up front so that the patching function
         // is infallible.
         jsbytecode* pc = script->offsetToPC(pcOffset);
 
         // XXX: Work around compiler error disallowing using bitfields
         // with the template magic of new_.
-        ICEntry::Kind kind = frameKind;
+        RetAddrEntry::Kind kind = frameKind;
         recompInfo = cx->new_<BaselineDebugModeOSRInfo>(pc, kind);
         return !!recompInfo;
     }
 
     ICFallbackStub* fallbackStub() const {
         MOZ_ASSERT(script);
         MOZ_ASSERT(oldStub);
         return script->baselineScript()->icEntryFromPCOffset(pcOffset).fallbackStub();
@@ -199,34 +199,36 @@ CollectJitStackScripts(JSContext* cx, co
 
             BaselineFrame* baselineFrame = frame.baselineFrame();
 
             if (BaselineDebugModeOSRInfo* info = baselineFrame->getDebugModeOSRInfo()) {
                 // If patching a previously patched yet unpopped frame, we can
                 // use the BaselineDebugModeOSRInfo on the frame directly to
                 // patch. Indeed, we cannot use frame.returnAddressToFp(), as
                 // it points into the debug mode OSR handler and cannot be
-                // used to look up a corresponding ICEntry.
+                // used to look up a corresponding RetAddrEntry.
                 //
                 // See case F in PatchBaselineFramesForDebugMode.
                 if (!entries.append(DebugModeOSREntry(script, info))) {
                     return false;
                 }
             } else if (baselineFrame->hasOverridePc()) {
-                // If the frame is not settled on a pc with an ICEntry, overridePc
-                // will contain an explicit bytecode offset. We can (and must) use that.
+                // If the frame is not settled on a pc with a RetAddrEntry,
+                // overridePc will contain an explicit bytecode offset. We can
+                // (and must) use that.
                 uint32_t offset = script->pcToOffset(baselineFrame->overridePc());
                 if (!entries.append(DebugModeOSREntry(script, offset))) {
                     return false;
                 }
             } else {
-                // The frame must be settled on a pc with an ICEntry.
+                // The frame must be settled on a pc with a RetAddrEntry.
                 uint8_t* retAddr = frame.returnAddressToFp();
-                ICEntry& icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
-                if (!entries.append(DebugModeOSREntry(script, icEntry))) {
+                RetAddrEntry& retAddrEntry =
+                    script->baselineScript()->retAddrEntryFromReturnAddress(retAddr);
+                if (!entries.append(DebugModeOSREntry(script, retAddrEntry))) {
                     return false;
                 }
             }
 
             if (entries.back().needsRecompileInfo()) {
                 if (!entries.back().allocateRecompileInfo(cx)) {
                     return false;
                 }
@@ -292,51 +294,51 @@ CollectInterpreterStackScripts(JSContext
             }
         }
     }
     return true;
 }
 
 #ifdef JS_JITSPEW
 static const char*
-ICEntryKindToString(ICEntry::Kind kind)
+RetAddrEntryKindToString(RetAddrEntry::Kind kind)
 {
     switch (kind) {
-      case ICEntry::Kind_Op:
+      case RetAddrEntry::Kind::IC:
         return "IC";
-      case ICEntry::Kind_NonOp:
+      case RetAddrEntry::Kind::NonOpIC:
         return "non-op IC";
-      case ICEntry::Kind_CallVM:
+      case RetAddrEntry::Kind::CallVM:
         return "callVM";
-      case ICEntry::Kind_WarmupCounter:
+      case RetAddrEntry::Kind::WarmupCounter:
         return "warmup counter";
-      case ICEntry::Kind_StackCheck:
+      case RetAddrEntry::Kind::StackCheck:
         return "stack check";
-      case ICEntry::Kind_DebugTrap:
+      case RetAddrEntry::Kind::DebugTrap:
         return "debug trap";
-      case ICEntry::Kind_DebugPrologue:
+      case RetAddrEntry::Kind::DebugPrologue:
         return "debug prologue";
-      case ICEntry::Kind_DebugAfterYield:
+      case RetAddrEntry::Kind::DebugAfterYield:
         return "debug after yield";
-      case ICEntry::Kind_DebugEpilogue:
+      case RetAddrEntry::Kind::DebugEpilogue:
         return "debug epilogue";
       default:
-        MOZ_CRASH("bad ICEntry kind");
+        MOZ_CRASH("bad RetAddrEntry kind");
     }
 }
 #endif // JS_JITSPEW
 
 static void
-SpewPatchBaselineFrame(uint8_t* oldReturnAddress, uint8_t* newReturnAddress,
-                       JSScript* script, ICEntry::Kind frameKind, jsbytecode* pc)
+SpewPatchBaselineFrame(const uint8_t* oldReturnAddress, const uint8_t* newReturnAddress,
+                       JSScript* script, RetAddrEntry::Kind frameKind, const jsbytecode* pc)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
             "Patch return %p -> %p on BaselineJS frame (%s:%u:%u) from %s at %s",
             oldReturnAddress, newReturnAddress, script->filename(), script->lineno(),
-            script->column(), ICEntryKindToString(frameKind), CodeName[(JSOp)*pc]);
+            script->column(), RetAddrEntryKindToString(frameKind), CodeName[(JSOp)*pc]);
 }
 
 static void
 SpewPatchBaselineFrameFromExceptionHandler(uint8_t* oldReturnAddress, uint8_t* newReturnAddress,
                                            JSScript* script, jsbytecode* pc)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
             "Patch return %p -> %p on BaselineJS frame (%s:%u:%u) from exception handler at %s",
@@ -412,44 +414,45 @@ PatchBaselineFramesForDebugMode(JSContex
             JSScript* script = entry.script;
             uint32_t pcOffset = entry.pcOffset;
             jsbytecode* pc = script->offsetToPC(pcOffset);
 
             MOZ_ASSERT(script == frame.script());
             MOZ_ASSERT(pcOffset < script->length());
 
             BaselineScript* bl = script->baselineScript();
-            ICEntry::Kind kind = entry.frameKind;
+            RetAddrEntry::Kind kind = entry.frameKind;
 
-            if (kind == ICEntry::Kind_Op) {
+            if (kind == RetAddrEntry::Kind::IC) {
                 // Case A above.
                 //
                 // Patching these cases needs to patch both the stub frame and
                 // the baseline frame. The stub frame is patched below. For
                 // the baseline frame here, we resume right after the IC
                 // returns.
                 //
                 // Since we're using the same IC stub code, we can resume
                 // directly to the IC resume address.
-                uint8_t* retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
+                RetAddrEntry& retAddrEntry = bl->retAddrEntryFromPCOffset(pcOffset, kind);
+                uint8_t* retAddr = bl->returnAddressForEntry(retAddrEntry);
                 SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
                 DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(
                     cx, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
 
-            if (kind == ICEntry::Kind_Invalid) {
+            if (kind == RetAddrEntry::Kind::Invalid) {
                 // Cases G and H above.
                 //
                 // We are recompiling a frame with an override pc.
                 // This may occur from inside the exception handler,
                 // by way of an onExceptionUnwind invocation, on a pc
-                // without an ICEntry. It may also happen if we call
+                // without a RetAddrEntry. It may also happen if we call
                 // GeneratorThrowOrReturn and trigger onEnterFrame.
                 //
                 // If profiling is off, patch the resume address to nullptr,
                 // to ensure the old address is not used anywhere.
                 // If profiling is on, JSJitProfilingFrameIterator requires a
                 // valid return address.
                 MOZ_ASSERT(frame.baselineFrame()->overridePc() == pc);
                 uint8_t* retAddr;
@@ -471,113 +474,102 @@ PatchBaselineFramesForDebugMode(JSContex
             //
             // We undo a previous recompile by handling cases B, C, D, E, I or J
             // like normal, except that we retrieve the pc information via
             // the previous OSR debug info stashed on the frame.
             BaselineDebugModeOSRInfo* info = frame.baselineFrame()->getDebugModeOSRInfo();
             if (info) {
                 MOZ_ASSERT(info->pc == pc);
                 MOZ_ASSERT(info->frameKind == kind);
-                MOZ_ASSERT(kind == ICEntry::Kind_CallVM ||
-                           kind == ICEntry::Kind_WarmupCounter ||
-                           kind == ICEntry::Kind_StackCheck ||
-                           kind == ICEntry::Kind_DebugTrap ||
-                           kind == ICEntry::Kind_DebugPrologue ||
-                           kind == ICEntry::Kind_DebugAfterYield ||
-                           kind == ICEntry::Kind_DebugEpilogue);
+                MOZ_ASSERT(kind == RetAddrEntry::Kind::CallVM ||
+                           kind == RetAddrEntry::Kind::WarmupCounter ||
+                           kind == RetAddrEntry::Kind::StackCheck ||
+                           kind == RetAddrEntry::Kind::DebugTrap ||
+                           kind == RetAddrEntry::Kind::DebugPrologue ||
+                           kind == RetAddrEntry::Kind::DebugAfterYield ||
+                           kind == RetAddrEntry::Kind::DebugEpilogue);
 
                 // We will have allocated a new recompile info, so delete the
                 // existing one.
                 frame.baselineFrame()->deleteDebugModeOSRInfo();
             }
 
             // The RecompileInfo must already be allocated so that this
             // function may be infallible.
             BaselineDebugModeOSRInfo* recompInfo = entry.takeRecompInfo();
 
             bool popFrameReg;
             switch (kind) {
-              case ICEntry::Kind_CallVM: {
+              case RetAddrEntry::Kind::CallVM: {
                 // Case B above.
                 //
                 // Patching returns from a VM call. After fixing up the the
                 // continuation for unsynced values (the frame register is
                 // popped by the callVM trampoline), we resume at the
                 // return-from-callVM address. The assumption here is that all
                 // callVMs which can trigger debug mode OSR are the *only*
                 // callVMs generated for their respective pc locations in the
                 // baseline JIT code.
-                ICEntry& callVMEntry = bl->callVMEntryFromPCOffset(pcOffset);
-                recompInfo->resumeAddr = bl->returnAddressForIC(callVMEntry);
+                RetAddrEntry& retAddrEntry = bl->retAddrEntryFromPCOffset(pcOffset, kind);
+                recompInfo->resumeAddr = bl->returnAddressForEntry(retAddrEntry);
                 popFrameReg = false;
                 break;
               }
 
-              case ICEntry::Kind_WarmupCounter: {
-                // Case J above.
+              case RetAddrEntry::Kind::WarmupCounter:
+              case RetAddrEntry::Kind::StackCheck: {
+                // Cases I and J above.
                 //
                 // Patching mechanism is identical to a CallVM. This is
-                // handled especially only because the warmup counter VM call is
-                // part of the prologue, and not tied an opcode.
-                ICEntry& warmupCountEntry = bl->warmupCountICEntry();
-                recompInfo->resumeAddr = bl->returnAddressForIC(warmupCountEntry);
+                // handled especially only because these VM calls are part of
+                // the prologue, and not tied to an opcode.
+                RetAddrEntry& entry = bl->prologueRetAddrEntry(kind);
+                recompInfo->resumeAddr = bl->returnAddressForEntry(entry);
                 popFrameReg = false;
                 break;
               }
 
-              case ICEntry::Kind_StackCheck: {
-                // Case I above.
-                //
-                // Patching mechanism is identical to a CallVM. This is
-                // handled especially only because the stack check VM call is
-                // part of the prologue, and not tied an opcode.
-                ICEntry& stackCheckEntry = bl->stackCheckICEntry();
-                recompInfo->resumeAddr = bl->returnAddressForIC(stackCheckEntry);
-                popFrameReg = false;
-                break;
-              }
-
-              case ICEntry::Kind_DebugTrap:
+              case RetAddrEntry::Kind::DebugTrap:
                 // Case C above.
                 //
                 // Debug traps are emitted before each op, so we resume at the
                 // same op. Calling debug trap handlers is done via a toggled
                 // call to a thunk (DebugTrapHandler) that takes care tearing
                 // down its own stub frame so we don't need to worry about
                 // popping the frame reg.
                 recompInfo->resumeAddr = bl->nativeCodeForPC(script, pc, &recompInfo->slotInfo);
                 popFrameReg = false;
                 break;
 
-              case ICEntry::Kind_DebugPrologue:
+              case RetAddrEntry::Kind::DebugPrologue:
                 // Case D above.
                 //
                 // We patch a jump directly to the right place in the prologue
                 // after popping the frame reg and checking for forced return.
                 recompInfo->resumeAddr = bl->postDebugPrologueAddr();
                 popFrameReg = true;
                 break;
 
-              case ICEntry::Kind_DebugAfterYield:
+              case RetAddrEntry::Kind::DebugAfterYield:
                 // Case K above.
                 //
                 // Resume at the next instruction.
                 MOZ_ASSERT(*pc == JSOP_DEBUGAFTERYIELD);
                 recompInfo->resumeAddr = bl->nativeCodeForPC(script,
                                                              pc + JSOP_DEBUGAFTERYIELD_LENGTH,
                                                              &recompInfo->slotInfo);
                 popFrameReg = true;
                 break;
 
               default:
                 // Case E above.
                 //
                 // We patch a jump directly to the epilogue after popping the
                 // frame reg and checking for forced return.
-                MOZ_ASSERT(kind == ICEntry::Kind_DebugEpilogue);
+                MOZ_ASSERT(kind == RetAddrEntry::Kind::DebugEpilogue);
                 recompInfo->resumeAddr = bl->epilogueEntryAddr();
                 popFrameReg = true;
                 break;
             }
 
             SpewPatchBaselineFrame(prev->returnAddress(), recompInfo->resumeAddr,
                                    script, kind, recompInfo->pc);
 
@@ -742,17 +734,17 @@ CloneOldBaselineStub(JSContext* cx, Debu
 
     // If this script was not recompiled (because it already had the correct
     // debug instrumentation), don't clone to avoid attaching duplicate stubs.
     if (!entry.recompiled()) {
         entry.newStub = nullptr;
         return true;
     }
 
-    if (entry.frameKind == ICEntry::Kind_Invalid) {
+    if (entry.frameKind == RetAddrEntry::Kind::Invalid) {
         // The exception handler can modify the frame's override pc while
         // unwinding scopes. This is fine, but if we have a stub frame, the code
         // code below will get confused: the entry's pcOffset doesn't match the
         // stub that's still on the stack. To prevent that, we just set the new
         // stub to nullptr as we will never return to this stub frame anyway.
         entry.newStub = nullptr;
         return true;
     }
@@ -784,17 +776,17 @@ CloneOldBaselineStub(JSContext* cx, Debu
         entry.newStub = fallbackStub;
         return true;
     }
 
     // Check if we have already cloned the stub on a younger frame. Ignore
     // frames that entered the exception handler (entries[i].newStub is nullptr
     // in that case, see above).
     for (size_t i = 0; i < entryIndex; i++) {
-        if (oldStub == entries[i].oldStub && entries[i].frameKind != ICEntry::Kind_Invalid) {
+        if (oldStub == entries[i].oldStub && entries[i].frameKind != RetAddrEntry::Kind::Invalid) {
             MOZ_ASSERT(entries[i].newStub);
             entry.newStub = entries[i].newStub;
             return true;
         }
     }
 
     ICStubSpace* stubSpace = ICStubCompiler::StubSpaceForStub(oldStub->makesGCCalls(),
                                                               entry.script);
@@ -973,62 +965,63 @@ BaselineDebugModeOSRInfo::popValueInto(P
     }
 
     stackAdjust++;
 }
 
 static inline bool
 HasForcedReturn(BaselineDebugModeOSRInfo* info, bool rv)
 {
-    ICEntry::Kind kind = info->frameKind;
+    RetAddrEntry::Kind kind = info->frameKind;
 
     // The debug epilogue always checks its resumption value, so we don't need
     // to check rv.
-    if (kind == ICEntry::Kind_DebugEpilogue) {
+    if (kind == RetAddrEntry::Kind::DebugEpilogue) {
         return true;
     }
 
     // |rv| is the value in ReturnReg. If true, in the case of the prologue or
     // after yield, it means a forced return.
-    if (kind == ICEntry::Kind_DebugPrologue || kind == ICEntry::Kind_DebugAfterYield) {
+    if (kind == RetAddrEntry::Kind::DebugPrologue || kind == RetAddrEntry::Kind::DebugAfterYield) {
         return rv;
     }
 
     // N.B. The debug trap handler handles its own forced return, so no
     // need to deal with it here.
     return false;
 }
 
 static inline bool
 IsReturningFromCallVM(BaselineDebugModeOSRInfo* info)
 {
     // Keep this in sync with EmitBranchIsReturningFromCallVM.
     //
     // The stack check entries are returns from a callVM, but have a special
     // kind because they do not exist in a 1-1 relationship with a pc offset.
-    return info->frameKind == ICEntry::Kind_CallVM ||
-           info->frameKind == ICEntry::Kind_WarmupCounter ||
-           info->frameKind == ICEntry::Kind_StackCheck;
+    return info->frameKind == RetAddrEntry::Kind::CallVM ||
+           info->frameKind == RetAddrEntry::Kind::WarmupCounter ||
+           info->frameKind == RetAddrEntry::Kind::StackCheck;
 }
 
 static void
-EmitBranchICEntryKind(MacroAssembler& masm, Register entry, ICEntry::Kind kind, Label* label)
+EmitBranchRetAddrEntryKind(MacroAssembler& masm, Register entry, RetAddrEntry::Kind kind,
+                          Label* label)
 {
     masm.branch32(MacroAssembler::Equal,
                   Address(entry, offsetof(BaselineDebugModeOSRInfo, frameKind)),
-                  Imm32(kind), label);
+                  Imm32(uint32_t(kind)), label);
 }
 
 static void
 EmitBranchIsReturningFromCallVM(MacroAssembler& masm, Register entry, Label* label)
 {
     // Keep this in sync with IsReturningFromCallVM.
-    EmitBranchICEntryKind(masm, entry, ICEntry::Kind_CallVM, label);
-    EmitBranchICEntryKind(masm, entry, ICEntry::Kind_WarmupCounter, label);
-    EmitBranchICEntryKind(masm, entry, ICEntry::Kind_StackCheck, label);
+    EmitBranchRetAddrEntryKind(masm, entry, RetAddrEntry::Kind::CallVM, label);
+    EmitBranchRetAddrEntryKind(masm, entry, RetAddrEntry::Kind::WarmupCounter, label);
+    EmitBranchRetAddrEntryKind(masm, entry, RetAddrEntry::Kind::StackCheck, label);
 }
 
 static void
 SyncBaselineDebugModeOSRInfo(BaselineFrame* frame, Value* vp, bool rv)
 {
     AutoUnsafeCallWithABI unsafe;
     BaselineDebugModeOSRInfo* info = frame->debugModeOSRInfo();
     MOZ_ASSERT(info);
--- a/js/src/jit/BaselineDebugModeOSR.h
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -101,24 +101,24 @@ class DebugModeOSRVolatileJitFrameIter :
 //
 // Auxiliary info to help the DebugModeOSRHandler fix up state.
 //
 struct BaselineDebugModeOSRInfo
 {
     uint8_t* resumeAddr;
     jsbytecode* pc;
     PCMappingSlotInfo slotInfo;
-    ICEntry::Kind frameKind;
+    RetAddrEntry::Kind frameKind;
 
     // Filled in by SyncBaselineDebugModeOSRInfo.
     uintptr_t stackAdjust;
     Value valueR0;
     Value valueR1;
 
-    BaselineDebugModeOSRInfo(jsbytecode* pc, ICEntry::Kind kind)
+    BaselineDebugModeOSRInfo(jsbytecode* pc, RetAddrEntry::Kind kind)
       : resumeAddr(nullptr),
         pc(pc),
         slotInfo(0),
         frameKind(kind),
         stackAdjust(0),
         valueR0(UndefinedValue()),
         valueR1(UndefinedValue())
     { }
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -145,22 +145,23 @@ BaselineFrame::initForOsr(InterpreterFra
     if (fp->isDebuggee()) {
         JSContext* cx = TlsContext.get();
 
         // For debuggee frames, update any Debugger.Frame objects for the
         // InterpreterFrame to point to the BaselineFrame.
 
         // The caller pushed a fake return address. ScriptFrameIter, used by the
         // debugger, wants a valid return address, but it's okay to just pick one.
-        // In debug mode there's always at least 1 ICEntry (since there are always
-        // debug prologue/epilogue calls).
+        // In debug mode there's always at least one RetAddrEntry (since there are
+        // always debug prologue/epilogue calls).
         JSJitFrameIter frame(cx->activation()->asJit());
         MOZ_ASSERT(frame.returnAddress() == nullptr);
         BaselineScript* baseline = fp->script()->baselineScript();
-        frame.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0)));
+        uint8_t* retAddr = baseline->returnAddressForEntry(baseline->retAddrEntry(0));
+        frame.current()->setReturnAddress(retAddr);
 
         if (!Debugger::handleBaselineOsr(cx, fp, this)) {
             return false;
         }
 
         setIsDebuggee();
     }
 
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -53,25 +53,26 @@ class BaselineFrame
         // Frame has over-recursed on an early check.
         OVER_RECURSED    = 1 << 9,
 
         // Frame has a BaselineRecompileInfo stashed in the scratch value
         // slot. See PatchBaselineFramesForDebugMode.
         HAS_DEBUG_MODE_OSR_INFO = 1 << 10,
 
         // This flag is intended for use whenever the frame is settled on a
-        // native code address without a corresponding ICEntry. In this case,
-        // the frame contains an explicit bytecode offset for frame iterators.
+        // native code address without a corresponding RetAddrEntry. In this
+        // case, the frame contains an explicit bytecode offset for frame
+        // iterators.
         //
         // There can also be an override pc if the frame has had its
         // environment chain unwound to a pc during exception handling that is
         // different from its current pc.
         //
         // This flag should never be set on the top frame while we're
-        // executing JIT code. In debug mode, it is checked before and
+        // executing JIT code. In debug builds, it is checked before and
         // after VM calls.
         HAS_OVERRIDE_PC = 1 << 11,
 
         // If set, we're handling an exception for this frame. This is set for
         // debug mode OSR sanity checking when it handles corner cases which
         // only arise during exception handling.
         HANDLING_EXCEPTION = 1 << 12,
     };
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -113,19 +113,16 @@ ICFallbackStub*
 ICEntry::fallbackStub() const
 {
     return firstStub()->getChainFallback();
 }
 
 void
 ICEntry::trace(JSTracer* trc)
 {
-    if (!hasStub()) {
-        return;
-    }
     for (ICStub* stub = firstStub(); stub; stub = stub->next()) {
         stub->trace(trc);
     }
 }
 
 ICStubConstIterator&
 ICStubConstIterator::operator++()
 {
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -209,142 +209,68 @@ void FallbackICSpew(JSContext* cx, ICFal
     MOZ_FORMAT_PRINTF(3, 4);
 void TypeFallbackICSpew(JSContext* cx, ICTypeMonitor_Fallback* stub, const char* fmt, ...)
     MOZ_FORMAT_PRINTF(3, 4);
 #else
 #define FallbackICSpew(...)
 #define TypeFallbackICSpew(...)
 #endif
 
-//
-// An entry in the JIT IC descriptor table.
-//
+// An entry in the BaselineScript IC descriptor table. There's one ICEntry per
+// IC.
 class ICEntry
 {
-  private:
-    // A pointer to the shared IC stub for this instruction.
+    // A pointer to the first IC stub for this instruction.
     ICStub* firstStub_;
 
-    // Offset from the start of the JIT code where the IC
-    // load and call instructions are.
-    uint32_t returnOffset_;
-
     // The PC of this IC's bytecode op within the JSScript.
-    uint32_t pcOffset_ : 28;
+    uint32_t pcOffset_ : 31;
+    uint32_t isForOp_ : 1;
 
   public:
-    enum Kind {
-        // A for-op IC entry.
-        Kind_Op = 0,
-
-        // A non-op IC entry.
-        Kind_NonOp,
-
-        // A fake IC entry for returning from a callVM for an op.
-        Kind_CallVM,
-
-        // A fake IC entry for returning from a callVM not for an op (e.g., in
-        // the prologue).
-        Kind_NonOpCallVM,
-
-        // A fake IC entry for returning from a callVM to after the
-        // warmup counter.
-        Kind_WarmupCounter,
-
-        // A fake IC entry for returning from a callVM to the interrupt
-        // handler via the over-recursion check on function entry.
-        Kind_StackCheck,
-
-        // A fake IC entry for returning from DebugTrapHandler.
-        Kind_DebugTrap,
-
-        // A fake IC entry for returning from a callVM to
-        // Debug{Prologue,AfterYield,Epilogue}.
-        Kind_DebugPrologue,
-        Kind_DebugAfterYield,
-        Kind_DebugEpilogue,
-
-        Kind_Invalid
-    };
-
-  private:
-    // What this IC is for.
-    Kind kind_ : 4;
-
-    // Set the kind and asserts that it's sane.
-    void setKind(Kind kind) {
-        MOZ_ASSERT(kind < Kind_Invalid);
-        kind_ = kind;
-        MOZ_ASSERT(this->kind() == kind);
+    ICEntry(ICStub* firstStub, uint32_t pcOffset, bool isForOp)
+      : firstStub_(firstStub), pcOffset_(pcOffset), isForOp_(uint32_t(isForOp))
+    {
+        // The offset must fit in at least 31 bits, since we shave off 1 for
+        // the isForOp_ flag.
+        MOZ_ASSERT(pcOffset_ == pcOffset);
+        JS_STATIC_ASSERT(BaselineMaxScriptLength <= (1u << 31) - 1);
+        MOZ_ASSERT(pcOffset <= BaselineMaxScriptLength);
     }
 
-  public:
-    ICEntry(uint32_t pcOffset, Kind kind)
-      : firstStub_(nullptr), returnOffset_(), pcOffset_(pcOffset)
-    {
-        // The offset must fit in at least 28 bits, since we shave off 4 for
-        // the Kind enum.
-        MOZ_ASSERT(pcOffset_ == pcOffset);
-        JS_STATIC_ASSERT(BaselineScript::MAX_JSSCRIPT_LENGTH <= (1u << 28) - 1);
-        MOZ_ASSERT(pcOffset <= BaselineScript::MAX_JSSCRIPT_LENGTH);
-        setKind(kind);
-    }
-
-    CodeOffset returnOffset() const {
-        return CodeOffset(returnOffset_);
-    }
-
-    void setReturnOffset(CodeOffset offset) {
-        MOZ_ASSERT(offset.offset() <= (size_t) UINT32_MAX);
-        returnOffset_ = (uint32_t) offset.offset();
-    }
-
-    uint32_t pcOffset() const {
-        return pcOffset_;
-    }
-
-    jsbytecode* pc(JSScript* script) const {
-        return script->offsetToPC(pcOffset_);
-    }
-
-    Kind kind() const {
-        // MSVC compiles enums as signed.
-        return Kind(kind_ & 0xf);
-    }
-    bool isForOp() const {
-        return kind() == Kind_Op;
-    }
-
-    void setFakeKind(Kind kind) {
-        MOZ_ASSERT(kind != Kind_Op && kind != Kind_NonOp);
-        setKind(kind);
-    }
-
-    bool hasStub() const {
-        return firstStub_ != nullptr;
-    }
     ICStub* firstStub() const {
-        MOZ_ASSERT(hasStub());
+        MOZ_ASSERT(firstStub_);
         return firstStub_;
     }
 
     ICFallbackStub* fallbackStub() const;
 
     void setFirstStub(ICStub* stub) {
         firstStub_ = stub;
     }
 
+    uint32_t pcOffset() const {
+        return pcOffset_;
+    }
+    jsbytecode* pc(JSScript* script) const {
+        return script->offsetToPC(pcOffset_);
+    }
+
     static inline size_t offsetOfFirstStub() {
         return offsetof(ICEntry, firstStub_);
     }
 
     inline ICStub** addressOfFirstStub() {
         return &firstStub_;
     }
 
+    bool isForOp() const {
+        return !!isForOp_;
+    }
+
     void trace(JSTracer* trc);
 };
 
 class ICMonitoredStub;
 class ICMonitoredFallbackStub;
 class ICUpdatedStub;
 
 // Constant iterator that traverses arbitrary chains of ICStubs.
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -287,21 +287,21 @@ CanEnterBaselineJIT(JSContext* cx, Handl
 {
     MOZ_ASSERT(jit::IsBaselineEnabled(cx));
 
     // Skip if the script has been disabled.
     if (!script->canBaselineCompile()) {
         return Method_Skipped;
     }
 
-    if (script->length() > BaselineScript::MAX_JSSCRIPT_LENGTH) {
+    if (script->length() > BaselineMaxScriptLength) {
         return Method_CantCompile;
     }
 
-    if (script->nslots() > BaselineScript::MAX_JSSCRIPT_SLOTS) {
+    if (script->nslots() > BaselineMaxScriptSlots) {
         return Method_CantCompile;
     }
 
     if (script->hasBaselineScript()) {
         return Method_Compiled;
     }
 
     // Check this before calling ensureJitRealmExists, so we're less
@@ -382,37 +382,41 @@ jit::CanEnterBaselineMethod(JSContext* c
 
 BaselineScript*
 BaselineScript::New(JSScript* jsscript,
                     uint32_t prologueOffset, uint32_t epilogueOffset,
                     uint32_t profilerEnterToggleOffset,
                     uint32_t profilerExitToggleOffset,
                     uint32_t postDebugPrologueOffset,
                     size_t icEntries,
+                    size_t retAddrEntries,
                     size_t pcMappingIndexEntries, size_t pcMappingSize,
                     size_t bytecodeTypeMapEntries,
                     size_t yieldEntries,
                     size_t traceLoggerToggleOffsetEntries)
 {
     static const unsigned DataAlignment = sizeof(uintptr_t);
 
     size_t icEntriesSize = icEntries * sizeof(ICEntry);
+    size_t retAddrEntriesSize = retAddrEntries * sizeof(RetAddrEntry);
     size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry);
     size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t);
     size_t yieldEntriesSize = yieldEntries * sizeof(uintptr_t);
     size_t tlEntriesSize = traceLoggerToggleOffsetEntries * sizeof(uint32_t);
 
     size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment);
+    size_t paddedRetAddrEntriesSize = AlignBytes(retAddrEntriesSize, DataAlignment);
     size_t paddedPCMappingIndexEntriesSize = AlignBytes(pcMappingIndexEntriesSize, DataAlignment);
     size_t paddedPCMappingSize = AlignBytes(pcMappingSize, DataAlignment);
     size_t paddedBytecodeTypesMapSize = AlignBytes(bytecodeTypeMapSize, DataAlignment);
     size_t paddedYieldEntriesSize = AlignBytes(yieldEntriesSize, DataAlignment);
     size_t paddedTLEntriesSize = AlignBytes(tlEntriesSize, DataAlignment);
 
     size_t allocBytes = paddedICEntriesSize +
+                        paddedRetAddrEntriesSize +
                         paddedPCMappingIndexEntriesSize +
                         paddedPCMappingSize +
                         paddedBytecodeTypesMapSize +
                         paddedYieldEntriesSize +
                         paddedTLEntriesSize;
 
     BaselineScript* script = jsscript->zone()->pod_malloc_with_extra<BaselineScript, uint8_t>(allocBytes);
     if (!script) {
@@ -424,16 +428,20 @@ BaselineScript::New(JSScript* jsscript,
 
     size_t offsetCursor = sizeof(BaselineScript);
     MOZ_ASSERT(offsetCursor == AlignBytes(sizeof(BaselineScript), DataAlignment));
 
     script->icEntriesOffset_ = offsetCursor;
     script->icEntries_ = icEntries;
     offsetCursor += paddedICEntriesSize;
 
+    script->retAddrEntriesOffset_ = offsetCursor;
+    script->retAddrEntries_ = retAddrEntries;
+    offsetCursor += paddedRetAddrEntriesSize;
+
     script->pcMappingIndexOffset_ = offsetCursor;
     script->pcMappingIndexEntries_ = pcMappingIndexEntries;
     offsetCursor += paddedPCMappingIndexEntriesSize;
 
     script->pcMappingOffset_ = offsetCursor;
     script->pcMappingSize_ = pcMappingSize;
     offsetCursor += paddedPCMappingSize;
 
@@ -560,16 +568,23 @@ BaselineScript::removeDependentWasmImpor
 
 ICEntry&
 BaselineScript::icEntry(size_t index)
 {
     MOZ_ASSERT(index < numICEntries());
     return icEntryList()[index];
 }
 
+RetAddrEntry&
+BaselineScript::retAddrEntry(size_t index)
+{
+    MOZ_ASSERT(index < numRetAddrEntries());
+    return retAddrEntryList()[index];
+}
+
 PCMappingIndexEntry&
 BaselineScript::pcMappingIndexEntry(size_t index)
 {
     MOZ_ASSERT(index < numPCMappingIndexEntries());
     return pcMappingIndexEntryList()[index];
 }
 
 CompactBufferReader
@@ -582,99 +597,125 @@ BaselineScript::pcMappingReader(size_t i
         ? pcMappingData() + pcMappingSize_
         : pcMappingData() + pcMappingIndexEntry(indexEntry + 1).bufferOffset;
 
     return CompactBufferReader(dataStart, dataEnd);
 }
 
 struct ICEntries
 {
+    using EntryT = ICEntry;
+
     BaselineScript* const baseline_;
 
     explicit ICEntries(BaselineScript* baseline) : baseline_(baseline) {}
 
+    size_t numEntries() const {
+        return baseline_->numICEntries();
+    }
     ICEntry& operator[](size_t index) const {
         return baseline_->icEntry(index);
     }
 };
 
-ICEntry&
-BaselineScript::icEntryFromReturnOffset(CodeOffset returnOffset)
+struct RetAddrEntries
+{
+    using EntryT = RetAddrEntry;
+
+    BaselineScript* const baseline_;
+
+    explicit RetAddrEntries(BaselineScript* baseline) : baseline_(baseline) {}
+
+    size_t numEntries() const {
+        return baseline_->numRetAddrEntries();
+    }
+    RetAddrEntry& operator[](size_t index) const {
+        return baseline_->retAddrEntry(index);
+    }
+};
+
+RetAddrEntry&
+BaselineScript::retAddrEntryFromReturnOffset(CodeOffset returnOffset)
 {
     size_t loc;
 #ifdef DEBUG
     bool found =
 #endif
-        BinarySearchIf(ICEntries(this), 0, numICEntries(),
-                       [&returnOffset](ICEntry& entry) {
+        BinarySearchIf(RetAddrEntries(this), 0, numRetAddrEntries(),
+                       [&returnOffset](const RetAddrEntry& entry) {
                            size_t roffset = returnOffset.offset();
                            size_t entryRoffset = entry.returnOffset().offset();
                            if (roffset < entryRoffset) {
                                return -1;
                            }
                            if (entryRoffset < roffset) {
                                return 1;
                            }
                            return 0;
                        },
                        &loc);
 
     MOZ_ASSERT(found);
-    MOZ_ASSERT(loc < numICEntries());
-    MOZ_ASSERT(icEntry(loc).returnOffset().offset() == returnOffset.offset());
-    return icEntry(loc);
+    MOZ_ASSERT(loc < numRetAddrEntries());
+    MOZ_ASSERT(retAddrEntry(loc).returnOffset().offset() == returnOffset.offset());
+    return retAddrEntry(loc);
 }
 
+template <typename Entries>
 static inline bool
 ComputeBinarySearchMid(BaselineScript* baseline, uint32_t pcOffset, size_t* loc)
 {
-    return BinarySearchIf(ICEntries(baseline), 0, baseline->numICEntries(),
-                          [pcOffset](ICEntry& entry) {
+    Entries entries(baseline);
+    return BinarySearchIf(entries, 0, entries.numEntries(),
+                          [pcOffset](typename Entries::EntryT& entry) {
                               uint32_t entryOffset = entry.pcOffset();
                               if (pcOffset < entryOffset) {
                                   return -1;
                               }
                               if (entryOffset < pcOffset) {
                                   return 1;
                               }
                               return 0;
                           },
                           loc);
 }
 
 uint8_t*
-BaselineScript::returnAddressForIC(const ICEntry& ent)
+BaselineScript::returnAddressForEntry(const RetAddrEntry& ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
 ICEntry*
 BaselineScript::maybeICEntryFromPCOffset(uint32_t pcOffset)
 {
     // Multiple IC entries can have the same PC offset, but this method only looks for
     // those which have isForOp() set.
     size_t mid;
-    if (!ComputeBinarySearchMid(this, pcOffset, &mid)) {
+    if (!ComputeBinarySearchMid<ICEntries>(this, pcOffset, &mid)) {
         return nullptr;
     }
 
     MOZ_ASSERT(mid < numICEntries());
 
     // Found an IC entry with a matching PC offset.  Search backward, and then
     // forward from this IC entry, looking for one with the same PC offset which
     // has isForOp() set.
     for (size_t i = mid; icEntry(i).pcOffset() == pcOffset; i--) {
         if (icEntry(i).isForOp()) {
             return &icEntry(i);
         }
         if (i == 0) {
             break;
         }
     }
-    for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
+    for (size_t i = mid + 1; i < numICEntries(); i++) {
+        if (icEntry(i).pcOffset() != pcOffset) {
+            break;
+        }
         if (icEntry(i).isForOp()) {
             return &icEntry(i);
         }
     }
     return nullptr;
 }
 
 ICEntry&
@@ -711,76 +752,68 @@ BaselineScript::maybeICEntryFromPCOffset
 ICEntry&
 BaselineScript::icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry)
 {
     ICEntry* entry = maybeICEntryFromPCOffset(pcOffset, prevLookedUpEntry);
     MOZ_RELEASE_ASSERT(entry);
     return *entry;
 }
 
-ICEntry&
-BaselineScript::callVMEntryFromPCOffset(uint32_t pcOffset)
+RetAddrEntry&
+BaselineScript::retAddrEntryFromPCOffset(uint32_t pcOffset, RetAddrEntry::Kind kind)
 {
-    // Like icEntryFromPCOffset, but only looks for the fake ICEntries
-    // inserted by VM calls.
     size_t mid;
-    MOZ_ALWAYS_TRUE(ComputeBinarySearchMid(this, pcOffset, &mid));
-    MOZ_ASSERT(mid < numICEntries());
+    MOZ_ALWAYS_TRUE(ComputeBinarySearchMid<RetAddrEntries>(this, pcOffset, &mid));
+    MOZ_ASSERT(mid < numRetAddrEntries());
 
-    for (size_t i = mid; icEntry(i).pcOffset() == pcOffset; i--) {
-        if (icEntry(i).kind() == ICEntry::Kind_CallVM) {
-            return icEntry(i);
+    for (size_t i = mid; retAddrEntry(i).pcOffset() == pcOffset; i--) {
+        if (retAddrEntry(i).kind() == kind) {
+            return retAddrEntry(i);
         }
         if (i == 0) {
             break;
         }
     }
-    for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
-        if (icEntry(i).kind() == ICEntry::Kind_CallVM) {
-            return icEntry(i);
+    for (size_t i = mid + 1; i < numRetAddrEntries(); i++) {
+        if (retAddrEntry(i).pcOffset() != pcOffset) {
+            break;
+        }
+        if (retAddrEntry(i).kind() == kind) {
+            return retAddrEntry(i);
         }
     }
-    MOZ_CRASH("Invalid PC offset for callVM entry.");
+    MOZ_CRASH("Didn't find RetAddrEntry.");
 }
 
-ICEntry&
-BaselineScript::stackCheckICEntry()
+RetAddrEntry&
+BaselineScript::prologueRetAddrEntry(RetAddrEntry::Kind kind)
 {
-    // The stack check will always be at offset 0, so just do a linear search
-    // from the beginning. This is only needed for debug mode OSR, when
-    // patching a frame that has invoked a Debugger hook via the interrupt
-    // handler via the stack check, which is part of the prologue.
-    for (size_t i = 0; i < numICEntries() && icEntry(i).pcOffset() == 0; i++) {
-        if (icEntry(i).kind() == ICEntry::Kind_StackCheck) {
-            return icEntry(i);
+    MOZ_ASSERT(kind == RetAddrEntry::Kind::StackCheck ||
+               kind == RetAddrEntry::Kind::WarmupCounter);
+
+    // The prologue entries will always be at a very low offset, so just do a
+    // linear search from the beginning.
+    for (size_t i = 0; i < numRetAddrEntries(); i++) {
+        if (retAddrEntry(i).pcOffset() != 0) {
+            break;
+        }
+        if (retAddrEntry(i).kind() == kind) {
+            return retAddrEntry(i);
         }
     }
-    MOZ_CRASH("No stack check ICEntry found.");
+    MOZ_CRASH("Didn't find prologue RetAddrEntry.");
 }
 
-ICEntry&
-BaselineScript::warmupCountICEntry()
-{
-    // The stack check will be at a very low offset, so just do a linear search
-    // from the beginning.
-    for (size_t i = 0; i < numICEntries() && icEntry(i).pcOffset() == 0; i++) {
-        if (icEntry(i).kind() == ICEntry::Kind_WarmupCounter) {
-            return icEntry(i);
-        }
-    }
-    MOZ_CRASH("No warmup count ICEntry found.");
-}
-
-ICEntry&
-BaselineScript::icEntryFromReturnAddress(uint8_t* returnAddr)
+RetAddrEntry&
+BaselineScript::retAddrEntryFromReturnAddress(uint8_t* returnAddr)
 {
     MOZ_ASSERT(returnAddr > method_->raw());
     MOZ_ASSERT(returnAddr < method_->raw() + method_->instructionsSize());
     CodeOffset offset(returnAddr - method_->raw());
-    return icEntryFromReturnOffset(offset);
+    return retAddrEntryFromReturnOffset(offset);
 }
 
 void
 BaselineScript::copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets)
 {
     uint8_t** entries = yieldEntryList();
 
     for (size_t i = 0; i < yieldAndAwaitOffsets.length(); i++) {
@@ -793,21 +826,16 @@ void
 BaselineScript::copyICEntries(JSScript* script, const ICEntry* entries)
 {
     // Fix up the return offset in the IC entries and copy them in.
     // Also write out the IC entry ptrs in any fallback stubs that were added.
     for (uint32_t i = 0; i < numICEntries(); i++) {
         ICEntry& realEntry = icEntry(i);
         realEntry = entries[i];
 
-        if (!realEntry.hasStub()) {
-            // VM call without any stubs.
-            continue;
-        }
-
         // If the attached stub is a fallback stub, then fix it up with
         // a pointer to the (now available) realEntry.
         if (realEntry.firstStub()->isFallback()) {
             realEntry.firstStub()->toFallbackStub()->fixupICEntry(&realEntry);
         }
 
         if (realEntry.firstStub()->isTypeMonitor_Fallback()) {
             ICTypeMonitor_Fallback* stub = realEntry.firstStub()->toTypeMonitor_Fallback();
@@ -817,16 +845,24 @@ BaselineScript::copyICEntries(JSScript* 
         if (realEntry.firstStub()->isTableSwitch()) {
             ICTableSwitch* stub = realEntry.firstStub()->toTableSwitch();
             stub->fixupJumpTable(script, this);
         }
     }
 }
 
 void
+BaselineScript::copyRetAddrEntries(JSScript* script, const RetAddrEntry* entries)
+{
+    for (uint32_t i = 0; i < numRetAddrEntries(); i++) {
+        retAddrEntry(i) = entries[i];
+    }
+}
+
+void
 BaselineScript::adoptFallbackStubs(FallbackICStubSpace* stubSpace)
 {
     fallbackStubSpace_.adoptFrom(stubSpace);
 }
 
 void
 BaselineScript::copyPCMappingEntries(const CompactBufferWriter& entries)
 {
@@ -1104,20 +1140,16 @@ BaselineScript::toggleProfilerInstrument
 
 void
 BaselineScript::purgeOptimizedStubs(Zone* zone)
 {
     JitSpew(JitSpew_BaselineIC, "Purging optimized stubs");
 
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry& entry = icEntry(i);
-        if (!entry.hasStub()) {
-            continue;
-        }
-
         ICStub* lastStub = entry.firstStub();
         while (lastStub->next()) {
             lastStub = lastStub->next();
         }
 
         if (lastStub->isFallback()) {
             // Unlink all stubs allocated in the optimized space.
             ICStub* stub = entry.firstStub();
@@ -1149,20 +1181,16 @@ BaselineScript::purgeOptimizedStubs(Zone
             MOZ_ASSERT(lastStub->isTableSwitch());
         }
     }
 
 #ifdef DEBUG
     // All remaining stubs must be allocated in the fallback space.
     for (size_t i = 0; i < numICEntries(); i++) {
         ICEntry& entry = icEntry(i);
-        if (!entry.hasStub()) {
-            continue;
-        }
-
         ICStub* stub = entry.firstStub();
         while (stub->next()) {
             MOZ_ASSERT(stub->allocatedInFallbackSpace());
             stub = stub->next();
         }
     }
 #endif
 }
@@ -1191,19 +1219,16 @@ DumpICInfo(JSScript* script)
     Fprinter& out = JitSpewPrinter();
 
     const char* filename = script->filename() ? script->filename() : "unknown";
     out.printf("Dumping IC info for %s:%d\n", filename,
             PCToLineNumber(script, script->code()));
 
     for (size_t i = 0; i < blScript->numICEntries(); i++) {
         ICEntry& entry = blScript->icEntry(i);
-        if (!entry.hasStub()) {
-            continue;
-        }
 
         unsigned column;
         jsbytecode* pc = entry.pc(script);
         unsigned int line = PCToLineNumber(script, pc, &column);
         out.printf("\t%s:%u:%u (%s) \t", filename, line, column, CodeName[*pc]);
 
         ICStub* stub = entry.firstStub();
         while (stub) {
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -20,16 +20,17 @@
 
 namespace js {
 namespace jit {
 
 class StackValue;
 class ICEntry;
 class ICStub;
 class ControlFlowGraph;
+class ReturnAddressEntry;
 
 class PCMappingSlotInfo
 {
     uint8_t slotInfo_;
 
   public:
     // SlotInfo encoding:
     //  Bits 0 & 1: number of slots at top of stack which are unsynced.
@@ -103,34 +104,138 @@ struct DependentWasmImport
     size_t importIndex;
 
     DependentWasmImport(wasm::Instance& instance, size_t importIndex)
       : instance(&instance),
         importIndex(importIndex)
     { }
 };
 
+// Largest script that the baseline compiler will attempt to compile.
+#if defined(JS_CODEGEN_ARM)
+// ARM branches can only reach 32MB, and the macroassembler doesn't mitigate
+// that limitation. Use a stricter limit on the acceptable script size to
+// avoid crashing when branches go out of range.
+static constexpr uint32_t BaselineMaxScriptLength = 1000000u;
+#else
+static constexpr uint32_t BaselineMaxScriptLength = 0x0fffffffu;
+#endif
+
+// Limit the locals on a given script so that stack check on baseline frames
+// doesn't overflow a uint32_t value.
+// (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t.
+static constexpr uint32_t BaselineMaxScriptSlots = 0xffffu;
+
+// An entry in the BaselineScript return address table. These entries are used
+// to determine the bytecode pc for a return address into Baseline code.
+//
+// There must be an entry for each location where we can end up calling into
+// C++ (directly or via script/trampolines) and C++ can request the current
+// bytecode pc (this includes anything that may throw an exception, GC, or walk
+// the stack). We currently add entries for each:
+//
+// * callVM
+// * IC
+// * DebugTrap (trampoline call)
+// * JSOP_RESUME (because this is like a scripted call)
+//
+// Note: see also BaselineFrame::HAS_OVERRIDE_PC.
+class RetAddrEntry
+{
+    // Offset from the start of the JIT code where call instruction is.
+    uint32_t returnOffset_;
+
+    // The offset of this bytecode op within the JSScript.
+    uint32_t pcOffset_ : 28;
+
+  public:
+    enum class Kind : uint32_t {
+        // A for-op IC.
+        IC,
+
+        // A non-op IC.
+        NonOpIC,
+
+        // A callVM for an op.
+        CallVM,
+
+        // A callVM not for an op (e.g., in the prologue).
+        NonOpCallVM,
+
+        // A callVM for the warmup counter.
+        WarmupCounter,
+
+        // A callVM for the over-recursion check on function entry.
+        StackCheck,
+
+        // DebugTrapHandler (for debugger breakpoints/stepping).
+        DebugTrap,
+
+        // A callVM for Debug{Prologue,AfterYield,Epilogue}.
+        DebugPrologue,
+        DebugAfterYield,
+        DebugEpilogue,
+
+        Invalid
+    };
+
+  private:
+    // What this entry is for.
+    uint32_t kind_ : 4;
+
+  public:
+    RetAddrEntry(uint32_t pcOffset, Kind kind, CodeOffset retOffset)
+      : returnOffset_(uint32_t(retOffset.offset())), pcOffset_(pcOffset)
+    {
+        MOZ_ASSERT(returnOffset_ == retOffset.offset(),
+                   "retOffset must fit in returnOffset_");
+
+        // The pc offset must fit in at least 28 bits, since we shave off 4 for
+        // the Kind enum.
+        MOZ_ASSERT(pcOffset_ == pcOffset);
+        JS_STATIC_ASSERT(BaselineMaxScriptLength <= (1u << 28) - 1);
+        MOZ_ASSERT(pcOffset <= BaselineMaxScriptLength);
+        setKind(kind);
+    }
+
+    // Set the kind and asserts that it's sane.
+    void setKind(Kind kind) {
+        MOZ_ASSERT(kind < Kind::Invalid);
+        kind_ = uint32_t(kind);
+        MOZ_ASSERT(this->kind() == kind);
+    }
+
+    CodeOffset returnOffset() const {
+        return CodeOffset(returnOffset_);
+    }
+
+    uint32_t pcOffset() const {
+        return pcOffset_;
+    }
+
+    jsbytecode* pc(JSScript* script) const {
+        return script->offsetToPC(pcOffset_);
+    }
+
+    Kind kind() const {
+        MOZ_ASSERT(kind_ < uint32_t(Kind::Invalid));
+        return Kind(kind_);
+    }
+    bool isForOp() const {
+        return kind() == Kind::IC;
+    }
+
+    void setNonICKind(Kind kind) {
+        MOZ_ASSERT(kind != Kind::IC && kind != Kind::NonOpIC);
+        setKind(kind);
+    }
+};
+
 struct BaselineScript
 {
-  public:
-    // Largest script that the baseline compiler will attempt to compile.
-#if defined(JS_CODEGEN_ARM)
-    // ARM branches can only reach 32MB, and the macroassembler doesn't mitigate
-    // that limitation. Use a stricter limit on the acceptable script size to
-    // avoid crashing when branches go out of range.
-    static const uint32_t MAX_JSSCRIPT_LENGTH = 1000000u;
-#else
-    static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu;
-#endif
-
-    // Limit the locals on a given script so that stack check on baseline frames
-    // doesn't overflow a uint32_t value.
-    // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t.
-    static const uint32_t MAX_JSSCRIPT_SLOTS = 0xffffu;
-
   private:
     // Code pointer containing the actual method.
     HeapPtr<JitCode*> method_;
 
     // For functions with a call object, template objects to use for the call
     // object and decl env object (linked via the call object's enclosing
     // scope).
     HeapPtr<EnvironmentObject*> templateEnv_;
@@ -206,16 +311,19 @@ struct BaselineScript
     uint32_t flags_;
 
   private:
     void trace(JSTracer* trc);
 
     uint32_t icEntriesOffset_;
     uint32_t icEntries_;
 
+    uint32_t retAddrEntriesOffset_;
+    uint32_t retAddrEntries_;
+
     uint32_t pcMappingIndexOffset_;
     uint32_t pcMappingIndexEntries_;
 
     uint32_t pcMappingOffset_;
     uint32_t pcMappingSize_;
 
     // List mapping indexes of bytecode type sets to the offset of the opcode
     // they correspond to, for use by TypeScript::BytecodeTypes.
@@ -261,16 +369,17 @@ struct BaselineScript
     }
 
     static BaselineScript* New(JSScript* jsscript,
                                uint32_t prologueOffset, uint32_t epilogueOffset,
                                uint32_t profilerEnterToggleOffset,
                                uint32_t profilerExitToggleOffset,
                                uint32_t postDebugPrologueOffset,
                                size_t icEntries,
+                               size_t retAddrEntries,
                                size_t pcMappingIndexEntries, size_t pcMappingSize,
                                size_t bytecodeTypeMapEntries,
                                size_t yieldEntries,
                                size_t traceLoggerToggleOffsetEntries);
 
     static void Trace(JSTracer* trc, BaselineScript* script);
     static void Destroy(FreeOp* fop, BaselineScript* script);
 
@@ -353,16 +462,19 @@ struct BaselineScript
     }
     uint8_t* postDebugPrologueAddr() const {
         return method_->raw() + postDebugPrologueOffset_;
     }
 
     ICEntry* icEntryList() {
         return (ICEntry*)(reinterpret_cast<uint8_t*>(this) + icEntriesOffset_);
     }
+    RetAddrEntry* retAddrEntryList() {
+        return (RetAddrEntry*)(reinterpret_cast<uint8_t*>(this) + retAddrEntriesOffset_);
+    }
     uint8_t** yieldEntryList() {
         return (uint8_t**)(reinterpret_cast<uint8_t*>(this) + yieldEntriesOffset_);
     }
     PCMappingIndexEntry* pcMappingIndexEntryList() {
         return (PCMappingIndexEntry*)(reinterpret_cast<uint8_t*>(this) + pcMappingIndexOffset_);
     }
     uint8_t* pcMappingData() {
         return reinterpret_cast<uint8_t*>(this) + pcMappingOffset_;
@@ -388,33 +500,41 @@ struct BaselineScript
     }
 
     bool containsCodeAddress(uint8_t* addr) const {
         return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
     }
 
     ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
     ICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
-                                              ICEntry* prevLookedUpEntry);
+                                      ICEntry* prevLookedUpEntry);
 
     ICEntry& icEntry(size_t index);
-    ICEntry& icEntryFromReturnOffset(CodeOffset returnOffset);
     ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
     ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
-    ICEntry& callVMEntryFromPCOffset(uint32_t pcOffset);
-    ICEntry& stackCheckICEntry();
-    ICEntry& warmupCountICEntry();
-    ICEntry& icEntryFromReturnAddress(uint8_t* returnAddr);
-    uint8_t* returnAddressForIC(const ICEntry& ent);
+
+    uint8_t* returnAddressForEntry(const RetAddrEntry& ent);
+
+    RetAddrEntry& retAddrEntry(size_t index);
+    RetAddrEntry& retAddrEntryFromPCOffset(uint32_t pcOffset, RetAddrEntry::Kind kind);
+    RetAddrEntry& prologueRetAddrEntry(RetAddrEntry::Kind kind);
+    RetAddrEntry& retAddrEntryFromReturnOffset(CodeOffset returnOffset);
+    RetAddrEntry& retAddrEntryFromReturnAddress(uint8_t* returnAddr);
 
     size_t numICEntries() const {
         return icEntries_;
     }
 
+    size_t numRetAddrEntries() const {
+        return retAddrEntries_;
+    }
+
     void copyICEntries(JSScript* script, const ICEntry* entries);
+    void copyRetAddrEntries(JSScript* script, const RetAddrEntry* entries);
+
     void adoptFallbackStubs(FallbackICStubSpace* stubSpace);
 
     void copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets);
 
     PCMappingIndexEntry& pcMappingIndexEntry(size_t index);
     CompactBufferReader pcMappingReader(size_t indexEntry);
 
     size_t numPCMappingIndexEntries() const {
--- a/js/src/jit/JSJitFrameIter.cpp
+++ b/js/src/jit/JSJitFrameIter.cpp
@@ -142,20 +142,20 @@ JSJitFrameIter::baselineScriptAndPc(JSSc
     // Use the frame's override pc, if we have one. This should only happen
     // when we're in FinishBailoutToBaseline, handling an exception or toggling
     // debug mode.
     if (jsbytecode* overridePc = baselineFrame()->maybeOverridePc()) {
         *pcRes = overridePc;
         return;
     }
 
-    // Else, there must be an ICEntry for the current return address.
+    // Else, there must be a VMCallEntry for the current return address.
     uint8_t* retAddr = returnAddressToFp();
-    ICEntry& icEntry = script->baselineScript()->icEntryFromReturnAddress(retAddr);
-    *pcRes = icEntry.pc(script);
+    RetAddrEntry& entry = script->baselineScript()->retAddrEntryFromReturnAddress(retAddr);
+    *pcRes = entry.pc(script);
 }
 
 Value*
 JSJitFrameIter::actualArgs() const
 {
     return jsFrame()->argv() + 1;
 }
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1111,17 +1111,17 @@ InitRestParameter(JSContext* cx, uint32_
 }
 
 bool
 HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr, bool* mustReturn)
 {
     *mustReturn = false;
 
     RootedScript script(cx, frame->script());
-    jsbytecode* pc = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script);
+    jsbytecode* pc = script->baselineScript()->retAddrEntryFromReturnAddress(retAddr).pc(script);
 
     if (*pc == JSOP_DEBUGAFTERYIELD) {
         // JSOP_DEBUGAFTERYIELD will set the frame's debuggee flag and call the
         // onEnterFrame handler, but if we set a breakpoint there we have to do
         // it now.
         MOZ_ASSERT(!frame->isDebuggee());
 
         if (!DebugAfterYield(cx, frame, pc, mustReturn)) {
--- a/js/src/jit/arm/SharedICHelpers-arm.h
+++ b/js/src/jit/arm/SharedICHelpers-arm.h
@@ -26,32 +26,33 @@ EmitRestoreTailCallReg(MacroAssembler& m
 
 inline void
 EmitRepushTailCallReg(MacroAssembler& masm)
 {
     // No-op on ARM because link register is always holding the return address.
 }
 
 inline void
-EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
+EmitCallIC(MacroAssembler& masm, CodeOffset* patchOffset, CodeOffset* callOffset)
 {
     // Move ICEntry offset into ICStubReg
     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into ICStubReg
     masm.loadPtr(Address(ICStubReg, ICEntry::offsetOfFirstStub()), ICStubReg);
 
     // Load stubcode pointer from BaselineStubEntry.
     // R2 won't be active when we call ICs, so we can use r0.
     MOZ_ASSERT(R2 == ValueOperand(r1, r0));
     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), r0);
 
     // Call the stubcode via a direct branch-and-link.
     masm.ma_blx(r0);
+    *callOffset = CodeOffset(masm.currentOffset());
 }
 
 inline void
 EmitEnterTypeMonitorIC(MacroAssembler& masm,
                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
 {
     // This is expected to be called from within an IC, when ICStubReg is
     // properly initialized to point to the stub.
--- a/js/src/jit/arm64/SharedICHelpers-arm64.h
+++ b/js/src/jit/arm64/SharedICHelpers-arm64.h
@@ -26,32 +26,33 @@ EmitRestoreTailCallReg(MacroAssembler& m
 
 inline void
 EmitRepushTailCallReg(MacroAssembler& masm)
 {
     // No-op on ARM because link register is always holding the return address.
 }
 
 inline void
-EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
+EmitCallIC(MacroAssembler& masm, CodeOffset* patchOffset, CodeOffset* callOffset)
 {
     // Move ICEntry offset into ICStubReg
     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into ICStubReg
     masm.loadPtr(Address(ICStubReg, ICEntry::offsetOfFirstStub()), ICStubReg);
 
     // Load stubcode pointer from BaselineStubEntry.
     // R2 won't be active when we call ICs, so we can use r0.
     MOZ_ASSERT(R2 == ValueOperand(r0));
     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), r0);
 
     // Call the stubcode via a direct branch-and-link.
     masm.Blr(x0);
+    *callOffset = CodeOffset(masm.currentOffset());
 }
 
 inline void
 EmitEnterTypeMonitorIC(MacroAssembler& masm,
                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
 {
     // This is expected to be called from within an IC, when ICStubReg is
     // properly initialized to point to the stub.
--- a/js/src/jit/mips-shared/SharedICHelpers-mips-shared.h
+++ b/js/src/jit/mips-shared/SharedICHelpers-mips-shared.h
@@ -38,31 +38,32 @@ EmitRestoreTailCallReg(MacroAssembler& m
 
 inline void
 EmitRepushTailCallReg(MacroAssembler& masm)
 {
     // No-op on MIPS because ra register is always holding the return address.
 }
 
 inline void
-EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
+EmitCallIC(MacroAssembler& masm, CodeOffset* patchOffset, CodeOffset* callOffset)
 {
     // Move ICEntry offset into ICStubReg.
     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into ICStubReg.
     masm.loadPtr(Address(ICStubReg, ICEntry::offsetOfFirstStub()), ICStubReg);
 
     // Load stubcode pointer from BaselineStubEntry.
     // R2 won't be active when we call ICs, so we can use it as scratch.
     masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg());
 
     // Call the stubcode via a direct jump-and-link
     masm.call(R2.scratchReg());
+    *callOffset = CodeOffset(masm.currentOffset());
 }
 
 inline void
 EmitEnterTypeMonitorIC(MacroAssembler& masm,
                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
 {
     // This is expected to be called from within an IC, when ICStubReg
     // is properly initialized to point to the stub.
--- a/js/src/jit/none/SharedICHelpers-none.h
+++ b/js/src/jit/none/SharedICHelpers-none.h
@@ -9,17 +9,17 @@
 
 namespace js {
 namespace jit {
 
 static const size_t ICStackValueOffset = 0;
 
 inline void EmitRestoreTailCallReg(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitRepushTailCallReg(MacroAssembler&) { MOZ_CRASH(); }
-inline void EmitCallIC(CodeOffset*, MacroAssembler&) { MOZ_CRASH(); }
+inline void EmitCallIC(MacroAssembler&, CodeOffset*, CodeOffset*) { MOZ_CRASH(); }
 inline void EmitEnterTypeMonitorIC(MacroAssembler&, size_t v = 0) { MOZ_CRASH(); }
 inline void EmitReturnFromIC(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitChangeICReturnAddress(MacroAssembler&, Register) { MOZ_CRASH(); }
 inline void EmitBaselineLeaveStubFrame(MacroAssembler&, bool v = false) { MOZ_CRASH(); }
 inline void EmitStubGuardFailure(MacroAssembler&) { MOZ_CRASH(); }
 
 template <typename T> inline void EmitPreBarrier(MacroAssembler&, T, MIRType) { MOZ_CRASH(); }
 
--- a/js/src/jit/x64/SharedICHelpers-x64.h
+++ b/js/src/jit/x64/SharedICHelpers-x64.h
@@ -26,28 +26,29 @@ EmitRestoreTailCallReg(MacroAssembler& m
 
 inline void
 EmitRepushTailCallReg(MacroAssembler& masm)
 {
     masm.Push(ICTailCallReg);
 }
 
 inline void
-EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
+EmitCallIC(MacroAssembler& masm, CodeOffset* patchOffset, CodeOffset* callOffset)
 {
     // Move ICEntry offset into ICStubReg
     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into ICStubReg
     masm.loadPtr(Address(ICStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
                  ICStubReg);
 
     // Call the stubcode.
     masm.call(Address(ICStubReg, ICStub::offsetOfStubCode()));
+    *callOffset = CodeOffset(masm.currentOffset());
 }
 
 inline void
 EmitEnterTypeMonitorIC(MacroAssembler& masm,
                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
 {
     // This is expected to be called from within an IC, when ICStubReg
     // is properly initialized to point to the stub.
--- a/js/src/jit/x86/SharedICHelpers-x86.h
+++ b/js/src/jit/x86/SharedICHelpers-x86.h
@@ -26,29 +26,30 @@ EmitRestoreTailCallReg(MacroAssembler& m
 
 inline void
 EmitRepushTailCallReg(MacroAssembler& masm)
 {
     masm.Push(ICTailCallReg);
 }
 
 inline void
-EmitCallIC(CodeOffset* patchOffset, MacroAssembler& masm)
+EmitCallIC(MacroAssembler& masm, CodeOffset* patchOffset, CodeOffset* callOffset)
 {
     // Move ICEntry offset into ICStubReg
     CodeOffset offset = masm.movWithPatch(ImmWord(-1), ICStubReg);
     *patchOffset = offset;
 
     // Load stub pointer into ICStubReg
     masm.loadPtr(Address(ICStubReg, (int32_t) ICEntry::offsetOfFirstStub()),
                  ICStubReg);
 
     // Load stubcode pointer from BaselineStubEntry into ICTailCallReg
     // ICTailCallReg will always be unused in the contexts where ICs are called.
     masm.call(Address(ICStubReg, ICStub::offsetOfStubCode()));
+    *callOffset = CodeOffset(masm.currentOffset());
 }
 
 inline void
 EmitEnterTypeMonitorIC(MacroAssembler& masm,
                        size_t monitorStubOffset = ICMonitoredStub::offsetOfFirstMonitorStub())
 {
     // This is expected to be called from within an IC, when ICStubReg
     // is properly initialized to point to the stub.
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -3213,30 +3213,28 @@ nsLineLayout::TextAlignLine(nsLineBox* a
   // 'text-align-last: auto' is equivalent to the value of the 'text-align'
   // property except when 'text-align' is set to 'justify', in which case it
   // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
   //
   // XXX: the code below will have to change when we implement text-justify
   //
   nscoord dx = 0;
   uint8_t textAlign = mStyleText->mTextAlign;
-  bool textAlignTrue = mStyleText->mTextAlignTrue;
   if (aIsLastLine) {
-    textAlignTrue = mStyleText->mTextAlignLastTrue;
     if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) {
       if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
         textAlign = NS_STYLE_TEXT_ALIGN_START;
       }
     } else {
       textAlign = mStyleText->mTextAlignLast;
     }
   }
 
   bool isSVG = nsSVGUtils::IsInSVGTextSubtree(mBlockReflowInput->mFrame);
-  bool doTextAlign = remainingISize > 0 || textAlignTrue;
+  bool doTextAlign = remainingISize > 0;
 
   int32_t additionalGaps = 0;
   if (!isSVG && (mHasRuby || (doTextAlign &&
                               textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY))) {
     JustificationComputationState computeState;
     ComputeFrameJustification(psd, computeState);
     if (mHasRuby && computeState.mFirstParticipant) {
       PerFrameData* firstFrame = computeState.mFirstParticipant;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2704,40 +2704,22 @@ nsComputedDOMStyle::DoGetVerticalAlign()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   SetValueToCoord(val, StyleDisplay()->mVerticalAlign, false,
                   nullptr, nsCSSProps::kVerticalAlignKTable);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
-nsComputedDOMStyle::CreateTextAlignValue(uint8_t aAlign, bool aAlignTrue,
-                                         const KTableEntry aTable[])
+nsComputedDOMStyle::DoGetTextAlign()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetIdent(nsCSSProps::ValueToKeywordEnum(aAlign, aTable));
-  if (!aAlignTrue) {
-    return val.forget();
-  }
-
-  RefPtr<nsROCSSPrimitiveValue> first = new nsROCSSPrimitiveValue;
-  first->SetIdent(eCSSKeyword_unsafe);
-
-  RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
-  valueList->AppendCSSValue(first.forget());
-  valueList->AppendCSSValue(val.forget());
-  return valueList.forget();
-}
-
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetTextAlign()
-{
-  const nsStyleText* style = StyleText();
-  return CreateTextAlignValue(style->mTextAlign, style->mTextAlignTrue,
-                              nsCSSProps::kTextAlignKTable);
+  val->SetIdent(nsCSSProps::ValueToKeywordEnum(
+      StyleText()->mTextAlign, nsCSSProps::kTextAlignKTable));
+  return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetTextDecoration()
 {
   const nsStyleTextReset* textReset = StyleTextReset();
 
   bool isInitialStyle =
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -144,20 +144,16 @@ private:
 
   void AssertFlushedPendingReflows() {
     NS_ASSERTION(mFlushedPendingReflows,
                  "property getter should have been marked layout-dependent");
   }
 
   nsMargin GetAdjustedValuesForBoxSizing();
 
-  // Helper method for DoGetTextAlign[Last].
-  already_AddRefed<CSSValue> CreateTextAlignValue(uint8_t aAlign,
-                                                  bool aAlignTrue,
-                                                  const KTableEntry aTable[]);
   // This indicates error by leaving mComputedStyle null.
   void UpdateCurrentStyleSources(bool aNeedsLayoutFlush);
   void ClearCurrentStyleSources();
 
   // Helper functions called by UpdateCurrentStyleSources.
   void ClearComputedStyle();
   void SetResolvedComputedStyle(RefPtr<mozilla::ComputedStyle>&& aContext,
                                 uint64_t aGeneration);
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -4308,18 +4308,16 @@ AreShadowArraysEqual(nsCSSShadowArray* l
 
 // --------------------
 // nsStyleText
 //
 
 nsStyleText::nsStyleText(const nsPresContext* aContext)
   : mTextAlign(NS_STYLE_TEXT_ALIGN_START)
   , mTextAlignLast(NS_STYLE_TEXT_ALIGN_AUTO)
-  , mTextAlignTrue(false)
-  , mTextAlignLastTrue(false)
   , mTextJustify(StyleTextJustify::Auto)
   , mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE)
   , mWhiteSpace(StyleWhiteSpace::Normal)
   , mWordBreak(NS_STYLE_WORDBREAK_NORMAL)
   , mOverflowWrap(NS_STYLE_OVERFLOWWRAP_NORMAL)
   , mHyphens(StyleHyphens::Manual)
   , mRubyAlign(NS_STYLE_RUBY_ALIGN_SPACE_AROUND)
   , mRubyPosition(NS_STYLE_RUBY_POSITION_OVER)
@@ -4345,18 +4343,16 @@ nsStyleText::nsStyleText(const nsPresCon
     nsStyleUtil::MatchesLanguagePrefix(language, u"zh") ?
     NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH :
     NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
 }
 
 nsStyleText::nsStyleText(const nsStyleText& aSource)
   : mTextAlign(aSource.mTextAlign)
   , mTextAlignLast(aSource.mTextAlignLast)
-  , mTextAlignTrue(false)
-  , mTextAlignLastTrue(false)
   , mTextJustify(aSource.mTextJustify)
   , mTextTransform(aSource.mTextTransform)
   , mWhiteSpace(aSource.mWhiteSpace)
   , mWordBreak(aSource.mWordBreak)
   , mOverflowWrap(aSource.mOverflowWrap)
   , mHyphens(aSource.mHyphens)
   , mRubyAlign(aSource.mRubyAlign)
   , mRubyPosition(aSource.mRubyPosition)
@@ -4397,18 +4393,16 @@ nsStyleText::CalcDifference(const nsStyl
 
   if (mTextCombineUpright != aNewData.mTextCombineUpright ||
       mControlCharacterVisibility != aNewData.mControlCharacterVisibility) {
     return nsChangeHint_ReconstructFrame;
   }
 
   if ((mTextAlign != aNewData.mTextAlign) ||
       (mTextAlignLast != aNewData.mTextAlignLast) ||
-      (mTextAlignTrue != aNewData.mTextAlignTrue) ||
-      (mTextAlignLastTrue != aNewData.mTextAlignLastTrue) ||
       (mTextTransform != aNewData.mTextTransform) ||
       (mWhiteSpace != aNewData.mWhiteSpace) ||
       (mWordBreak != aNewData.mWordBreak) ||
       (mOverflowWrap != aNewData.mOverflowWrap) ||
       (mHyphens != aNewData.mHyphens) ||
       (mRubyAlign != aNewData.mRubyAlign) ||
       (mRubyPosition != aNewData.mRubyPosition) ||
       (mTextSizeAdjust != aNewData.mTextSizeAdjust) ||
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1635,18 +1635,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   ~nsStyleText();
   void FinishStyle(nsPresContext*, const nsStyleText*) {}
   const static bool kHasFinishStyle = false;
 
   nsChangeHint CalcDifference(const nsStyleText& aNewData) const;
 
   uint8_t mTextAlign;                   // NS_STYLE_TEXT_ALIGN_*
   uint8_t mTextAlignLast;               // NS_STYLE_TEXT_ALIGN_*
-  bool mTextAlignTrue : 1;
-  bool mTextAlignLastTrue : 1;
   mozilla::StyleTextJustify mTextJustify;
   uint8_t mTextTransform;               // NS_STYLE_TEXT_TRANSFORM_*
   mozilla::StyleWhiteSpace mWhiteSpace;
   uint8_t mWordBreak;                   // NS_STYLE_WORDBREAK_*
   uint8_t mOverflowWrap;                // NS_STYLE_OVERFLOWWRAP_*
   mozilla::StyleHyphens mHyphens;
   uint8_t mRubyAlign;                   // NS_STYLE_RUBY_ALIGN_*
   uint8_t mRubyPosition;                // NS_STYLE_RUBY_POSITION_*
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1824,17 +1824,17 @@ impl Animate for Quaternion {
         // Dot product, clamped between -1 and 1.
         let dot =
             (self.0 * other.0 +
              self.1 * other.1 +
              self.2 * other.2 +
              self.3 * other.3)
             .min(1.0).max(-1.0);
 
-        if dot == 1.0 {
+        if dot.abs() == 1.0 {
             return Ok(*self);
         }
 
         let theta = dot.acos();
         let rsintheta = 1.0 / (1.0 - dot * dot).sqrt();
 
         let right_weight = (other_weight * theta).sin() * rsintheta;
         let left_weight = (other_weight * theta).cos() - dot * right_weight;
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozdebug/mozdebug/setup.cfg
@@ -0,0 +1,2 @@
+[bdist_wheel]
+universal=1
\ No newline at end of file
--- a/testing/mozbase/mozdebug/setup.py
+++ b/testing/mozbase/mozdebug/setup.py
@@ -1,17 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 
 from setuptools import setup
 
-PACKAGE_VERSION = '0.1'
+PACKAGE_VERSION = '0.1.1'
 
 setup(name='mozdebug',
       version=PACKAGE_VERSION,
       description="Utilities for running applications under native code debuggers "
       "intended for use in Mozilla testing",
       long_description="see https://firefox-source-docs.mozilla.org/mozbase/index.html",
       classifiers=['Programming Language :: Python :: 2.7',
                    'Programming Language :: Python :: 2 :: Only'],
--- a/testing/mozbase/mozdebug/tests/manifest.ini
+++ b/testing/mozbase/mozdebug/tests/manifest.ini
@@ -1,4 +1,3 @@
 [DEFAULT]
 subsuite = mozbase, os == "linux"
-skip-if = python == 3
 [test.py]
--- a/testing/mozbase/mozrunner/setup.py
+++ b/testing/mozbase/mozrunner/setup.py
@@ -2,27 +2,27 @@
 # 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/.
 
 from __future__ import absolute_import
 
 from setuptools import setup, find_packages
 
 PACKAGE_NAME = 'mozrunner'
-PACKAGE_VERSION = '7.1.0'
+PACKAGE_VERSION = '7.2.0'
 
 desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
 
 deps = [
     'mozdevice>=1.*',
     'mozfile>=1.2',
     'mozinfo>=0.7,<2',
     'mozlog==3.*',
     'mozprocess>=0.23,<1',
-    'mozprofile>=1.1.0,<3',
+    'mozprofile~=2.1',
     'six>=1.10.0,<2',
 ]
 
 EXTRAS_REQUIRE = {'crash': ['mozcrash >= 1.0']}
 
 
 setup(name=PACKAGE_NAME,
       version=PACKAGE_VERSION,
--- a/testing/tps/setup.py
+++ b/testing/tps/setup.py
@@ -10,17 +10,17 @@ version = '0.6'
 
 deps = ['httplib2 == 0.9.2',
         'mozfile >= 1.2',
         'mozhttpd == 0.7',
         'mozinfo >= 0.10',
         'mozinstall == 1.16',
         'mozprocess == 0.26',
         'mozprofile ~= 2.1',
-        'mozrunner == 7.0.2',
+        'mozrunner ~= 7.2',
         'mozversion == 1.5',
        ]
 
 # we only support python 2.6+ right now
 assert sys.version_info[0] == 2
 assert sys.version_info[1] >= 6
 
 setup(name='tps',
--- a/testing/web-platform/meta/editing/run/multitest.html.ini
+++ b/testing/web-platform/meta/editing/run/multitest.html.ini
@@ -924,28 +924,16 @@
     expected: FAIL
 
   [[["hilitecolor","#00FFFF"\],["outdent",""\]\] "foo[\]bar" queryCommandValue("hilitecolor") after]
     expected: FAIL
 
   [[["hilitecolor","#00FFFF"\],["outdent",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandValue("hilitecolor") after]
     expected: FAIL
 
-  [[["superscript",""\],["subscript",""\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
-    expected: FAIL
-
-  [[["superscript",""\],["subscript",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandState("superscript") after]
-    expected: FAIL
-
-  [[["subscript",""\],["superscript",""\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
-    expected: FAIL
-
-  [[["subscript",""\],["superscript",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandState("subscript") after]
-    expected: FAIL
-
   [[["createlink","http://www.google.com/"\],["forecolor","#0000FF"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
 
   [[["forecolor","#0000FF"\],["createlink","http://www.google.com/"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
 
   [[["createlink","http://www.google.com/"\],["forecolor","blue"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
@@ -2180,28 +2168,16 @@
     expected: FAIL
 
   [[["hilitecolor","#00FFFF"\],["outdent",""\]\] "foo[\]bar" queryCommandValue("hilitecolor") after]
     expected: FAIL
 
   [[["hilitecolor","#00FFFF"\],["outdent",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandValue("hilitecolor") after]
     expected: FAIL
 
-  [[["superscript",""\],["subscript",""\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
-    expected: FAIL
-
-  [[["superscript",""\],["subscript",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandState("superscript") after]
-    expected: FAIL
-
-  [[["subscript",""\],["superscript",""\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
-    expected: FAIL
-
-  [[["subscript",""\],["superscript",""\],["inserttext","a"\]\] "foo[\]bar" queryCommandState("subscript") after]
-    expected: FAIL
-
   [[["createlink","http://www.google.com/"\],["forecolor","#0000FF"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
 
   [[["forecolor","#0000FF"\],["createlink","http://www.google.com/"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
 
   [[["createlink","http://www.google.com/"\],["forecolor","blue"\],["inserttext","a"\]\] "foo[\]bar" compare innerHTML]
     expected: FAIL
--- a/testing/web-platform/meta/editing/run/subscript.html.ini
+++ b/testing/web-platform/meta/editing/run/subscript.html.ini
@@ -1,45 +1,21 @@
 [subscript.html]
   [[["subscript",""\]\] "<span>foo[</span><span>\]bar</span>" queryCommandState("subscript") after]
     expected: FAIL
 
-  [[["stylewithcss","true"\],["subscript",""\]\] "foo<sup>[bar\]</sup>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup>[bar\]</sup>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","true"\],["subscript",""\]\] "foo<sup>b[a\]r</sup>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup>b[a\]r</sup>baz" compare innerHTML]
-    expected: FAIL
-
   [[["subscript",""\]\] "foo<sub><sub>b[a\]r</sub></sub>baz" compare innerHTML]
     expected: FAIL
 
-  [[["stylewithcss","true"\],["subscript",""\]\] "foo<sup><sup>[bar\]</sup></sup>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup><sup>[bar\]</sup></sup>baz" compare innerHTML]
-    expected: FAIL
-
   [[["stylewithcss","true"\],["subscript",""\]\] "foo<sup><sup>b[a\]r</sup></sup>baz" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup><sup>b[a\]r</sup></sup>baz" compare innerHTML]
     expected: FAIL
 
-  [[["stylewithcss","true"\],["subscript",""\]\] "foo<sup>b<sup>[a\]</sup>r</sup>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup>b<sup>[a\]</sup>r</sup>baz" compare innerHTML]
-    expected: FAIL
-
   [[["stylewithcss","true"\],["subscript",""\]\] "foo<sub><sup>[bar\]</sup></sub>baz" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","true"\],["subscript",""\]\] "foo<sub><sup>[bar\]</sup></sub>baz" queryCommandIndeterm("subscript") before]
     expected: FAIL
 
   [[["stylewithcss","true"\],["subscript",""\]\] "foo<sub><sup>[bar\]</sup></sub>baz" queryCommandState("subscript") before]
     expected: FAIL
@@ -174,14 +150,11 @@
     expected: FAIL
 
   [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup>b<sub>[a\]</sub>r</sup>baz" queryCommandState("subscript") before]
     expected: FAIL
 
   [[["stylewithcss","false"\],["subscript",""\]\] "foo<sup>b<sub>[a\]</sub>r</sup>baz" queryCommandState("subscript") after]
     expected: FAIL
 
-  [[["subscript",""\]\] "<sub>fo[o</sub><sup>b\]ar</sup>" compare innerHTML]
-    expected: FAIL
-
   [subscript - HTML editing conformance tests]
     expected: FAIL
 
--- a/testing/web-platform/meta/editing/run/superscript.html.ini
+++ b/testing/web-platform/meta/editing/run/superscript.html.ini
@@ -1,42 +1,18 @@
 [superscript.html]
   [[["superscript",""\]\] "<span>foo[</span><span>\]bar</span>" queryCommandState("superscript") after]
     expected: FAIL
 
-  [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub>[bar\]</sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["superscript",""\]\] "foo<sub>[bar\]</sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub>b[a\]r</sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["superscript",""\]\] "foo<sub>b[a\]r</sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub><sub>[bar\]</sub></sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["superscript",""\]\] "foo<sub><sub>[bar\]</sub></sub>baz" compare innerHTML]
-    expected: FAIL
-
   [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub><sub>b[a\]r</sub></sub>baz" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","false"\],["superscript",""\]\] "foo<sub><sub>b[a\]r</sub></sub>baz" compare innerHTML]
     expected: FAIL
 
-  [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub>b<sub>[a\]</sub>r</sub>baz" compare innerHTML]
-    expected: FAIL
-
-  [[["stylewithcss","false"\],["superscript",""\]\] "foo<sub>b<sub>[a\]</sub>r</sub>baz" compare innerHTML]
-    expected: FAIL
-
   [[["superscript",""\]\] "foo<sup><sup>b[a\]r</sup></sup>baz" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub><sup>[bar\]</sup></sub>baz" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","true"\],["superscript",""\]\] "foo<sub><sup>[bar\]</sup></sub>baz" queryCommandIndeterm("superscript") before]
     expected: FAIL
@@ -174,19 +150,16 @@
     expected: FAIL
 
   [[["stylewithcss","false"\],["superscript",""\]\] "foo<sup>b<sub>[a\]</sub>r</sup>baz" queryCommandState("superscript") before]
     expected: FAIL
 
   [[["stylewithcss","false"\],["superscript",""\]\] "foo<sup>b<sub>[a\]</sub>r</sup>baz" queryCommandState("superscript") after]
     expected: FAIL
 
-  [[["superscript",""\]\] "<sup>fo[o</sup><sub>b\]ar</sub>" compare innerHTML]
-    expected: FAIL
-
   [[["stylewithcss","true"\],["superscript",""\]\] "foo<sup>[bar\]<br></sup>" compare innerHTML]
     expected: FAIL
 
   [[["stylewithcss","false"\],["superscript",""\]\] "foo<sup>[bar\]<br></sup>" compare innerHTML]
     expected: FAIL
 
   [superscript - HTML editing conformance tests]
     expected: FAIL
--- a/testing/web-platform/meta/netinfo/idlharness.any.js.ini
+++ b/testing/web-platform/meta/netinfo/idlharness.any.js.ini
@@ -1,32 +1,46 @@
 [idlharness.any.html]
   [Untitled]
     expected: FAIL
 
   [NetworkInformation interface: existence and properties of interface object]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface object length]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface object name]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: attribute type]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: attribute effectiveType]
     expected: FAIL
 
   [NetworkInformation interface: attribute downlinkMax]
     expected: FAIL
 
   [NetworkInformation interface: attribute downlink]
@@ -37,23 +51,29 @@
 
   [NetworkInformation interface: attribute saveData]
     expected: FAIL
 
   [NetworkInformation interface: attribute onchange]
     expected: FAIL
 
   [NetworkInformation must be primary interface of navigator.connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [Stringification of navigator.connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "type" with the proper type]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "effectiveType" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "downlinkMax" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "downlink" with the proper type]
@@ -64,46 +84,69 @@
 
   [NetworkInformation interface: navigator.connection must inherit property "saveData" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "onchange" with the proper type]
     expected: FAIL
 
   [Navigator interface: attribute connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [Navigator interface: navigator must inherit property "connection" with the proper type]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
 
 [idlharness.any.worker.html]
   [Untitled]
     expected: FAIL
 
+  [Navigator interface: navigator must not have property "connection"]
+    expected:
+      if os == "android": FAIL
+      PASS
+
   [NetworkInformation interface: existence and properties of interface object]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface object length]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface object name]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: existence and properties of interface prototype object's @@unscopables property]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: attribute type]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: attribute effectiveType]
     expected: FAIL
 
   [NetworkInformation interface: attribute downlinkMax]
     expected: FAIL
 
   [NetworkInformation interface: attribute downlink]
@@ -114,23 +157,29 @@
 
   [NetworkInformation interface: attribute saveData]
     expected: FAIL
 
   [NetworkInformation interface: attribute onchange]
     expected: FAIL
 
   [NetworkInformation must be primary interface of navigator.connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [Stringification of navigator.connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "type" with the proper type]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "effectiveType" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "downlinkMax" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "downlink" with the proper type]
@@ -141,10 +190,12 @@
 
   [NetworkInformation interface: navigator.connection must inherit property "saveData" with the proper type]
     expected: FAIL
 
   [NetworkInformation interface: navigator.connection must inherit property "onchange" with the proper type]
     expected: FAIL
 
   [WorkerNavigator interface: attribute connection]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
--- a/testing/web-platform/meta/netinfo/netinfo-basics.html.ini
+++ b/testing/web-platform/meta/netinfo/netinfo-basics.html.ini
@@ -10,17 +10,19 @@
 
   [NetInfo basic functionality 3]
     expected: FAIL
 
   [NetInfo basic functionality 4]
     expected: FAIL
 
   [type attribute]
-    expected: FAIL
+    expected:
+      if os == "android": PASS
+      FAIL
 
   [downlinkMax attribute]
     expected: FAIL
 
   [effectiveType attribute]
     expected: FAIL
 
   [rtt attribute]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-transforms/animation/matrix-interpolation.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Matrix interpolation</title>
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#interpolation-of-3d-matrices">
+<meta name="assert" content="When interpolating between two matrices, each matrix is decomposed into the corresponding translation, rotation, scale, skew and (for a 3D matrix) perspective values">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/interpolation-testcommon.js"></script>
+<body>
+<script>
+// Test interpolation of quaternions when the dot product is -1.
+//
+// We need to be particularly careful not to use a rotate function with a zero
+// angle since the handling of zero angle rotations may change in future as per:
+//
+//   https://github.com/w3c/csswg-drafts/issues/3236
+//
+// For rotateY(360deg) we should get a quaternion of:
+//    [ 0, sin(2 * PI / 2), 0, cos(2 * PI / 2) ]
+//  = [ 0, 0, 0, -1 ]
+//
+// For rotateX(720deg) we should get a quaternion of:
+//    [ 0, 0, sin(4 * PI / 2), cos(4 * PI / 2) ]
+//  = [ 0, 0, 0, 1 ]
+//
+// Dot product = 0 * 0 + 0 * 0 + 0 * 0 + 1 * -1 = -1
+test_interpolation(
+  {
+    property: 'transform',
+    from: 'rotateY(360deg)',
+    to: 'rotateX(720deg)',
+  },
+  [{ at: 0.5, expect: 'matrix(1, 0, 0, 1, 0, 0)' }]
+);
+</script>
+</body>
--- a/third_party/rust/plane-split/.cargo-checksum.json
+++ b/third_party/rust/plane-split/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"641e7f59b524470c415901d3db5c19035172b376a6a26afdaafab8ab79362a93","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"25358cd319195ed1f23068be70aab86f057b12a6117d88be3fdf111cc8c6353c","src/clip.rs":"838cee6106d240581d1cfcba5d47b67f99a1360748ce7c56531dc4582121fc34","src/lib.rs":"a9ce93011a0b0702a1df2a342aeeb9982a3c8a819b20fefa284686ee0fa04d08","src/polygon.rs":"81f5123058fe0d7b57c45feb5bfaaf8923bd1f75efee884abc94b24b610c3a38","tests/clip.rs":"3335364fd6849697d3919084be5dce6c49acb31d8c53adc93a4cd05ee2ea93a9","tests/main.rs":"05e675a165b1e8a8b6513674591a179a74cdce6b35cd969a5895a8140149c064","tests/split.rs":"0eb1afb1f26cdecd5fffbf32d57e889f8f69254c0a57eecb8ccbbdf38efcdf27"},"package":"d252db71f3d2109c4936e87d9f29f3c737e89f9ac239999d78866bdd60b9deda"}
\ No newline at end of file
+{"files":{".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"7b16d31dbd148b3dae6e90b30d4ea17798e13f5256647da48e96f4e678dbd4fe","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"a65ed5c817c867fe23bc2029f34baea4a645a07dd5d101a0027e796d2923be58","benches/split.rs":"632a011dfc6d8235dea853785061b7bbfe0362eb85b91b3b01fbf77a7f1c7f26","src/bsp.rs":"5ee2a20ce632d6c5283787f908fa3eac856ec55e4f1ed6e615cecb6fe041ed21","src/clip.rs":"838cee6106d240581d1cfcba5d47b67f99a1360748ce7c56531dc4582121fc34","src/lib.rs":"a9ce93011a0b0702a1df2a342aeeb9982a3c8a819b20fefa284686ee0fa04d08","src/polygon.rs":"31ba899f7e10082644b95fff7527b43b2786720d16fc2d4c225bc7b63ada75ee","tests/clip.rs":"3335364fd6849697d3919084be5dce6c49acb31d8c53adc93a4cd05ee2ea93a9","tests/main.rs":"86dd9b91db2a5c28451164b14ca5179f2c08598562d04ee52f578a17640b134f","tests/split.rs":"7da8d6f7cce4643ae9c5ce4917aa11aef503c4267dfaeae7b2a4b9bc813cb095"},"package":"9b1d9a84aa3bbc2dafd06856bdb1dc333eb1d442ad8987b9d596c7344b3ed969"}
\ No newline at end of file
old mode 100644
new mode 100755
--- a/third_party/rust/plane-split/Cargo.toml
+++ b/third_party/rust/plane-split/Cargo.toml
@@ -7,17 +7,17 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "plane-split"
-version = "0.13.2"
+version = "0.13.3"
 authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
 description = "Plane splitting"
 documentation = "https://docs.rs/plane-split"
 keywords = ["geometry", "math"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/plane-split"
 [dependencies.binary-space-partition]
 version = "0.1.2"
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
--- a/third_party/rust/plane-split/src/bsp.rs
+++ b/third_party/rust/plane-split/src/bsp.rs
@@ -15,22 +15,23 @@ impl<T, U> BspPlane for Polygon<T, U> wh
         ops::Mul<T, Output=T> + ops::Div<T, Output=T> +
         Zero + One + Float,
     U: fmt::Debug,
 {
     fn cut(&self, mut poly: Self) -> PlaneCut<Self> {
         debug!("\tCutting anchor {} by {}", poly.anchor, self.anchor);
         trace!("\t\tbase {:?}", self.plane);
 
-        let (intersection, dist) = if self.plane.normal
-            .dot(poly.plane.normal)
-            .approx_eq(&T::one())
-        {
-            debug!("\t\tNormals roughly match");
+        let ndot = self.plane.normal.dot(poly.plane.normal);
+        let (intersection, dist) = if ndot.approx_eq(&T::one()) {
+            debug!("\t\tNormals roughly point to the same direction");
             (Intersection::Coplanar, self.plane.offset - poly.plane.offset)
+        } else if ndot.approx_eq(&-T::one()) {
+            debug!("\t\tNormals roughly point to opposite directions");
+            (Intersection::Coplanar, self.plane.offset + poly.plane.offset)
         } else {
             let is = self.intersect(&poly);
             let dist = self.plane.signed_distance_sum_to(&poly);
             (is, dist)
         };
 
         match intersection {
             //Note: we deliberately make the comparison wider than just with T::epsilon().
old mode 100644
new mode 100755
old mode 100644
new mode 100755
old mode 100644
new mode 100755
--- a/third_party/rust/plane-split/src/polygon.rs
+++ b/third_party/rust/plane-split/src/polygon.rs
@@ -386,17 +386,17 @@ impl<T, U> Polygon<T, U> where
             // (a, d) + t * (b-a, d) - (r, d) = k
             // a + t * (b-a) = r + t * (b-a, d) * d + (a-r, d) * d
             // t * ((b-a) - (b-a, d)*d) = (r-a) - (r-a, d) * d
             let pr = line.origin - a - line.dir * line.dir.dot(line.origin - a);
             let pb = b - a - line.dir * line.dir.dot(b - a);
             let denom = pb.dot(pb);
             if !denom.approx_eq(&T::zero()) {
                 let t = pr.dot(pb) / denom;
-                if t > T::zero() && t < T::one() {
+                if t > T::approx_epsilon() && t < T::one() - T::approx_epsilon() {
                     *cut = Some(a + (b - a) * t);
                 }
             }
         }
 
         let first = match cuts.iter().position(|c| c.is_some()) {
             Some(pos) => pos,
             None => return (None, None),
old mode 100644
new mode 100755
old mode 100644
new mode 100755
--- a/third_party/rust/plane-split/tests/main.rs
+++ b/third_party/rust/plane-split/tests/main.rs
@@ -67,44 +67,44 @@ fn empty() {
             point3(0.0, 0.00001, 1.0),
             point3(1.0, 0.0, 0.0),
         ],
         1,
     );
     assert_eq!(None, poly);
 }
 
-fn test_trasnformed(rect: TypedRect<f32, ()>, transform: TypedTransform3D<f32, (), ()>) {
+fn test_transformed(rect: TypedRect<f32, ()>, transform: TypedTransform3D<f32, (), ()>) {
     let poly = Polygon::from_transformed_rect(rect, transform, 0).unwrap();
     assert!(poly.is_valid());
 
     let inv_transform = transform.inverse().unwrap();
     let poly2 = Polygon::from_transformed_rect_with_inverse(rect, &transform, &inv_transform, 0).unwrap();
     assert_eq!(poly.points, poly2.points);
     assert!(poly.plane.offset.approx_eq(&poly2.plane.offset));
     assert!(poly.plane.normal.dot(poly2.plane.normal).approx_eq(&1.0));
 }
 
 #[test]
 fn from_transformed_rect() {
     let rect = TypedRect::new(point2(10.0, 10.0), TypedSize2D::new(20.0, 30.0));
     let transform =
         TypedTransform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Angle::radians(5.0))
         .pre_translate(vec3(0.0, 0.0, 10.0));
-    test_trasnformed(rect, transform);
+    test_transformed(rect, transform);
 }
 
 #[test]
 fn from_transformed_rect_perspective() {
     let rect = TypedRect::new(point2(-10.0, -5.0), TypedSize2D::new(20.0, 30.0));
     let mut transform =
         TypedTransform3D::create_perspective(400.0)
         .pre_translate(vec3(0.0, 0.0, 100.0));
     transform.m44 = 0.7; //for fun
-    test_trasnformed(rect, transform);
+    test_transformed(rect, transform);
 }
 
 #[test]
 fn untransform_point() {
     let poly: Polygon<f32, ()> = Polygon {
         points: [
             point3(0.0, 0.0, 0.0),
             point3(0.5, 1.0, 0.0),
old mode 100644
new mode 100755
--- a/third_party/rust/plane-split/tests/split.rs
+++ b/third_party/rust/plane-split/tests/split.rs
@@ -1,13 +1,15 @@
+extern crate binary_space_partition;
 extern crate euclid;
 extern crate plane_split;
 
 use std::f32::consts::FRAC_PI_4;
-use euclid::{Angle, TypedTransform3D, TypedRect, vec3};
+use binary_space_partition::{Plane as Plane_, PlaneCut};
+use euclid::{Angle, TypedTransform3D, TypedRect, rect, vec3};
 use plane_split::{BspSplitter, Polygon, Splitter, make_grid};
 
 
 fn grid_impl(count: usize, splitter: &mut Splitter<f32, ()>) {
     let polys = make_grid(count);
     let result = splitter.solve(&polys, vec3(0.0, 0.0, 1.0));
     assert_eq!(result.len(), count + count*count + count*count*count);
 }
@@ -21,17 +23,17 @@ fn grid_bsp() {
 fn sort_rotation(splitter: &mut Splitter<f32, ()>) {
     let transform0: TypedTransform3D<f32, (), ()> =
         TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(-FRAC_PI_4));
     let transform1: TypedTransform3D<f32, (), ()> =
         TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(0.0));
     let transform2: TypedTransform3D<f32, (), ()> =
         TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(FRAC_PI_4));
 
-    let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
+    let rect: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
     let p1 = Polygon::from_transformed_rect(rect, transform0, 0);
     let p2 = Polygon::from_transformed_rect(rect, transform1, 1);
     let p3 = Polygon::from_transformed_rect(rect, transform2, 2);
     assert!(p1.is_some() && p2.is_some() && p3.is_some(), "Cannot construct transformed polygons");
 
     let polys = [ p1.unwrap(), p2.unwrap(), p3.unwrap() ];
     let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
     let ids: Vec<_> = result.iter().map(|poly| poly.anchor).collect();
@@ -41,17 +43,17 @@ fn sort_rotation(splitter: &mut Splitter
 #[test]
 fn rotation_bsp() {
     sort_rotation(&mut BspSplitter::new());
 }
 
 
 fn sort_trivial(splitter: &mut Splitter<f32, ()>) {
     let anchors: Vec<_> = (0usize .. 10).collect();
-    let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
+    let rect: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
     let polys: Vec<_> = anchors.iter().map(|&anchor| {
         let transform: TypedTransform3D<f32, (), ()> = TypedTransform3D::create_translation(0.0, 0.0, anchor as f32);
         let poly = Polygon::from_transformed_rect(rect, transform, anchor);
         assert!(poly.is_some(), "Cannot construct transformed polygons");
         poly.unwrap()
     }).collect();
 
     let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
@@ -60,8 +62,36 @@ fn sort_trivial(splitter: &mut Splitter<
     anchors2.sort_by_key(|&a| -(a as i32));
     assert_eq!(anchors1, anchors2); //make sure Z is sorted backwards
 }
 
 #[test]
 fn trivial_bsp() {
     sort_trivial(&mut BspSplitter::new());
 }
+
+#[test]
+fn test_cut() {
+    let rect: TypedRect<f32, ()> = rect(-10.0, -10.0, 20.0, 20.0);
+    let poly = Polygon::from_rect(rect, 0);
+    let mut poly2 = Polygon::from_rect(rect, 0);
+    poly2.plane.normal.z += 0.00000001;
+    match poly.cut(poly2.clone()) {
+        PlaneCut::Sibling(p) => assert_eq!(p, poly2),
+        PlaneCut::Cut { .. } => panic!("wrong cut!"),
+    }
+    poly2.plane.normal *= -1.0;
+    match poly.cut(poly2.clone()) {
+        PlaneCut::Sibling(p) => assert_eq!(p, poly2),
+        PlaneCut::Cut { .. } => panic!("wrong cut!"),
+    }
+
+    poly2.plane.offset += 0.1;
+    match poly.cut(poly2.clone()) {
+        PlaneCut::Cut { ref front, ref back } => assert_eq!((front.len(), back.len()), (1, 0)),
+        PlaneCut::Sibling(_) => panic!("wrong sibling!"),
+    }
+    poly2.plane.normal *= -1.0;
+    match poly.cut(poly2.clone()) {
+        PlaneCut::Cut { ref front, ref back } => assert_eq!((front.len(), back.len()), (0, 1)),
+        PlaneCut::Sibling(_) => panic!("wrong sibling!"),
+    }
+}
--- a/toolkit/components/telemetry/docs/data/crash-ping.rst
+++ b/toolkit/components/telemetry/docs/data/crash-ping.rst
@@ -1,18 +1,18 @@
 
 "crash" ping
 ============
 
 This ping is captured after the main Firefox process crashes or after a child process
 process crashes, whether or not the crash report is submitted to
 crash-stats.mozilla.org. It includes non-identifying metadata about the crash.
 
-This ping is sent either by the ```CrashManager``` or by the crash reporter
-client. The ```CrashManager``` is responsible for sending crash pings for the
+This ping is sent either by the ``CrashManager`` or by the crash reporter
+client. The ``CrashManager`` is responsible for sending crash pings for the
 child processes crashes, which are sent right after the crash is detected,
 as well as for main process crashes, which are sent after Firefox restarts
 successfully. The crash reporter client sends crash pings only for main process
 crashes whether or not the user also reports the crash. The crash reporter
 client will not send the crash ping if telemetry has been disabled in Firefox.
 
 The environment block that is sent with this ping varies: if Firefox was running long enough to record the environment block before the crash, then the environment at the time of the crash will be recorded and ``hasCrashEnvironment`` will be true. If Firefox crashed before the environment was recorded, ``hasCrashEnvironment`` will be false and the recorded environment will be the environment at time of submission.
 
@@ -39,17 +39,17 @@ Structure:
         version: 1,
         sessionId: <UUID>, // may be missing for crashes that happen early
                            // in startup. Added in Firefox 48 with the
                            // intention of uplifting to Firefox 46
         crashId: <UUID>, // Optional, ID of the associated crash
         minidumpSha256Hash: <hash>, // SHA256 hash of the minidump file
         processType: <type>, // Type of process that crashed, see below for a list of types
         stackTraces: { ... }, // Optional, see below
-        metadata: { // Annotations saved while Firefox was running. See nsExceptionHandler.cpp for more information
+        metadata: { // Annotations saved while Firefox was running. See CrashAnnotations.yaml for more information
           ProductID: "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
           ProductName: "Firefox",
           ReleaseChannel: <channel>,
           Version: <version number>,
           BuildID: "YYYYMMDDHHMMSS",
           AsyncShutdownTimeout: <json>, // Optional, present when a shutdown blocker failed to respond within a reasonable amount of time
           AvailablePageFile: <size>, // Windows-only, available paging file in bytes
           AvailablePhysicalMemory: <size>, // Windows-only, available physical memory in bytes
@@ -57,16 +57,17 @@ Structure:
           BlockedDllList: <list>, // Windows-only, see WindowsDllBlocklist.cpp for details
           BlocklistInitFailed: "1", // Windows-only, present only if the DLL blocklist initialization failed
           CrashTime: <time>, // Seconds since the Epoch
           ContainsMemoryReport: "1", // Optional, if set indicates that the crash had a memory report attached
           EventLoopNestingLevel: <levels>, // Optional, present only if >0, indicates the nesting level of the event-loop
           ipc_channel_error: <error string>, // Optional, contains the string processing error reason for an ipc-based content crash
           IsGarbageCollecting: "1", // Optional, if set indicates that the crash occurred while the garbage collector was running
           LowCommitSpaceEvents: <num>, // Windows-only, present only if >0, number of low commit space events detected by the available memory tracker
+          MemoryErrorCorrection: <type>, // Windows-only, indicates the type of ECC memory in use, see below
           MozCrashReason: <reason>, // Optional, contains the string passed to MOZ_CRASH()
           OOMAllocationSize: <size>, // Size of the allocation that caused an OOM
           RemoteType: <type>, // Optional, type of content process, see below for a list of types
           SecondsSinceLastCrash: <duration>, // Seconds elapsed since the last crash occurred
           ShutdownProgress: <phase>, // Optional, contains a string describing the shutdown phase in which the crash occurred
           SystemMemoryUsePercentage: <percentage>, // Windows-only, percent of memory in use
           StartupCrash: "1", // Optional, if set indicates that Firefox crashed during startup
           TelemetrySessionId: <id>, // Active telemetry session ID when the crash was recorded
@@ -205,12 +206,39 @@ The trust levels are (from least trusted
 +---------------+---------------------------------------------------+
 | none          | Unknown, this is most likely not a valid frame    |
 +---------------+---------------------------------------------------+
 
 The ``code_id`` field holds a unique ID used to distinguish between different
 versions and builds of the same module. See `breakpad <https://chromium.googlesource.com/breakpad/breakpad/+/24f5931c5e0120982c0cbf1896641e3ef2bdd52f/src/google_breakpad/processor/code_module.h#60>`__'s
 description for further information. This field is populated only on Windows.
 
+The value of the ``MemoryErrorCorrection`` metadata field contains the type
+of memory error correction available on the machine, it can be one of the
+following types:
+
++----------------+-----------------------------------------------------------+
+| Type           | Description                                               |
++================+===========================================================+
+| Reserved       | Should never be set, assume no error correction available |
++----------------+-----------------------------------------------------------+
+| Other          | Assume no error correction available                      |
++----------------+-----------------------------------------------------------+
+| Unknown        | Assume no error correction available                      |
++----------------+-----------------------------------------------------------+
+| None           | No error correction available                             |
++----------------+-----------------------------------------------------------+
+| Parity         | Single-bit error detection, no correction.                |
++----------------+-----------------------------------------------------------+
+| Single-bit ECC | SECDED ECC (single-bit correction, double-bit detection)  |
++----------------+-----------------------------------------------------------+
+| Multi-bit ECC  | Usually single-device data correction (SDDC, Chipkill)    |
++----------------+-----------------------------------------------------------+
+| CRC            | Multi-device data correction (DDDC or similar)            |
++----------------+-----------------------------------------------------------+
+
 Version History
 ---------------
 
 - Firefox 58: Added ipc_channel_error (`bug 1410143 <https://bugzilla.mozilla.org/show_bug.cgi?id=1410143>`_).
+- Firefox 62: Added LowCommitSpaceEvents (`bug 1464773 <https://bugzilla.mozilla.org/show_bug.cgi?id=1464773>`_).
+- Firefox 63: Added RecordReplayError (`bug 1481009 <https://bugzilla.mozilla.org/show_bug.cgi?id=1481009>`_).
+- Firefox 64: Added MemoryErrorCorrection (`bug 1498609 <https://bugzilla.mozilla.org/show_bug.cgi?id=1498609>`_).
--- a/toolkit/crashreporter/CrashAnnotations.yaml
+++ b/toolkit/crashreporter/CrashAnnotations.yaml
@@ -478,16 +478,17 @@ MarshalActCtxManifestPath:
   type: string
 
 MemoryErrorCorrection:
   description: >
     Windows only, type of error correction used by system memory.  See
     documentation for MemoryErrorCorrection property of
     Win32_PhysicalMemoryArray WMI class.
   type: string
+  ping: true
 
 MozCrashReason:
   description: >
     Plaintext description of why Firefox crashed, this is usually set by
     assertions and the like.
   type: string
   ping: true