Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 04 Oct 2016 12:03:18 +0200
changeset 316384 edee4625c0be084f87c896dd3fefd72c44ec5bce
parent 316383 1bb61a26fb376b8b8fc08ee686eaaa46c7e32469 (current diff)
parent 316354 42c95d88aaaa7c2eca1d278399421d437441ac4d (diff)
child 316385 1a5030f96cdb8ac74e44d8ba776633354d1fa428
push id30770
push userkwierso@gmail.com
push dateWed, 05 Oct 2016 00:00:48 +0000
treeherdermozilla-central@3470e326025c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
devtools/client/webconsole/new-console-output/test/actions/filters.test.js
devtools/client/webconsole/new-console-output/test/actions/messages.test.js
devtools/client/webconsole/new-console-output/test/actions/ui.test.js
devtools/client/webconsole/new-console-output/test/components/repeat.test.js
gfx/layers/apz/test/mochitest/test_bug1285070.html
--- a/devtools/client/debugger/new/index.html
+++ b/devtools/client/debugger/new/index.html
@@ -1,13 +1,13 @@
 <!-- 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/. -->
 <!DOCTYPE html>
-<html>
+<html dir="">
   <head>
     <link rel="stylesheet"
           type="text/css"
           href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" />
     <link rel="stylesheet"
           type="text/css"
           href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" />
     <link rel="stylesheet"
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -481,16 +481,20 @@ Toolbox.prototype = {
   get React() {
     return this.browserRequire("devtools/client/shared/vendor/react");
   },
 
   get ReactDOM() {
     return this.browserRequire("devtools/client/shared/vendor/react-dom");
   },
 
+  get ReactRedux() {
+    return this.browserRequire("devtools/client/shared/vendor/react-redux");
+  },
+
   // Return HostType id for telemetry
   _getTelemetryHostId: function () {
     switch (this.hostType) {
       case Toolbox.HostType.BOTTOM: return 0;
       case Toolbox.HostType.SIDE: return 1;
       case Toolbox.HostType.WINDOW: return 2;
       case Toolbox.HostType.CUSTOM: return 3;
       default: return 9;
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -24,16 +24,17 @@ const Telemetry = require("devtools/clie
 const Menu = require("devtools/client/framework/menu");
 const MenuItem = require("devtools/client/framework/menu-item");
 
 const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
 const {ComputedViewTool} = require("devtools/client/inspector/computed/computed");
 const {FontInspector} = require("devtools/client/inspector/fonts/fonts");
 const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs");
 const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
+const {LayoutViewTool} = require("devtools/client/inspector/layout/layout");
 const {MarkupView} = require("devtools/client/inspector/markup/markup");
 const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
 const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
 const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
 const clipboardHelper = require("devtools/shared/platform/clipboard");
 
 const {LocalizationHelper, localizeMarkup} = require("devtools/shared/l10n");
 const INSPECTOR_L10N = new LocalizationHelper("devtools/locale/inspector.properties");
@@ -412,16 +413,20 @@ Inspector.prototype = {
   get React() {
     return this._toolbox.React;
   },
 
   get ReactDOM() {
     return this._toolbox.ReactDOM;
   },
 
+  get ReactRedux() {
+    return this._toolbox.ReactRedux;
+  },
+
   get browserRequire() {
     return this._toolbox.browserRequire;
   },
 
   get InspectorTabPanel() {
     if (!this._InspectorTabPanel) {
       this._InspectorTabPanel =
         this.React.createFactory(this.browserRequire(
@@ -554,16 +559,26 @@ Inspector.prototype = {
       Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
     };
 
     this.sidebar.on("select", this._setDefaultSidebar);
 
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
+    if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
+      this.sidebar.addExistingTab(
+        "layoutview",
+        INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"),
+        defaultTab == "layoutview"
+      );
+
+      this.layoutview = new LayoutViewTool(this, this.panelWin);
+    }
+
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
         defaultTab == "animationinspector");
     }
 
@@ -580,16 +595,30 @@ Inspector.prototype = {
 
     // Setup the splitter before the sidebar is displayed so,
     // we don't miss any events.
     this.setupSplitter();
 
     this.sidebar.show(defaultTab);
   },
 
+  /**
+   * Register a side-panel tab. This API can be used outside of
+   * DevTools (e.g. from an extension) as well as by DevTools
+   * code base.
+   *
+   * @param {string} tab uniq id
+   * @param {string} title tab title
+   * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
+   * @param {boolean} selected true if the panel should be selected
+   */
+  addSidebarTab: function (id, title, panel, selected) {
+    this.sidebar.addTab(id, title, panel, selected);
+  },
+
   setupToolbar: function () {
     this.teardownToolbar();
 
     // Setup the sidebar toggle button.
     let SidebarToggle = this.React.createFactory(this.browserRequire(
       "devtools/client/shared/components/sidebar-toggle"));
 
     let sidebarToggle = SidebarToggle({
@@ -861,16 +890,20 @@ Inspector.prototype = {
     if (this.ruleview) {
       this.ruleview.destroy();
     }
 
     if (this.computedview) {
       this.computedview.destroy();
     }
 
+    if (this.layoutview) {
+      this.layoutview.destroy();
+    }
+
     if (this.fontInspector) {
       this.fontInspector.destroy();
     }
 
     let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => {
       if (front) {
         front.destroy();
       }
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -179,16 +179,21 @@
             <div id="propertyContainer" class="theme-separator" tabindex="0">
             </div>
 
             <div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"/>
           </div>
         </div>
       </div>
 
+      <div id="sidebar-panel-layoutview" class="devtools-monospace theme-sidebar inspector-tabpanel"
+           data-localization-bundle="devtools/locale/inspector.properties">
+        <div id="layout-root"></div>
+      </div>
+
       <div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel"
                 data-localization-bundle="devtools/locale/font-inspector.properties">
         <div class="devtools-toolbar">
           <div class="devtools-searchbox">
             <input id="font-preview-text-input" class="devtools-textinput" type="search"
                         data-localization="placeholder=fontinspector.previewText"/>
           </div>
           <label id="font-showall" class="theme-link"
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/actions/index.js
@@ -0,0 +1,5 @@
+/* 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";
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/actions/moz.build
@@ -0,0 +1,5 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/components/App.js
@@ -0,0 +1,24 @@
+/* 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 { createClass, DOM: dom } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+let App = createClass({
+
+  displayName: "App",
+
+  render() {
+    return dom.div(
+      {
+        id: "app",
+      }
+    );
+  },
+
+});
+
+module.exports = connect(state => state)(App);
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'App.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/layout.js
@@ -0,0 +1,37 @@
+/* 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";
+
+function LayoutViewTool(inspector, window) {
+  this.inspector = inspector;
+  this.document = window.document;
+  this.store = null;
+
+  this.init();
+}
+
+LayoutViewTool.prototype = {
+
+  init() {
+    const { React, ReactDOM, ReactRedux, browserRequire } = this.inspector;
+
+    const Store = browserRequire("devtools/client/inspector/layout/store");
+    const App = React.createFactory(
+      browserRequire("devtools/client/inspector/layout/components/App"));
+
+    let store = this.store = Store();
+    let provider = React.createElement(ReactRedux.Provider, { store }, App());
+    ReactDOM.render(provider, this.document.querySelector("#layout-root"));
+  },
+
+  destroy() {
+    this.inspector = null;
+    this.document = null;
+    this.store = null;
+  },
+
+};
+
+exports.LayoutViewTool = LayoutViewTool;
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+]
+
+DevToolsModules(
+    'layout.js',
+    'store.js',
+    'types.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/reducers/grids.js
@@ -0,0 +1,21 @@
+/* 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 INITIAL_GRIDS = {
+
+};
+
+let reducers = {
+
+};
+
+module.exports = function (grids = INITIAL_GRIDS, action) {
+  let reducer = reducers[action.type];
+  if (!reducer) {
+    return grids;
+  }
+  return reducer(grids, action);
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/reducers/index.js
@@ -0,0 +1,7 @@
+/* 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";
+
+exports.grids = require("./grids");
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/reducers/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'grids.js',
+    'index.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/store.js
@@ -0,0 +1,33 @@
+/* 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
+const createStore = require("devtools/client/shared/redux/create-store");
+const reducers = require("./reducers/index");
+const flags = require("devtools/shared/flags");
+
+module.exports = function () {
+  let shouldLog = false;
+  let history;
+
+  // If testing, store the action history in an array
+  // we'll later attach to the store
+  if (flags.testing) {
+    history = [];
+    shouldLog = true;
+  }
+
+  let store = createStore({
+    log: shouldLog,
+    history
+  })(combineReducers(reducers), {});
+
+  if (history) {
+    store.history = history;
+  }
+
+  return store;
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/types.js
@@ -0,0 +1,5 @@
+/* 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";
--- a/devtools/client/inspector/moz.build
+++ b/devtools/client/inspector/moz.build
@@ -1,16 +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/.
 
 DIRS += [
     'components',
     'computed',
     'fonts',
+    'layout',
     'markup',
     'rules',
     'shared'
 ]
 
 DevToolsModules(
     'breadcrumbs.js',
     'inspector-commands.js',
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -40,16 +40,17 @@ support-files =
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_inspector_addNode_01.js]
 [browser_inspector_addNode_02.js]
 [browser_inspector_addNode_03.js]
+[browser_inspector_addSidebarTab.js]
 [browser_inspector_breadcrumbs.js]
 [browser_inspector_breadcrumbs_highlight_hover.js]
 [browser_inspector_breadcrumbs_keybinding.js]
 [browser_inspector_breadcrumbs_keyboard_trap.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_inspector_breadcrumbs_mutations.js]
 [browser_inspector_breadcrumbs_namespaced.js]
 [browser_inspector_breadcrumbs_visibility.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_addSidebarTab.js
@@ -0,0 +1,62 @@
+/* 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 TEST_URI = "data:text/html;charset=UTF-8," +
+  "<h1>browser_inspector_addtabbar.js</h1>";
+
+const CONTENT_TEXT = "Hello World!";
+
+/**
+ * Verify InspectorPanel.addSidebarTab() API that can be consumed
+ * by DevTools extensions as well as DevTools code base.
+ */
+add_task(function* () {
+  let { inspector } = yield openInspectorForURL(TEST_URI);
+
+  const React = inspector.React;
+  const { div } = React.DOM;
+
+  info("Adding custom panel.");
+
+  // Define custom side-panel.
+  let tabPanel = React.createFactory(React.createClass({
+    displayName: "myTabPanel",
+    render: function () {
+      return (
+        div({className: "my-tab-panel"},
+          CONTENT_TEXT
+        )
+      );
+    }
+  }));
+
+  // Append custom panel (tab) into the Inspector panel and
+  // make sure it's selected by default (the last arg = true).
+  inspector.addSidebarTab("myPanel", "My Panel", tabPanel, true);
+  is(inspector.sidebar.getCurrentTabID(), "myPanel",
+     "My Panel is selected by default");
+
+  // Define another custom side-panel.
+  tabPanel = React.createFactory(React.createClass({
+    displayName: "myTabPanel2",
+    render: function () {
+      return (
+        div({className: "my-tab-panel2"},
+          "Another Content"
+        )
+      );
+    }
+  }));
+
+  // Append second panel, but don't select it by default.
+  inspector.addSidebarTab("myPanel", "My Panel", tabPanel, false);
+  is(inspector.sidebar.getCurrentTabID(), "myPanel",
+     "My Panel is selected by default");
+
+  // Check the the panel content is properly rendered.
+  let tabPanelNode = inspector.panelDoc.querySelector(".my-tab-panel");
+  is(tabPanelNode.textContent, CONTENT_TEXT,
+    "Side panel content has been rendered.");
+});
--- a/devtools/client/inspector/toolsidebar.js
+++ b/devtools/client/inspector/toolsidebar.js
@@ -79,49 +79,69 @@ ToolSidebar.prototype = {
       toolbox: this._toolPanel._toolbox,
       showAllTabsMenu: true,
       onSelect: this.handleSelectionChange.bind(this),
     });
 
     this._tabbar = this.ReactDOM.render(sidebar, this._tabbox);
   },
 
+  /**
+   * Register a side-panel tab.
+   *
+   * @param {string} tab uniq id
+   * @param {string} title tab title
+   * @param {React.Component} panel component. See `InspectorPanelTab` as an example.
+   * @param {boolean} selected true if the panel should be selected
+   */
+  addTab: function (id, title, panel, selected) {
+    this._tabbar.addTab(id, title, selected, panel);
+    this.emit("new-tab-registered", id);
+  },
+
+  /**
+   * Helper API for adding side-panels that use existing DOM nodes
+   * (defined within inspector.xhtml) as the content.
+   *
+   * @param {string} tab uniq id
+   * @param {string} title tab title
+   * @param {boolean} selected true if the panel should be selected
+   */
   addExistingTab: function (id, title, selected) {
     let panel = this.InspectorTabPanel({
       id: id,
       idPrefix: this.TABPANEL_ID_PREFIX,
       key: id,
       title: title,
     });
 
-    this._tabbar.addTab(id, title, selected, panel);
-
-    this.emit("new-tab-registered", id);
+    this.addTab(id, title, panel, selected);
   },
 
   /**
-   * Register a tab. A tab is a document.
+   * Helper API for adding side-panels that use existing <iframe> nodes
+   * (defined within inspector.xhtml) as the content.
    * The document must have a title, which will be used as the name of the tab.
    *
    * @param {string} tab uniq id
+   * @param {string} title tab title
    * @param {string} url
+   * @param {boolean} selected true if the panel should be selected
    */
   addFrameTab: function (id, title, url, selected) {
     let panel = this.InspectorTabPanel({
       id: id,
       idPrefix: this.TABPANEL_ID_PREFIX,
       key: id,
       title: title,
       url: url,
       onMount: this.onSidePanelMounted.bind(this),
     });
 
-    this._tabbar.addTab(id, title, selected, panel);
-
-    this.emit("new-tab-registered", id);
+    this.addTab(id, title, panel, selected);
   },
 
   onSidePanelMounted: function (content, props) {
     let iframe = content.querySelector("iframe");
     if (!iframe || iframe.getAttribute("src")) {
       return;
     }
 
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -308,16 +308,21 @@ inspector.sidebar.fontInspectorTitle=Fon
 inspector.sidebar.ruleViewTitle=Rules
 
 # LOCALIZATION NOTE (inspector.sidebar.computedViewTitle):
 # This is the title shown in a tab in the side panel of the Inspector panel
 # that corresponds to the tool displaying the list of computed CSS values
 # used in the page.
 inspector.sidebar.computedViewTitle=Computed
 
+# LOCALIZATION NOTE (inspector.sidebar.computedViewTitle):
+# This is the title shown in a tab in the side panel of the Inspector panel
+# that corresponds to the tool displaying layout information defined in the page.
+inspector.sidebar.layoutViewTitle=Layout
+
 # LOCALIZATION NOTE (inspector.sidebar.animationInspectorTitle):
 # This is the title shown in a tab in the side panel of the Inspector panel
 # that corresponds to the tool displaying animations defined in the page.
 inspector.sidebar.animationInspectorTitle=Animations
 
 # LOCALIZATION NOTE (inspector.eyedropper.label): A string displayed as the tooltip of
 # a button in the inspector which toggles the Eyedropper tool
 inspector.eyedropper.label=Grab a color from the page
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -60,16 +60,25 @@ pref("devtools.inspector.show_pseudo_ele
 pref("devtools.inspector.imagePreviewTooltipSize", 300);
 // Enable user agent style inspection in rule-view
 pref("devtools.inspector.showUserAgentStyles", false);
 // Show all native anonymous content (like controls in <video> tags)
 pref("devtools.inspector.showAllAnonymousContent", false);
 // Enable the MDN docs tooltip
 pref("devtools.inspector.mdnDocsTooltip.enabled", true);
 
+// Enable the Font Inspector
+pref("devtools.fontinspector.enabled", true);
+
+// Enable the Layout View
+pref("devtools.layoutview.enabled", false);
+
+// By how many times eyedropper will magnify pixels
+pref("devtools.eyedropper.zoom", 6);
+
 // Enable to collapse attributes that are too long.
 pref("devtools.markup.collapseAttributes", true);
 
 // Length to collapse attributes
 pref("devtools.markup.collapseAttributeLength", 120);
 
 // DevTools default color unit
 pref("devtools.defaultColorUnit", "authored");
@@ -227,31 +236,34 @@ pref("devtools.webaudioeditor.inspectorW
 
 // Default theme ("dark" or "light")
 #ifdef MOZ_DEV_EDITION
 sticky_pref("devtools.theme", "dark");
 #else
 sticky_pref("devtools.theme", "light");
 #endif
 
-// Remember the Web Console filters
+// Web console filters
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+pref("devtools.webconsole.filter.debug", true);
+pref("devtools.webconsole.filter.net", false);
+pref("devtools.webconsole.filter.netxhr", false);
+// Deprecated - old console frontend
 pref("devtools.webconsole.filter.network", true);
 pref("devtools.webconsole.filter.networkinfo", false);
 pref("devtools.webconsole.filter.netwarn", true);
-pref("devtools.webconsole.filter.netxhr", false);
 pref("devtools.webconsole.filter.csserror", true);
 pref("devtools.webconsole.filter.cssparser", false);
 pref("devtools.webconsole.filter.csslog", false);
 pref("devtools.webconsole.filter.exception", true);
 pref("devtools.webconsole.filter.jswarn", true);
 pref("devtools.webconsole.filter.jslog", false);
-pref("devtools.webconsole.filter.error", true);
-pref("devtools.webconsole.filter.warn", true);
-pref("devtools.webconsole.filter.info", true);
-pref("devtools.webconsole.filter.log", true);
 pref("devtools.webconsole.filter.secerror", true);
 pref("devtools.webconsole.filter.secwarn", true);
 pref("devtools.webconsole.filter.serviceworkers", true);
 pref("devtools.webconsole.filter.sharedworkers", false);
 pref("devtools.webconsole.filter.windowlessworkers", false);
 pref("devtools.webconsole.filter.servererror", false);
 pref("devtools.webconsole.filter.serverwarn", false);
 pref("devtools.webconsole.filter.serverinfo", false);
@@ -277,16 +289,19 @@ pref("devtools.browserconsole.filter.sec
 pref("devtools.browserconsole.filter.serviceworkers", true);
 pref("devtools.browserconsole.filter.sharedworkers", true);
 pref("devtools.browserconsole.filter.windowlessworkers", true);
 pref("devtools.browserconsole.filter.servererror", false);
 pref("devtools.browserconsole.filter.serverwarn", false);
 pref("devtools.browserconsole.filter.serverinfo", false);
 pref("devtools.browserconsole.filter.serverlog", false);
 
+// Web console filter settings bar
+pref("devtools.webconsole.ui.filterbar", false);
+
 // Max number of inputs to store in web console history.
 pref("devtools.webconsole.inputHistoryCount", 50);
 
 // Persistent logging: |true| if you want the Web Console to keep all of the
 // logged messages after reloading the page, |false| if you want the output to
 // be cleared each time page navigation happens.
 pref("devtools.webconsole.persistlog", false);
 
@@ -311,37 +326,31 @@ pref("devtools.hud.loglimit", 1000);
 // The number of lines that are displayed in the web console for the Net,
 // CSS, JS and Web Developer categories. These defaults should be kept in sync
 // with DEFAULT_LOG_LIMIT in the webconsole frontend.
 pref("devtools.hud.loglimit.network", 1000);
 pref("devtools.hud.loglimit.cssparser", 1000);
 pref("devtools.hud.loglimit.exception", 1000);
 pref("devtools.hud.loglimit.console", 1000);
 
-// By how many times eyedropper will magnify pixels
-pref("devtools.eyedropper.zoom", 6);
-
 // The developer tools editor configuration:
 // - tabsize: how many spaces to use when a Tab character is displayed.
 // - expandtab: expand Tab characters to spaces.
 // - keymap: which keymap to use (can be 'default', 'emacs' or 'vim')
 // - autoclosebrackets: whether to permit automatic bracket/quote closing.
 // - detectindentation: whether to detect the indentation from the file
 // - enableCodeFolding: Whether to enable code folding or not.
 pref("devtools.editor.tabsize", 2);
 pref("devtools.editor.expandtab", true);
 pref("devtools.editor.keymap", "default");
 pref("devtools.editor.autoclosebrackets", true);
 pref("devtools.editor.detectindentation", true);
 pref("devtools.editor.enableCodeFolding", true);
 pref("devtools.editor.autocomplete", true);
 
-// Enable the Font Inspector
-pref("devtools.fontinspector.enabled", true);
-
 // Pref to store the browser version at the time of a telemetry ping for an
 // opened developer tool. This allows us to ping telemetry just once per browser
 // version for each user.
 pref("devtools.telemetry.tools.opened.version", "{}");
 
 // Enable the JSON View tool (an inspector for application/json documents) on
 // Nightly and Dev. Edition.
 #ifdef RELEASE_BUILD
--- a/devtools/client/shared/components/reps/rep.js
+++ b/devtools/client/shared/components/reps/rep.js
@@ -97,17 +97,17 @@ define(function (require, exports, modul
    *
    * @param defaultObject {React.Component} The default template
    * that should be used to render given object if none is found.
    */
   function getRep(object, defaultRep = Obj) {
     let type = typeof object;
     if (type == "object" && object instanceof String) {
       type = "string";
-    } else if (type == "object" && object.type === "symbol") {
+    } else if (object && type == "object" && object.type === "symbol") {
       type = "symbol";
     }
 
     if (isGrip(object)) {
       type = object.class;
     }
 
     for (let i = 0; i < reps.length; i++) {
--- a/devtools/client/shared/components/tabs/tabbar.js
+++ b/devtools/client/shared/components/tabs/tabbar.js
@@ -49,17 +49,21 @@ let Tabbar = createClass({
     let newState = Object.assign({}, this.state, {
       tabs: tabs,
     });
 
     if (selected) {
       newState.activeTab = tabs.length - 1;
     }
 
-    this.setState(newState);
+    this.setState(newState, () => {
+      if (this.props.onSelect && selected) {
+        this.props.onSelect(id);
+      }
+    });
   },
 
   toggleTab: function (tabId, isVisible) {
     let index = this.getTabIndex(tabId);
     if (index < 0) {
       return;
     }
 
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -684,16 +684,17 @@ a.learn-more-link.webconsole-learn-more-
 #output-container {
   height: 100%;
 }
 
 .webconsole-output-wrapper {
   display: flex;
   flex-direction: column;
   height: 100%;
+  -moz-user-focus: normal;
 }
 
 .webconsole-filterbar-wrapper {
   flex-grow: 0;
 }
 
 .webconsole-output {
   flex: 1;
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -372,17 +372,17 @@ JSTerm.prototype = {
         result.type == "undefined" &&
         helperResult && !helperHasRawOutput) {
       callback && callback();
       return;
     }
 
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
       this.hud.newConsoleOutput.dispatchMessageAdd(response);
-      callback && callback(this.hud.newConsoleOutput.getLastMessage());
+      callback && callback();
       return;
     }
     let msg = new Messages.JavaScriptEvalOutput(response,
                                                 errorMessage, errorDocLink);
     this.hud.output.addMessage(msg);
 
     if (callback) {
       let oldFlushCallback = this.hud._flushCallback;
--- a/devtools/client/webconsole/new-console-output/actions/filters.js
+++ b/devtools/client/webconsole/new-console-output/actions/filters.js
@@ -1,39 +1,55 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
+const Services = require("Services");
+
 const {
   FILTER_TEXT_SET,
   FILTER_TOGGLE,
-  FILTERS_CLEAR
-} = require("../constants");
+  FILTERS_CLEAR,
+  PREFS,
+} = require("devtools/client/webconsole/new-console-output/constants");
 
 function filterTextSet(text) {
   return {
     type: FILTER_TEXT_SET,
     text
   };
 }
 
 function filterToggle(filter) {
-  return {
-    type: FILTER_TOGGLE,
-    filter,
+  return (dispatch, getState) => {
+    dispatch({
+      type: FILTER_TOGGLE,
+      filter,
+    });
+    const filterState = getAllFilters(getState());
+    Services.prefs.setBoolPref(PREFS.FILTER[filter.toUpperCase()],
+      filterState.get(filter));
   };
 }
 
 function filtersClear() {
-  return {
-    type: FILTERS_CLEAR
+  return (dispatch, getState) => {
+    dispatch({
+      type: FILTERS_CLEAR,
+    });
+
+    const filterState = getAllFilters(getState());
+    for (let filter in filterState) {
+      Services.prefs.clearUserPref(PREFS.FILTER[filter.toUpperCase()]);
+    }
   };
 }
 
 module.exports = {
   filterTextSet,
   filterToggle,
   filtersClear
 };
--- a/devtools/client/webconsole/new-console-output/actions/ui.js
+++ b/devtools/client/webconsole/new-console-output/actions/ui.js
@@ -1,19 +1,27 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
+const Services = require("Services");
+
 const {
   FILTER_BAR_TOGGLE,
-} = require("../constants");
+  PREFS,
+} = require("devtools/client/webconsole/new-console-output/constants");
 
 function filterBarToggle(show) {
-  return {
-    type: FILTER_BAR_TOGGLE
+  return (dispatch, getState) => {
+    dispatch({
+      type: FILTER_BAR_TOGGLE
+    });
+    const uiState = getAllUi(getState());
+    Services.prefs.setBoolPref(PREFS.UI.FILTER_BAR, uiState.get("filterBarVisible"));
   };
 }
 
 exports.filterBarToggle = filterBarToggle;
--- a/devtools/client/webconsole/new-console-output/components/collapse-button.js
+++ b/devtools/client/webconsole/new-console-output/components/collapse-button.js
@@ -35,9 +35,9 @@ const CollapseButton = createClass({
     return dom.a({
       className: classes.join(" "),
       onClick,
       title: l10n.getStr("messageToggleDetails"),
     });
   }
 });
 
-module.exports.CollapseButton = CollapseButton;
+module.exports = CollapseButton;
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -8,89 +8,117 @@ const {
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { getAllMessages, getAllMessagesUiById, getAllMessagesTableDataById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { getScrollSetting } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
 
 const ConsoleOutput = createClass({
 
+  displayName: "ConsoleOutput",
+
   propTypes: {
     hudProxyClient: PropTypes.object.isRequired,
     messages: PropTypes.object.isRequired,
     messagesUi: PropTypes.object.isRequired,
     sourceMapService: PropTypes.object,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     openNetworkPanel: PropTypes.func.isRequired,
     openLink: PropTypes.func.isRequired,
+    emitNewMessage: PropTypes.func.isRequired,
+    autoscroll: PropTypes.bool.isRequired,
+  },
+
+  componentDidMount() {
+    scrollToBottom(this.outputNode);
   },
 
-  displayName: "ConsoleOutput",
+  componentWillUpdate(nextProps, nextState) {
+    if (!this.outputNode) {
+      return;
+    }
 
-  componentWillUpdate() {
-    let node = ReactDOM.findDOMNode(this);
-    if (node.lastChild) {
-      this.shouldScrollBottom = isScrolledToBottom(node.lastChild, node);
+    const outputNode = this.outputNode;
+
+    // Figure out if we are at the bottom. If so, then any new message should be scrolled
+    // into view.
+    if (this.props.autoscroll && outputNode.lastChild) {
+      this.shouldScrollBottom = isScrolledToBottom(outputNode.lastChild, outputNode);
     }
   },
 
   componentDidUpdate() {
     if (this.shouldScrollBottom) {
-      let node = ReactDOM.findDOMNode(this);
-      node.scrollTop = node.scrollHeight;
+      scrollToBottom(this.outputNode);
     }
   },
 
   render() {
     let {
       dispatch,
+      autoscroll,
       hudProxyClient,
       messages,
       messagesUi,
       messagesTableData,
       sourceMapService,
       onViewSourceInDebugger,
       openNetworkPanel,
       openLink,
+      emitNewMessage,
     } = this.props;
 
     let messageNodes = messages.map((message) => {
       return (
         MessageContainer({
           dispatch,
           hudProxyClient,
           message,
           key: message.id,
           sourceMapService,
           onViewSourceInDebugger,
           openNetworkPanel,
           openLink,
+          emitNewMessage,
           open: messagesUi.includes(message.id),
           tableData: messagesTableData.get(message.id),
+          autoscroll,
         })
       );
     });
     return (
-      dom.div({className: "webconsole-output"}, messageNodes)
+      dom.div({
+        className: "webconsole-output",
+        ref: node => {
+          this.outputNode = node;
+        },
+      }, messageNodes
+      )
     );
   }
 });
 
+function scrollToBottom(node) {
+  node.scrollTop = node.scrollHeight;
+}
+
 function isScrolledToBottom(outputNode, scrollNode) {
   let lastNodeHeight = outputNode.lastChild ?
                        outputNode.lastChild.clientHeight : 0;
   return scrollNode.scrollTop + scrollNode.clientHeight >=
          scrollNode.scrollHeight - lastNodeHeight / 2;
 }
 
 function mapStateToProps(state) {
   return {
     messages: getAllMessages(state),
     messagesUi: getAllMessagesUiById(state),
     messagesTableData: getAllMessagesTableDataById(state),
+    autoscroll: getScrollSetting(state),
   };
 }
 
 module.exports = connect(mapStateToProps)(ConsoleOutput);
--- a/devtools/client/webconsole/new-console-output/components/console-table.js
+++ b/devtools/client/webconsole/new-console-output/components/console-table.js
@@ -7,17 +7,17 @@ const {
   createClass,
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { ObjectClient } = require("devtools/shared/client/main");
 const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
 const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
-const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
+const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
 
 const TABLE_ROW_MAX_ITEMS = 1000;
 const TABLE_COLUMN_MAX_ITEMS = 10;
 
 const ConsoleTable = createClass({
 
   displayName: "ConsoleTable",
 
@@ -192,9 +192,9 @@ function getTableItems(data = {}, type, 
   }
 
   return {
     columns,
     items
   };
 }
 
-exports.ConsoleTable = ConsoleTable;
+module.exports = ConsoleTable;
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -13,17 +13,17 @@ const { connect } = require("devtools/cl
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const { filterTextSet, filtersClear } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { messagesClear } = require("devtools/client/webconsole/new-console-output/actions/index");
 const uiActions = require("devtools/client/webconsole/new-console-output/actions/index");
 const {
   MESSAGE_LEVEL
 } = require("../constants");
-const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
+const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
 
 const FilterBar = createClass({
 
   displayName: "FilterBar",
 
   propTypes: {
     filter: PropTypes.object.isRequired,
     ui: PropTypes.object.isRequired
@@ -68,17 +68,17 @@ const FilterBar = createClass({
         value: filter.text,
         placeholder: "Filter output",
         onInput: this.onSearchInput
       })
     ));
 
     if (filterBarVisible) {
       children.push(
-        dom.div({className: "devtools-toolbar"},
+        dom.div({className: "devtools-toolbar webconsole-filterbar-secondary"},
           FilterButton({
             active: filter.error,
             label: "Errors",
             filterKey: MESSAGE_LEVEL.ERROR,
             dispatch
           }),
           FilterButton({
             active: filter.warn,
@@ -109,19 +109,19 @@ const FilterBar = createClass({
           }),
           FilterButton({
             active: filter.netxhr,
             label: "XHR",
             filterKey: "netxhr",
             dispatch
           }),
           FilterButton({
-            active: filter.network,
+            active: filter.net,
             label: "Requests",
-            filterKey: "network",
+            filterKey: "net",
             dispatch
           })
         )
       );
     }
 
     if (ui.filteredMessageVisible) {
       children.push(
--- a/devtools/client/webconsole/new-console-output/components/filter-button.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-button.js
@@ -21,23 +21,26 @@ const FilterButton = createClass({
     dispatch: PropTypes.func.isRequired,
   },
 
   onClick: function () {
     this.props.dispatch(actions.filterToggle(this.props.filterKey));
   },
 
   render() {
-    const {label, active} = this.props;
+    const {active, label, filterKey} = this.props;
 
-    let classList = ["menu-filter-button"];
+    let classList = [
+      "menu-filter-button",
+      filterKey,
+    ];
     if (active) {
       classList.push("checked");
     }
 
     return dom.button({
       className: classList.join(" "),
       onClick: this.onClick
     }, label);
   }
 });
 
-exports.FilterButton = FilterButton;
+module.exports = FilterButton;
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -15,17 +15,17 @@ if (typeof define === "undefined") {
 // React
 const {
   createFactory,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
 const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
 const StringRep = createFactories(require("devtools/client/shared/components/reps/string").StringRep).rep;
-const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link").VariablesViewLink);
+const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link"));
 const { Grip } = require("devtools/client/shared/components/reps/grip");
 
 GripMessageBody.displayName = "GripMessageBody";
 
 GripMessageBody.propTypes = {
   grip: PropTypes.oneOfType([
     PropTypes.string,
     PropTypes.number,
@@ -48,9 +48,9 @@ function GripMessageBody(props) {
         object: grip,
         objectLink: VariablesViewLink,
         defaultRep: Grip,
         mode: props.mode,
       })
   );
 }
 
-module.exports.GripMessageBody = GripMessageBody;
+module.exports = GripMessageBody;
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -14,75 +14,56 @@ const {
 } = require("devtools/client/shared/vendor/react");
 
 const {
   MESSAGE_SOURCE,
   MESSAGE_TYPE
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const componentMap = new Map([
-  ["ConsoleApiCall", require("./message-types/console-api-call").ConsoleApiCall],
-  ["ConsoleCommand", require("./message-types/console-command").ConsoleCommand],
-  ["DefaultRenderer", require("./message-types/default-renderer").DefaultRenderer],
-  ["EvaluationResult", require("./message-types/evaluation-result").EvaluationResult],
-  ["NetworkEventMessage", require("./message-types/network-event-message").NetworkEventMessage],
-  ["PageError", require("./message-types/page-error").PageError]
+  ["ConsoleApiCall", require("./message-types/console-api-call")],
+  ["ConsoleCommand", require("./message-types/console-command")],
+  ["DefaultRenderer", require("./message-types/default-renderer")],
+  ["EvaluationResult", require("./message-types/evaluation-result")],
+  ["NetworkEventMessage", require("./message-types/network-event-message")],
+  ["PageError", require("./message-types/page-error")]
 ]);
 
 const MessageContainer = createClass({
   displayName: "MessageContainer",
 
   propTypes: {
     message: PropTypes.object.isRequired,
     sourceMapService: PropTypes.object,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     openNetworkPanel: PropTypes.func.isRequired,
     openLink: PropTypes.func.isRequired,
     open: PropTypes.bool.isRequired,
     hudProxyClient: PropTypes.object.isRequired,
+    autoscroll: PropTypes.bool.isRequired,
   },
 
   getDefaultProps: function () {
     return {
       open: false
     };
   },
 
   shouldComponentUpdate(nextProps, nextState) {
     const repeatChanged = this.props.message.repeat !== nextProps.message.repeat;
     const openChanged = this.props.open !== nextProps.open;
     const tableDataChanged = this.props.tableData !== nextProps.tableData;
     return repeatChanged || openChanged || tableDataChanged;
   },
 
   render() {
-    const {
-      dispatch,
-      message,
-      sourceMapService,
-      onViewSourceInDebugger,
-      openNetworkPanel,
-      openLink,
-      open,
-      tableData,
-      hudProxyClient,
-    } = this.props;
+    const { message } = this.props;
 
     let MessageComponent = createFactory(getMessageComponent(message));
-    return MessageComponent({
-      dispatch,
-      message,
-      sourceMapService,
-      onViewSourceInDebugger,
-      openNetworkPanel,
-      openLink,
-      open,
-      tableData,
-      hudProxyClient,
-    });
+    return MessageComponent(this.props);
   }
 });
 
 function getMessageComponent(message) {
   switch (message.source) {
     case MESSAGE_SOURCE.CONSOLE_API:
       return componentMap.get("ConsoleApiCall");
     case MESSAGE_SOURCE.NETWORK:
--- a/devtools/client/webconsole/new-console-output/components/message-icon.js
+++ b/devtools/client/webconsole/new-console-output/components/message-icon.js
@@ -24,9 +24,9 @@ function MessageIcon(props) {
 
   const title = l10n.getStr("level." + level);
   return dom.div({
     className: "icon",
     title
   });
 }
 
-module.exports.MessageIcon = MessageIcon;
+module.exports = MessageIcon;
--- a/devtools/client/webconsole/new-console-output/components/message-repeat.js
+++ b/devtools/client/webconsole/new-console-output/components/message-repeat.js
@@ -7,22 +7,30 @@
 
 "use strict";
 
 // React & Redux
 const {
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
+const { PluralForm } = require("devtools/shared/plural-form");
+const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
 
 MessageRepeat.displayName = "MessageRepeat";
 
 MessageRepeat.propTypes = {
   repeat: PropTypes.number.isRequired
 };
 
 function MessageRepeat(props) {
   const { repeat } = props;
   const visibility = repeat > 1 ? "visible" : "hidden";
-  return dom.span({className: "message-repeats", style: {visibility}}, repeat);
+
+  return dom.span({
+    className: "message-repeats",
+    style: {visibility},
+    title: PluralForm.get(repeat, l10n.getStr("messageRepeats.tooltip2"))
+      .replace("#1", repeat)
+  }, repeat);
 }
 
-exports.MessageRepeat = MessageRepeat;
+module.exports = MessageRepeat;
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
@@ -7,24 +7,20 @@
 "use strict";
 
 // React & Redux
 const {
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const FrameView = createFactory(require("devtools/client/shared/components/frame"));
-const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
-const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
-const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
-const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
-const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table").ConsoleTable);
-const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
+const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table"));
+
+const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
 
 ConsoleApiCall.displayName = "ConsoleApiCall";
 
 ConsoleApiCall.propTypes = {
   message: PropTypes.object.isRequired,
   sourceMapService: PropTypes.object,
   onViewSourceInDebugger: PropTypes.func.isRequired,
   open: PropTypes.bool,
@@ -38,114 +34,83 @@ ConsoleApiCall.defaultProps = {
 function ConsoleApiCall(props) {
   const {
     dispatch,
     message,
     sourceMapService,
     onViewSourceInDebugger,
     open,
     hudProxyClient,
-    tableData
+    tableData,
+    emitNewMessage,
   } = props;
-  const {source, level, stacktrace, type, frame, parameters } = message;
+  const {
+    id: messageId,
+    source, type,
+    level,
+    repeat,
+    stacktrace,
+    frame,
+    parameters
+  } = message;
 
   let messageBody;
   if (type === "trace") {
     messageBody = dom.span({className: "cm-variable"}, "console.trace()");
   } else if (type === "assert") {
     let reps = formatReps(parameters);
     messageBody = dom.span({ className: "cm-variable" }, "Assertion failed: ", reps);
   } else if (type === "table") {
     // TODO: Chrome does not output anything, see if we want to keep this
     messageBody = dom.span({className: "cm-variable"}, "console.table()");
   } else if (parameters) {
     messageBody = formatReps(parameters);
   } else {
     messageBody = message.messageText;
   }
 
-  const icon = MessageIcon({ level });
-  const repeat = MessageRepeat({ repeat: message.repeat });
-  const shouldRenderFrame = frame && frame.source !== "debugger eval code";
-  const location = dom.span({ className: "message-location devtools-monospace" },
-    shouldRenderFrame ? FrameView({
-      frame,
-      onClick: onViewSourceInDebugger,
-      showEmptyPathAsHost: true,
-      sourceMapService
-    }) : null
-  );
-
-  let collapse = "";
-  let attachment = "";
-  if (stacktrace) {
-    if (open) {
-      attachment = dom.div({ className: "stacktrace devtools-monospace" },
-        StackTrace({
-          stacktrace: stacktrace,
-          onViewSourceInDebugger: onViewSourceInDebugger
-        })
-      );
-    }
-
-    collapse = CollapseButton({
-      open,
-      onClick: function () {
-        if (open) {
-          dispatch(actions.messageClose(message.id));
-        } else {
-          dispatch(actions.messageOpen(message.id));
-        }
-      },
-    });
-  } else if (type === "table") {
+  let attachment = null;
+  if (type === "table") {
     attachment = ConsoleTable({
       dispatch,
       id: message.id,
       hudProxyClient,
       parameters: message.parameters,
       tableData
     });
   }
 
-  const classes = ["message", "cm-s-mozilla"];
-
-  classes.push(source);
-  classes.push(type);
-  classes.push(level);
-
-  if (open === true) {
-    classes.push("open");
-  }
+  const topLevelClasses = ["cm-s-mozilla"];
 
-  return dom.div({ className: classes.join(" ") },
-    // @TODO add timestamp
-    // @TODO add indent if necessary
-    icon,
-    collapse,
-    dom.span({ className: "message-body-wrapper" },
-      dom.span({ className: "message-flex-body" },
-        dom.span({ className: "message-body devtools-monospace" },
-          messageBody
-        ),
-        repeat,
-        location
-      ),
-      attachment
-    )
-  );
+  return Message({
+    messageId,
+    open,
+    source,
+    type,
+    level,
+    topLevelClasses,
+    messageBody,
+    repeat,
+    frame,
+    stacktrace,
+    attachment,
+    onViewSourceInDebugger,
+    sourceMapService,
+    emitNewMessage,
+    dispatch,
+  });
 }
 
 function formatReps(parameters) {
   return (
     parameters
       // Get all the grips.
       .map((grip, key) => GripMessageBody({ grip, key }))
       // Interleave spaces.
       .reduce((arr, v, i) => {
         return i + 1 < parameters.length
           ? arr.concat(v, dom.span({}, " "))
           : arr.concat(v);
       }, [])
   );
 }
 
-module.exports.ConsoleApiCall = ConsoleApiCall;
+module.exports = ConsoleApiCall;
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-command.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-command.js
@@ -4,52 +4,42 @@
  * 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";
 
 // React & Redux
 const {
   createFactory,
-  DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
+const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
 
 ConsoleCommand.displayName = "ConsoleCommand";
 
 ConsoleCommand.propTypes = {
   message: PropTypes.object.isRequired,
+  autoscroll: PropTypes.bool.isRequired,
 };
 
 /**
  * Displays input from the console.
  */
 function ConsoleCommand(props) {
-  const { message } = props;
-  const {source, type, level} = message;
-
-  const icon = MessageIcon({level});
-
-  const classes = ["message"];
-
-  classes.push(source);
-  classes.push(type);
-  classes.push(level);
+  const {
+    source,
+    type,
+    level,
+    messageText: messageBody,
+  } = props.message;
 
-  return dom.div({
-    className: classes.join(" "),
-    ariaLive: "off",
-  },
-    // @TODO add timestamp
-    // @TODO add indent if necessary
-    icon,
-    dom.span({ className: "message-body-wrapper" },
-      dom.span({ className: "message-flex-body" },
-        dom.span({ className: "message-body devtools-monospace" },
-          message.messageText
-        )
-      )
-    )
-  );
+  const childProps = {
+    source,
+    type,
+    level,
+    topLevelClasses: [],
+    messageBody,
+    scrollToMessage: props.autoscroll,
+  };
+  return Message(childProps);
 }
 
-module.exports.ConsoleCommand = ConsoleCommand;
+module.exports = ConsoleCommand;
--- a/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js
@@ -14,9 +14,9 @@ const {
 DefaultRenderer.displayName = "DefaultRenderer";
 
 function DefaultRenderer(props) {
   return dom.div({},
     "This message type is not supported yet."
   );
 }
 
-module.exports.DefaultRenderer = DefaultRenderer;
+module.exports = DefaultRenderer;
--- a/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/evaluation-result.js
@@ -4,56 +4,50 @@
  * 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";
 
 // React & Redux
 const {
   createFactory,
-  DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
+const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
+const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
 
 EvaluationResult.displayName = "EvaluationResult";
 
 EvaluationResult.propTypes = {
   message: PropTypes.object.isRequired,
 };
 
 function EvaluationResult(props) {
   const { message } = props;
-  const {source, type, level} = message;
-  const icon = MessageIcon({level});
+  const {
+    source,
+    type,
+    level,
+    emitNewMessage,
+  } = message;
 
   let messageBody;
   if (message.messageText) {
     messageBody = message.messageText;
   } else {
     messageBody = GripMessageBody({grip: message.parameters});
   }
 
-
-  const classes = ["message", "cm-s-mozilla"];
-
-  classes.push(source);
-  classes.push(type);
-  classes.push(level);
+  const topLevelClasses = ["cm-s-mozilla"];
 
-  return dom.div({
-    className: classes.join(" ")
-  },
-    // @TODO add timestamp
-    // @TODO add indent if needed with console.group
-    icon,
-    dom.span({ className: "message-body-wrapper" },
-      dom.span({ className: "message-flex-body" },
-        dom.span({ className: "message-body devtools-monospace" },
-          messageBody
-        )
-      )
-    )
-  );
+  const childProps = {
+    source,
+    type,
+    level,
+    topLevelClasses,
+    messageBody,
+    scrollToMessage: props.autoscroll,
+    emitNewMessage,
+  };
+  return Message(childProps);
 }
 
-module.exports.EvaluationResult = EvaluationResult;
+module.exports = EvaluationResult;
--- a/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js
@@ -7,69 +7,49 @@
 "use strict";
 
 // React & Redux
 const {
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
-const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
+const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
-const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 
 NetworkEventMessage.displayName = "NetworkEventMessage";
 
 NetworkEventMessage.propTypes = {
   message: PropTypes.object.isRequired,
   openNetworkPanel: PropTypes.func.isRequired,
-  // @TODO: openLink will be used for mixed-content handling
-  openLink: PropTypes.func.isRequired,
-  open: PropTypes.bool.isRequired,
 };
 
 function NetworkEventMessage(props) {
-  const { dispatch, message, openNetworkPanel, open } = props;
-  const { actor, source, type, level, request, response, isXHR, totalTime } = message;
-  let { method, url } = request;
-  let { httpVersion, status, statusText } = response;
-
-  let classes = ["message", "cm-s-mozilla"];
-
-  classes.push(source);
-  classes.push(type);
-  classes.push(level);
+  const { message, openNetworkPanel, emitNewMessage } = props;
+  const { actor, source, type, level, request, isXHR } = message;
 
-  if (open) {
-    classes.push("open");
-  }
-
-  let statusInfo = "[]";
-
-  // @TODO: Status will be enabled after NetworkUpdateEvent packet arrives
-  if (httpVersion && status && statusText && totalTime) {
-    statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`;
-  }
-
-  let xhr = l10n.getStr("webConsoleXhrIndicator");
+  const topLevelClasses = [ "cm-s-mozilla" ];
 
   function onUrlClick() {
     openNetworkPanel(actor);
   }
 
-  return dom.div({ className: classes.join(" ") },
-    // @TODO add timestamp
-    // @TODO add indent if necessary
-    MessageIcon({ level }),
-    dom.span({
-      className: "message-body-wrapper message-body devtools-monospace",
-      "aria-haspopup": "true"
-    },
-      dom.span({ className: "method" }, method),
-      isXHR ? dom.span({ className: "xhr" }, xhr) : null,
-      dom.a({ className: "url", title: url, onClick: onUrlClick },
-        url.replace(/\?.+/, ""))
-    )
-  );
+  const method = dom.span({className: "method" }, request.method);
+  const xhr = isXHR
+    ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
+    : null;
+  const url = dom.a({ className: "url", title: request.url, onClick: onUrlClick },
+        request.url.replace(/\?.+/, ""));
+
+  const messageBody = dom.span({}, method, xhr, url);
+
+  const childProps = {
+    source,
+    type,
+    level,
+    topLevelClasses,
+    messageBody,
+    emitNewMessage,
+  };
+  return Message(childProps);
 }
 
-module.exports.NetworkEventMessage = NetworkEventMessage;
+module.exports = NetworkEventMessage;
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
@@ -4,97 +4,61 @@
  * 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";
 
 // React & Redux
 const {
   createFactory,
-  DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const FrameView = createFactory(require("devtools/client/shared/components/frame"));
-const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
-const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
-const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
-
-const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
 
 PageError.displayName = "PageError";
 
 PageError.propTypes = {
   message: PropTypes.object.isRequired,
   open: PropTypes.bool,
 };
 
 PageError.defaultProps = {
   open: false
 };
 
 function PageError(props) {
-  const { dispatch, message, open, sourceMapService, onViewSourceInDebugger } = props;
-  const { source, type, level, stacktrace, frame } = message;
-
-  const repeat = MessageRepeat({repeat: message.repeat});
-  const icon = MessageIcon({level});
-  const shouldRenderFrame = frame && frame.source !== "debugger eval code";
-  const location = dom.span({ className: "message-location devtools-monospace" },
-    shouldRenderFrame ? FrameView({
-      frame,
-      onClick: onViewSourceInDebugger,
-      showEmptyPathAsHost: true,
-      sourceMapService
-    }) : null
-  );
-
-  let collapse = "";
-  let attachment = "";
-  if (stacktrace) {
-    if (open) {
-      attachment = dom.div({ className: "stacktrace devtools-monospace" },
-        StackTrace({
-          stacktrace: stacktrace,
-          onViewSourceInDebugger: onViewSourceInDebugger
-        })
-      );
-    }
+  const {
+    message,
+    open,
+    sourceMapService,
+    onViewSourceInDebugger,
+    emitNewMessage,
+  } = props;
+  const {
+    id: messageId,
+    source,
+    type,
+    level,
+    messageText: messageBody,
+    repeat,
+    stacktrace,
+    frame
+  } = message;
 
-    collapse = CollapseButton({
-      open,
-      onClick: function () {
-        if (open) {
-          dispatch(actions.messageClose(message.id));
-        } else {
-          dispatch(actions.messageOpen(message.id));
-        }
-      },
-    });
-  }
-
-  const classes = ["message"];
-  classes.push(source);
-  classes.push(type);
-  classes.push(level);
-  if (open === true) {
-    classes.push("open");
-  }
-
-  return dom.div({
-    className: classes.join(" ")
-  },
-    icon,
-    collapse,
-    dom.span({ className: "message-body-wrapper" },
-      dom.span({ className: "message-flex-body" },
-        dom.span({ className: "message-body devtools-monospace" },
-          message.messageText
-        ),
-        repeat,
-        location
-      ),
-      attachment
-    )
-  );
+  const childProps = {
+    messageId,
+    open,
+    source,
+    type,
+    level,
+    topLevelClasses: [],
+    messageBody,
+    repeat,
+    frame,
+    stacktrace,
+    onViewSourceInDebugger,
+    sourceMapService,
+    emitNewMessage,
+  };
+  return Message(childProps);
 }
 
-module.exports.PageError = PageError;
+module.exports = PageError;
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/components/message.js
@@ -0,0 +1,139 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// React & Redux
+const {
+  createClass,
+  createFactory,
+  DOM: dom,
+  PropTypes
+} = require("devtools/client/shared/vendor/react");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button"));
+const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon"));
+const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat"));
+const FrameView = createFactory(require("devtools/client/shared/components/frame"));
+const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
+
+const Message = createClass({
+  displayName: "Message",
+
+  propTypes: {
+    open: PropTypes.bool,
+    source: PropTypes.string.isRequired,
+    type: PropTypes.string.isRequired,
+    level: PropTypes.string.isRequired,
+    topLevelClasses: PropTypes.array.isRequired,
+    messageBody: PropTypes.any.isRequired,
+    repeat: PropTypes.any,
+    frame: PropTypes.any,
+    attachment: PropTypes.any,
+    stacktrace: PropTypes.any,
+    messageId: PropTypes.string,
+    scrollToMessage: PropTypes.bool,
+    onViewSourceInDebugger: PropTypes.func,
+    sourceMapService: PropTypes.any,
+  },
+
+  componentDidMount() {
+    if (this.messageNode && this.props.emitNewMessage) {
+      this.props.emitNewMessage(this.messageNode);
+      if (this.props.scrollToMessage) {
+        this.messageNode.scrollIntoView();
+      }
+    }
+  },
+
+  render() {
+    const {
+      messageId,
+      open,
+      source,
+      type,
+      level,
+      topLevelClasses,
+      messageBody,
+      frame,
+      stacktrace,
+      onViewSourceInDebugger,
+      sourceMapService,
+      dispatch,
+    } = this.props;
+
+    topLevelClasses.push("message", source, type, level);
+    if (open) {
+      topLevelClasses.push("open");
+    }
+
+    const icon = MessageIcon({level});
+
+    // Figure out if there is an expandable part to the message.
+    let attachment = null;
+    if (this.props.attachment) {
+      attachment = this.props.attachment;
+    } else if (stacktrace) {
+      const child = open ? StackTrace({
+        stacktrace: stacktrace,
+        onViewSourceInDebugger: onViewSourceInDebugger
+      }) : null;
+      attachment = dom.div({ className: "stacktrace devtools-monospace" }, child);
+    }
+
+    // If there is an expandable part, make it collapsible.
+    let collapse = null;
+    if (attachment) {
+      collapse = CollapseButton({
+        open,
+        onClick: function () {
+          if (open) {
+            dispatch(actions.messageClose(messageId));
+          } else {
+            dispatch(actions.messageOpen(messageId));
+          }
+        },
+      });
+    }
+
+    const repeat = this.props.repeat ? MessageRepeat({repeat: this.props.repeat}) : null;
+
+    // Configure the location.
+    const shouldRenderFrame = frame && frame.source !== "debugger eval code";
+    const location = dom.span({ className: "message-location devtools-monospace" },
+      shouldRenderFrame ? FrameView({
+        frame,
+        onClick: onViewSourceInDebugger,
+        showEmptyPathAsHost: true,
+        sourceMapService
+      }) : null
+    );
+
+    return dom.div({
+      className: topLevelClasses.join(" "),
+      ref: node => {
+        this.messageNode = node;
+      }
+    },
+      // @TODO add timestamp
+      // @TODO add indent if necessary
+      icon,
+      collapse,
+      dom.span({ className: "message-body-wrapper" },
+        dom.span({ className: "message-flex-body" },
+          dom.span({ className: "message-body devtools-monospace" },
+            messageBody
+          ),
+          repeat,
+          location
+        ),
+        attachment
+      )
+    );
+  }
+});
+
+module.exports = Message;
--- a/devtools/client/webconsole/new-console-output/components/moz.build
+++ b/devtools/client/webconsole/new-console-output/components/moz.build
@@ -12,10 +12,11 @@ DevToolsModules(
     'console-output.js',
     'console-table.js',
     'filter-bar.js',
     'filter-button.js',
     'grip-message-body.js',
     'message-container.js',
     'message-icon.js',
     'message-repeat.js',
+    'message.js',
     'variables-view-link.js'
 )
--- a/devtools/client/webconsole/new-console-output/components/variables-view-link.js
+++ b/devtools/client/webconsole/new-console-output/components/variables-view-link.js
@@ -26,9 +26,9 @@ function VariablesViewLink(props) {
     dom.a({
       onClick: openVariablesView.bind(null, object),
       className: "cm-variable",
       draggable: false,
     }, children)
   );
 }
 
-module.exports.VariablesViewLink = VariablesViewLink;
+module.exports = VariablesViewLink;
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -13,16 +13,33 @@ const actionTypes = {
   MESSAGE_CLOSE: "MESSAGE_CLOSE",
   MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
   FILTER_TOGGLE: "FILTER_TOGGLE",
   FILTER_TEXT_SET: "FILTER_TEXT_SET",
   FILTERS_CLEAR: "FILTERS_CLEAR",
   FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
 };
 
+const prefs = {
+  PREFS: {
+    FILTER: {
+      ERROR: "devtools.webconsole.filter.error",
+      WARN: "devtools.webconsole.filter.warn",
+      INFO: "devtools.webconsole.filter.info",
+      LOG: "devtools.webconsole.filter.log",
+      DEBUG: "devtools.webconsole.filter.debug",
+      NET: "devtools.webconsole.filter.net",
+      NETXHR: "devtools.webconsole.filter.netxhr",
+    },
+    UI: {
+      FILTER_BAR: "devtools.webconsole.ui.filterbar"
+    }
+  }
+};
+
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
     XML: "xml",
     JAVASCRIPT: "javascript",
     NETWORK: "network",
     CONSOLE_API: "console-api",
     STORAGE: "storage",
     APPCACHE: "appcache",
@@ -56,9 +73,9 @@ const chromeRDPEnums = {
     ERROR: "error",
     WARN: "warn",
     DEBUG: "debug",
     INFO: "info"
   }
 };
 
 // Combine into a single constants object
-module.exports = Object.assign({}, actionTypes, chromeRDPEnums);
+module.exports = Object.assign({}, actionTypes, prefs, chromeRDPEnums);
--- a/devtools/client/webconsole/new-console-output/main.js
+++ b/devtools/client/webconsole/new-console-output/main.js
@@ -13,12 +13,12 @@ const { BrowserLoader } = Cu.import("res
 
 // Initialize module loader and load all modules of the new inline
 // preview feature. The entire code-base doesn't need any extra
 // privileges and runs entirely in content scope.
 const NewConsoleOutputWrapper = BrowserLoader({
   baseURI: "resource://devtools/client/webconsole/new-console-output/",
   window}).require("./new-console-output-wrapper");
 
-this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner) {
+this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, emitNewMessage) {
   console.log("Creating NewConsoleOutput", parentNode, NewConsoleOutputWrapper);
-  return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner);
+  return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, emitNewMessage);
 };
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -10,20 +10,21 @@ const { Provider } = require("devtools/c
 
 const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
 
 const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output"));
 const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
 
 const store = configureStore();
+let queuedActions = [];
+let throttledDispatchTimeout = false;
 
 function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner) {
   this.parentNode = parentNode;
-  this.parentNode = parentNode;
   this.jsterm = jsterm;
   this.toolbox = toolbox;
   this.owner = owner;
 
   this.init = this.init.bind(this);
 }
 
 NewConsoleOutputWrapper.prototype = {
@@ -41,41 +42,51 @@ NewConsoleOutputWrapper.prototype = {
       openNetworkPanel: (requestId) => {
         return this.toolbox.selectTool("netmonitor").then(panel => {
           return panel.panelWin.NetMonitorController.inspectRequest(requestId);
         });
       },
       openLink: (url) => {
         this.owner.openLink(url);
       },
+      emitNewMessage: (node) => {
+        this.jsterm.hud.emit("new-messages", new Set([{
+          node
+        }]));
+      },
     });
     let filterBar = FilterBar({});
     let provider = React.createElement(
       Provider,
       { store },
       React.DOM.div(
         {className: "webconsole-output-wrapper"},
         filterBar,
         childComponent
     ));
 
     this.body = ReactDOM.render(provider, this.parentNode);
   },
   dispatchMessageAdd: (message) => {
-    store.dispatch(actions.messageAdd(message));
+    batchedMessageAdd(actions.messageAdd(message));
   },
   dispatchMessagesAdd: (messages) => {
     const batchedActions = messages.map(message => actions.messageAdd(message));
     store.dispatch(actions.batchActions(batchedActions));
   },
   dispatchMessagesClear: () => {
     store.dispatch(actions.messagesClear());
   },
-  getLastMessage: function() {
-    // Return the last message in the DOM as the message that was just dispatched. This may not
-    // always be correct in the case of filtered messages, but it's close enough for our tests.
-    let messageNodes = this.parentNode.querySelectorAll(".message");
-    return messageNodes[messageNodes.length - 1]
-  },
 };
 
+function batchedMessageAdd(action) {
+  queuedActions.push(action);
+  if (!throttledDispatchTimeout) {
+    throttledDispatchTimeout = setTimeout(() => {
+      store.dispatch(actions.batchActions(queuedActions));
+      queuedActions = [];
+      throttledDispatchTimeout = null;
+    }, 50);
+  }
+}
+
 // Exports from this module
 module.exports = NewConsoleOutputWrapper;
--- a/devtools/client/webconsole/new-console-output/reducers/filters.js
+++ b/devtools/client/webconsole/new-console-output/reducers/filters.js
@@ -8,18 +8,18 @@
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
 const FilterState = Immutable.Record({
   debug: true,
   error: true,
   info: true,
   log: true,
-  network: true,
-  netxhr: true,
+  net: false,
+  netxhr: false,
   text: "",
   warn: true,
 });
 
 function filters(state = new FilterState(), action) {
   switch (action.type) {
     case constants.FILTER_TOGGLE:
       const {filter} = action;
--- a/devtools/client/webconsole/new-console-output/reducers/ui.js
+++ b/devtools/client/webconsole/new-console-output/reducers/ui.js
@@ -1,25 +1,39 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const constants = require("devtools/client/webconsole/new-console-output/constants");
+const {
+  FILTER_BAR_TOGGLE,
+  MESSAGE_ADD,
+} = require("devtools/client/webconsole/new-console-output/constants");
 const Immutable = require("devtools/client/shared/vendor/immutable");
 
-const Ui = Immutable.Record({
+const UiState = Immutable.Record({
   filterBarVisible: false,
   filteredMessageVisible: false,
+  autoscroll: true,
 });
 
-function ui(state = new Ui(), action) {
+function ui(state = new UiState(), action) {
+  // Autoscroll should be set for all action types. If the last action was not message
+  // add, then turn it off. This prevents us from scrolling after someone toggles a
+  // filter, or to the bottom of the attachement when an expandable message at the bottom
+  // of the list is expanded. It does depend on the MESSAGE_ADD action being the last in
+  // its batch, though.
+  state = state.set("autoscroll", action.type == MESSAGE_ADD);
+
   switch (action.type) {
-    case constants.FILTER_BAR_TOGGLE:
+    case FILTER_BAR_TOGGLE:
       return state.set("filterBarVisible", !state.filterBarVisible);
   }
 
   return state;
 }
 
-exports.ui = ui;
+module.exports = {
+  UiState,
+  ui,
+};
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -44,17 +44,17 @@ function filterLevel(messages, filters) 
       || [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type);
   });
 }
 
 function filterNetwork(messages, filters) {
   return messages.filter((message) => {
     return (
       message.source !== MESSAGE_SOURCE.NETWORK
-      || (filters.get("network") === true && message.isXHR === false)
+      || (filters.get("net") === true && message.isXHR === false)
       || (filters.get("netxhr") === true && message.isXHR === true)
       || [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type)
     );
   });
 }
 
 function search(messages, text = "") {
   if (text === "") {
@@ -94,16 +94,19 @@ function search(messages, text = "") {
       || (message.parameters !== null
           && message.parameters.join("").toLocaleLowerCase()
               .includes(text.toLocaleLowerCase()))
     );
   });
 }
 
 function isTextInFrame(text, frame) {
+  if (!frame) {
+    return false;
+  }
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   return Object.keys(frame)
     .map(key => frame[key])
     .join(":")
     .toLocaleLowerCase()
     .includes(text.toLocaleLowerCase());
 }
 
--- a/devtools/client/webconsole/new-console-output/selectors/ui.js
+++ b/devtools/client/webconsole/new-console-output/selectors/ui.js
@@ -1,12 +1,20 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 "use strict";
 
 function getAllUi(state) {
   return state.ui;
 }
 
-exports.getAllUi = getAllUi;
+function getScrollSetting(state) {
+  return getAllUi(state).autoscroll;
+}
+
+module.exports = {
+  getAllUi,
+  getScrollSetting,
+};
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -1,38 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters");
 const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs");
+const {UiState} = require("devtools/client/webconsole/new-console-output/reducers/ui");
 const {
   applyMiddleware,
   combineReducers,
   compose,
   createStore
 } = require("devtools/client/shared/vendor/redux");
 const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
-const constants = require("devtools/client/webconsole/new-console-output/constants");
+const {
+  BATCH_ACTIONS,
+  PREFS,
+} = require("devtools/client/webconsole/new-console-output/constants");
 const { reducers } = require("./reducers/index");
 const Services = require("Services");
 
 function configureStore() {
   const initialState = {
     prefs: new PrefState({
       logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
     }),
     filters: new FilterState({
-      error: Services.prefs.getBoolPref("devtools.webconsole.filter.error"),
-      warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"),
-      info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"),
-      log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"),
-      network: Services.prefs.getBoolPref("devtools.webconsole.filter.network"),
-      netxhr: Services.prefs.getBoolPref("devtools.webconsole.filter.netxhr"),
+      error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
+      warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
+      info: Services.prefs.getBoolPref(PREFS.FILTER.INFO),
+      log: Services.prefs.getBoolPref(PREFS.FILTER.LOG),
+      net: Services.prefs.getBoolPref(PREFS.FILTER.NET),
+      netxhr: Services.prefs.getBoolPref(PREFS.FILTER.NETXHR),
+    }),
+    ui: new UiState({
+      filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
     })
   };
 
   return createStore(
     combineReducers(reducers),
     initialState,
     compose(applyMiddleware(thunk), enableBatching())
   );
@@ -40,17 +47,17 @@ function configureStore() {
 
 /**
  * A enhancer for the store to handle batched actions.
  */
 function enableBatching() {
   return next => (reducer, initialState, enhancer) => {
     function batchingReducer(state, action) {
       switch (action.type) {
-        case constants.BATCH_ACTIONS:
+        case BATCH_ACTIONS:
           return action.actions.reduce(batchingReducer, state);
         default:
           return reducer(state, action);
       }
     }
 
     if (typeof initialState === "function" && typeof enhancer === "undefined") {
       enhancer = initialState;
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/actions/filters.test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-const actions = require("devtools/client/webconsole/new-console-output/actions/index");
-const {
-  FILTER_TEXT_SET,
-  FILTER_TOGGLE,
-  FILTERS_CLEAR,
-  MESSAGE_LEVEL
-} = require("devtools/client/webconsole/new-console-output/constants");
-
-const expect = require("expect");
-
-describe("Filter actions:", () => {
-  describe("filterTextSet", () => {
-    it("creates expected action", () => {
-      const action = actions.filterTextSet("test");
-      const expected = {
-        type: FILTER_TEXT_SET,
-        text: "test"
-      };
-
-      expect(action).toEqual(expected);
-    });
-  });
-
-  describe("filterToggle", () => {
-    it("creates expected action", () => {
-      const action = actions.filterToggle(MESSAGE_LEVEL.ERROR);
-      const expected = {
-        type: FILTER_TOGGLE,
-        filter: "error"
-      };
-
-      expect(action).toEqual(expected);
-    });
-  });
-
-  describe("filterTextApply", () => {
-
-  });
-
-  describe("filtersClear", () => {
-    it("creates expected action", () => {
-      const action = actions.filtersClear();
-      const expected = {
-        type: FILTERS_CLEAR
-      };
-
-      expect(action).toEqual(expected);
-    });
-  });
-});
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/actions/messages.test.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
-const configureStore = require("redux-mock-store").default;
-const { getRepeatId } = require("devtools/client/webconsole/new-console-output/utils/messages");
-const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
-const { setupActions } = require("devtools/client/webconsole/new-console-output/test/helpers");
-const constants = require("devtools/client/webconsole/new-console-output/constants");
-
-const mockStore = configureStore([ thunk ]);
-
-const expect = require("expect");
-
-let actions;
-
-describe("Message actions:", () => {
-  beforeEach(()=>{
-    actions = setupActions();
-  });
-
-  describe("messageAdd", () => {
-    it("dispatches expected action given a packet", () => {
-      const packet = stubPackets.get("console.log('foobar', 'test')");
-      const store = mockStore({});
-      store.dispatch(actions.messageAdd(packet));
-
-      const actualActions = store.getActions();
-      expect(actualActions.length).toEqual(1);
-
-      const addAction = actualActions[0];
-      const {message} = addAction;
-      const expectedAction = {
-        type: constants.MESSAGE_ADD,
-        message: stubPreparedMessages.get("console.log('foobar', 'test')")
-      };
-      expect(message.toJS()).toEqual(expectedAction.message.toJS());
-    });
-
-    it("dispatches expected actions given a console.clear packet", () => {
-      const packet = stubPackets.get("console.clear()");
-      const store = mockStore({});
-      store.dispatch(actions.messageAdd(packet));
-
-      const actualActions = store.getActions();
-      expect(actualActions.length).toEqual(1);
-
-      const [clearAction, addAction] = actualActions[0].actions;
-      expect(clearAction.type).toEqual(constants.MESSAGES_CLEAR);
-
-      const {message} = addAction;
-      const expectedAction = {
-        type: constants.MESSAGE_ADD,
-        message: stubPreparedMessages.get("console.clear()")
-      };
-      expect(addAction.type).toEqual(constants.MESSAGE_ADD);
-      expect(message.toJS()).toEqual(expectedAction.message.toJS());
-    });
-
-    it("dispatches expected action given a console.table packet", () => {
-      const packet = stubPackets.get("console.table(['a', 'b', 'c'])");
-      const store = mockStore({});
-      store.dispatch(actions.messageAdd(packet));
-
-      const expectedActions = store.getActions();
-      expect(expectedActions.length).toEqual(1);
-
-      const addAction = expectedActions[0];
-      const {message} = addAction;
-      const expected = {
-        type: constants.MESSAGE_ADD,
-        message: stubPreparedMessages.get("console.table(['a', 'b', 'c'])")
-      };
-      expect(message.toJS()).toEqual(expected.message.toJS());
-    });
-  });
-
-  describe("messagesClear", () => {
-    it("creates expected action", () => {
-      const action = actions.messagesClear();
-      const expected = {
-        type: constants.MESSAGES_CLEAR,
-      };
-      expect(action).toEqual(expected);
-    });
-  });
-});
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/actions/ui.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-const actions = require("devtools/client/webconsole/new-console-output/actions/index");
-const {
-  FILTER_BAR_TOGGLE
-} = require("devtools/client/webconsole/new-console-output/constants");
-
-const expect = require("expect");
-
-describe("UI actions:", () => {
-  describe("filterBarToggle", () => {
-    it("creates expected action", () => {
-      const action = actions.filterBarToggle();
-      const expected = {
-        type: FILTER_BAR_TOGGLE
-      };
-
-      expect(action).toEqual(expected);
-    });
-  });
-});
--- a/devtools/client/webconsole/new-console-output/test/chrome/chrome.ini
+++ b/devtools/client/webconsole/new-console-output/test/chrome/chrome.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 
 support-files =
   head.js
 
 [test_render_perf.html]
-skip-if = debug
+skip-if = true # Bug 1306783
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
@@ -5,42 +5,48 @@
 // Test utils.
 const expect = require("expect");
 const { render } = require("enzyme");
 
 // React
 const { createFactory } = require("devtools/client/shared/vendor/react");
 
 // Components under test.
-const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call").ConsoleApiCall);
+const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call"));
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const onViewSourceInDebugger = () => {};
 
 const tempfilePath = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js";
 
 describe("ConsoleAPICall component:", () => {
   describe("console.log", () => {
     it("renders string grips", () => {
       const message = stubPreparedMessages.get("console.log('foobar', 'test')");
       const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
 
       expect(wrapper.find(".message-body").text()).toBe("foobar test");
       expect(wrapper.find(".objectBox-string").length).toBe(2);
       expect(wrapper.find("div.message.cm-s-mozilla span span.message-flex-body span.message-body.devtools-monospace").length).toBe(1);
+
+      // There should be the location
+      const locationLink = wrapper.find(`.message-location`);
+      expect(locationLink.length).toBe(1);
+      expect(locationLink.text()).toBe("test-tempfile.js:1:27");
     });
 
     it("renders repeat node", () => {
       const message =
         stubPreparedMessages.get("console.log('foobar', 'test')")
         .set("repeat", 107);
       const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
 
       expect(wrapper.find(".message-repeats").text()).toBe("107");
+      expect(wrapper.find(".message-repeats").prop("title")).toBe("107 repeats");
 
       expect(wrapper.find("span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats").length).toBe(1);
     });
   });
 
   describe("console.count", () => {
     it("renders", () => {
       const message = stubPreparedMessages.get("console.count('bar')");
--- a/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/evaluation-result.test.js
@@ -5,17 +5,17 @@
 // Test utils.
 const expect = require("expect");
 const { render } = require("enzyme");
 
 // React
 const { createFactory } = require("devtools/client/shared/vendor/react");
 
 // Components under test.
-const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result").EvaluationResult);
+const EvaluationResult = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result"));
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 
 describe("EvaluationResult component:", () => {
   it("renders a grip result", () => {
     const message = stubPreparedMessages.get("new Date(0)");
     const wrapper = render(EvaluationResult({ message }));
--- a/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/filter-bar.test.js
@@ -4,17 +4,17 @@
 
 const expect = require("expect");
 const sinon = require("sinon");
 const { render, mount } = require("enzyme");
 
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const Provider = createFactory(require("react-redux").Provider);
 
-const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
+const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
 const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
 const {
   MESSAGES_CLEAR,
   MESSAGE_LEVEL
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
--- a/devtools/client/webconsole/new-console-output/test/components/filter-button.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/filter-button.test.js
@@ -1,49 +1,34 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const expect = require("expect");
-const sinon = require("sinon");
-const { render, shallow } = require("enzyme");
+const { render } = require("enzyme");
 
 const { createFactory } = require("devtools/client/shared/vendor/react");
 
-const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
-const {
-  FILTER_TOGGLE,
-  MESSAGE_LEVEL
-} = require("devtools/client/webconsole/new-console-output/constants");
+const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button"));
+const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
 
 describe("FilterButton component:", () => {
   const props = {
     active: true,
     label: "Error",
     filterKey: MESSAGE_LEVEL.ERROR,
-    dispatch: sinon.spy()
   };
 
   it("displays as active when turned on", () => {
     const wrapper = render(FilterButton(props));
     expect(wrapper.html()).toBe(
-      "<button class=\"menu-filter-button checked\">Error</button>"
+      "<button class=\"menu-filter-button error checked\">Error</button>"
     );
   });
 
   it("displays as inactive when turned off", () => {
     const inactiveProps = Object.assign({}, props, { active: false });
     const wrapper = render(FilterButton(inactiveProps));
     expect(wrapper.html()).toBe(
-      "<button class=\"menu-filter-button\">Error</button>"
+      "<button class=\"menu-filter-button error\">Error</button>"
     );
   });
-
-  it("fires FILTER_TOGGLE action when clicked", () => {
-    const wrapper = shallow(FilterButton(props));
-    wrapper.find("button").simulate("click");
-    const call = props.dispatch.getCall(0);
-    expect(call.args[0]).toEqual({
-      type: FILTER_TOGGLE,
-      filter: MESSAGE_LEVEL.ERROR
-    });
-  });
 });
--- a/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/message-container.test.js
@@ -6,19 +6,19 @@
 const expect = require("expect");
 const {
   renderComponent,
   shallowRenderComponent
 } = require("devtools/client/webconsole/new-console-output/test/helpers");
 
 // Components under test.
 const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container");
-const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
-const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
-const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
+const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
+const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
+const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const onViewSourceInDebugger = () => {};
 
 describe("MessageContainer component:", () => {
   it("pipes data to children as expected", () => {
     const message = stubPreparedMessages.get("console.log('foobar', 'test')");
--- a/devtools/client/webconsole/new-console-output/test/components/message-icon.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/message-icon.test.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const {
   MESSAGE_LEVEL,
 } = require("devtools/client/webconsole/new-console-output/constants");
-const { MessageIcon } = require("devtools/client/webconsole/new-console-output/components/message-icon");
+const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
 
 const expect = require("expect");
 
 const {
   renderComponent
 } = require("devtools/client/webconsole/new-console-output/test/helpers");
 
 describe("MessageIcon component:", () => {
rename from devtools/client/webconsole/new-console-output/test/components/repeat.test.js
rename to devtools/client/webconsole/new-console-output/test/components/message-repeat.test.js
--- a/devtools/client/webconsole/new-console-output/test/components/repeat.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/message-repeat.test.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const { MessageRepeat } = require("devtools/client/webconsole/new-console-output/components/message-repeat");
+const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
 
 const expect = require("expect");
 
 const {
   renderComponent
 } = require("devtools/client/webconsole/new-console-output/test/helpers");
 
 describe("MessageRepeat component:", () => {
--- a/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js
@@ -5,17 +5,17 @@
 // Test utils.
 const expect = require("expect");
 const { render } = require("enzyme");
 
 // React
 const { createFactory } = require("devtools/client/shared/vendor/react");
 
 // Components under test.
-const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message").NetworkEventMessage);
+const NetworkEventMessage = createFactory(require("devtools/client/webconsole/new-console-output/components/message-types/network-event-message"));
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const onViewSourceInDebugger = () => {};
 const openNetworkPanel = () => {};
 const openLink = () => {};
 const EXPECTED_URL = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html";
 
--- a/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test utils.
 const expect = require("expect");
 const { render } = require("enzyme");
 
 // Components under test.
-const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
+const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 
 describe("PageError component:", () => {
   it("renders", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const wrapper = render(PageError({ message }));
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/PluralForm.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+  PluralForm: {
+    get: function (occurence, str) {
+      // @TODO Remove when loading the actual strings from webconsole.properties
+      // is done in the L10n fixture.
+      if (str === "messageRepeats.tooltip2") {
+        return `${occurence} repeats`;
+      }
+
+      return str;
+    }
+  }
+};
--- a/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/Services.js
@@ -1,21 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+const { PREFS } = require("devtools/client/webconsole/new-console-output/constants");
+
 module.exports = {
   prefs: {
     getIntPref: pref => {
       switch (pref) {
         case "devtools.hud.loglimit":
           return 1000;
       }
     },
     getBoolPref: pref => {
-      switch (pref) {
-        default:
-          return true;
-      }
-    }
+      const falsey = [
+        PREFS.FILTER.NET,
+        PREFS.FILTER.NETXHR,
+        PREFS.UI.FILTER_BAR,
+      ];
+      return !falsey.includes(pref);
+    },
+    setBoolPref: () => {},
+    clearUserPref: () => {},
   }
 };
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   !/devtools/client/framework/test/shared-head.js
   test-console-table.html
   test-console.html
+  test-console-filters.html
 
 [browser_webconsole_console_table.js]
+[browser_webconsole_filters.js]
 [browser_webconsole_init.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_vview_close_on_esc_key.js]
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_filters.js
@@ -0,0 +1,72 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests filters.
+
+"use strict";
+
+const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html";
+
+add_task(function* () {
+  let hud = yield openNewTabAndConsole(TEST_URI);
+  const outputNode = hud.ui.experimentalOutputNode;
+
+  const toolbar = yield waitFor(() => {
+    return outputNode.querySelector(".webconsole-filterbar-primary");
+  });
+  ok(toolbar, "Toolbar found");
+
+  // Show the filter bar
+  toolbar.querySelector(".devtools-filter-icon").click();
+  const filterBar = yield waitFor(() => {
+    return outputNode.querySelector(".webconsole-filterbar-secondary");
+  });
+  ok(filterBar, "Filter bar is shown when filter icon is clicked.");
+
+  // Check defaults.
+  Object.values(MESSAGE_LEVEL).forEach(level => {
+    ok(filterIsEnabled(filterBar.querySelector(`.${level}`)),
+      `Filter button for ${level} is on by default`);
+  });
+  ["net", "netxhr"].forEach(category => {
+    ok(!filterIsEnabled(filterBar.querySelector(`.${category}`)),
+      `Filter button for ${category} is off by default`);
+  });
+
+  // Check that messages are shown as expected. This depends on cached messages being
+  // shown.
+  ok(findMessages(hud, "").length == 5,
+    "Messages of all levels shown when filters are on.");
+
+  // Check that messages are not shown when their filter is turned off.
+  filterBar.querySelector(".error").click();
+  yield waitFor(() => findMessages(hud, "").length == 4);
+  ok(true, "When a filter is turned off, its messages are not shown.");
+
+  // Check that the ui settings were persisted.
+  yield closeTabAndToolbox();
+  yield testFilterPersistence();
+});
+
+function filterIsEnabled(button) {
+  return button.classList.contains("checked");
+}
+
+function* testFilterPersistence() {
+  let hud = yield openNewTabAndConsole(TEST_URI);
+  const outputNode = hud.ui.experimentalOutputNode;
+  const filterBar = yield waitFor(() => {
+    return outputNode.querySelector(".webconsole-filterbar-secondary");
+  });
+  ok(filterBar, "Filter bar ui setting is persisted.");
+
+  // Check that the filter settings were persisted.
+  ok(!filterIsEnabled(filterBar.querySelector(".error")),
+    "Filter button setting is persisted");
+  ok(findMessages(hud, "").length == 4,
+    "Messages of all levels shown when filters are on.");
+}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
@@ -104,14 +104,30 @@ function* waitFor(condition, message = "
  * @param object hud
  *        The web console.
  * @param string text
  *        A substring that can be found in the message.
  * @param selector [optional]
  *        The selector to use in finding the message.
  */
 function findMessage(hud, text, selector = ".message") {
+  const elements = findMessages(hud, text, selector);
+  return elements.pop();
+}
+
+/**
+ * Find multiple messages in the output.
+ *
+ * @param object hud
+ *        The web console.
+ * @param string text
+ *        A substring that can be found in the message.
+ * @param selector [optional]
+ *        The selector to use in finding the message.
+ */
+function findMessages(hud, text, selector = ".message") {
+  const messages = hud.ui.experimentalOutputNode.querySelectorAll(selector);
   const elements = Array.prototype.filter.call(
-    hud.ui.experimentalOutputNode.querySelectorAll(selector),
+    messages,
     (el) => el.textContent.includes(text)
   );
-  return elements.length > 0 ? elements.pop() : false;
+  return elements;
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/test-console-filters.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Webconsole filters test page</title>
+  </head>
+  <body>
+    <p>Webconsole filters test page</p>
+    <script>
+      console.log("console log");
+      console.warn("console warn");
+      console.error("console error");
+      console.info("console info");
+      console.count("console debug");
+    </script>
+  </body>
+</html>
--- a/devtools/client/webconsole/new-console-output/test/requireHelper.js
+++ b/devtools/client/webconsole/new-console-output/test/requireHelper.js
@@ -22,15 +22,17 @@ requireHacker.global_hook("default", pat
 
   // Some modules depend on Chrome APIs which don't work in mocha. When such a module
   // is required, replace it with a mock version.
   switch (path) {
     case "devtools/client/webconsole/utils":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`;
     case "devtools/shared/l10n":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`;
+    case "devtools/shared/plural-form":
+      return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/PluralForm")`;
     case "Services":
     case "Services.default":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`;
     case "devtools/shared/client/main":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`;
   }
 });
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -60,33 +60,33 @@ describe("Filtering", () => {
       expect(messages.size).toEqual(numMessages - 1);
     });
 
     it("filters xhr messages", () => {
       let message = stubPreparedMessages.get("XHR GET request");
       store.dispatch(messageAdd(message));
 
       let messages = getAllMessages(store.getState());
-      expect(messages.size).toEqual(numMessages + 1);
+      expect(messages.size).toEqual(numMessages);
 
       store.dispatch(actions.filterToggle("netxhr"));
       messages = getAllMessages(store.getState());
-      expect(messages.size).toEqual(numMessages);
+      expect(messages.size).toEqual(numMessages + 1);
     });
 
     it("filters network messages", () => {
       let message = stubPreparedMessages.get("GET request");
       store.dispatch(messageAdd(message));
 
       let messages = getAllMessages(store.getState());
-      expect(messages.size).toEqual(numMessages + 1);
+      expect(messages.size).toEqual(numMessages);
 
-      store.dispatch(actions.filterToggle("network"));
+      store.dispatch(actions.filterToggle("net"));
       messages = getAllMessages(store.getState());
-      expect(messages.size).toEqual(numMessages);
+      expect(messages.size).toEqual(numMessages + 1);
     });
   });
 
   describe("Text filter", () => {
     it("matches on value grips", () => {
       store.dispatch(actions.filterTextSet("danger"));
 
       let messages = getAllMessages(store.getState());
@@ -166,32 +166,32 @@ describe("Clear filters", () => {
     store.dispatch(actions.filterTextSet("foobar"));
 
     let filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
       "debug": true,
       "error": false,
       "info": true,
       "log": true,
-      "network": true,
-      "netxhr": false,
+      "net": false,
+      "netxhr": true,
       "warn": true,
       "text": "foobar"
     });
 
     store.dispatch(actions.filtersClear());
 
     filters = getAllFilters(store.getState());
     expect(filters.toJS()).toEqual({
       "debug": true,
       "error": true,
       "info": true,
       "log": true,
-      "network": true,
-      "netxhr": true,
+      "net": false,
+      "netxhr": false,
       "warn": true,
       "text": ""
     });
   });
 });
 
 function prepareBaseStore() {
   const store = setupStore([
--- a/devtools/client/webconsole/package.json
+++ b/devtools/client/webconsole/package.json
@@ -5,17 +5,16 @@
     "amd-loader": "0.0.5",
     "babel-preset-es2015": "^6.6.0",
     "babel-register": "^6.7.2",
     "enzyme": "^2.4.1",
     "expect": "^1.16.0",
     "jsdom": "^9.4.1",
     "jsdom-global": "^2.0.0",
     "mocha": "^2.5.3",
-    "redux-mock-store": "^1.1.4",
     "require-hacker": "^2.1.4",
     "sinon": "^1.17.5"
   },
   "scripts": {
     "postinstall": "cd ../ && npm install && cd webconsole",
     "test": "NODE_PATH=`pwd`/../../../:`pwd`/../../../devtools/client/shared/vendor/ mocha new-console-output/test/**/*.test.js --compilers js:babel-register -r jsdom-global/register -r ./new-console-output/test/requireHelper.js"
   }
 }
--- a/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js
@@ -8,30 +8,32 @@
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,Web Console mixed content test";
 const TEST_HTTPS_URI = "https://example.com/browser/devtools/client/" +
                        "webconsole/test/test-bug-737873-mixedcontent.html";
 const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
                        "Mixed_content";
 
+registerCleanupFunction(function*() {
+  Services.prefs.clearUserPref("security.mixed_content.block_display_content");
+  Services.prefs.clearUserPref("security.mixed_content.block_active_content");
+});
+
 add_task(function* () {
   Services.prefs.setBoolPref("security.mixed_content.block_display_content",
                              false);
   Services.prefs.setBoolPref("security.mixed_content.block_active_content",
                              false);
 
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
   yield testMixedContent(hud);
-
-  Services.prefs.clearUserPref("security.mixed_content.block_display_content");
-  Services.prefs.clearUserPref("security.mixed_content.block_active_content");
 });
 
 var testMixedContent = Task.async(function* (hud) {
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_HTTPS_URI);
 
   let results = yield waitForMessages({
     webconsole: hud,
     messages: [{
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -586,16 +586,17 @@ WebConsoleFrame.prototype = {
       // XXX: We should actually stop output from happening on old output
       // panel, but for now let's just hide it.
       this.experimentalOutputNode = this.outputNode.cloneNode();
       this.experimentalOutputNode.removeAttribute("tabindex");
       this.outputNode.hidden = true;
       this.outputNode.parentNode.appendChild(this.experimentalOutputNode);
       // @TODO Once the toolbox has been converted to React, see if passing
       // in JSTerm is still necessary.
+
       this.newConsoleOutput = new this.window.NewConsoleOutput(
         this.experimentalOutputNode, this.jsterm, toolbox, this.owner);
       console.log("Created newConsoleOutput", this.newConsoleOutput);
 
       let filterToolbar = doc.querySelector(".hud-console-filter-toolbar");
       filterToolbar.hidden = true;
     }
 
@@ -3261,20 +3262,16 @@ WebConsoleConnectionProxy.prototype = {
     this.webConsoleFrame._onUpdateListeners();
   },
 
   /**
    * Dispatch a message add on the new frontend and emit an event for tests.
    */
   dispatchMessageAdd: function(packet) {
     this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
-    this.webConsoleFrame.emit("new-messages", new Set([{
-      response: packet,
-      node: this.webConsoleFrame.newConsoleOutput.getLastMessage(),
-    }]));
   },
 
   /**
    * Batched dispatch of messages.
    */
   dispatchMessagesAdd: function(packets) {
     this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
   },
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -1109,17 +1109,17 @@ const ThreadActor = ActorClassWithSpec(t
    * @param EventTarget eventTarget
    *        The target the event was dispatched on.
    * @returns Array
    */
   _getAllEventListeners: function (eventTarget) {
     let els = Cc["@mozilla.org/eventlistenerservice;1"]
                 .getService(Ci.nsIEventListenerService);
 
-    let targets = els.getEventTargetChainFor(eventTarget);
+    let targets = els.getEventTargetChainFor(eventTarget, true);
     let listeners = [];
 
     for (let target of targets) {
       let handlers = els.getListenerInfoFor(target);
       for (let handler of handlers) {
         // Null is returned for all-events handlers, and native event listeners
         // don't provide any listenerObject, which makes them not that useful to
         // a JS debugger.
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -801,60 +801,17 @@ nsIContent::PreHandleEvent(EventChainPre
       parent = aVisitor.mDestInsertionPoints.LastElement();
       aVisitor.mDestInsertionPoints.SetLength(
         aVisitor.mDestInsertionPoints.Length() - 1);
     }
   }
 
   ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
   if (thisShadowRoot) {
-    // The following events must always be stopped at the root node of the node tree:
-    //   abort
-    //   error
-    //   select
-    //   change
-    //   load
-    //   reset
-    //   resize
-    //   scroll
-    //   selectstart
-    bool stopEvent = false;
-    switch (aVisitor.mEvent->mMessage) {
-      case eImageAbort:
-      case eLoadError:
-      case eFormSelect:
-      case eFormChange:
-      case eLoad:
-      case eFormReset:
-      case eResize:
-      case eScroll:
-      case eSelectStart:
-        stopEvent = true;
-        break;
-      case eUnidentifiedEvent:
-        if (aVisitor.mDOMEvent) {
-          nsAutoString eventType;
-          aVisitor.mDOMEvent->GetType(eventType);
-          if (eventType.EqualsLiteral("abort") ||
-              eventType.EqualsLiteral("error") ||
-              eventType.EqualsLiteral("select") ||
-              eventType.EqualsLiteral("change") ||
-              eventType.EqualsLiteral("load") ||
-              eventType.EqualsLiteral("reset") ||
-              eventType.EqualsLiteral("resize") ||
-              eventType.EqualsLiteral("scroll")) {
-            stopEvent = true;
-          }
-        }
-        break;
-      default:
-        break;
-    }
-
-    if (stopEvent) {
+    if (!aVisitor.mEvent->mFlags.mComposed) {
       // If we do stop propagation, we still want to propagate
       // the event to chrome (nsPIDOMWindow::GetParentTarget()).
       // The load event is special in that we don't ever propagate it
       // to chrome.
       nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
       EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
         ? win->GetParentTarget() : nullptr;
 
@@ -902,17 +859,21 @@ nsIContent::PreHandleEvent(EventChainPre
     NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
                    aVisitor.mEventTargetAtParent != insertionParent),
                  "Retargeting and having insertion parent!");
     if (insertionParent) {
       parent = insertionParent;
     }
   }
 
-  if (parent) {
+  if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
+      IsRootOfNativeAnonymousSubtree() && OwnerDoc() &&
+      OwnerDoc()->GetWindow()) {
+    aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget();
+  } else if (parent) {
     aVisitor.mParentTarget = parent;
   } else {
     aVisitor.mParentTarget = GetComposedDoc();
   }
   return NS_OK;
 }
 
 bool
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -16702,16 +16702,17 @@ class CGEventMethod(CGNativeMember):
 
         self.body = fill(
             """
             RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
             bool trusted = e->Init(aOwner);
             e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
             $*{members}
             e->SetTrusted(trusted);
+            e->SetComposed(${eventInit}.mComposed);
             $*{holdJS}
             return e.forget();
             """,
             nativeType=self.descriptorProvider.nativeType.split('::')[-1],
             eventType=self.args[0].name,
             eventInit=self.args[1].name,
             members=members,
             holdJS=holdJS)
--- a/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothLeDeviceEvent.cpp
@@ -102,16 +102,17 @@ BluetoothLeDeviceEvent::Constructor(
                                          scanRecord.Data());
     if (!e->mScanRecord) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return nullptr;
     }
   }
 
   e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 BluetoothDevice*
 BluetoothLeDeviceEvent::GetDevice() const
 {
   return mDevice;
 }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2266,18 +2266,20 @@ CanvasRenderingContext2D::SetMozCurrentT
   if (ObjectToMatrix(aCx, aCurrentTransform, newCTM, aError) && newCTM.IsFinite()) {
     mTarget->SetTransform(newCTM);
   }
 }
 
 void
 CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* aCx,
                                                  JS::MutableHandle<JSObject*> aResult,
-                                                 ErrorResult& aError) const
-{
+                                                 ErrorResult& aError)
+{
+  EnsureTarget();
+
   MatrixToJSObject(aCx, mTarget ? mTarget->GetTransform() : Matrix(),
                    aResult, aError);
 }
 
 void
 CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* aCx,
                                                         JS::Handle<JSObject*> aCurrentTransform,
                                                         ErrorResult& aError)
@@ -2295,18 +2297,20 @@ CanvasRenderingContext2D::SetMozCurrentT
       mTarget->SetTransform(newCTMInverse);
     }
   }
 }
 
 void
 CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* aCx,
                                                         JS::MutableHandle<JSObject*> aResult,
-                                                        ErrorResult& aError) const
-{
+                                                        ErrorResult& aError)
+{
+  EnsureTarget();
+
   if (!mTarget) {
     MatrixToJSObject(aCx, Matrix(), aResult, aError);
     return;
   }
 
   Matrix ctm = mTarget->GetTransform();
 
   if (!ctm.Invert()) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -348,23 +348,23 @@ public:
   void Arc(double aX, double aY, double aRadius, double aStartAngle,
            double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError);
   void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
                double aRotation, double aStartAngle, double aEndAngle,
                bool aAnticlockwise, ErrorResult& aError);
 
   void GetMozCurrentTransform(JSContext* aCx,
                               JS::MutableHandle<JSObject*> aResult,
-                              mozilla::ErrorResult& aError) const;
+                              mozilla::ErrorResult& aError);
   void SetMozCurrentTransform(JSContext* aCx,
                               JS::Handle<JSObject*> aCurrentTransform,
                               mozilla::ErrorResult& aError);
   void GetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::MutableHandle<JSObject*> aResult,
-                                     mozilla::ErrorResult& aError) const;
+                                     mozilla::ErrorResult& aError);
   void SetMozCurrentTransformInverse(JSContext* aCx,
                                      JS::Handle<JSObject*> aCurrentTransform,
                                      mozilla::ErrorResult& aError);
   void GetFillRule(nsAString& aFillRule);
   void SetFillRule(const nsAString& aFillRule);
   void GetMozDash(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
                   mozilla::ErrorResult& aError);
   void SetMozDash(JSContext* aCx, const JS::Value& aMozDash,
--- a/dom/events/AnimationEvent.cpp
+++ b/dom/events/AnimationEvent.cpp
@@ -47,16 +47,17 @@ AnimationEvent::Constructor(const Global
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
 
   InternalAnimationEvent* internalEvent = e->mEvent->AsAnimationEvent();
   internalEvent->mAnimationName = aParam.mAnimationName;
   internalEvent->mElapsedTime = aParam.mElapsedTime;
   internalEvent->mPseudoElement = aParam.mPseudoElement;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 AnimationEvent::GetAnimationName(nsAString& aAnimationName)
 {
   aAnimationName = mEvent->AsAnimationEvent()->mAnimationName;
   return NS_OK;
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -79,16 +79,17 @@ ClipboardEvent::Constructor(const Global
                              Some(aGlobal.GetSubjectPrincipal()), aRv);
       NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData)
 {
   NS_IF_ADDREF(*aClipboardData = GetClipboardData());
   return NS_OK;
--- a/dom/events/CustomEvent.cpp
+++ b/dom/events/CustomEvent.cpp
@@ -55,16 +55,17 @@ CustomEvent::Constructor(const GlobalObj
                          ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<CustomEvent> e = new CustomEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   JS::Rooted<JS::Value> detail(aGlobal.Context(), aParam.mDetail);
   e->InitCustomEvent(aGlobal.Context(), aType, aParam.mBubbles, aParam.mCancelable, detail, aRv);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 JSObject*
 CustomEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::CustomEventBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/events/DeviceMotionEvent.cpp
+++ b/dom/events/DeviceMotionEvent.cpp
@@ -94,17 +94,17 @@ DeviceMotionEvent::Constructor(const Glo
 
   e->mRotationRate = new DeviceRotationRate(e,
     aEventInitDict.mRotationRate.mAlpha,
     aEventInitDict.mRotationRate.mBeta,
     aEventInitDict.mRotationRate.mGamma);
 
   e->mInterval = aEventInitDict.mInterval;
   e->SetTrusted(trusted);
-
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 /******************************************************************************
  * DeviceAcceleration
  *****************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DeviceAcceleration, mOwner)
--- a/dom/events/DragEvent.cpp
+++ b/dom/events/DragEvent.cpp
@@ -106,16 +106,17 @@ DragEvent::Constructor(const GlobalObjec
   e->InitDragEvent(aType, aParam.mBubbles, aParam.mCancelable,
                    aParam.mView, aParam.mDetail, aParam.mScreenX,
                    aParam.mScreenY, aParam.mClientX, aParam.mClientY,
                    aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey,
                    aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget,
                    aParam.mDataTransfer);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -410,16 +410,17 @@ Event::Constructor(const GlobalObject& a
                    const EventInit& aParam,
                    ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<Event> e = new Event(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 uint16_t
 Event::EventPhase() const
 {
   // Note, remember to check that this works also
   // if or when Bug 235441 is fixed.
@@ -562,21 +563,24 @@ Event::PreventDefaultInternal(bool aCall
 void
 Event::SetEventType(const nsAString& aEventTypeArg)
 {
   if (mIsMainThreadEvent) {
     mEvent->mSpecifiedEventTypeString.Truncate();
     mEvent->mSpecifiedEventType =
       nsContentUtils::GetEventMessageAndAtom(aEventTypeArg, mEvent->mClass,
                                              &(mEvent->mMessage));
+    mEvent->SetDefaultComposed();
   } else {
     mEvent->mSpecifiedEventType = nullptr;
     mEvent->mMessage = eUnidentifiedEvent;
     mEvent->mSpecifiedEventTypeString = aEventTypeArg;
+    mEvent->SetComposed(aEventTypeArg);
   }
+  mEvent->SetDefaultComposedInNativeAnonymousContent();
 }
 
 void
 Event::InitEvent(const nsAString& aEventTypeArg,
                  bool aCanBubbleArg,
                  bool aCancelableArg)
 {
   // Make sure this event isn't already being dispatched.
@@ -1162,16 +1166,17 @@ Event::Serialize(IPC::Message* aMsg, boo
 
   nsString type;
   GetType(type);
   IPC::WriteParam(aMsg, type);
 
   IPC::WriteParam(aMsg, Bubbles());
   IPC::WriteParam(aMsg, Cancelable());
   IPC::WriteParam(aMsg, IsTrusted());
+  IPC::WriteParam(aMsg, Composed());
 
   // No timestamp serialization for now!
 }
 
 NS_IMETHODIMP_(bool)
 Event::Deserialize(const IPC::Message* aMsg, PickleIterator* aIter)
 {
   nsString type;
@@ -1181,18 +1186,22 @@ Event::Deserialize(const IPC::Message* a
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &bubbles), false);
 
   bool cancelable = false;
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &cancelable), false);
 
   bool trusted = false;
   NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &trusted), false);
 
+  bool composed = false;
+  NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &composed), false);
+
   InitEvent(type, bubbles, cancelable);
   SetTrusted(trusted);
+  SetComposed(composed);
 
   return true;
 }
 
 NS_IMETHODIMP_(void)
 Event::SetOwner(mozilla::dom::EventTarget* aOwner)
 {
   mOwner = nullptr;
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -165,16 +165,21 @@ public:
     return mEvent->mFlags.mBubbles;
   }
 
   bool Cancelable() const
   {
     return mEvent->mFlags.mCancelable;
   }
 
+  bool Composed() const
+  {
+    return mEvent->mFlags.mComposed;
+  }
+
   // xpidl implementation
   // void PreventDefault();
 
   // You MUST NOT call PreventDefaultJ(JSContext*) from C++ code.  A call of
   // this method always sets Event.defaultPrevented true for web contents.
   // If default action handler calls this, web applications meet wrong
   // defaultPrevented value.
   virtual void PreventDefault(JSContext* aCx);
@@ -269,16 +274,21 @@ protected:
   }
 
   /**
    * IsChrome() returns true if aCx is chrome context or the event is created
    * in chrome's thread.  Otherwise, false.
    */
   bool IsChrome(JSContext* aCx) const;
 
+  void SetComposed(bool aComposed)
+  {
+    mEvent->SetComposed(aComposed);
+  }
+
   mozilla::WidgetEvent*       mEvent;
   RefPtr<nsPresContext>     mPresContext;
   nsCOMPtr<EventTarget>       mExplicitOriginalTarget;
   nsCOMPtr<nsIGlobalObject>   mOwner;
   bool                        mEventIsInternal;
   bool                        mPrivateDataDuplicated;
   bool                        mIsMainThreadEvent;
   // True when popup control check should rely on event.type, not
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -215,23 +215,25 @@ EventListenerService::GetListenerInfoFor
     NS_ADDREF((*aOutArray)[i] = listenerInfos[i]);
   }
   *aCount = count;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
+                                             bool aComposed,
                                              uint32_t* aCount,
                                              nsIDOMEventTarget*** aOutArray)
 {
   *aCount = 0;
   *aOutArray = nullptr;
   NS_ENSURE_ARG(aEventTarget);
   WidgetEvent event(true, eVoidEvent);
+  event.SetComposed(aComposed);
   nsTArray<EventTarget*> targets;
   nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event,
                                           nullptr, nullptr, nullptr, &targets);
   NS_ENSURE_SUCCESS(rv, rv);
   int32_t count = targets.Length();
   if (count == 0) {
     return NS_OK;
   }
--- a/dom/events/FocusEvent.cpp
+++ b/dom/events/FocusEvent.cpp
@@ -60,16 +60,17 @@ FocusEvent::Constructor(const GlobalObje
                         ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<FocusEvent> e = new FocusEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitFocusEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                     aParam.mDetail, aParam.mRelatedTarget);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/InputEvent.cpp
+++ b/dom/events/InputEvent.cpp
@@ -51,16 +51,17 @@ InputEvent::Constructor(const GlobalObje
   RefPtr<InputEvent> e = new InputEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   auto* view = aParam.mView ? aParam.mView->AsInner() : nullptr;
   e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, view,
                  aParam.mDetail);
   InternalEditorInputEvent* internalEvent = e->mEvent->AsEditorInputEvent();
   internalEvent->mIsComposing = aParam.mIsComposing;
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -182,17 +182,17 @@ MouseEvent::Constructor(const GlobalObje
   bool trusted = e->Init(t);
   e->InitMouseEvent(aType, aParam.mBubbles, aParam.mCancelable,
                     aParam.mView, aParam.mDetail, aParam.mScreenX,
                     aParam.mScreenY, aParam.mClientX, aParam.mClientY,
                     aParam.mCtrlKey, aParam.mAltKey, aParam.mShiftKey,
                     aParam.mMetaKey, aParam.mButton, aParam.mRelatedTarget);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
-
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 void
 MouseEvent::InitNSMouseEvent(const nsAString& aType,
                              bool aCanBubble,
                              bool aCancelable,
                              nsGlobalWindow* aView,
--- a/dom/events/PointerEvent.cpp
+++ b/dom/events/PointerEvent.cpp
@@ -95,16 +95,17 @@ PointerEvent::Constructor(EventTarget* a
   widgetEvent->pressure = aParam.mPressure;
   widgetEvent->tiltX = aParam.mTiltX;
   widgetEvent->tiltY = aParam.mTiltY;
   widgetEvent->inputSource = ConvertStringToPointerType(aParam.mPointerType);
   widgetEvent->mIsPrimary = aParam.mIsPrimary;
   widgetEvent->buttons = aParam.mButtons;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 // static
 already_AddRefed<PointerEvent>
 PointerEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const PointerEventInit& aParam,
--- a/dom/events/SpeechRecognitionError.cpp
+++ b/dom/events/SpeechRecognitionError.cpp
@@ -26,16 +26,17 @@ SpeechRecognitionError::Constructor(cons
                                     const SpeechRecognitionErrorInit& aParam,
                                     ErrorResult& aRv)
 {
   nsCOMPtr<mozilla::dom::EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<SpeechRecognitionError> e = new SpeechRecognitionError(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitSpeechRecognitionError(aType, aParam.mBubbles, aParam.mCancelable, aParam.mError, aParam.mMessage);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 void
 SpeechRecognitionError::InitSpeechRecognitionError(const nsAString& aType,
                                                    bool aCanBubble,
                                                    bool aCancelable,
                                                    SpeechRecognitionErrorCode aError,
--- a/dom/events/StorageEvent.cpp
+++ b/dom/events/StorageEvent.cpp
@@ -60,17 +60,17 @@ StorageEvent::Constructor(EventTarget* a
   bool trusted = e->Init(aOwner);
   e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
   e->mKey = aEventInitDict.mKey;
   e->mOldValue = aEventInitDict.mOldValue;
   e->mNewValue = aEventInitDict.mNewValue;
   e->mUrl = aEventInitDict.mUrl;
   e->mStorageArea = aEventInitDict.mStorageArea;
   e->SetTrusted(trusted);
-
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 already_AddRefed<StorageEvent>
 StorageEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const StorageEventInit& aEventInitDict,
                           ErrorResult& aRv)
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -240,16 +240,17 @@ TouchEvent::Constructor(const GlobalObje
   RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
   RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
   RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
   e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                     aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
                     aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
                     changedTouches);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 
 already_AddRefed<TouchList>
 TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches)
 {
   RefPtr<TouchList> list = new TouchList(GetParentObject());
--- a/dom/events/TransitionEvent.cpp
+++ b/dom/events/TransitionEvent.cpp
@@ -47,16 +47,17 @@ TransitionEvent::Constructor(const Globa
   e->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
 
   InternalTransitionEvent* internalEvent = e->mEvent->AsTransitionEvent();
   internalEvent->mPropertyName = aParam.mPropertyName;
   internalEvent->mElapsedTime = aParam.mElapsedTime;
   internalEvent->mPseudoElement = aParam.mPseudoElement;
 
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMETHODIMP
 TransitionEvent::GetPropertyName(nsAString& aPropertyName)
 {
   aPropertyName = mEvent->AsTransitionEvent()->mPropertyName;
   return NS_OK;
--- a/dom/events/UIEvent.cpp
+++ b/dom/events/UIEvent.cpp
@@ -85,16 +85,17 @@ UIEvent::Constructor(const GlobalObject&
                      ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<UIEvent> e = new UIEvent(t, nullptr, nullptr);
   bool trusted = e->Init(t);
   e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                  aParam.mDetail);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(UIEvent, Event,
                                    mView)
 
 NS_IMPL_ADDREF_INHERITED(UIEvent, Event)
 NS_IMPL_RELEASE_INHERITED(UIEvent, Event)
--- a/dom/events/WheelEvent.cpp
+++ b/dom/events/WheelEvent.cpp
@@ -120,16 +120,17 @@ WheelEvent::Constructor(const GlobalObje
                     aParam.mView, aParam.mDetail,
                     aParam.mScreenX, aParam.mScreenY,
                     aParam.mClientX, aParam.mClientY,
                     aParam.mButton, aParam.mRelatedTarget,
                     EmptyString(), aParam.mDeltaX,
                     aParam.mDeltaY, aParam.mDeltaZ, aParam.mDeltaMode);
   e->InitializeExtraMouseEventDictionaryMembers(aParam);
   e->SetTrusted(trusted);
+  e->SetComposed(aParam.mComposed);
   return e.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/dom/events/nsIEventListenerService.idl
+++ b/dom/events/nsIEventListenerService.idl
@@ -73,16 +73,17 @@ interface nsIEventListenerService : nsIS
    * Returns an array of event targets.
    * aEventTarget will be at index 0.
    * The objects are the ones that would be used as DOMEvent.currentTarget while
    * dispatching an event to aEventTarget
    * @note Some events, especially 'load', may actually have a shorter
    *       event target chain than what this methods returns.
    */
   void getEventTargetChainFor(in nsIDOMEventTarget aEventTarget,
+                              in boolean composed,
                               [optional] out unsigned long aCount,
                               [retval, array, size_is(aCount)] out
                                 nsIDOMEventTarget aOutArray);
 
   /**
    * Returns true if a event target has any listener for the given type.
    */
   boolean hasListenersFor(in nsIDOMEventTarget aEventTarget,
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -30,17 +30,16 @@ support-files =
   support-files = pointerevent_lostpointercapture_is_first-manual.html
 [test_pointerevent_multiple_primary_pointers_boundary_events-manual.html]
   support-files = pointerevent_multiple_primary_pointers_boundary_events-manual.html
   disabled = should be investigated
 [test_pointerevent_pointercancel_touch-manual.html]
   support-files = pointerevent_pointercancel_touch-manual.html
 [test_pointerevent_pointerdown-manual.html]
   support-files = pointerevent_pointerdown-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerenter_does_not_bubble-manual.html]
   support-files = pointerevent_pointerenter_does_not_bubble-manual.html
 [test_pointerevent_pointerenter_nohover-manual.html]
   support-files = pointerevent_pointerenter_nohover-manual.html
 [test_pointerevent_pointerId_scope-manual.html]
   support-files =
     test_pointerevent_pointerId_scope-manual.html
     ./resources/pointerevent_pointerId_scope-iframe.html
@@ -86,17 +85,16 @@ support-files =
 [test_pointerevent_pointertype_mouse-manual.html]
   support-files = pointerevent_pointertype_mouse-manual.html
 [test_pointerevent_pointertype_pen-manual.html]
   support-files = pointerevent_pointertype_pen-manual.html
 [test_pointerevent_pointertype_touch-manual.html]
   support-files = pointerevent_pointertype_touch-manual.html
 [test_pointerevent_pointerup-manual.html]
   support-files = pointerevent_pointerup-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointerup_isprimary_same_as_pointerdown-manual.html
 [test_pointerevent_pointerup_pointertype-manual.html]
   support-files = pointerevent_pointerup_pointertype-manual.html
 [test_pointerevent_releasepointercapture_events_to_original_target-manual.html]
   support-files = pointerevent_releasepointercapture_events_to_original_target-manual.html
 [test_pointerevent_releasepointercapture_invalid_pointerid-manual.html]
   support-files = pointerevent_releasepointercapture_invalid_pointerid-manual.html
--- a/dom/events/test/test_bug448602.html
+++ b/dom/events/test/test_bug448602.html
@@ -89,17 +89,17 @@ function runTests() {
   is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)");
   is(SpecialPowers.unwrap(infos[0].listenerObject), l,
      "Should have the right listener object (4)");
 
   // Event target chain tests
   l2 = document.getElementById("testlevel2");
   l3 = document.getElementById("testlevel3");
   var textnode = l3.firstChild;
-  var chain = els.getEventTargetChainFor(textnode, {});
+  var chain = els.getEventTargetChainFor(textnode, true, {});
   ok(chain.length > 3, "Too short event target chain.");
   ok(SpecialPowers.compare(chain[0], textnode), "Wrong chain item (1)");
   ok(SpecialPowers.compare(chain[1], l3), "Wrong chain item (2)");
   ok(SpecialPowers.compare(chain[2], l2), "Wrong chain item (3)");
   ok(SpecialPowers.compare(chain[3], root), "Wrong chain item (4)");
 
   var hasDocumentInChain = false;
   var hasWindowInChain = false;
@@ -162,24 +162,24 @@ function testAllListener() {
     if (infos[i].type == null) {
       ++nullTypes;
     }
   }
   is(nullTypes, 1, "Should have one all-event-listener!");
 
   els.addListenerForAllEvents(root, allListener, false, true, true);
   els.addListenerForAllEvents(root, allListenerTrustedOnly, false, false, true);
-  l3.dispatchEvent(new Event("testevent", { bubbles: true }));
-  dispatchTrusted(l3, { bubbles: true });
+  l3.dispatchEvent(new Event("testevent", { bubbles: true, composed: true }));
+  dispatchTrusted(l3, { bubbles: true, composed: true });
   els.removeListenerForAllEvents(root, allListener, false);
   els.removeListenerForAllEvents(root, allListener, false, true);
   els.removeListenerForAllEvents(root, allListenerTrustedOnly, false, true);
   // make sure removeListenerForAllEvents works.
-  l3.dispatchEvent(new Event("testevent", { bubbles: true }));
-  dispatchTrusted(l3, { bubbles: true });
+  l3.dispatchEvent(new Event("testevent", { bubbles: true, composed : true }));
+  dispatchTrusted(l3, { bubbles: true, composed: true });
 
   // Test the order of event listeners.
   var clickListenerCalled = false;
   var allListenerCalled = false;
   function clickListener() {
     clickListenerCalled = true;
     ok(allListenerCalled, "Should have called '*' listener before normal listener!");
   }
--- a/dom/media/eme/MediaKeyMessageEvent.cpp
+++ b/dom/media/eme/MediaKeyMessageEvent.cpp
@@ -95,16 +95,17 @@ MediaKeyMessageEvent::Constructor(const 
   }
   e->mMessage = ArrayBuffer::Create(aGlobal.Context(), length, data);
   if (!e->mMessage) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
   e->mMessageType = aEventInitDict.mMessageType;
   e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
   return e.forget();
 }
 
 void
 MediaKeyMessageEvent::GetMessage(JSContext* cx,
                                  JS::MutableHandle<JSObject*> aMessage,
                                  ErrorResult& aRv)
 {
--- a/dom/notification/NotificationEvent.h
+++ b/dom/notification/NotificationEvent.h
@@ -38,16 +38,17 @@ public:
               const nsAString& aType,
               const NotificationEventInit& aOptions,
               ErrorResult& aRv)
   {
     RefPtr<NotificationEvent> e = new NotificationEvent(aOwner);
     bool trusted = e->Init(aOwner);
     e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
     e->SetTrusted(trusted);
+    e->SetComposed(aOptions.mComposed);
     e->mNotification = aOptions.mNotification;
     e->SetWantsPopupControlCheck(e->IsTrusted());
     return e.forget();
   }
 
   static already_AddRefed<NotificationEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
--- a/dom/tests/mochitest/webcomponents/test_event_dispatch.html
+++ b/dom/tests/mochitest/webcomponents/test_event_dispatch.html
@@ -22,17 +22,17 @@ function eventListener(e) {
 function isEventChain(actual, expected, msg) {
   is(actual.length, expected.length, msg);
   for (var i = 0; i < expected.length; i++) {
     is(actual[i], expected[i], msg + " at " + i);
   }
 
   // Check to make sure the event chain matches what we get back from nsIEventListenerService.getEventTargetChainFor
   if (0 < actual.length) {
-    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    var chain = els.getEventTargetChainFor(actual[0], true); // Events should be dispatched on actual[0].
     for (var i = 0; i < expected.length; i++) {
       ok(SpecialPowers.compare(chain[i], expected[i]), msg + " at " + i + " for nsIEventListenerService");
     }
   }
 }
 
 /*
  * Test 1: Test of event dispatch through a basic ShadowRoot with content a insertion point.
@@ -59,17 +59,17 @@ elemFour.addEventListener("custom", even
 var shadowOne = elemOne.createShadowRoot();
 shadowOne.addEventListener("custom", eventListener);
 
 elemThree.appendChild(elemFour);
 shadowOne.appendChild(elemThree);
 elemOne.appendChild(elemTwo);
 
 var eventChain = [];
-var customEvent = new CustomEvent("custom", { "bubbles" : true });
+var customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemFour, elemThree, shadowOne, elemOne], "Event path for test 1 for event dispatched on elemTwo.");
 
 /*
  * Test 2: Test of event dispatch through a nested ShadowRoots with content insertion points.
  *
  * <div elemFive> --- <shadow-root shadowTwo>
  *       |                       |
@@ -106,17 +106,17 @@ shadowTwo.addEventListener("custom", eve
 
 elemFive.appendChild(elemOne);
 shadowTwo.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowOne.appendChild(elemSix);
 elemSix.appendChild(elemThree);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemOne.dispatchEvent(customEvent);
 is(elemOne.getDestinationInsertionPoints().length, 2, "yes");
 isEventChain(eventChain, [elemOne, elemThree, elemSix, shadowOne, elemTwo, elemFour, shadowTwo, elemFive], "Event path for test 2 for event dispatched on elemOne.");
 
 /*
  * Test 3: Test of event dispatch through nested ShadowRoot with content insertion points.
  *
  * <div elemOne> ------- <shadow-root shadowOne>
@@ -154,17 +154,17 @@ shadowTwo.addEventListener("custom", eve
 
 elemOne.appendChild(elemTwo);
 shadowOne.appendChild(elemThree);
 elemThree.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSix);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemFive, elemFour, elemSix, shadowTwo, elemThree, shadowOne, elemOne], "Event path for test 3 for event dispatched on elemTwo.");
 
 /*
  * Test 4: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
  *
  * <div elemSeven> --- <shadow-root shadowTwo> (younger ShadowRoot)
  *       |         |             |
@@ -218,22 +218,22 @@ elemSeven.appendChild(elemOne);
 shadowTwo.appendChild(elemSix);
 elemSix.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowThree.appendChild(elemEight);
 shadowThree.appendChild(elemThree);
 shadowOne.appendChild(elemFive);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemOne.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemOne, elemFive, shadowOne, elemThree, shadowThree, elemTwo, elemFour, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemOne.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemEight.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemEight, elemFive, shadowOne, elemSix, shadowTwo, elemSeven], "Event path for test 4 for event dispatched on elemEight.");
 
 /*
  * Test 5: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>
  *    |      |                        |
@@ -276,22 +276,22 @@ shadowOne.addEventListener("custom", eve
 elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemSix);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSeven, shadowTwo, elemSix, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 5 for event dispatched on elemThree.");
 
 /*
  * Test 6: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>;
  *    |      |                        |
@@ -334,22 +334,22 @@ shadowOne.addEventListener("custom", eve
 elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemSix);
 shadowTwo.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSix, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 6 for event dispatched on elemThree.");
 
 /*
  * Test 7: Test of event dispatch through nested shadowroot with insertion points that match specific tags.
  *
  * <div elemOne> --------- <shadow-root shadowOne>
  *    |      |                        |
@@ -398,22 +398,22 @@ elemOne.appendChild(elemTwo);
 elemOne.appendChild(elemThree);
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemFive);
 shadowTwo.appendChild(elemEight);
 elemEight.appendChild(elemSix);
 elemEight.appendChild(elemSeven);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemTwo.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemTwo, elemSix, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemTwo.");
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, elemSeven, elemEight, shadowTwo, elemFive, elemFour, shadowOne, elemOne], "Event path for test 7 for event dispatched on elemThree.");
 
 /*
  * Test 8: Test of event dispatch through host with multiple ShadowRoots with shadow insertion point.
  *
  * <div elemOne> --- <shadow-root shadowOne> (younger ShadowRoot)
  *               |             |
@@ -444,15 +444,15 @@ shadowTwo.addEventListener("custom", eve
 shadowOne = elemOne.createShadowRoot();
 shadowOne.addEventListener("custom", eventListener);
 
 shadowOne.appendChild(elemFour);
 elemFour.appendChild(elemTwo);
 shadowTwo.appendChild(elemThree);
 
 eventChain = [];
-customEvent = new CustomEvent("custom", { "bubbles" : true });
+customEvent = new CustomEvent("custom", { "bubbles" : true, "composed" : true });
 elemThree.dispatchEvent(customEvent);
 isEventChain(eventChain, [elemThree, shadowTwo, elemTwo, elemFour, shadowOne, elemOne], "Event path for test 8 for event dispatched on elemThree.");
 
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
+++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
@@ -21,17 +21,17 @@ function eventListener(e) {
 
 function isEventChain(actual, expected, msg) {
   is(actual.length, expected.length, msg);
   for (var i = 0; i < expected.length; i++) {
     is(actual[i], expected[i], msg + " at " + i);
   }
 
   if (0 < actual.length) {
-    var chain = els.getEventTargetChainFor(actual[0]); // Events should be dispatched on actual[0].
+    var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
     ok(expected.length < chain.length, "There should be additional chrome event targets.");
   }
 }
 
 /*
  * <div elemOne> ------ <shadow-root shadowOne>
  *                                 |
  *                          <span elemTwo>
--- a/dom/webidl/Event.webidl
+++ b/dom/webidl/Event.webidl
@@ -36,16 +36,18 @@ interface Event {
   readonly attribute boolean cancelable;
   void preventDefault();
   [Pure]
   readonly attribute boolean defaultPrevented;
   [ChromeOnly, Pure]
   readonly attribute boolean defaultPreventedByChrome;
   [ChromeOnly, Pure]
   readonly attribute boolean defaultPreventedByContent;
+  [Pure]
+  readonly attribute boolean composed;
 
   [Unforgeable, Pure]
   readonly attribute boolean isTrusted;
   [Pure]
   readonly attribute DOMHighResTimeStamp timeStamp;
 
   void initEvent(DOMString type, boolean bubbles, boolean cancelable);
 };
@@ -64,9 +66,10 @@ partial interface Event {
   [ChromeOnly] readonly attribute boolean isSynthesized;
 
   boolean getPreventDefault();
 };
 
 dictionary EventInit {
   boolean bubbles = false;
   boolean cancelable = false;
+  boolean composed = false;
 };
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -144,16 +144,17 @@ FetchEvent::Constructor(const GlobalObje
                         ErrorResult& aRv)
 {
   RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports());
   MOZ_ASSERT(owner);
   RefPtr<FetchEvent> e = new FetchEvent(owner);
   bool trusted = e->Init(owner);
   e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aOptions.mComposed);
   e->mRequest = aOptions.mRequest;
   e->mClientId = aOptions.mClientId;
   e->mIsReload = aOptions.mIsReload;
   return e.forget();
 }
 
 namespace {
 
@@ -1136,16 +1137,17 @@ PushEvent::Constructor(mozilla::dom::Eve
                        const nsAString& aType,
                        const PushEventInit& aOptions,
                        ErrorResult& aRv)
 {
   RefPtr<PushEvent> e = new PushEvent(aOwner);
   bool trusted = e->Init(aOwner);
   e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
   e->SetTrusted(trusted);
+  e->SetComposed(aOptions.mComposed);
   if(aOptions.mData.WasPassed()){
     nsTArray<uint8_t> bytes;
     nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return nullptr;
     }
     e->mData = new PushMessageData(aOwner, Move(bytes));
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -72,16 +72,17 @@ public:
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
               const EventInit& aOptions)
   {
     RefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner);
     bool trusted = e->Init(aOwner);
     e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
     e->SetTrusted(trusted);
+    e->SetComposed(aOptions.mComposed);
     return e.forget();
   }
 
   static already_AddRefed<ExtendableEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const EventInit& aOptions,
               ErrorResult& aRv)
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,15 +1,16 @@
 gfx/harfbuzz status as of 2016-08-21:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
 Current version: 1.3.0 + recent fixes from trunk,
-up to 547ddb0721365dca985aef5b759d08718f7c5f82.
+up to 547ddb0721365dca985aef5b759d08718f7c5f82
++ the relevant part of https://github.com/behdad/harfbuzz/pull/334.
 
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
--- a/gfx/harfbuzz/src/hb-glib.cc
+++ b/gfx/harfbuzz/src/hb-glib.cc
@@ -377,24 +377,26 @@ hb_glib_get_unicode_funcs (void)
       HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
 #undef HB_UNICODE_FUNC_IMPLEMENT
     }
   };
 
   return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
 }
 
+#if GLIB_CHECK_VERSION(2,31,10)
 /**
  * hb_glib_blob_create:
  *
  * Since: 0.9.38
  **/
 hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes)
 {
   gsize size = 0;
   gconstpointer data = g_bytes_get_data (gbytes, &size);
   return hb_blob_create ((const char *) data,
 			 size,
 			 HB_MEMORY_MODE_READONLY,
 			 g_bytes_ref (gbytes),
 			 (hb_destroy_func_t) g_bytes_unref);
 }
+#endif
--- a/gfx/harfbuzz/src/hb-glib.h
+++ b/gfx/harfbuzz/src/hb-glib.h
@@ -41,15 +41,16 @@ hb_glib_script_to_script (GUnicodeScript
 
 HB_EXTERN GUnicodeScript
 hb_glib_script_from_script (hb_script_t script);
 
 
 HB_EXTERN hb_unicode_funcs_t *
 hb_glib_get_unicode_funcs (void);
 
+#if GLIB_CHECK_VERSION(2,31,10)
 HB_EXTERN hb_blob_t *
 hb_glib_blob_create (GBytes *gbytes);
-
+#endif
 
 HB_END_DECLS
 
 #endif /* HB_GLIB_H */
--- a/gfx/harfbuzz/src/moz.build
+++ b/gfx/harfbuzz/src/moz.build
@@ -54,16 +54,25 @@ UNIFIED_SOURCES += [
     'hb-ot-tag.cc',
     'hb-set.cc',
     'hb-shape.cc',
     'hb-shaper.cc',
     'hb-unicode.cc',
     'hb-warning.cc',
 ]
 
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+    EXPORTS.harfbuzz += [
+        'hb-glib.h',
+    ]
+    UNIFIED_SOURCES += [
+        'hb-glib.cc',
+    ]
+    CXXFLAGS += CONFIG['GLIB_CFLAGS']
+
 # We allow warnings for third-party code that can be updated from upstream.
 ALLOW_COMPILER_WARNINGS = True
 
 FINAL_LIBRARY = 'gkmedias'
 
 DEFINES['PACKAGE_VERSION'] = '"moz"'
 DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"'
 DEFINES['HAVE_OT'] = 1
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -649,17 +649,19 @@ RotatedContentBuffer::BeginPaint(Painted
           }
 
           if (!result.mDidSelfCopy) {
             destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
             CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
                          &destDTBuffer, &destDTBufferOnWhite);
             if (!destDTBuffer ||
                 (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-              gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+              if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+                gfxCriticalNote << "Failed 1 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+              }
               return result;
             }
           }
         }
       } else {
         mBufferRect = destBufferRect;
         mBufferRotation = newRotation;
       }
@@ -671,17 +673,19 @@ RotatedContentBuffer::BeginPaint(Painted
       mBufferRotation = IntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
     CreateBuffer(result.mContentType, destBufferRect, bufferFlags,
                  &destDTBuffer, &destDTBufferOnWhite);
     if (!destDTBuffer ||
         (!destDTBufferOnWhite && (bufferFlags & BUFFER_COMPONENT_ALPHA))) {
-      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height)))) << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+      if (Factory::ReasonableSurfaceSize(IntSize(destBufferRect.width, destBufferRect.height))) {
+        gfxCriticalNote << "Failed 2 buffer db=" << hexa(destDTBuffer.get()) << " dw=" << hexa(destDTBufferOnWhite.get()) << " for " << destBufferRect.x << ", " << destBufferRect.y << ", " << destBufferRect.width << ", " << destBufferRect.height;
+      }
       return result;
     }
   }
 
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || destBufferRect == neededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
   // If we have no buffered data already, then destBuffer will be a fresh buffer
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1299195.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width; initial-scale=1.0">
+  <title>Test pointer events are dispatched once for touch tap</title>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript">
+    /** Test for Bug 1299195 **/
+    function runTests() {
+      let target0 = document.getElementById("target0");
+      let mouseup_count = 0;
+      let mousedown_count = 0;
+      let pointerup_count = 0;
+      let pointerdown_count = 0;
+
+      target0.addEventListener("mouseup", () => {
+        ++mouseup_count;
+        if (mouseup_count == 2) {
+          is(mousedown_count, 2, "Double tap with touch should fire 2 mousedown events");
+          is(mouseup_count, 2, "Double tap with touch should fire 2 mouseup events");
+          is(pointerdown_count, 2, "Double tap with touch should fire 2 pointerdown events");
+          is(pointerup_count, 2, "Double tap with touch should fire 2 pointerup events");
+          subtestDone();
+        }
+      });
+      target0.addEventListener("mousedown", () => {
+        ++mousedown_count;
+      });
+      target0.addEventListener("pointerup", () => {
+        ++pointerup_count;
+      });
+      target0.addEventListener("pointerdown", () => {
+        ++pointerdown_count;
+      });
+      synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+      synthesizeNativeTap(document.getElementById('target0'), 100, 100);
+    }
+    waitUntilApzStable().then(runTests);
+    </script>
+</head>
+<body>
+  <div id="target0" style="width: 200px; height: 200px; background: green"></div>
+</body>
+</html>
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -24,16 +24,17 @@ support-files =
   helper_scroll_inactive_perspective.html
   helper_scroll_inactive_zindex.html
   helper_bug1280013.html
   helper_tall.html
   helper_drag_scroll.html
   helper_touch_action_complex.html
   helper_tap_fullzoom.html
   helper_bug1162771.html
+  helper_bug1299195.html
 tags = apz
 [test_bug982141.html]
 [test_bug1151663.html]
 [test_bug1277814.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_scroll.html]
 skip-if = (os == 'android') || (os == 'b2g') || (buildapp == 'mulet') # wheel events not supported on mobile; see bug 1164274 for mulet
 [test_wheel_transactions.html]
@@ -58,12 +59,12 @@ skip-if = (toolkit == 'android') # wheel
 [test_group_mouseevents.html]
 skip-if = (toolkit == 'android') # mouse events not supported on mobile
 [test_touch_listeners_impacting_wheel.html]
 skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
 [test_bug1253683.html]
 skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile
 [test_group_zoom.html]
 skip-if = (toolkit != 'android') # only android supports zoom
-[test_bug1285070.html]
+[test_group_pointerevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
 # On OS X we don't support touch events at all.
 skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
rename from gfx/layers/apz/test/mochitest/test_bug1285070.html
rename to gfx/layers/apz/test/mochitest/test_group_pointerevents.html
--- a/gfx/layers/apz/test/mochitest/test_bug1285070.html
+++ b/gfx/layers/apz/test/mochitest/test_group_pointerevents.html
@@ -7,17 +7,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 1285070</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   var subtests = [
-    {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
+    {'file': 'helper_bug1285070.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]},
+    {'file': 'helper_bug1299195.html', 'prefs': [["dom.w3c_pointer_events.enabled", true]]}
   ];
 
   if (isApzEnabled()) {
     SimpleTest.waitForExplicitFinish();
     window.onload = function() {
       runSubtestsSeriallyInFreshWindows(subtests)
       .then(SimpleTest.finish);
     };
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -184,16 +184,22 @@ ClientLayerManager::CreateReadbackLayer(
 {
   RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
   return layer.forget();
 }
 
 void
 ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
+  MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
+  if (!mForwarder->IPCOpen()) {
+    gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
+    return;
+  }
+
   mInTransaction = true;
   mTransactionStart = TimeStamp::Now();
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("[----- BeginTransaction"));
   Log();
 #endif
 
@@ -268,22 +274,16 @@ bool
 ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData,
                                            EndTransactionFlags)
 {
   PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
     js::ProfileEntry::Category::GRAPHICS);
 
   if (!mForwarder || !mForwarder->IPCOpen()) {
-    gfxCriticalError() << "LayerManager::EndTransaction while IPC is dead.";
-    // Pointless to try to render since the content cannot be sent to the
-    // compositor. We should not get here in the first place but I suspect
-    // This is happening during shutdown, tab-switch or some other scenario
-    // where we already started tearing the resources down but something
-    // triggered painting anyway.
     return false;
   }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   MOZ_LAYERS_LOG(("  ----- (beginning paint)"));
   Log();
 #endif
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -397,18 +397,19 @@ public:
 // our layer tree to a parent process.  Record the new layer creation
 // in the current open transaction as a side effect.
 template<typename CreatedMethod> void
 CreateShadowFor(ClientLayer* aLayer,
                 ClientLayerManager* aMgr,
                 CreatedMethod aMethod)
 {
   PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
-  // XXX error handling
-  MOZ_ASSERT(shadow, "failed to create shadow");
+  if (!shadow) {
+    return;
+  }
 
   aLayer->SetShadow(shadow);
   (aMgr->AsShadowForwarder()->*aMethod)(aLayer);
   aMgr->Hold(aLayer->AsLayer());
 }
 
 #define CREATE_SHADOW(_type)                                       \
   CreateShadowFor(layer, this,                                     \
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1066,17 +1066,16 @@ TextureClient::CreateForDrawing(TextureF
                                 int32_t aMaxTextureSize,
                                 BackendSelector aSelector,
                                 TextureFlags aTextureFlags,
                                 TextureAllocationFlags aAllocFlags)
 {
   gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector);
 
   // also test the validity of aAllocator
-  MOZ_ASSERT(aAllocator && aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
     return nullptr;
   }
 
@@ -1163,17 +1162,16 @@ TextureClient::CreateForDrawing(TextureF
 already_AddRefed<TextureClient>
 TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
                                  gfx::SourceSurface* aSurface,
                                  BackendSelector aSelector,
                                  TextureFlags aTextureFlags,
                                  TextureAllocationFlags aAllocFlags)
 {
   // also test the validity of aAllocator
-  MOZ_ASSERT(aAllocator && aAllocator->GetTextureForwarder()->IPCOpen());
   if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
     return nullptr;
   }
 
   gfx::IntSize size = aSurface->GetSize();
 
   if (!gfx::Factory::AllowedSurfaceSize(size)) {
     return nullptr;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -687,17 +687,19 @@ ShadowLayerForwarder::EndTransaction(Inf
 
     MOZ_LAYERS_LOG(("[LayersForwarder] OpSetLayerAttributes(%p)\n", mutant));
 
     mTxn->AddEdit(OpSetLayerAttributes(nullptr, Shadow(shadow), attrs));
   }
 
   AutoTArray<Edit, 10> cset;
   size_t nCsets = mTxn->mCset.size() + mTxn->mPaints.size();
-  MOZ_ASSERT(nCsets > 0 || mTxn->RotationChanged(), "should have bailed by now");
+  if (nCsets == 0 && !mTxn->RotationChanged()) {
+    return true;
+  }
 
   cset.SetCapacity(nCsets);
   if (!mTxn->mCset.empty()) {
     cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size());
   }
   // Paints after non-paint ops, including attribute changes.  See
   // above.
   if (!mTxn->mPaints.empty()) {
--- a/gfx/thebes/gfxFontconfigFonts.cpp
+++ b/gfx/thebes/gfxFontconfigFonts.cpp
@@ -11,16 +11,17 @@
 #include "gfxContext.h"
 #ifdef MOZ_WIDGET_GTK
 #include "gfxPlatformGtk.h"
 #endif
 #include "gfxFontconfigFonts.h"
 #include "gfxFT2FontBase.h"
 #include "gfxFT2Utils.h"
 #include "harfbuzz/hb.h"
+#include "harfbuzz/hb-glib.h"
 #include "harfbuzz/hb-ot.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeScriptCodes.h"
 #include "gfxFontconfigUtils.h"
 #include "gfxUserFontSet.h"
 #include "gfxFontConstants.h"
 #include "nsGkAtoms.h"
 #include "nsILanguageAtomService.h"
@@ -1618,17 +1619,17 @@ gfxPangoFontGroup::FindFontForChar(uint3
     }
 
     // Our MOZ_SCRIPT_* codes may not match the PangoScript enumeration values
     // (if we're using ICU's codes), so convert by mapping through ISO 15924 tag.
     // Note that PangoScript is defined to be compatible with GUnicodeScript:
     // https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html#PangoScript
     const hb_tag_t scriptTag = GetScriptTagForCode(aRunScript);
     const PangoScript script =
-      (const PangoScript)g_unicode_script_from_iso15924(scriptTag);
+      (const PangoScript)hb_glib_script_from_script(hb_script_from_iso15924_tag(scriptTag));
 
     // Might be nice to call pango_language_includes_script only once for the
     // run rather than for each character.
     PangoLanguage *scriptLang;
     if ((!basePattern ||
          !pango_language_includes_script(mPangoLanguage, script)) &&
         (scriptLang = pango_script_get_sample_language(script))) {
         fontSet = GetFontSet(scriptLang);
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -27,16 +27,17 @@ XPIDL_MODULE = 'locale'
 EXPORTS += [
     'nsCollation.h',
     'nsCollationCID.h',
     'nsDateTimeFormatCID.h',
     'nsIDateTimeFormat.h',
     'nsILanguageAtomService.h',
     'nsIPlatformCharset.h',
     'nsPosixLocale.h',
+    'nsUConvPropertySearch.h',
     'nsWin32Locale.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsCollation.cpp',
     'nsIDateTimeFormat.cpp',
     'nsLanguageAtomService.cpp',
     'nsLocale.cpp',
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -1042,17 +1042,17 @@ wasm::GenerateInterruptStub(MacroAssembl
                             FloatRegisterSet(uint32_t(0))));
 
     // Save both the APSR and FPSCR in non-volatile registers.
     masm.as_mrs(r4);
     masm.as_vmrs(r5);
     // Save the stack pointer in a non-volatile register.
     masm.mov(sp,r6);
     // Align the stack.
-    masm.ma_and(Imm32(~7), sp, sp);
+    masm.as_bic(sp, sp, Imm8(7));
 
     // Store resumePC into the return PC stack slot.
     masm.loadWasmActivationFromSymbolicAddress(IntArgReg0);
     masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1);
     masm.storePtr(IntArgReg1, Address(r6, 14 * sizeof(uint32_t*)));
 
     // Save all FP registers
     static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -886,21 +886,16 @@ BytecodeEmitter::EmitterScope::enterLexi
             return false;
 
         if (!tdzCache->noteTDZCheck(bce, bi.name(), CheckTDZ))
             return false;
     }
 
     updateFrameFixedSlots(bce, bi);
 
-    // Put frame slots in TDZ. Environment slots are poisoned during
-    // environment creation.
-    if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd()))
-        return false;
-
     // Create and intern the VM scope.
     auto createScope = [kind, bindings, firstFrameSlot](ExclusiveContext* cx,
                                                         HandleScope enclosing)
     {
         return LexicalScope::create(cx, kind, bindings, firstFrameSlot, enclosing);
     };
     if (!internScope(bce, createScope))
         return false;
@@ -910,16 +905,24 @@ BytecodeEmitter::EmitterScope::enterLexi
         if (!bce->emitInternedScopeOp(index(), JSOP_PUSHLEXICALENV))
             return false;
     }
 
     // Lexical scopes need notes to be mapped from a pc.
     if (!appendScopeNote(bce))
         return false;
 
+    // Put frame slots in TDZ. Environment slots are poisoned during
+    // environment creation.
+    //
+    // This must be done after appendScopeNote to be considered in the extent
+    // of the scope.
+    if (!deadZoneFrameSlotRange(bce, firstFrameSlot, frameSlotEnd()))
+        return false;
+
     return checkEnvironmentChainLength(bce);
 }
 
 bool
 BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox)
 {
     MOZ_ASSERT(this == bce->innermostEmitterScope);
     MOZ_ASSERT(funbox->namedLambdaBindings());
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2836,17 +2836,18 @@ Parser<ParseHandler>::checkFunctionDefin
         handler.setOp(pn, kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, bool tryAnnexB)
+Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, FunctionSyntaxKind kind,
+                                                bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
     MOZ_ASSERT(!fun->isLegacyGenerator());
@@ -2862,22 +2863,31 @@ Parser<FullParseHandler>::skipLazyInnerF
     PropagateTransitiveParseFlags(lazy, pc->sc());
 
     // The position passed to tokenStream.advance() is an offset of the sort
     // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
     // while LazyScript::{begin,end} offsets are relative to the outermost
     // script source.
     Rooted<LazyScript*> lazyOuter(context, handler.lazyOuterFunction());
     uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
-    return tokenStream.advance(fun->lazyScript()->end() - userbufBase);
+    if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
+        return false;
+
+    if (kind == Statement && fun->isExprBody()) {
+        if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+            return false;
+    }
+
+    return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, bool tryAnnexB)
+Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind,
+                                                  bool tryAnnexB)
 {
     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                                      TokenKind* ttp)
@@ -2965,17 +2975,17 @@ Parser<ParseHandler>::functionDefinition
     bool tryAnnexB = false;
     if (!checkFunctionDefinition(funName, pn, kind, generatorKind, &tryAnnexB))
         return null();
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
-        if (!skipLazyInnerFunction(pn, tryAnnexB))
+        if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
             return null();
         return pn;
     }
 
     RootedObject proto(context);
     if (generatorKind == StarGenerator) {
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1254,17 +1254,17 @@ class Parser final : private JS::AutoGCR
     bool declareFunctionThis();
     Node newInternalDotName(HandlePropertyName name);
     Node newThisName();
     Node newDotGeneratorName();
     bool declareDotGeneratorName();
 
     bool checkFunctionDefinition(HandleAtom funAtom, Node pn, FunctionSyntaxKind kind,
                                  GeneratorKind generatorKind, bool* tryAnnexB);
-    bool skipLazyInnerFunction(Node pn, bool tryAnnexB);
+    bool skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind, bool tryAnnexB);
     bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
                        InHandling inHandling, FunctionSyntaxKind kind,
                        GeneratorKind generatorKind, bool tryAnnexB,
                        Directives inheritedDirectives, Directives* newDirectives);
     bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
                                      FunctionSyntaxKind kind, GeneratorKind generatorKind,
                                      bool tryAnnexB, Directives inheritedDirectives,
                                      Directives* newDirectives);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-1298809.js
@@ -0,0 +1,6 @@
+function f() {
+  if (0)
+    function g() x;
+  else;
+}
+f();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2731,16 +2731,30 @@ MBinaryBitwiseInstruction::foldsTo(TempA
         return folded;
 
     return this;
 }
 
 MDefinition*
 MBinaryBitwiseInstruction::foldUnnecessaryBitop()
 {
+    // Fold unsigned shift right operator when the second operand is zero and
+    // the only use is an unsigned modulo. Thus, the expression
+    // |(x >>> 0) % y| becomes |x % y|.
+    if (isUrsh() && hasOneDefUse() && getOperand(1)->isConstant()) {
+        MConstant* constant = getOperand(1)->toConstant();
+        if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+            for (MUseDefIterator use(this); use; use++) {
+                if (use.def()->isMod() && use.def()->toMod()->isUnsigned())
+                    return getOperand(0);
+                break;
+            }
+        }
+    }
+
     if (specialization_ != MIRType::Int32)
         return this;
 
     // Eliminate bitwise operations that are no-ops when used on integer
     // inputs, such as (x | 0).
 
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7134,33 +7134,61 @@ class MDiv : public MBinaryArithInstruct
         return unsigned_ == other->isUnsigned();
     }
 
     ALLOW_CLONE(MDiv)
 };
 
 class MMod : public MBinaryArithInstruction
 {
+  public:
+    enum class PossiblyUnsigned {
+        NotPossible,
+        LHSPossible,
+        RHSPossible,
+        BothPossible,
+    };
+
+  protected:
     bool unsigned_;
     bool canBeNegativeDividend_;
     bool canBePowerOfTwoDivisor_;
     bool canBeDivideByZero_;
     bool trapOnError_;
 
+    PossiblyUnsigned possiblyUnsigned_;
+
     MMod(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryArithInstruction(left, right),
         unsigned_(false),
         canBeNegativeDividend_(true),
         canBePowerOfTwoDivisor_(true),
         canBeDivideByZero_(true),
-        trapOnError_(false)
+        trapOnError_(false),
+        possiblyUnsigned_(PossiblyUnsigned::NotPossible)
     {
         if (type != MIRType::Value)
             specialization_ = type;
         setResultType(type);
+
+        if (left->isUrsh() && left->getOperand(1)->isConstant()) {
+            MConstant* constant = left->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0)
+                possiblyUnsigned_ = PossiblyUnsigned::LHSPossible;
+        }
+
+        if (right->isUrsh() && right->getOperand(1)->isConstant()) {
+            MConstant* constant = right->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+                if (possiblyUnsigned_ == PossiblyUnsigned::NotPossible)
+                    possiblyUnsigned_ = PossiblyUnsigned::RHSPossible;
+                else
+                    possiblyUnsigned_ = PossiblyUnsigned::BothPossible;
+            }
+        }
     }
 
   public:
     INSTRUCTION_HEADER(Mod)
     static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
         return new(alloc) MMod(left, right, MIRType::Value);
     }
     static MMod* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1556,23 +1556,50 @@ MMod::computeRange(TempAllocator& alloc)
 {
     if (specialization() != MIRType::Int32 && specialization() != MIRType::Double)
         return;
     Range lhs(getOperand(0));
     Range rhs(getOperand(1));
 
     // If either operand is a NaN, the result is NaN. This also conservatively
     // handles Infinity cases.
-    if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds())
+    if ((possiblyUnsigned_ == PossiblyUnsigned::NotPossible &&
+         !lhs.hasInt32Bounds()) || !rhs.hasInt32Bounds()) 
+    {
         return;
+    }
 
     // If RHS can be zero, the result can be NaN.
     if (rhs.lower() <= 0 && rhs.upper() >= 0)
         return;
 
+    // (x >>> 0) % y is an unsigned modulo operation but the lhs' range is not
+    // always >= 0. The lhs range assumes a signed integer 32 bit while the
+    // value is unsigned 32 bit. That breaks the assumption that range >= 0.
+    if (specialization() == MIRType::Int32) {
+        switch (possiblyUnsigned_) {
+          case PossiblyUnsigned::NotPossible:
+            break;
+          case PossiblyUnsigned::LHSPossible:
+            if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::RHSPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::BothPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            else if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+        }
+    }
+
     // If both operands are non-negative integers, we can optimize this to an
     // unsigned mod.
     if (specialization() == MIRType::Int32 && lhs.lower() >= 0 && rhs.lower() > 0 &&
         !lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart())
     {
         unsigned_ = true;
     }
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -1112,17 +1112,17 @@ Imm8::EncodeTwoImms(uint32_t imm)
     imm2 = ((imm >> (32 - imm2shift)) | (imm << imm2shift)) & 0xff;
     MOZ_ASSERT((imm1shift & 0x1) == 0);
     MOZ_ASSERT((imm2shift & 0x1) == 0);
     return TwoImm8mData(datastore::Imm8mData(imm1, imm1shift >> 1),
                         datastore::Imm8mData(imm2, imm2shift >> 1));
 }
 
 ALUOp
-jit::ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest)
+jit::ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest)
 {
     // Find an alternate ALUOp to get the job done, and use a different imm.
     *negDest = dest;
     switch (op) {
       case OpMov:
         *imm = Imm32(~imm->value);
         return OpMvn;
       case OpMvn:
@@ -1144,17 +1144,17 @@ jit::ALUNeg(ALUOp op, Register dest, Imm
         *imm = Imm32(-imm->value);
         return OpCmn;
       case OpCmn:
         *imm = Imm32(-imm->value);
         return OpCmp;
       case OpTst:
         MOZ_ASSERT(dest == InvalidReg);
         *imm = Imm32(~imm->value);
-        *negDest = ScratchRegister;
+        *negDest = scratch;
         return OpBic;
         // orr has orn on thumb2 only.
       default:
         return OpInvalid;
     }
 }
 
 bool
@@ -3416,8 +3416,13 @@ Assembler::GetPoolMaxOffset()
         char* poolMaxOffsetStr = getenv("ASM_POOL_MAX_OFFSET");
         uint32_t poolMaxOffset;
         if (poolMaxOffsetStr && sscanf(poolMaxOffsetStr, "%u", &poolMaxOffset) == 1)
             AsmPoolMaxOffset = poolMaxOffset;
         isSet = true;
     }
     return AsmPoolMaxOffset;
 }
+
+SecondScratchRegisterScope::SecondScratchRegisterScope(MacroAssembler &masm)
+  : AutoRegisterScope(masm, masm.getSecondScratchReg())
+{
+}
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -50,16 +50,21 @@ static constexpr Register ScratchRegiste
 // of code thinks it has exclusive ownership of the scratch register.
 struct ScratchRegisterScope : public AutoRegisterScope
 {
     explicit ScratchRegisterScope(MacroAssembler& masm)
       : AutoRegisterScope(masm, ScratchRegister)
     { }
 };
 
+struct SecondScratchRegisterScope : public AutoRegisterScope
+{
+    explicit SecondScratchRegisterScope(MacroAssembler& masm);
+};
+
 static constexpr Register OsrFrameReg = r3;
 static constexpr Register ArgumentsRectifierReg = r8;
 static constexpr Register CallTempReg0 = r5;
 static constexpr Register CallTempReg1 = r6;
 static constexpr Register CallTempReg2 = r7;
 static constexpr Register CallTempReg3 = r8;
 static constexpr Register CallTempReg4 = r0;
 static constexpr Register CallTempReg5 = r1;
@@ -404,17 +409,17 @@ enum VFPOp {
     OpvAbs  = 0xB << 20 | 0x3 << 6,
     OpvNeg  = 0xB << 20 | 0x1 << 6 | 0x1 << 16,
     OpvSqrt = 0xB << 20 | 0x3 << 6 | 0x1 << 16,
     OpvCmp  = 0xB << 20 | 0x1 << 6 | 0x4 << 16,
     OpvCmpz  = 0xB << 20 | 0x1 << 6 | 0x5 << 16
 };
 
 // Negate the operation, AND negate the immediate that we were passed in.
-ALUOp ALUNeg(ALUOp op, Register dest, Imm32* imm, Register* negDest);
+ALUOp ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest);
 bool can_dbl(ALUOp op);
 bool condsAreSafe(ALUOp op);
 
 // If there is a variant of op that has a dest (think cmp/sub) return that
 // variant of it.
 ALUOp getDestVariant(ALUOp op);
 
 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -66,17 +66,17 @@ OutOfLineBailout::accept(CodeGeneratorAR
 void
 CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch* test)
 {
     const LAllocation* opd = test->getOperand(0);
     MBasicBlock* ifTrue = test->ifTrue();
     MBasicBlock* ifFalse = test->ifFalse();
 
     // Test the operand
-    masm.ma_cmp(ToRegister(opd), Imm32(0));
+    masm.as_cmp(ToRegister(opd), Imm8(0));
 
     if (isNextBlock(ifFalse->lir())) {
         jumpToBlock(ifTrue, Assembler::NonZero);
     } else if (isNextBlock(ifTrue->lir())) {
         jumpToBlock(ifFalse, Assembler::Zero);
     } else {
         jumpToBlock(ifFalse, Assembler::Zero);
         jumpToBlock(ifTrue);
@@ -86,39 +86,47 @@ CodeGeneratorARM::visitTestIAndBranch(LT
 void
 CodeGeneratorARM::visitCompare(LCompare* comp)
 {
     Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop());
     const LAllocation* left = comp->getOperand(0);
     const LAllocation* right = comp->getOperand(1);
     const LDefinition* def = comp->getDef(0);
 
-    if (right->isConstant())
-        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
-    else if (right->isRegister())
+    ScratchRegisterScope scratch(masm);
+
+    if (right->isConstant()) {
+        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch);
+    } else if (right->isRegister()) {
         masm.ma_cmp(ToRegister(left), ToRegister(right));
-    else
-        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
+    } else {
+        SecondScratchRegisterScope scratch2(masm);
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2);
+    }
     masm.ma_mov(Imm32(0), ToRegister(def));
     masm.ma_mov(Imm32(1), ToRegister(def), cond);
 }
 
 void
 CodeGeneratorARM::visitCompareAndBranch(LCompareAndBranch* comp)
 {
     Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop());
     const LAllocation* left = comp->left();
     const LAllocation* right = comp->right();
 
-    if (right->isConstant())
-        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)));
-    else if (right->isRegister())
+    ScratchRegisterScope scratch(masm);
+
+    if (right->isConstant()) {
+        masm.ma_cmp(ToRegister(left), Imm32(ToInt32(right)), scratch);
+    } else if (right->isRegister()) {
         masm.ma_cmp(ToRegister(left), ToRegister(right));
-    else
-        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)));
+    } else {
+        SecondScratchRegisterScope scratch2(masm);
+        masm.ma_cmp(ToRegister(left), Operand(ToAddress(right)), scratch, scratch2);
+    }
     emitBranch(cond, comp->ifTrue(), comp->ifFalse());
 }
 
 bool
 CodeGeneratorARM::generateOutOfLineCode()
 {
     if (!CodeGeneratorShared::generateOutOfLineCode())
         return false;
@@ -276,18 +284,20 @@ CodeGeneratorARM::visitSqrtF(LSqrtF* ins
 
 void
 CodeGeneratorARM::visitAddI(LAddI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
 
+    ScratchRegisterScope scratch(masm);
+
     if (rhs->isConstant())
-        masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+        masm.ma_add(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC);
     else if (rhs->isRegister())
         masm.ma_add(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
         masm.ma_add(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
@@ -310,18 +320,20 @@ CodeGeneratorARM::visitAddI64(LAddI64* l
 
 void
 CodeGeneratorARM::visitSubI(LSubI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
 
+    ScratchRegisterScope scratch(masm);
+
     if (rhs->isConstant())
-        masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), SetCC);
+        masm.ma_sub(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, SetCC);
     else if (rhs->isRegister())
         masm.ma_sub(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), SetCC);
     else
         masm.ma_sub(ToRegister(lhs), Operand(ToAddress(rhs)), ToRegister(dest), SetCC);
 
     if (ins->snapshot())
         bailoutIf(Assembler::Overflow, ins->snapshot());
 }
@@ -353,23 +365,23 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
 
     if (rhs->isConstant()) {
         // Bailout when this condition is met.
         Assembler::Condition c = Assembler::Overflow;
         // Bailout on -0.0
         int32_t constant = ToInt32(rhs);
         if (mul->canBeNegativeZero() && constant <= 0) {
             Assembler::Condition bailoutCond = (constant == 0) ? Assembler::LessThan : Assembler::Equal;
-            masm.ma_cmp(ToRegister(lhs), Imm32(0));
+            masm.as_cmp(ToRegister(lhs), Imm8(0));
             bailoutIf(bailoutCond, ins->snapshot());
         }
         // TODO: move these to ma_mul.
         switch (constant) {
           case -1:
-            masm.ma_rsb(ToRegister(lhs), Imm32(0), ToRegister(dest), SetCC);
+            masm.as_rsb(ToRegister(dest), ToRegister(lhs), Imm8(0), SetCC);
             break;
           case 0:
             masm.ma_mov(Imm32(0), ToRegister(dest));
             return; // Escape overflow check;
           case 1:
             // Nop
             masm.ma_mov(ToRegister(lhs), ToRegister(dest));
             return; // Escape overflow check;
@@ -417,41 +429,44 @@ CodeGeneratorARM::visitMulI(LMulI* ins)
                         masm.as_cmp(ToRegister(lhs), asr(ToRegister(dest), shift));
                         c = Assembler::NotEqual;
                         handled = true;
                     }
                 }
             }
 
             if (!handled) {
+                ScratchRegisterScope scratch(masm);
                 if (mul->canOverflow())
-                    c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), c);
+                    c = masm.ma_check_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch, c);
                 else
-                    masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest));
+                    masm.ma_mul(ToRegister(lhs), Imm32(ToInt32(rhs)), ToRegister(dest), scratch);
             }
           }
         }
         // Bailout on overflow.
         if (mul->canOverflow())
             bailoutIf(c, ins->snapshot());
     } else {
         Assembler::Condition c = Assembler::Overflow;
 
-        if (mul->canOverflow())
-            c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), c);
-        else
+        if (mul->canOverflow()) {
+            ScratchRegisterScope scratch(masm);
+            c = masm.ma_check_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest), scratch, c);
+        } else {
             masm.ma_mul(ToRegister(lhs), ToRegister(rhs), ToRegister(dest));
+        }
 
         // Bailout on overflow.
         if (mul->canOverflow())
             bailoutIf(c, ins->snapshot());
 
         if (mul->canBeNegativeZero()) {
             Label done;
-            masm.ma_cmp(ToRegister(dest), Imm32(0));
+            masm.as_cmp(ToRegister(dest), Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
 
             // Result is -0 if lhs or rhs is negative.
             masm.ma_cmn(ToRegister(lhs), ToRegister(rhs));
             bailoutIf(Assembler::Signed, ins->snapshot());
 
             masm.bind(&done);
         }
@@ -498,24 +513,26 @@ CodeGeneratorARM::visitMulI64(LMulI64* l
         masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp);
     }
 }
 
 void
 CodeGeneratorARM::divICommon(MDiv* mir, Register lhs, Register rhs, Register output,
                              LSnapshot* snapshot, Label& done)
 {
+    ScratchRegisterScope scratch(masm);
+
     if (mir->canBeNegativeOverflow()) {
         // Handle INT32_MIN / -1;
         // The integer division will give INT32_MIN, but we want -(double)INT32_MIN.
 
         // Sets EQ if lhs == INT32_MIN.
-        masm.ma_cmp(lhs, Imm32(INT32_MIN));
+        masm.ma_cmp(lhs, Imm32(INT32_MIN), scratch);
         // If EQ (LHS == INT32_MIN), sets EQ if rhs == -1.
-        masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal);
+        masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal);
         if (mir->canTruncateOverflow()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerOverflow, Assembler::Equal);
             } else {
                 // (-INT32_MIN)|0 = INT32_MIN
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(INT32_MIN), output);
@@ -525,17 +542,17 @@ CodeGeneratorARM::divICommon(MDiv* mir, 
         } else {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Equal, snapshot);
         }
     }
 
     // Handle divide by zero.
     if (mir->canBeDivideByZero()) {
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         if (mir->canTruncateInfinities()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 // Infinity|0 == 0
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(0), output);
@@ -546,19 +563,19 @@ CodeGeneratorARM::divICommon(MDiv* mir, 
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Equal, snapshot);
         }
     }
 
     // Handle negative 0.
     if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) {
         Label nonzero;
-        masm.ma_cmp(lhs, Imm32(0));
+        masm.as_cmp(lhs, Imm8(0));
         masm.ma_b(&nonzero, Assembler::NotEqual);
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         MOZ_ASSERT(mir->fallible());
         bailoutIf(Assembler::LessThan, snapshot);
         masm.bind(&nonzero);
     }
 }
 
 void
 CodeGeneratorARM::visitDivI(LDivI* ins)
@@ -610,17 +627,17 @@ CodeGeneratorARM::visitSoftDivI(LSoftDiv
     if (gen->compilingAsmJS())
         masm.callWithABI(wasm::SymbolicAddress::aeabi_idivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod));
 
     // idivmod returns the quotient in r0, and the remainder in r1.
     if (!mir->canTruncateRemainder()) {
         MOZ_ASSERT(mir->fallible());
-        masm.ma_cmp(r1, Imm32(0));
+        masm.as_cmp(r1, Imm8(0));
         bailoutIf(Assembler::NonZero, ins->snapshot());
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitDivPowTwoI(LDivPowTwoI* ins)
@@ -682,18 +699,18 @@ CodeGeneratorARM::modICommon(MMod* mir, 
     // There are three cases: (Y < 0), (Y == 0) and (Y > 0).
     // If (Y < 0), then we compare X with 0, and bail if X == 0.
     // If (Y == 0), then we simply want to bail. Since this does not set the
     // flags necessary for LT to trigger, we don't test X, and take the bailout
     // because the EQ flag is set.
     // If (Y > 0), we don't set EQ, and we don't trigger LT, so we don't take
     // the bailout.
     if (mir->canBeDivideByZero() || mir->canBeNegativeDividend()) {
-        masm.ma_cmp(rhs, Imm32(0));
-        masm.ma_cmp(lhs, Imm32(0), Assembler::LessThan);
+        masm.as_cmp(rhs, Imm8(0));
+        masm.as_cmp(lhs, Imm8(0), Assembler::LessThan);
         if (mir->isTruncated()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 // NaN|0 == 0 and (0 % -X)|0 == 0
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 masm.ma_mov(Imm32(0), output);
@@ -717,28 +734,31 @@ CodeGeneratorARM::visitModI(LModI* ins)
     MMod* mir = ins->mir();
 
     // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
     masm.ma_mov(lhs, callTemp);
 
     Label done;
     modICommon(mir, lhs, rhs, output, ins->snapshot(), done);
 
-    masm.ma_smod(lhs, rhs, output);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_smod(lhs, rhs, output, scratch);
+    }
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0.
     if (mir->canBeNegativeDividend()) {
         if (mir->isTruncated()) {
             // -0.0|0 == 0
         } else {
             MOZ_ASSERT(mir->fallible());
             // See if X < 0
-            masm.ma_cmp(output, Imm32(0));
+            masm.as_cmp(output, Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
-            masm.ma_cmp(callTemp, Imm32(0));
+            masm.as_cmp(callTemp, Imm8(0));
             bailoutIf(Assembler::Signed, ins->snapshot());
         }
     }
 
     masm.bind(&done);
 }
 
 void
@@ -751,23 +771,27 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
     Register callTemp = ToRegister(ins->callTemp());
     MMod* mir = ins->mir();
     Label done;
 
     // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0.
     MOZ_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code());
     masm.ma_mov(lhs, callTemp);
 
+
     // Prevent INT_MIN % -1;
     // The integer division will give INT_MIN, but we want -(double)INT_MIN.
     if (mir->canBeNegativeDividend()) {
-        // Sets EQ if lhs == INT_MIN
-        masm.ma_cmp(lhs, Imm32(INT_MIN));
-        // If EQ (LHS == INT_MIN), sets EQ if rhs == -1
-        masm.ma_cmp(rhs, Imm32(-1), Assembler::Equal);
+        {
+            ScratchRegisterScope scratch(masm);
+            // Sets EQ if lhs == INT_MIN
+            masm.ma_cmp(lhs, Imm32(INT_MIN), scratch);
+            // If EQ (LHS == INT_MIN), sets EQ if rhs == -1
+            masm.ma_cmp(rhs, Imm32(-1), scratch, Assembler::Equal);
+        }
         if (mir->isTruncated()) {
             // (INT_MIN % -1)|0 == 0
             Label skip;
             masm.ma_b(&skip, Assembler::NotEqual);
             masm.ma_mov(Imm32(0), output);
             masm.ma_b(&done);
             masm.bind(&skip);
         } else {
@@ -788,39 +812,43 @@ CodeGeneratorARM::visitSoftModI(LSoftMod
 
     // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0
     if (mir->canBeNegativeDividend()) {
         if (mir->isTruncated()) {
             // -0.0|0 == 0
         } else {
             MOZ_ASSERT(mir->fallible());
             // See if X < 0
-            masm.ma_cmp(r1, Imm32(0));
+            masm.as_cmp(r1, Imm8(0));
             masm.ma_b(&done, Assembler::NotEqual);
-            masm.ma_cmp(callTemp, Imm32(0));
+            masm.as_cmp(callTemp, Imm8(0));
             bailoutIf(Assembler::Signed, ins->snapshot());
         }
     }
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitModPowTwoI(LModPowTwoI* ins)
 {
     Register in = ToRegister(ins->getOperand(0));
     Register out = ToRegister(ins->getDef(0));
     MMod* mir = ins->mir();
     Label fin;
     // bug 739870, jbramley has a different sequence that may help with speed
     // here.
+
     masm.ma_mov(in, out, SetCC);
     masm.ma_b(&fin, Assembler::Zero);
-    masm.ma_rsb(Imm32(0), out, LeaveCC, Assembler::Signed);
-    masm.ma_and(Imm32((1 << ins->shift()) - 1), out);
-    masm.ma_rsb(Imm32(0), out, SetCC, Assembler::Signed);
+    masm.as_rsb(out, out, Imm8(0), LeaveCC, Assembler::Signed);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_and(Imm32((1 << ins->shift()) - 1), out, scratch);
+    }
+    masm.as_rsb(out, out, Imm8(0), SetCC, Assembler::Signed);
     if (mir->canBeNegativeDividend()) {
         if (!mir->isTruncated()) {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Zero, ins->snapshot());
         } else {
             // -0|0 == 0
         }
     }
@@ -830,17 +858,22 @@ CodeGeneratorARM::visitModPowTwoI(LModPo
 void
 CodeGeneratorARM::visitModMaskI(LModMaskI* ins)
 {
     Register src = ToRegister(ins->getOperand(0));
     Register dest = ToRegister(ins->getDef(0));
     Register tmp1 = ToRegister(ins->getTemp(0));
     Register tmp2 = ToRegister(ins->getTemp(1));
     MMod* mir = ins->mir();
-    masm.ma_mod_mask(src, dest, tmp1, tmp2, ins->shift());
+
+    ScratchRegisterScope scratch(masm);
+    SecondScratchRegisterScope scratch2(masm);
+
+    masm.ma_mod_mask(src, dest, tmp1, tmp2, scratch, scratch2, ins->shift());
+
     if (mir->canBeNegativeDividend()) {
         if (!mir->isTruncated()) {
             MOZ_ASSERT(mir->fallible());
             bailoutIf(Assembler::Zero, ins->snapshot());
         } else {
             // -0|0 == 0
         }
     }
@@ -859,33 +892,36 @@ CodeGeneratorARM::visitBitNotI(LBitNotI*
 }
 
 void
 CodeGeneratorARM::visitBitOpI(LBitOpI* ins)
 {
     const LAllocation* lhs = ins->getOperand(0);
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
+
+    ScratchRegisterScope scratch(masm);
+
     // All of these bitops should be either imm32's, or integer registers.
     switch (ins->bitop()) {
       case JSOP_BITOR:
         if (rhs->isConstant())
-            masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_orr(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_orr(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       case JSOP_BITXOR:
         if (rhs->isConstant())
-            masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_eor(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_eor(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       case JSOP_BITAND:
         if (rhs->isConstant())
-            masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest));
+            masm.ma_and(Imm32(ToInt32(rhs)), ToRegister(lhs), ToRegister(dest), scratch);
         else
             masm.ma_and(ToRegister(rhs), ToRegister(lhs), ToRegister(dest));
         break;
       default:
         MOZ_CRASH("unexpected binary opcode");
     }
 }
 
@@ -913,42 +949,42 @@ CodeGeneratorARM::visitShiftI(LShiftI* i
             break;
           case JSOP_URSH:
             if (shift) {
                 masm.ma_lsr(Imm32(shift), lhs, dest);
             } else {
                 // x >>> 0 can overflow.
                 masm.ma_mov(lhs, dest);
                 if (ins->mir()->toUrsh()->fallible()) {
-                    masm.ma_cmp(dest, Imm32(0));
+                    masm.as_cmp(dest, Imm8(0));
                     bailoutIf(Assembler::LessThan, ins->snapshot());
                 }
             }
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     } else {
         // The shift amounts should be AND'ed into the 0-31 range since arm
         // shifts by the lower byte of the register (it will attempt to shift by
         // 250 if you ask it to).
-        masm.ma_and(Imm32(0x1F), ToRegister(rhs), dest);
+        masm.as_and(dest, ToRegister(rhs), Imm8(0x1F));
 
         switch (ins->bitop()) {
           case JSOP_LSH:
             masm.ma_lsl(dest, lhs, dest);
             break;
           case JSOP_RSH:
             masm.ma_asr(dest, lhs, dest);
             break;
           case JSOP_URSH:
             masm.ma_lsr(dest, lhs, dest);
             if (ins->mir()->toUrsh()->fallible()) {
                 // x >>> 0 can overflow.
-                masm.ma_cmp(dest, Imm32(0));
+                masm.as_cmp(dest, Imm8(0));
                 bailoutIf(Assembler::LessThan, ins->snapshot());
             }
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     }
 }
@@ -964,17 +1000,17 @@ CodeGeneratorARM::visitUrshD(LUrshD* ins
 
     if (rhs->isConstant()) {
         int32_t shift = ToInt32(rhs) & 0x1F;
         if (shift)
             masm.ma_lsr(Imm32(shift), lhs, temp);
         else
             masm.ma_mov(lhs, temp);
     } else {
-        masm.ma_and(Imm32(0x1F), ToRegister(rhs), temp);
+        masm.as_and(temp, ToRegister(rhs), Imm8(0x1F));
         masm.ma_lsr(temp, lhs, temp);
     }
 
     masm.convertUInt32ToDouble(temp, out);
 }
 
 void
 CodeGeneratorARM::visitClzI(LClzI* ins)
@@ -1114,20 +1150,22 @@ CodeGeneratorARM::emitTableSwitchDispatc
     // current instruction *PLUS 8*. This means that ldr foo, [pc, +0] reads
     // $pc+8. In other words, there is an empty word after the branch into the
     // switch table before the table actually starts. Since the only other
     // unhandled case is the default case (both out of range high and out of
     // range low) I then insert a branch to default case into the extra slot,
     // which ensures we don't attempt to execute the address table.
     Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
 
+    ScratchRegisterScope scratch(masm);
+
     int32_t cases = mir->numCases();
     // Lower value with low value.
-    masm.ma_sub(index, Imm32(mir->low()), index, SetCC);
-    masm.ma_rsb(index, Imm32(cases - 1), index, SetCC, Assembler::NotSigned);
+    masm.ma_sub(index, Imm32(mir->low()), index, scratch, SetCC);
+    masm.ma_rsb(index, Imm32(cases - 1), index, scratch, SetCC, Assembler::NotSigned);
     // Inhibit pools within the following sequence because we are indexing into
     // a pc relative table. The region will have one instruction for ma_ldr, one
     // for ma_b, and each table case takes one word.
     AutoForbidPools afp(&masm, 1 + 1 + cases);
     masm.ma_ldr(DTRAddr(pc, DtrRegImmShift(index, LSL, 2)), pc, Offset, Assembler::NotSigned);
     masm.ma_b(defaultcase);
 
     // To fill in the CodeLabels for the case entries, we need to first generate
@@ -1257,21 +1295,22 @@ CodeGeneratorARM::visitRoundF(LRoundF* l
     masm.roundf(input, output, &bail, tmp);
     bailoutFrom(&bail, lir->snapshot());
 }
 
 void
 CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest, Label* fail)
 {
     ScratchDoubleScope scratch(masm);
+    ScratchRegisterScope scratchReg(masm);
 
     masm.ma_vcvt_F64_I32(src, scratch);
     masm.ma_vxfer(scratch, dest);
-    masm.ma_cmp(dest, Imm32(0x7fffffff));
-    masm.ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    masm.ma_cmp(dest, Imm32(0x7fffffff), scratchReg);
+    masm.ma_cmp(dest, Imm32(0x80000000), scratchReg, Assembler::NotEqual);
     masm.ma_b(fail, Assembler::Equal);
 }
 
 void
 CodeGeneratorARM::visitTruncateDToInt32(LTruncateDToInt32* ins)
 {
     emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), ins->mir());
 }
@@ -1375,18 +1414,20 @@ CodeGeneratorARM::visitBoxFloatingPoint(
 void
 CodeGeneratorARM::visitUnbox(LUnbox* unbox)
 {
     // Note that for unbox, the type and payload indexes are switched on the
     // inputs.
     MUnbox* mir = unbox->mir();
     Register type = ToRegister(unbox->type());
 
+    ScratchRegisterScope scratch(masm);
+
     if (mir->fallible()) {
-        masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())));
+        masm.ma_cmp(type, Imm32(MIRTypeToTag(mir->type())), scratch);
         bailoutIf(Assembler::NotEqual, unbox->snapshot());
     }
 }
 
 void
 CodeGeneratorARM::visitDouble(LDouble* ins)
 {
     const LDefinition* out = ins->getDef(0);
@@ -1578,18 +1619,19 @@ CodeGeneratorARM::visitCompareBitwiseAnd
     jumpToBlock(notEqual, Assembler::NotEqual);
     masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
 }
 
 void
 CodeGeneratorARM::visitBitAndAndBranch(LBitAndAndBranch* baab)
 {
+    ScratchRegisterScope scratch(masm);
     if (baab->right()->isConstant())
-        masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
+        masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())), scratch);
     else
         masm.ma_tst(ToRegister(baab->left()), ToRegister(baab->right()));
     emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
 }
 
 void
 CodeGeneratorARM::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
@@ -1601,28 +1643,28 @@ CodeGeneratorARM::visitAsmJSUInt32ToFloa
 {
     masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorARM::visitNotI(LNotI* ins)
 {
     // It is hard to optimize !x, so just do it the basic way for now.
-    masm.ma_cmp(ToRegister(ins->input()), Imm32(0));
+    masm.as_cmp(ToRegister(ins->input()), Imm8(0));
     masm.emitSet(Assembler::Equal, ToRegister(ins->output()));
 }
 
 void
 CodeGeneratorARM::visitNotI64(LNotI64* lir)
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
     Register output = ToRegister(lir->output());
 
     masm.ma_orr(input.low, input.high, output);
-    masm.ma_cmp(output, Imm32(0));
+    masm.as_cmp(output, Imm8(0));
     masm.emitSet(Assembler::Equal, output);
 }
 
 void
 CodeGeneratorARM::visitNotD(LNotD* ins)
 {
     // Since this operation is not, we want to set a bit if the double is
     // falsey, which means 0.0, -0.0 or NaN. When comparing with 0, an input of
@@ -1635,17 +1677,17 @@ CodeGeneratorARM::visitNotD(LNotD* ins)
     // TODO There are three variations here to compare performance-wise.
     bool nocond = true;
     if (nocond) {
         // Load the value into the dest register.
         masm.as_vmrs(dest);
         masm.ma_lsr(Imm32(28), dest, dest);
         // 28 + 2 = 30
         masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr);
-        masm.ma_and(Imm32(1), dest);
+        masm.as_and(dest, dest, Imm8(1));
     } else {
         masm.as_vmrs(pc);
         masm.ma_mov(Imm32(0), dest);
         masm.ma_mov(Imm32(1), dest, Assembler::Equal);
         masm.ma_mov(Imm32(1), dest, Assembler::Overflow);
     }
 }
 
@@ -1663,60 +1705,64 @@ CodeGeneratorARM::visitNotF(LNotF* ins)
     // TODO There are three variations here to compare performance-wise.
     bool nocond = true;
     if (nocond) {
         // Load the value into the dest register.
         masm.as_vmrs(dest);
         masm.ma_lsr(Imm32(28), dest, dest);
         // 28 + 2 = 30
         masm.ma_alu(dest, lsr(dest, 2), dest, OpOrr);
-        masm.ma_and(Imm32(1), dest);
+        masm.as_and(dest, dest, Imm8(1));
     } else {
         masm.as_vmrs(pc);
         masm.ma_mov(Imm32(0), dest);
         masm.ma_mov(Imm32(1), dest, Assembler::Equal);
         masm.ma_mov(Imm32(1), dest, Assembler::Overflow);
     }
 }
 
 void
 CodeGeneratorARM::visitGuardShape(LGuardShape* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
 
+    ScratchRegisterScope scratch(masm);
     masm.ma_ldr(DTRAddr(obj, DtrOffImm(ShapedObject::offsetOfShape())), tmp);
-    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()));
+    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->shape()), scratch);
 
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::visitGuardObjectGroup(LGuardObjectGroup* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
     MOZ_ASSERT(obj != tmp);
 
+    ScratchRegisterScope scratch(masm);
     masm.ma_ldr(DTRAddr(obj, DtrOffImm(JSObject::offsetOfGroup())), tmp);
-    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()));
+    masm.ma_cmp(tmp, ImmGCPtr(guard->mir()->group()), scratch);
 
     Assembler::Condition cond =
         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
     bailoutIf(cond, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::visitGuardClass(LGuardClass* guard)
 {
     Register obj = ToRegister(guard->input());
     Register tmp = ToRegister(guard->tempInt());
 
+    ScratchRegisterScope scratch(masm);
+
     masm.loadObjClass(obj, tmp);
-    masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()));
+    masm.ma_cmp(tmp, Imm32((uint32_t)guard->mir()->getClass()), scratch);
     bailoutIf(Assembler::NotEqual, guard->snapshot());
 }
 
 void
 CodeGeneratorARM::generateInvalidateEpilogue()
 {
     // Ensure that there is enough space in the buffer for the OsiPoint patching
     // to occur. Otherwise, we could overwrite the invalidation epilogue.
@@ -2125,17 +2171,17 @@ CodeGeneratorARM::visitAtomicTypedArrayE
 }
 
 void
 CodeGeneratorARM::visitAsmSelect(LAsmSelect* ins)
 {
     MIRType mirType = ins->mir()->type();
 
     Register cond = ToRegister(ins->condExpr());
-    masm.ma_cmp(cond, Imm32(0));
+    masm.as_cmp(cond, Imm8(0));
 
     if (mirType == MIRType::Int32) {
         Register falseExpr = ToRegister(ins->falseExpr());
         Register out = ToRegister(ins->output());
         MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, "true expr input is reused for output");
         masm.ma_mov(falseExpr, out, LeaveCC, Assembler::Zero);
         return;
     }
@@ -2258,57 +2304,60 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
       default: MOZ_CRASH("unexpected array type");
     }
 
     if (ptr->isConstant()) {
         MOZ_ASSERT(!mir->needsBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
         if (isFloat) {
+            ScratchRegisterScope scratch(masm);
             VFPRegister vd(ToFloatRegister(ins->output()));
             if (size == 32)
-                masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
+                masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), scratch, Assembler::Always);
             else
-                masm.ma_vldr(Address(HeapReg, ptrImm), vd, Assembler::Always);
+                masm.ma_vldr(Address(HeapReg, ptrImm), vd, scratch, Assembler::Always);
         }  else {
+            ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
-                                  ToRegister(ins->output()), Offset, Assembler::Always);
+                                  ToRegister(ins->output()), scratch, Offset, Assembler::Always);
         }
     } else {
+        ScratchRegisterScope scratch(masm);
         Register ptrReg = ToRegister(ptr);
         if (isFloat) {
             FloatRegister output = ToFloatRegister(ins->output());
             if (size == 32)
                 output = output.singleOverlay();
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
                 BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
                 masm.append(wasm::BoundsCheck(cmp.getOffset()));
 
                 size_t nanOffset = size == 32 ? wasm::NaN32GlobalDataOffset : wasm::NaN64GlobalDataOffset;
-                masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output,
+                masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output, scratch,
                              Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
-            masm.ma_vldr(output, HeapReg, ptrReg, 0, cond);
+            masm.ma_vldr(output, HeapReg, ptrReg, scratch, 0, cond);
         } else {
             Register output = ToRegister(ins->output());
 
             Assembler::Condition cond = Assembler::Always;
             if (mir->needsBoundsCheck()) {
                 uint32_t cmpOffset = masm.as_cmp(ptrReg, Imm8(0)).getOffset();
                 masm.append(wasm::BoundsCheck(cmpOffset));
 
                 masm.ma_mov(Imm32(0), output, Assembler::AboveOrEqual);
                 cond = Assembler::Below;
             }
 
-            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset, cond);
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, scratch, Offset, cond);
         }
     }
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmLoad(T* lir)
 {
@@ -2317,52 +2366,53 @@ CodeGeneratorARM::emitWasmLoad(T* lir)
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
+        ScratchRegisterScope scratch(masm);
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         if (offset)
-            masm.ma_add(Imm32(offset), ptrPlusOffset);
+            masm.ma_add(Imm32(offset), ptrPlusOffset, scratch);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
     bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
                     type == Scalar::Int64;
     unsigned byteSize = mir->byteSize();
 
     memoryBarrier(mir->barrierBefore());
 
     if (mir->type() == MIRType::Int64) {
         Register64 output = ToOutRegister64(lir);
         if (type == Scalar::Int64) {
             MOZ_ASSERT(INT64LOW_OFFSET == 0);
             masm.ma_dataTransferN(IsLoad, 32, /* signed = */ false, HeapReg, ptr, output.low);
-            masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+            masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET));
             masm.ma_dataTransferN(IsLoad, 32, isSigned, HeapReg, ptr, output.high);
         } else {
             masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.low);
             if (isSigned)
                 masm.ma_asr(Imm32(31), output.low, output.high);
             else
                 masm.ma_mov(Imm32(0), output.high);
         }
     } else {
         AnyRegister output = ToAnyRegister(lir->output());
         bool isFloat = output.isFloat();
         if (isFloat) {
             MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
             ScratchRegisterScope scratch(masm);
             masm.ma_add(HeapReg, ptr, scratch);
-            masm.ma_vldr(Address(scratch, 0), output.fpu());
+            masm.ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), output.fpu());
         } else {
             masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
         }
     }
 
     memoryBarrier(mir->barrierAfter());
 }
 
@@ -2383,18 +2433,20 @@ void
 CodeGeneratorARM::emitWasmUnalignedLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
-    if (offset)
-        masm.ma_add(Imm32(offset), ptr);
+    if (offset) {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(Imm32(offset), ptr, scratch);
+    }
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
     bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
                     type == Scalar::Int64;
@@ -2462,17 +2514,18 @@ CodeGeneratorARM::visitWasmUnalignedLoad
 
 void
 CodeGeneratorARM::visitWasmAddOffset(LWasmAddOffset* lir)
 {
     MWasmAddOffset* mir = lir->mir();
     Register base = ToRegister(lir->base());
     Register out = ToRegister(lir->output());
 
-    masm.ma_add(base, Imm32(mir->offset()), out, SetCC);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_add(base, Imm32(mir->offset()), out, scratch, SetCC);
     masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet);
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
@@ -2481,41 +2534,42 @@ CodeGeneratorARM::emitWasmStore(T* lir)
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
+        ScratchRegisterScope scratch(masm);
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
         if (offset)
-            masm.ma_add(Imm32(offset), ptrPlusOffset);
+            masm.ma_add(Imm32(offset), ptrPlusOffset, scratch);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
     memoryBarrier(mir->barrierBefore());
 
     if (type == Scalar::Int64) {
         MOZ_ASSERT(INT64LOW_OFFSET == 0);
 
         Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex));
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, HeapReg, ptr, value.low);
-        masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
+        masm.as_add(ptr, ptr, Imm8(INT64HIGH_OFFSET));
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, HeapReg, ptr, value.high);
     } else {
         AnyRegister value = ToAnyRegister(lir->getOperand(lir->ValueIndex));
         if (value.isFloat()) {
+            ScratchRegisterScope scratch(masm);
             FloatRegister val = value.fpu();
             MOZ_ASSERT((byteSize == 4) == val.isSingle());
-            ScratchRegisterScope scratch(masm);
             masm.ma_add(HeapReg, ptr, scratch);
-            masm.ma_vstr(val, Address(scratch, 0));
+            masm.ma_vstr(val, Operand(Address(scratch, 0)).toVFPAddr());
         } else {
             bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
             Register val = value.gpr();
             masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
         }
     }
 
     memoryBarrier(mir->barrierAfter());
@@ -2538,18 +2592,20 @@ void
 CodeGeneratorARM::emitWasmUnalignedStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
-    if (offset)
-        masm.ma_add(Imm32(offset), ptr);
+    if (offset) {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_add(Imm32(offset), ptr, scratch);
+    }
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
     MIRType mirType = mir->value()->type();
 
     memoryBarrier(mir->barrierAfter());
 
@@ -2625,39 +2681,42 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             Address addr(HeapReg, ptrImm);
             if (size == 32)
                 masm.storeFloat32(vd, addr);
             else
                 masm.storeDouble(vd, addr);
         } else {
+            ScratchRegisterScope scratch(masm);
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
-                                  ToRegister(ins->value()), Offset, Assembler::Always);
+                                  ToRegister(ins->value()), scratch, Offset, Assembler::Always);
         }
     } else {
         Register ptrReg = ToRegister(ptr);
 
         Assembler::Condition cond = Assembler::Always;
         if (mir->needsBoundsCheck()) {
             BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
             masm.append(wasm::BoundsCheck(cmp.getOffset()));
 
             cond = Assembler::Below;
         }
 
         if (isFloat) {
+            ScratchRegisterScope scratch(masm);
             FloatRegister value = ToFloatRegister(ins->value());
             if (size == 32)
                 value = value.singleOverlay();
 
-            masm.ma_vstr(value, HeapReg, ptrReg, 0, 0, Assembler::Below);
+            masm.ma_vstr(value, HeapReg, ptrReg, scratch, 0, Assembler::Below);
         } else {
+            ScratchRegisterScope scratch(masm);
             Register value = ToRegister(ins->value());
-            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset, cond);
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, scratch, Offset, cond);
         }
     }
 }
 
 void
 CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
@@ -2836,23 +2895,27 @@ CodeGeneratorARM::visitAsmJSAtomicBinopC
     }
 }
 
 void
 CodeGeneratorARM::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins)
 {
     const MAsmJSPassStackArg* mir = ins->mir();
     Address dst(StackPointer, mir->spOffset());
+    ScratchRegisterScope scratch(masm);
+    SecondScratchRegisterScope scratch2(masm);
+
     if (ins->arg()->isConstant()) {
-        masm.ma_storeImm(Imm32(ToInt32(ins->arg())), dst);
+        masm.ma_mov(Imm32(ToInt32(ins->arg())), scratch);
+        masm.ma_str(scratch, dst, scratch2);
     } else {
         if (ins->arg()->isGeneralReg())
-            masm.ma_str(ToRegister(ins->arg()), dst);
+            masm.ma_str(ToRegister(ins->arg()), dst, scratch);
         else
-            masm.ma_vstr(ToFloatRegister(ins->arg()), dst);
+            masm.ma_vstr(ToFloatRegister(ins->arg()), dst, scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitUDiv(LUDiv* ins)
 {
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
@@ -2861,17 +2924,17 @@ CodeGeneratorARM::visitUDiv(LUDiv* ins)
     Label done;
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir());
 
     masm.ma_udiv(lhs, rhs, output);
 
     // Check for large unsigned result - represent as double.
     if (!ins->mir()->isTruncated()) {
         MOZ_ASSERT(ins->mir()->fallible());
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     // Check for non-zero remainder if not truncating to int.
     if (!ins->mir()->canTruncateRemainder()) {
         MOZ_ASSERT(ins->mir()->fallible());
         {
             ScratchRegisterScope scratch(masm);
@@ -2890,38 +2953,41 @@ CodeGeneratorARM::visitUMod(LUMod* ins)
 {
     Register lhs = ToRegister(ins->lhs());
     Register rhs = ToRegister(ins->rhs());
     Register output = ToRegister(ins->output());
 
     Label done;
     generateUDivModZeroCheck(rhs, output, &done, ins->snapshot(), ins->mir());
 
-    masm.ma_umod(lhs, rhs, output);
+    {
+        ScratchRegisterScope scratch(masm);
+        masm.ma_umod(lhs, rhs, output, scratch);
+    }
 
     // Check for large unsigned result - represent as double.
     if (!ins->mir()->isTruncated()) {
         MOZ_ASSERT(ins->mir()->fallible());
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     if (done.used())
         masm.bind(&done);
 }
 
 template<class T>
 void
 CodeGeneratorARM::generateUDivModZeroCheck(Register rhs, Register output, Label* done,
                                            LSnapshot* snapshot, T* mir)
 {
     if (!mir)
         return;
     if (mir->canBeDivideByZero()) {
-        masm.ma_cmp(rhs, Imm32(0));
+        masm.as_cmp(rhs, Imm8(0));
         if (mir->isTruncated()) {
             if (mir->trapOnError()) {
                 masm.ma_b(wasm::JumpTarget::IntegerDivideByZero, Assembler::Equal);
             } else {
                 Label skip;
                 masm.ma_b(&skip, Assembler::NotEqual);
                 // Infinity|0 == 0
                 masm.ma_mov(Imm32(0), output);
@@ -2962,98 +3028,108 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSo
     if (gen->compilingAsmJS())
         masm.callWithABI(wasm::SymbolicAddress::aeabi_uidivmod);
     else
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod));
 
     // uidivmod returns the quotient in r0, and the remainder in r1.
     if (div && !div->canTruncateRemainder()) {
         MOZ_ASSERT(div->fallible());
-        masm.ma_cmp(r1, Imm32(0));
+        masm.as_cmp(r1, Imm8(0));
         bailoutIf(Assembler::NonZero, ins->snapshot());
     }
 
     // Bailout for big unsigned results
     if ((div && !div->isTruncated()) || (mod && !mod->isTruncated())) {
         DebugOnly<bool> isFallible = (div && div->fallible()) || (mod && mod->fallible());
         MOZ_ASSERT(isFallible);
-        masm.ma_cmp(output, Imm32(0));
+        masm.as_cmp(output, Imm8(0));
         bailoutIf(Assembler::LessThan, ins->snapshot());
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGeneratorARM::visitEffectiveAddress(LEffectiveAddress* ins)
 {
     const MEffectiveAddress* mir = ins->mir();
     Register base = ToRegister(ins->base());
     Register index = ToRegister(ins->index());
     Register output = ToRegister(ins->output());
+
+    ScratchRegisterScope scratch(masm);
+
     masm.as_add(output, base, lsl(index, mir->scale()));
-    masm.ma_add(Imm32(mir->displacement()), output);
+    masm.ma_add(Imm32(mir->displacement()), output, scratch);
 }
 
 void
 CodeGeneratorARM::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins)
 {
     const MWasmLoadGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
+
+    ScratchRegisterScope scratch(masm);
+
     if (mir->type() == MIRType::Int32) {
-        masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()));
+        masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr), ToRegister(ins->output()), scratch);
     } else if (mir->type() == MIRType::Float32) {
         VFPRegister vd(ToFloatRegister(ins->output()));
-        masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay());
+        masm.ma_vldr(Address(GlobalReg, addr), vd.singleOverlay(), scratch);
     } else {
         MOZ_ASSERT(mir->type() == MIRType::Double);
-        masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()));
+        masm.ma_vldr(Address(GlobalReg, addr), ToFloatRegister(ins->output()), scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins)
 {
     const MWasmLoadGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     MOZ_ASSERT(mir->type() == MIRType::Int64);
     Register64 output = ToOutRegister64(ins);
 
-    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low);
-    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64LOW_OFFSET), output.low, scratch);
+    masm.ma_dtr(IsLoad, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), output.high, scratch);
 }
 
 void
 CodeGeneratorARM::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
 {
     const MWasmStoreGlobalVar* mir = ins->mir();
     MIRType type = mir->value()->type();
 
+    ScratchRegisterScope scratch(masm);
+
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     if (type == MIRType::Int32) {
-        masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()));
+        masm.ma_dtr(IsStore, GlobalReg, Imm32(addr), ToRegister(ins->value()), scratch);
     } else if (type == MIRType::Float32) {
         VFPRegister vd(ToFloatRegister(ins->value()));
-        masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr));
+        masm.ma_vstr(vd.singleOverlay(), Address(GlobalReg, addr), scratch);
     } else {
         MOZ_ASSERT(type == MIRType::Double);
-        masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr));
+        masm.ma_vstr(ToFloatRegister(ins->value()), Address(GlobalReg, addr), scratch);
     }
 }
 
 void
 CodeGeneratorARM::visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins)
 {
     const MWasmStoreGlobalVar* mir = ins->mir();
     unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias;
     MOZ_ASSERT (mir->value()->type() == MIRType::Int64);
     Register64 input = ToRegister64(ins->value());
 
-    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low);
-    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64LOW_OFFSET), input.low, scratch);
+    masm.ma_dtr(IsStore, GlobalReg, Imm32(addr + INT64HIGH_OFFSET), input.high, scratch);
 }
 
 void
 CodeGeneratorARM::visitNegI(LNegI* ins)
 {
     Register input = ToRegister(ins->input());
     masm.ma_neg(input, ToRegister(ins->output()));
 }
@@ -3123,52 +3199,53 @@ CodeGeneratorARM::visitWasmTruncateToInt
             masm.compareFloat(input, input);
         else
             MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
         masm.ma_b(ool->entry(), Assembler::VFP_Unordered);
     }
 
     ScratchDoubleScope scratchScope(masm);
+    ScratchRegisterScope scratchReg(masm);
     FloatRegister scratch = scratchScope.uintOverlay();
 
     // ARM conversion instructions clamp the value to ensure it fits within the
     // target's type bounds, so every time we see those, we need to check the
     // input.
     if (mir->isUnsigned()) {
         if (fromType == MIRType::Double)
             masm.ma_vcvt_F64_U32(input, scratch);
         else if (fromType == MIRType::Float32)
             masm.ma_vcvt_F32_U32(input, scratch);
         else
             MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
         masm.ma_vxfer(scratch, output);
 
         // int32_t(UINT32_MAX) == -1.
-        masm.ma_cmp(output, Imm32(-1));
-        masm.ma_cmp(output, Imm32(0), Assembler::NotEqual);
+        masm.ma_cmp(output, Imm32(-1), scratchReg);
+        masm.as_cmp(output, Imm8(0), Assembler::NotEqual);
         masm.ma_b(ool->entry(), Assembler::Equal);
 
         masm.bind(ool->rejoin());
         return;
     }
 
     scratch = scratchScope.sintOverlay();
 
     if (fromType == MIRType::Double)
         masm.ma_vcvt_F64_I32(input, scratch);
     else if (fromType == MIRType::Float32)
         masm.ma_vcvt_F32_I32(input, scratch);
     else
         MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
 
     masm.ma_vxfer(scratch, output);
-    masm.ma_cmp(output, Imm32(INT32_MAX));
-    masm.ma_cmp(output, Imm32(INT32_MIN), Assembler::NotEqual);
+    masm.ma_cmp(output, Imm32(INT32_MAX), scratchReg);
+    masm.ma_cmp(output, Imm32(INT32_MIN), scratchReg, Assembler::NotEqual);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGeneratorARM::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
 {
@@ -3194,18 +3271,19 @@ CodeGeneratorARM::visitWasmTruncateToInt
     masm.passABIArg(inputDouble, MoveOp::DOUBLE);
     if (lir->mir()->isUnsigned())
         masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToUint64);
     else
         masm.callWithABI(wasm::SymbolicAddress::TruncateDoubleToInt64);
 
     masm.Pop(input);
 
-    masm.ma_cmp(output.high, Imm32(0x80000000));
-    masm.ma_cmp(output.low, Imm32(0x00000000), Assembler::Equal);
+    ScratchRegisterScope scratch(masm);
+    masm.ma_cmp(output.high, Imm32(0x80000000), scratch);
+    masm.as_cmp(output.low, Imm8(0x00000000), Assembler::Equal);
     masm.ma_b(ool->entry(), Assembler::Equal);
 
     masm.bind(ool->rejoin());
 
     MOZ_ASSERT(ReturnReg64 == output);
 }
 
 void
@@ -3331,21 +3409,23 @@ CodeGeneratorARM::visitCopySignF(LCopySi
     FloatRegister output = ToFloatRegister(ins->getDef(0));
 
     Register lhsi = ToRegister(ins->getTemp(0));
     Register rhsi = ToRegister(ins->getTemp(1));
 
     masm.ma_vxfer(lhs, lhsi);
     masm.ma_vxfer(rhs, rhsi);
 
+    ScratchRegisterScope scratch(masm);
+
     // Clear lhs's sign.
-    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi);
+    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch);
 
     // Keep rhs's sign.
-    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi);
+    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch);
 
     // Combine.
     masm.ma_orr(lhsi, rhsi, rhsi);
 
     masm.ma_vxfer(rhsi, output);
 }
 
 void
@@ -3357,21 +3437,23 @@ CodeGeneratorARM::visitCopySignD(LCopySi
 
     Register lhsi = ToRegister(ins->getTemp(0));
     Register rhsi = ToRegister(ins->getTemp(1));
 
     // Manipulate high words of double inputs.
     masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 1);
     masm.as_vxfer(rhsi, InvalidReg, rhs, Assembler::FloatToCore, Assembler::Always, 1);
 
+    ScratchRegisterScope scratch(masm);
+
     // Clear lhs's sign.
-    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi);
+    masm.ma_and(Imm32(INT32_MAX), lhsi, lhsi, scratch);
 
     // Keep rhs's sign.
-    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi);
+    masm.ma_and(Imm32(INT32_MIN), rhsi, rhsi, scratch);
 
     // Combine.
     masm.ma_orr(lhsi, rhsi, rhsi);
 
     // Reconstruct the output.
     masm.as_vxfer(lhsi, InvalidReg, lhs, Assembler::FloatToCore, Assembler::Always, 0);
     masm.ma_vxfer(lhsi, rhsi, output);
 }
@@ -3675,23 +3757,24 @@ void
 CodeGeneratorARM::visitAsmSelectI64(LAsmSelectI64* lir)
 {
     Register cond = ToRegister(lir->condExpr());
     const LInt64Allocation falseExpr = lir->falseExpr();
 
     Register64 out = ToOutRegister64(lir);
     MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, "true expr is reused for input");
 
-    masm.ma_cmp(cond, Imm32(0));
+    masm.as_cmp(cond, Imm8(0));
     if (falseExpr.low().isRegister()) {
         masm.ma_mov(ToRegister(falseExpr.low()), out.low, LeaveCC, Assembler::Equal);
         masm.ma_mov(ToRegister(falseExpr.high()), out.high, LeaveCC, Assembler::Equal);
     } else {
-        masm.ma_ldr(ToAddress(falseExpr.low()), out.low, Offset, Assembler::Equal);
-        masm.ma_ldr(ToAddress(falseExpr.high()), out.high, Offset, Assembler::Equal);
+        ScratchRegisterScope scratch(masm);
+        masm.ma_ldr(ToAddress(falseExpr.low()), out.low, scratch, Offset, Assembler::Equal);
+        masm.ma_ldr(ToAddress(falseExpr.high()), out.high, scratch, Offset, Assembler::Equal);
     }
 }
 
 void
 CodeGeneratorARM::visitAsmReinterpretFromI64(LAsmReinterpretFromI64* lir)
 {
     MOZ_ASSERT(lir->mir()->type() == MIRType::Double);
     MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64);
@@ -3742,13 +3825,13 @@ CodeGeneratorARM::visitCtzI64(LCtzI64* l
     masm.move32(Imm32(0), output.high);
 }
 
 void
 CodeGeneratorARM::visitTestI64AndBranch(LTestI64AndBranch* lir)
 {
     Register64 input = ToRegister64(lir->getInt64Operand(0));
 
-    masm.ma_cmp(input.high, Imm32(0));
+    masm.as_cmp(input.high, Imm8(0));
     jumpToBlock(lir->ifTrue(), Assembler::NonZero);
-    masm.ma_cmp(input.low, Imm32(0));
+    masm.as_cmp(input.low, Imm8(0));
     emitBranch(Assembler::NonZero, lir->ifTrue(), lir->ifFalse());
 }
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -65,46 +65,52 @@ void
 MacroAssembler::and32(Register src, Register dest)
 {
     ma_and(src, dest, SetCC);
 }
 
 void
 MacroAssembler::and32(Imm32 imm, Register dest)
 {
-    ma_and(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_and(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::and32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_and(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_and(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::and32(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_and(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::andPtr(Register src, Register dest)
 {
     ma_and(src, dest);
 }
 
 void
 MacroAssembler::andPtr(Imm32 imm, Register dest)
 {
-    ma_and(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_and(imm, dest, scratch);
 }
 
 void
 MacroAssembler::and64(Imm64 imm, Register64 dest)
 {
     if (imm.low().value != int32_t(0xFFFFFFFF))
         and32(imm.low(), dest.low);
     if (imm.hi().value != int32_t(0xFFFFFFFF))
@@ -133,38 +139,42 @@ void
 MacroAssembler::or32(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, Register dest)
 {
-    ma_orr(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_orr(imm, dest, scratch);
 }
 
 void
 MacroAssembler::or32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_orr(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_orr(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::orPtr(Register src, Register dest)
 {
     ma_orr(src, dest);
 }
 
 void
 MacroAssembler::orPtr(Imm32 imm, Register dest)
 {
-    ma_orr(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_orr(imm, dest, scratch);
 }
 
 void
 MacroAssembler::and64(Register64 src, Register64 dest)
 {
     and32(src.low, dest.low);
     and32(src.high, dest.high);
 }
@@ -187,109 +197,121 @@ void
 MacroAssembler::xor32(Register src, Register dest)
 {
     ma_eor(src, dest, SetCC);
 }
 
 void
 MacroAssembler::xor32(Imm32 imm, Register dest)
 {
-    ma_eor(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_eor(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::xorPtr(Register src, Register dest)
 {
     ma_eor(src, dest);
 }
 
 void
 MacroAssembler::xorPtr(Imm32 imm, Register dest)
 {
-    ma_eor(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_eor(imm, dest, scratch);
 }
 
 // ===============================================================
 // Arithmetic functions
 
 void
 MacroAssembler::add32(Register src, Register dest)
 {
     ma_add(src, dest, SetCC);
 }
 
 void
 MacroAssembler::add32(Imm32 imm, Register dest)
 {
-    ma_add(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::add32(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(dest, scratch);
-    ma_add(imm, scratch, SetCC);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_add(imm, scratch, scratch2, SetCC);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::addPtr(Register src, Register dest)
 {
     ma_add(src, dest);
 }
 
 void
 MacroAssembler::addPtr(Imm32 imm, Register dest)
 {
-    ma_add(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest, scratch);
 }
 
 void
 MacroAssembler::addPtr(ImmWord imm, Register dest)
 {
     addPtr(Imm32(imm.value), dest);
 }
 
 void
 MacroAssembler::addPtr(Imm32 imm, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(dest, scratch);
-    addPtr(imm, scratch);
-    storePtr(scratch, dest);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
+    ma_add(imm, scratch, scratch2);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::addPtr(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_add(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::add64(Register64 src, Register64 dest)
 {
     ma_add(src.low, dest.low, SetCC);
     ma_adc(src.high, dest.high);
 }
 
 void
 MacroAssembler::add64(Imm32 imm, Register64 dest)
 {
-    ma_add(imm, dest.low, SetCC);
-    ma_adc(Imm32(0), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm, dest.low, scratch, SetCC);
+    as_adc(dest.high, dest.high, Imm8(0), LeaveCC);
 }
 
 void
 MacroAssembler::add64(Imm64 imm, Register64 dest)
 {
-    ma_add(imm.low(), dest.low, SetCC);
-    ma_adc(imm.hi(), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_add(imm.low(), dest.low, scratch, SetCC);
+    ma_adc(imm.hi(), dest.high, scratch, LeaveCC);
 }
 
 void
 MacroAssembler::addDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vadd(dest, src, dest);
 }
 
@@ -303,68 +325,77 @@ void
 MacroAssembler::sub32(Register src, Register dest)
 {
     ma_sub(src, dest, SetCC);
 }
 
 void
 MacroAssembler::sub32(Imm32 imm, Register dest)
 {
-    ma_sub(imm, dest, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm, dest, scratch, SetCC);
 }
 
 void
 MacroAssembler::sub32(const Address& src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    load32(src, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(src, scratch, scratch2);
     ma_sub(scratch, dest, SetCC);
 }
 
 void
 MacroAssembler::subPtr(Register src, Register dest)
 {
     ma_sub(src, dest);
 }
 
 void
 MacroAssembler::subPtr(Register src, const Address& dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(dest, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(dest, scratch, scratch2);
     ma_sub(src, scratch);
-    storePtr(scratch, dest);
+    ma_str(scratch, dest, scratch2);
 }
 
 void
 MacroAssembler::subPtr(Imm32 imm, Register dest)
 {
-    ma_sub(imm, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm, dest, scratch);
 }
 
 void
 MacroAssembler::subPtr(const Address& addr, Register dest)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(addr, scratch);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(addr, scratch, scratch2);
     ma_sub(scratch, dest);
 }
 
 void
 MacroAssembler::sub64(Register64 src, Register64 dest)
 {
     ma_sub(src.low, dest.low, SetCC);
     ma_sbc(src.high, dest.high, LeaveCC);
 }
 
 void
 MacroAssembler::sub64(Imm64 imm, Register64 dest)
 {
-    ma_sub(imm.low(), dest.low, SetCC);
-    ma_sbc(imm.hi(), dest.high, LeaveCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(imm.low(), dest.low, scratch, SetCC);
+    ma_sbc(imm.hi(), dest.high, scratch, LeaveCC);
 }
 
 void
 MacroAssembler::subDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vsub(dest, src, dest);
 }
 
@@ -383,52 +414,56 @@ MacroAssembler::mul32(Register rhs, Regi
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest)
 {
     // LOW32  = LOW(LOW(dest) * LOW(imm));
     // HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
     //        + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
     //        + HIGH(LOW(dest) * LOW(imm)) [carry]
 
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
     // HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
-    ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister);
-    as_mul(dest.high, dest.high, ScratchRegister);
+    ma_mov(Imm32(imm.value & 0xFFFFFFFFL), scratch);
+    as_mul(dest.high, dest.high, scratch);
 
     // high:low = LOW(dest) * LOW(imm);
-    as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister);
+    as_umull(scratch2, scratch, dest.low, scratch);
 
     // HIGH(dest) += high;
-    as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+    as_add(dest.high, dest.high, O2Reg(scratch2));
 
     // HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
     if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
-        as_add(secondScratchReg_, dest.low, lsl(dest.low, 2));
+        as_add(scratch2, dest.low, lsl(dest.low, 2));
     else
         MOZ_CRASH("Not supported imm");
-    as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
+    as_add(dest.high, dest.high, O2Reg(scratch2));
 
     // LOW(dest) = low;
-    ma_mov(ScratchRegister, dest.low);
+    ma_mov(scratch, dest.low);
 }
 
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest, const Register temp)
 {
     // LOW32  = LOW(LOW(dest) * LOW(src));                                  (1)
     // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits]   (2)
     //        + LOW(LOW(dest) * HIGH(src)) [multiply dest into upper bits]  (3)
     //        + HIGH(LOW(dest) * LOW(src)) [carry]                          (4)
 
     MOZ_ASSERT(temp != dest.high && temp != dest.low);
 
     // Compute mul64
-    ma_mul(dest.high, imm.low(), dest.high); // (2)
-    ma_mul(dest.low, imm.hi(), temp); // (3)
+    ScratchRegisterScope scratch(*this);
+    ma_mul(dest.high, imm.low(), dest.high, scratch); // (2)
+    ma_mul(dest.low, imm.hi(), temp, scratch); // (3)
     ma_add(dest.high, temp, temp);
-    ma_umull(dest.low, imm.low(), dest.high, dest.low); // (4) + (1)
+    ma_umull(dest.low, imm.low(), dest.high, dest.low, scratch); // (4) + (1)
     ma_add(temp, dest.high, dest.high);
 }
 
 void
 MacroAssembler::mul64(const Register64& src, const Register64& dest, const Register temp)
 {
     // LOW32  = LOW(LOW(dest) * LOW(src));                                  (1)
     // HIGH32 = LOW(HIGH(dest) * LOW(src)) [multiply src into upper bits]   (2)
@@ -462,39 +497,44 @@ void
 MacroAssembler::mulDouble(FloatRegister src, FloatRegister dest)
 {
     ma_vmul(dest, src, dest);
 }
 
 void
 MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest)
 {
-    movePtr(imm, ScratchRegister);
-    loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
-    mulDouble(ScratchDoubleReg, dest);
+    ScratchRegisterScope scratch(*this);
+    ScratchDoubleScope scratchDouble(*this);
+
+    movePtr(imm, scratch);
+    ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
+    mulDouble(scratchDouble, dest);
 }
 
 void
 MacroAssembler::quotient32(Register rhs, Register srcDest, bool isUnsigned)
 {
     MOZ_ASSERT(HasIDIV());
     if (isUnsigned)
         ma_udiv(srcDest, rhs, srcDest);
     else
         ma_sdiv(srcDest, rhs, srcDest);
 }
 
 void
 MacroAssembler::remainder32(Register rhs, Register srcDest, bool isUnsigned)
 {
     MOZ_ASSERT(HasIDIV());
+
+    ScratchRegisterScope scratch(*this);
     if (isUnsigned)
-        ma_umod(srcDest, rhs, srcDest);
+        ma_umod(srcDest, rhs, srcDest, scratch);
     else
-        ma_smod(srcDest, rhs, srcDest);
+        ma_smod(srcDest, rhs, srcDest, scratch);
 }
 
 void
 MacroAssembler::divFloat32(FloatRegister src, FloatRegister dest)
 {
     ma_vdiv_f32(dest, src, dest);
 }
 
@@ -509,34 +549,34 @@ MacroAssembler::inc64(AbsoluteAddress de
 {
     ScratchRegisterScope scratch(*this);
 
     ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex);
 
     ma_mov(Imm32((int32_t)dest.addr), scratch);
     ma_ldrd(EDtrAddr(scratch, EDtrOffImm(0)), r0, r1);
 
-    ma_add(Imm32(1), r0, SetCC);
-    ma_adc(Imm32(0), r1, LeaveCC);
+    as_add(r0, r0, Imm8(1), SetCC);
+    as_adc(r1, r1, Imm8(0), LeaveCC);
 
     ma_strd(r0, r1, EDtrAddr(scratch, EDtrOffImm(0)));
     ma_ldrd(EDtrAddr(sp, EDtrOffImm(8)), r0, r1, PostIndex);
 }
 
 void
 MacroAssembler::neg32(Register reg)
 {
     ma_neg(reg, reg, SetCC);
 }
 
 void
 MacroAssembler::neg64(Register64 reg)
 {
-    ma_rsb(Imm32(0), reg.low, SetCC);
-    ma_rsc(Imm32(0), reg.high);
+    as_rsb(reg.low, reg.low, Imm8(0), SetCC);
+    as_rsc(reg.high, reg.high, Imm8(0));
 }
 
 void
 MacroAssembler::negateDouble(FloatRegister reg)
 {
     ma_vneg(reg, reg);
 }
 
@@ -626,23 +666,23 @@ MacroAssembler::lshift64(Imm32 imm, Regi
 
 void
 MacroAssembler::lshift64(Register unmaskedShift, Register64 dest)
 {
     // dest.high = dest.high << shift | dest.low << shift - 32 | dest.low >> 32 - shift
     // Note: one of the two dest.low shift will always yield zero due to negative shift.
 
     ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, lsl(dest.high, shift));
-    ma_sub(shift, Imm32(32), shift);
+    as_sub(shift, shift, Imm8(32));
     as_orr(dest.high, dest.high, lsl(dest.low, shift));
     ma_neg(shift, shift);
     as_orr(dest.high, dest.high, lsr(dest.low, shift));
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsl(dest.low, shift));
 }
 
 void
 MacroAssembler::lshift32(Register src, Register dest)
 {
     ma_lsl(src, dest, dest);
 }
@@ -705,29 +745,29 @@ MacroAssembler::rshift64Arithmetic(Regis
     Label proceed;
 
     // dest.low = dest.low >>> shift | dest.high <<< 32 - shift
     // if (shift - 32 >= 0)
     //   dest.low |= dest.high >>> shift - 32
     // Note: Negative shifts yield a zero as result, except for the signed
     //       right shift. Therefore we need to test for it and only do it if
     //       it isn't negative.
+    ScratchRegisterScope shift(*this);
 
-    ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsr(dest.low, shift));
-    ma_rsb(shift, Imm32(32), shift);
+    as_rsb(shift, shift, Imm8(32));
     as_orr(dest.low, dest.low, lsl(dest.high, shift));
     ma_neg(shift, shift, SetCC);
     ma_b(&proceed, Signed);
 
     as_orr(dest.low, dest.low, asr(dest.high, shift));
 
     bind(&proceed);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, asr(dest.high, shift));
 }
 
 void
 MacroAssembler::rshift32Arithmetic(Register src, Register dest)
 {
     ma_asr(src, dest, dest);
 }
@@ -759,23 +799,23 @@ MacroAssembler::rshift64(Imm32 imm, Regi
 
 void
 MacroAssembler::rshift64(Register unmaskedShift, Register64 dest)
 {
     // dest.low = dest.low >> shift | dest.high >> shift - 32 | dest.high << 32 - shift
     // Note: one of the two dest.high shifts will always yield zero due to negative shift.
 
     ScratchRegisterScope shift(*this);
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.low, lsr(dest.low, shift));
-    ma_sub(shift, Imm32(32), shift);
+    as_sub(shift, shift, Imm8(32));
     as_orr(dest.low, dest.low, lsr(dest.high, shift));
     ma_neg(shift, shift);
     as_orr(dest.low, dest.low, lsl(dest.high, shift));
-    ma_and(Imm32(0x3f), unmaskedShift, shift);
+    as_and(shift, unmaskedShift, Imm8(0x3f));
     as_mov(dest.high, lsr(dest.high, shift));
 }
 
 // ===============================================================
 // Rotate functions
 void
 MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest)
 {
@@ -783,17 +823,18 @@ MacroAssembler::rotateLeft(Imm32 count, 
         ma_rol(count, input, dest);
     else
         ma_mov(input, dest);
 }
 
 void
 MacroAssembler::rotateLeft(Register count, Register input, Register dest)
 {
-    ma_rol(count, input, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_rol(count, input, dest, scratch);
 }
 
 void
 MacroAssembler::rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
 {
     MOZ_ASSERT(temp == InvalidReg);
     MOZ_ASSERT(input.low != dest.high && input.high != dest.low);
 
@@ -828,45 +869,44 @@ MacroAssembler::rotateLeft64(Register sh
     MOZ_ASSERT(temp != src.low && temp != src.high);
     MOZ_ASSERT(shift != src.low && shift != src.high);
     MOZ_ASSERT(temp != InvalidReg);
 
     ScratchRegisterScope shift_value(*this);
     Label high, done;
 
     ma_mov(src.high, temp);
-    ma_and(Imm32(0x3f), shift, shift_value);
-
-    ma_cmp(shift_value, Imm32(32));
+    as_and(shift_value, shift, Imm8(0x3f));
+    as_cmp(shift_value, Imm8(32));
     ma_b(&high, GreaterThanOrEqual);
 
     // high = high << shift | low >> 32 - shift
     // low = low << shift | high >> 32 - shift
     as_mov(dest.high, lsl(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsr(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsl(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsr(temp, shift_value));
 
     ma_b(&done);
 
     // A 32 - 64 shift is a 0 - 32 shift in the other direction.
     bind(&high);
-    ma_rsb(Imm32(64), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(64));
 
     as_mov(dest.high, lsr(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsl(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsr(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsl(temp, shift_value));
 
     bind(&done);
 }
 
 void
 MacroAssembler::rotateRight(Imm32 count, Register input, Register dest)
 {
@@ -919,45 +959,44 @@ MacroAssembler::rotateRight64(Register s
     MOZ_ASSERT(temp != src.low && temp != src.high);
     MOZ_ASSERT(shift != src.low && shift != src.high);
     MOZ_ASSERT(temp != InvalidReg);
 
     ScratchRegisterScope shift_value(*this);
     Label high, done;
 
     ma_mov(src.high, temp);
-    ma_and(Imm32(0x3f), shift, shift_value);
-
-    ma_cmp(shift_value, Imm32(32));
+    as_and(shift_value, shift, Imm8(0x3f));
+    as_cmp(shift_value, Imm8(32));
     ma_b(&high, GreaterThanOrEqual);
 
     // high = high >> shift | low << 32 - shift
     // low = low >> shift | high << 32 - shift
     as_mov(dest.high, lsr(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsl(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsr(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsl(temp, shift_value));
 
     ma_b(&done);
 
     // A 32 - 64 shift is a 0 - 32 shift in the other direction.
     bind(&high);
-    ma_rsb(Imm32(64), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(64));
 
     as_mov(dest.high, lsl(src.high, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.high, dest.high, lsr(src.low, shift_value));
 
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_mov(dest.low, lsl(src.low, shift_value));
-    ma_rsb(Imm32(32), shift_value);
+    as_rsb(shift_value, shift_value, Imm8(32));
     as_orr(dest.low, dest.low, lsr(temp, shift_value));
 
     bind(&done);
 }
 
 // ===============================================================
 // Bit counting functions
 
@@ -966,63 +1005,67 @@ MacroAssembler::clz32(Register src, Regi
 {
     ma_clz(src, dest);
 }
 
 void
 MacroAssembler::clz64(Register64 src, Register dest)
 {
     ScratchRegisterScope scratch(*this);
+
     ma_clz(src.high, scratch);
-    ma_cmp(scratch, Imm32(32));
+    as_cmp(scratch, Imm8(32));
     ma_mov(scratch, dest, LeaveCC, NotEqual);
     ma_clz(src.low, dest, Equal);
-    ma_add(Imm32(32), dest, LeaveCC, Equal);
+    as_add(dest, dest, Imm8(32), LeaveCC, Equal);
 }
 
 void
 MacroAssembler::ctz32(Register src, Register dest, bool knownNotZero)
 {
-    ma_ctz(src, dest);
+    ScratchRegisterScope scratch(*this);
+    ma_ctz(src, dest, scratch);
 }
 
 void
 MacroAssembler::ctz64(Register64 src, Register dest)
 {
     Label done, high;
-
-    ma_cmp(src.low, Imm32(0));
+    as_cmp(src.low, Imm8(0));
     ma_b(&high, Equal);
 
     ctz32(src.low, dest, /* knownNotZero = */ true);
     ma_b(&done);
 
     bind(&high);
     ctz32(src.high, dest, /* knownNotZero = */ false);
-    ma_add(Imm32(32), dest);
+    as_add(dest, dest, Imm8(32));
 
     bind(&done);
 }
 
 void
 MacroAssembler::popcnt32(Register input,  Register output, Register tmp)
 {
     // Equivalent to GCC output of mozilla::CountPopulation32()
 
+    ScratchRegisterScope scratch(*this);
+
     if (input != output)
         ma_mov(input, output);
     as_mov(tmp, asr(output, 1));
-    ma_and(Imm32(0x55555555), tmp);
+    ma_and(Imm32(0x55555555), tmp, scratch);
     ma_sub(output, tmp, output);
     as_mov(tmp, asr(output, 2));
-    ma_and(Imm32(0x33333333), output);
-    ma_and(Imm32(0x33333333), tmp);
+    ma_mov(Imm32(0x33333333), scratch);
+    ma_and(scratch, output);
+    ma_and(scratch, tmp);
     ma_add(output, tmp, output);
     as_add(output, output, lsr(output, 4));
-    ma_and(Imm32(0xF0F0F0F), output);
+    ma_and(Imm32(0xF0F0F0F), output, scratch);
     as_add(output, output, lsl(output, 8));
     as_add(output, output, lsl(output, 16));
     as_mov(output, asr(output, 24));
 }
 
 void
 MacroAssembler::popcnt64(Register64 src, Register64 dest, Register tmp)
 {
@@ -1053,70 +1096,103 @@ MacroAssembler::branch32(Condition cond,
     ma_cmp(lhs, rhs);
     ma_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branch32(Condition cond, Register lhs, Imm32 rhs, L label)
 {
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(*this);
+
+    ma_cmp(lhs, rhs, scratch);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
-    load32(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
-    // branch32 will use ScratchRegister.
-    AutoRegisterScope scratch(*this, secondScratchReg_);
-    load32(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    loadPtr(lhs, scratch2); // ma_cmp will use the scratch register.
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(*this);
+
+    // Load into scratch.
+    movePtr(ImmWord(uintptr_t(lhs.addr)), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    loadPtr(lhs, scratch2); // ma_cmp will use the scratch register.
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
+
+    // Load into scratch.
+    movePtr(ImmWord(uintptr_t(lhs.addr)), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs, scratch2);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label)
 {
-    // branch32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    load32(lhs, scratch2);
+    SecondScratchRegisterScope scratch2(*this);
+    {
+        ScratchRegisterScope scratch(*this);
+
+        Register base = lhs.base;
+        uint32_t scale = Imm32::ShiftOf(lhs.scale).value;
+
+        // Load lhs into scratch2.
+        if (lhs.offset != 0) {
+            ma_add(base, Imm32(lhs.offset), scratch, scratch2);
+            ma_ldr(DTRAddr(scratch, DtrRegImmShift(lhs.index, LSL, scale)), scratch2);
+        } else {
+            ma_ldr(DTRAddr(base, DtrRegImmShift(lhs.index, LSL, scale)), scratch2);
+        }
+    }
     branch32(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
 {
     ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branch32(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+
+    movePtr(lhs, scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(0)), scratch);
+
+    ma_cmp(scratch, rhs, scratch2);
+    ma_b(label, cond);
 }
 
 void
 MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, Label* label)
 {
     MOZ_ASSERT(cond == Assembler::NotEqual,
                "other condition codes not supported");
 
@@ -1292,68 +1368,71 @@ void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label)
 {
     branchPtr(cond, lhs, ImmWord(uintptr_t(rhs.value)), label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     loadPtr(lhs, scratch2);
     branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     loadPtr(lhs, scratch2);
     branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
 {
-    ScratchRegisterScope scratch(*this);
-    loadPtr(lhs, scratch);
-    branchPtr(cond, scratch, rhs, label);
+    SecondScratchRegisterScope scratch2(*this);
+    loadPtr(lhs, scratch2);
+    branchPtr(cond, scratch2, rhs, label);
 }
 
 template <typename T>
-CodeOffsetJump
+inline CodeOffsetJump
 MacroAssembler::branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label)
 {
-    ma_cmp(lhs, rhs);
+    cmpPtr(lhs, rhs);
     return jumpWithPatch(label, cond);
 }
 
 template <typename T>
-CodeOffsetJump
+inline CodeOffsetJump
 MacroAssembler::branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label)
 {
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
-    ma_ldr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    SecondScratchRegisterScope scratch2(*this);
+    {
+        ScratchRegisterScope scratch(*this);
+        ma_ldr(lhs, scratch2, scratch);
+    }
+    cmpPtr(scratch2, rhs);
     return jumpWithPatch(label, cond);
 }
 
 void
 MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label)
 {
     branchPtr(cond, lhs, rhs, label);
 }
@@ -1386,21 +1465,23 @@ void
 MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
 {
     branchTruncateFloat32ToInt32(src, dest, fail);
 }
 
 void
 MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
 {
-    ScratchFloat32Scope scratch(*this);
-    ma_vcvt_F32_I32(src, scratch.sintOverlay());
-    ma_vxfer(scratch, dest);
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ScratchFloat32Scope scratchFloat32(*this);
+    ScratchRegisterScope scratch(*this);
+
+    ma_vcvt_F32_I32(src, scratchFloat32.sintOverlay());
+    ma_vxfer(scratchFloat32, dest);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 void
 MacroAssembler::branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label)
 {
     compareDouble(lhs, rhs);
@@ -1435,86 +1516,87 @@ MacroAssembler::branchTruncateDoubleMayb
 // was clamped to INT_MIN/INT_MAX, and we can test it. NOTE: if the value
 // really was supposed to be INT_MAX / INT_MIN then it will be wrong.
 //
 // 2. Convert the floating point value to an integer, if it did not fit, then it
 // set one or two bits in the fpcsr. Check those.
 void
 MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
-    ScratchDoubleScope scratch(*this);
-    FloatRegister scratchSIntReg = scratch.sintOverlay();
+    ScratchDoubleScope scratchDouble(*this);
+    FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
+    ScratchRegisterScope scratch(*this);
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     ma_vxfer(scratchSIntReg, dest);
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 template <typename T, typename L>
 void
 MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L label)
 {
     add32(src, dest);
     as_b(label, cond);
 }
 
 template <typename T>
 void
 MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
 {
-    ma_sub(src, dest, SetCC);
+    sub32(src, dest);
     j(cond, label);
 }
 
 void
 MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label)
 {
-    ma_sub(rhs, lhs, SetCC);
+    ScratchRegisterScope scratch(*this);
+    ma_sub(rhs, lhs, scratch, SetCC);
     as_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
     // x86 likes test foo, foo rather than cmp foo, #0.
     // Convert the former into the latter.
     if (lhs == rhs && (cond == Zero || cond == NonZero))
-        ma_cmp(lhs, Imm32(0));
+        as_cmp(lhs, Imm8(0));
     else
         ma_tst(lhs, rhs);
     ma_b(label, cond);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest32(Condition cond, Register lhs, Imm32 rhs, L label)
 {
     MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
-    ma_tst(lhs, rhs);
+    ScratchRegisterScope scratch(*this);
+    ma_tst(lhs, rhs, scratch);
     ma_b(label, cond);
 }
 
 void
 MacroAssembler::branchTest32(Condition cond, const Address& lhs, Imm32 rhs, Label* label)
 {
-    // branchTest32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     load32(lhs, scratch2);
     branchTest32(cond, scratch2, rhs, label);
 }
 
 void
 MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
 {
-    // branchTest32 will use ScratchRegister.
-    AutoRegisterScope scratch2(*this, secondScratchReg_);
+    SecondScratchRegisterScope scratch2(*this);
     load32(lhs, scratch2);
     branchTest32(cond, scratch2, rhs, label);
 }
 
 template <class L>
 void
 MacroAssembler::branchTestPtr(Condition cond, Register lhs, Register rhs, L label)
 {
@@ -1533,21 +1615,23 @@ MacroAssembler::branchTestPtr(Condition 
     branchTest32(cond, lhs, rhs, label);
 }
 
 template <class L>
 void
 MacroAssembler::branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
                              L label)
 {
+    ScratchRegisterScope scratch(*this);
+
     if (cond == Assembler::Zero) {
         MOZ_ASSERT(lhs.low == rhs.low);
         MOZ_ASSERT(lhs.high == rhs.high);
-        ma_orr(lhs.low, lhs.high, ScratchRegister);
-        branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
+        ma_orr(lhs.low, lhs.high, scratch);
+        branchTestPtr(cond, scratch, scratch, label);
     } else {
         MOZ_CRASH("Unsupported condition");
     }
 }
 
 void
 MacroAssembler::branchTestUndefined(Condition cond, Register tag, Label* label)
 {
@@ -1920,35 +2004,41 @@ MacroAssembler::branchTestMagic(Conditio
     branch32(cond, ToPayload(valaddr), Imm32(why), label);
 }
 
 // ========================================================================
 // Memory access primitives.
 void
 MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const Address& addr)
 {
-    ma_vstr(src, addr);
+    ScratchRegisterScope scratch(*this);
+    ma_vstr(src, addr, scratch);
 }
 void
 MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& addr)
 {
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-    ma_vstr(src, addr.base, addr.index, scale, addr.offset);
+    ma_vstr(src, addr.base, addr.index, scratch, scratch2, scale, addr.offset);
 }
 
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const Address& addr)
 {
-    ma_vstr(src.asSingle(), addr);
+    ScratchRegisterScope scratch(*this);
+    ma_vstr(src.asSingle(), addr, scratch);
 }
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
+    ScratchRegisterScope scratch(*this);
+    SecondScratchRegisterScope scratch2(*this);
     uint32_t scale = Imm32::ShiftOf(addr.scale).value;
-    ma_vstr(src.asSingle(), addr.base, addr.index, scale, addr.offset);
+    ma_vstr(src.asSingle(), addr.base, addr.index, scratch, scratch2, scale, addr.offset);
 }
 
 void
 MacroAssembler::storeFloat32x3(FloatRegister src, const Address& dest)
 {
     MOZ_CRASH("NYI");
 }
 void
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -40,48 +40,49 @@ isValueDTRDCandidate(ValueOperand& val)
     return true;
 }
 
 void
 MacroAssemblerARM::convertBoolToInt32(Register source, Register dest)
 {
     // Note that C++ bool is only 1 byte, so zero extend it to clear the
     // higher-order bits.
-    ma_and(Imm32(0xff), source, dest);
+    as_and(dest, source, Imm8(0xff));
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     as_vcvt(dest, dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const Address& src, FloatRegister dest)
 {
     ScratchDoubleScope scratch(asMasm());
-    ma_vldr(src, scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_vldr(src, scratch, scratch2);
     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (src.offset != 0) {
-        ma_mov(base, scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         base = scratch;
-        ma_add(Imm32(src.offset), base);
     }
     ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), scratch);
     convertInt32ToDouble(scratch, dest);
 }
 
 void
 MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
 {
@@ -98,22 +99,27 @@ MacroAssemblerARMCompat::convertUInt64To
 {
     return false;
 }
 
 void
 MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, FloatRegister dest, Register temp)
 {
     MOZ_ASSERT(temp == Register::Invalid());
+    ScratchDoubleScope scratchDouble(asMasm());
+
     convertUInt32ToDouble(src.high, dest);
-    movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
-    loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
-    asMasm().mulDouble(ScratchDoubleReg, dest);
-    convertUInt32ToDouble(src.low, ScratchDoubleReg);
-    asMasm().addDouble(ScratchDoubleReg, dest);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), scratch);
+        ma_vldr(Operand(Address(scratch, 0)).toVFPAddr(), scratchDouble);
+    }
+    asMasm().mulDouble(scratchDouble, dest);
+    convertUInt32ToDouble(src.low, scratchDouble);
+    asMasm().addDouble(scratchDouble, dest);
 }
 
 void
 MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
 {
     // Direct conversions aren't possible.
     VFPRegister dest = VFPRegister(dest_);
     as_vxfer(src, InvalidReg, dest.uintOverlay(), CoreToFloat);
@@ -132,33 +138,35 @@ void MacroAssemblerARM::convertDoubleToF
 void
 MacroAssemblerARM::convertDoubleToInt32(FloatRegister src, Register dest,
                                         Label* fail, bool negativeZeroCheck)
 {
     // Convert the floating point value to an integer, if it did not fit, then
     // when we convert it *back* to a float, it will have a different value,
     // which we can test.
     ScratchDoubleScope scratchDouble(asMasm());
+    ScratchRegisterScope scratch(asMasm());
+
     FloatRegister scratchSIntReg = scratchDouble.sintOverlay();
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     // Move the value into the dest register.
     ma_vxfer(scratchSIntReg, dest);
     ma_vcvt_I32_F64(scratchSIntReg, scratchDouble);
     ma_vcmp(src, scratchDouble);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     if (negativeZeroCheck) {
-        ma_cmp(dest, Imm32(0));
+        as_cmp(dest, Imm8(0));
         // Test and bail for -0.0, when integer result is 0. Move the top word
         // of the double into the output reg, if it is non-zero, then the
         // original value was -0.0.
         as_vxfer(dest, InvalidReg, src, FloatToCore, Assembler::Equal, 1);
-        ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
+        ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal);
         ma_b(fail, Assembler::Equal);
     }
 }
 
 // Checks whether a float32 is representable as a 32-bit integer. If so, the
 // integer is written to the output register. Otherwise, a bailout is taken to
 // the given snapshot. This function overwrites the scratch float register.
 void
@@ -166,40 +174,41 @@ MacroAssemblerARM::convertFloat32ToInt32
                                          Label* fail, bool negativeZeroCheck)
 {
     // Converting the floating point value to an integer and then converting it
     // back to a float32 would not work, as float to int32 conversions are
     // clamping (e.g. float(INT32_MAX + 1) would get converted into INT32_MAX
     // and then back to float(INT32_MAX + 1)).  If this ever happens, we just
     // bail out.
     ScratchFloat32Scope scratchFloat(asMasm());
+    ScratchRegisterScope scratch(asMasm());
 
     FloatRegister ScratchSIntReg = scratchFloat.sintOverlay();
     ma_vcvt_F32_I32(src, ScratchSIntReg);
 
     // Store the result
     ma_vxfer(ScratchSIntReg, dest);
 
     ma_vcvt_I32_F32(ScratchSIntReg, scratchFloat);
     ma_vcmp(src, scratchFloat);
     as_vmrs(pc);
     ma_b(fail, Assembler::VFP_NotEqualOrUnordered);
 
     // Bail out in the clamped cases.
-    ma_cmp(dest, Imm32(0x7fffffff));
-    ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
+    ma_cmp(dest, Imm32(0x7fffffff), scratch);
+    ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 
     if (negativeZeroCheck) {
-        ma_cmp(dest, Imm32(0));
+        as_cmp(dest, Imm8(0));
         // Test and bail for -0.0, when integer result is 0. Move the float into
         // the output reg, and if it is non-zero then the original value was
         // -0.0
         as_vxfer(dest, InvalidReg, VFPRegister(src).singleOverlay(), FloatToCore, Assembler::Equal, 0);
-        ma_cmp(dest, Imm32(0x80000000), Assembler::Equal);
+        ma_cmp(dest, Imm32(0x80000000), scratch, Assembler::Equal);
         ma_b(fail, Assembler::Equal);
     }
 }
 
 void
 MacroAssemblerARM::convertFloat32ToDouble(FloatRegister src, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
@@ -214,17 +223,18 @@ MacroAssemblerARM::convertInt32ToFloat32
     as_vxfer(src, InvalidReg, dest.sintOverlay(), CoreToFloat);
     as_vcvt(dest.singleOverlay(), dest.sintOverlay());
 }
 
 void
 MacroAssemblerARM::convertInt32ToFloat32(const Address& src, FloatRegister dest)
 {
     ScratchFloat32Scope scratch(asMasm());
-    ma_vldr(src, scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_vldr(src, scratch, scratch2);
     as_vcvt(dest, VFPRegister(scratch).sintOverlay());
 }
 
 bool
 MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
                            SBit s, Condition c)
 {
     if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op))
@@ -241,22 +251,23 @@ MacroAssemblerARM::alu_dbl(Register src1
     // don't do second operation if first operation overflowed. This preserves
     // the overflow condition code. Unfortunately, it is horribly brittle.
     as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c);
     as_alu(dest, dest, Operand2(both.snd), op, s, c);
     return true;
 }
 
 void
-MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
+MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch,
                           ALUOp op, SBit s, Condition c)
 {
     // ma_mov should be used for moves.
     MOZ_ASSERT(op != OpMov);
     MOZ_ASSERT(op != OpMvn);
+    MOZ_ASSERT(src1 != scratch);
 
     // As it turns out, if you ask for a compare-like instruction you *probably*
     // want it to set condition codes.
     if (dest == InvalidReg)
         MOZ_ASSERT(s == SetCC);
 
     // The operator gives us the ability to determine how this can be used.
     Imm8 imm8 = Imm8(imm.value);
@@ -264,24 +275,22 @@ MacroAssemblerARM::ma_alu(Register src1,
     if (!imm8.invalid) {
         as_alu(dest, src1, imm8, op, s, c);
         return;
     }
 
     // One instruction, negated:
     Imm32 negImm = imm;
     Register negDest;
-    ALUOp negOp = ALUNeg(op, dest, &negImm, &negDest);
+    ALUOp negOp = ALUNeg(op, dest, scratch, &negImm, &negDest);
     Imm8 negImm8 = Imm8(negImm.value);
-    // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'. For bonus
-    // points, dest can be replaced (nearly always invalid => ScratchRegister)
+    // 'add r1, r2, -15' can be replaced with 'sub r1, r2, 15'.
+    // The dest can be replaced (InvalidReg => scratch).
     // This is useful if we wish to negate tst. tst has an invalid (aka not
-    // used) dest, but its negation is bic *requires* a dest. We can accomodate,
-    // but it will need to clobber *something*, and the scratch register isn't
-    // being used, so...
+    // used) dest, but its negation bic requires a dest.
     if (negOp != OpInvalid && !negImm8.invalid) {
         as_alu(negDest, src1, negImm8, negOp, s, c);
         return;
     }
 
     // Start by attempting to generate a two instruction form. Some things
     // cannot be made into two-inst forms correctly. Namely, adds dest, src,
     // 0xffff. Since we want the condition codes (and don't know which ones
@@ -291,29 +300,16 @@ MacroAssemblerARM::ma_alu(Register src1,
     // instruction variant.
     if (alu_dbl(src1, imm, dest, op, s, c))
         return;
 
     // And try with its negative.
     if (negOp != OpInvalid && alu_dbl(src1, negImm, negDest, negOp, s, c))
         return;
 
-    // Often this code is called with dest as the ScratchRegister.  The register
-    // is logically owned by the caller after this call.
-    const Register& scratch = ScratchRegister;
-    MOZ_ASSERT(src1 != scratch);
-#ifdef DEBUG
-    if (dest != scratch) {
-        // If the destination register is not the scratch register, double check
-        // that the current function does not erase the content of the scratch
-        // register.
-        ScratchRegisterScope assertScratch(asMasm());
-    }
-#endif
-
     ma_mov(imm, scratch, c);
     as_alu(dest, src1, O2Reg(scratch), op, s, c);
 }
 
 void
 MacroAssemblerARM::ma_alu(Register src1, Operand op2, Register dest, ALUOp op,
             SBit s, Assembler::Condition c)
 {
@@ -394,22 +390,19 @@ MacroAssemblerARM::ma_mov(Imm32 imm, Reg
     // Try mov with Imm8 operand.
     Imm8 imm8 = Imm8(imm.value);
     if (!imm8.invalid) {
         as_alu(dest, InvalidReg, imm8, OpMov, LeaveCC, c);
         return;
     }
 
     // Try mvn with Imm8 operand.
-    Imm32 negImm = imm;
-    Register negDest;
-    ALUOp negOp = ALUNeg(OpMov, dest, &negImm, &negDest);
-    Imm8 negImm8 = Imm8(negImm.value);
-    if (negOp != OpInvalid && !negImm8.invalid) {
-        as_alu(negDest, InvalidReg, negImm8, negOp, LeaveCC, c);
+    Imm8 negImm8 = Imm8(~imm.value);
+    if (!negImm8.invalid) {
+        as_alu(dest, InvalidReg, negImm8, OpMvn, LeaveCC, c);
         return;
     }
 
     // Try movw/movt.
     if (HasMOVWT()) {
         // ARMv7 supports movw/movt. movw zero-extends its 16 bit argument,
         // so we can set the register this way. movt leaves the bottom 16
         // bits in tact, so we always need a movw.
@@ -490,20 +483,19 @@ MacroAssemblerARM::ma_asr(Register shift
 
 void
 MacroAssemblerARM::ma_ror(Register shift, Register src, Register dst)
 {
     as_mov(dst, ror(src, shift));
 }
 
 void
-MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst)
-{
-    ScratchRegisterScope scratch(asMasm());
-    ma_rsb(shift, Imm32(32), scratch);
+MacroAssemblerARM::ma_rol(Register shift, Register src, Register dst, AutoRegisterScope& scratch)
+{
+    as_rsb(scratch, shift, Imm8(32));
     as_mov(dst, ror(src, scratch));
 }
 
 // Move not (dest <- ~src)
 void
 MacroAssemblerARM::ma_mvn(Register src1, Register dest, SBit s, Assembler::Condition c)
 {
     as_alu(dest, InvalidReg, O2Reg(src1), OpMvn, s, c);
@@ -526,33 +518,34 @@ MacroAssemblerARM::ma_and(Register src, 
 void
 MacroAssemblerARM::ma_and(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_and(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_and(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpAnd, s, c);
-}
-
-void
-MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_and(Imm32 imm, Register dest, AutoRegisterScope& scratch,
                           SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpAnd, s, c);
+    ma_alu(dest, imm, dest, scratch, OpAnd, s, c);
+}
+
+void
+MacroAssemblerARM::ma_and(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
+                          SBit s, Assembler::Condition c)
+{
+    ma_alu(src1, imm, dest, scratch, OpAnd, s, c);
 }
 
 // Bit clear (dest <- dest & ~imm) or (dest <- src1 & ~src2).
 void
-MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpBic, s, c);
+MacroAssemblerARM::ma_bic(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpBic, s, c);
 }
 
 // Exclusive or.
 void
 MacroAssemblerARM::ma_eor(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_eor(dest, src, dest, s, c);
 }
@@ -560,26 +553,26 @@ MacroAssemblerARM::ma_eor(Register src, 
 void
 MacroAssemblerARM::ma_eor(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_eor(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpEor, s, c);
-}
-
-void
-MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_eor(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpEor, s, c);
+}
+
+void
+MacroAssemblerARM::ma_eor(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
        SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpEor, s, c);
+    ma_alu(src1, imm, dest, scratch, OpEor, s, c);
 }
 
 // Or.
 void
 MacroAssemblerARM::ma_orr(Register src, Register dest, SBit s, Assembler::Condition c)
 {
     ma_orr(dest, src, dest, s, c);
 }
@@ -587,53 +580,53 @@ MacroAssemblerARM::ma_orr(Register src, 
 void
 MacroAssemblerARM::ma_orr(Register src1, Register src2, Register dest,
                           SBit s, Assembler::Condition c)
 {
     as_orr(dest, src1, O2Reg(src2), s, c);
 }
 
 void
-MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, SBit s, Assembler::Condition c)
-{
-    ma_alu(dest, imm, dest, OpOrr, s, c);
-}
-
-void
-MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest,
+MacroAssemblerARM::ma_orr(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Assembler::Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpOrr, s, c);
+}
+
+void
+MacroAssemblerARM::ma_orr(Imm32 imm, Register src1, Register dest, AutoRegisterScope& scratch,
                           SBit s, Assembler::Condition c)
 {
-    ma_alu(src1, imm, dest, OpOrr, s, c);
+    ma_alu(src1, imm, dest, scratch, OpOrr, s, c);
 }
 
 // Arithmetic-based ops.
 // Add with carry.
 void
-MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpAdc, s, c);
+MacroAssemblerARM::ma_adc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpAdc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_adc(Register src, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src), OpAdc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_adc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpAdc, s, c);
 }
 
 // Add.
 void
-MacroAssemblerARM::ma_add(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpAdd, s, c);
+MacroAssemblerARM::ma_add(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpAdd, s, c);
 }
 
 void
 MacroAssemblerARM::ma_add(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, O2Reg(src1), dest, OpAdd, s, c);
 }
 
@@ -645,45 +638,45 @@ MacroAssemblerARM::ma_add(Register src1,
 
 void
 MacroAssemblerARM::ma_add(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpAdd, s, c);
 }
 
 void
-MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op, dest, OpAdd, s, c);
+MacroAssemblerARM::ma_add(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op, dest, scratch, OpAdd, s, c);
 }
 
 // Subtract with carry.
 void
-MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpSbc, s, c);
+MacroAssemblerARM::ma_sbc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpSbc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpSbc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sbc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpSbc, s, c);
 }
 
 // Subtract.
 void
-MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpSub, s, c);
+MacroAssemblerARM::ma_sub(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpSub, s, c);
 }
 
 void
 MacroAssemblerARM::ma_sub(Register src1, Register dest, SBit s, Condition c)
 {
     ma_alu(dest, Operand(src1), dest, OpSub, s, c);
 }
 
@@ -695,51 +688,51 @@ MacroAssemblerARM::ma_sub(Register src1,
 
 void
 MacroAssemblerARM::ma_sub(Register src1, Operand op, Register dest, SBit s, Condition c)
 {
     ma_alu(src1, op, dest, OpSub, s, c);
 }
 
 void
-MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op, dest, OpSub, s, c);
+MacroAssemblerARM::ma_sub(Register src1, Imm32 op, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op, dest, scratch, OpSub, s, c);
 }
 
 // Reverse subtract.
 void
-MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpRsb, s, c);
+MacroAssemblerARM::ma_rsb(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpRsb, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(dest), OpRsb, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsb(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsb, s, c);
 }
 
 void
-MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, SBit s, Condition c)
-{
-    ma_alu(src1, op2, dest, OpRsb, s, c);
+MacroAssemblerARM::ma_rsb(Register src1, Imm32 op2, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(src1, op2, dest, scratch, OpRsb, s, c);
 }
 
 // Reverse subtract with carry.
 void
-MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, SBit s, Condition c)
-{
-    ma_alu(dest, imm, dest, OpRsc, s, c);
+MacroAssemblerARM::ma_rsc(Imm32 imm, Register dest, AutoRegisterScope& scratch, SBit s, Condition c)
+{
+    ma_alu(dest, imm, dest, scratch, OpRsc, s, c);
 }
 
 void
 MacroAssemblerARM::ma_rsc(Register src1, Register dest, SBit s, Condition c)
 {
     as_alu(dest, dest, O2Reg(src1), OpRsc, s, c);
 }
 
@@ -747,102 +740,109 @@ void
 MacroAssemblerARM::ma_rsc(Register src1, Register src2, Register dest, SBit s, Condition c)
 {
     as_alu(dest, src1, O2Reg(src2), OpRsc, s, c);
 }
 
 // Compares/tests.
 // Compare negative (sets condition codes as src1 + src2 would).
 void
-MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpCmn, SetCC, c);
+MacroAssemblerARM::ma_cmn(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpCmn, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_cmn(Register src1, Register src2, Condition c)
 {
     as_alu(InvalidReg, src2, O2Reg(src1), OpCmn, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_cmn(Register src1, Operand op, Condition c)
 {
     MOZ_CRASH("Feature NYI");
 }
 
 // Compare (src - src2).
 void
-MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpCmp, SetCC, c);
-}
-
-void
-MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, Condition c)
-{
-    ma_cmp(src1, Imm32(ptr.value), c);
-}
-
-void
-MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, Condition c)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_cmp(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpCmp, SetCC, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmTag tag, Condition c)
+{
+    // ImmTag comparisons can always be done without use of a scratch register.
+    Imm8 negtag = Imm8(-tag.value);
+    MOZ_ASSERT(!negtag.invalid);
+    as_cmn(src1, negtag, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmWord ptr, AutoRegisterScope& scratch, Condition c)
+{
+    ma_cmp(src1, Imm32(ptr.value), scratch, c);
+}
+
+void
+MacroAssemblerARM::ma_cmp(Register src1, ImmGCPtr ptr, AutoRegisterScope& scratch, Condition c)
+{
     ma_mov(ptr, scratch);
     ma_cmp(src1, scratch, c);
 }
 
 void
-MacroAssemblerARM::ma_cmp(Register src1, Operand op, Condition c)
+MacroAssemblerARM::ma_cmp(Register src1, Operand op, AutoRegisterScope& scratch,
+                          AutoRegisterScope& scratch2, Condition c)
 {
     switch (op.getTag()) {
       case Operand::OP2:
         as_cmp(src1, op.toOp2(), c);
         break;
-      case Operand::MEM: {
-        ScratchRegisterScope scratch(asMasm());
-        ma_ldr(op.toAddress(), scratch);
+      case Operand::MEM:
+        ma_ldr(op.toAddress(), scratch, scratch2);
         as_cmp(src1, O2Reg(scratch), c);
         break;
-      }
       default:
         MOZ_CRASH("trying to compare FP and integer registers");
     }
 }
 
 void
 MacroAssemblerARM::ma_cmp(Register src1, Register src2, Condition c)
 {
     as_cmp(src1, O2Reg(src2), c);
 }
 
 // Test for equality, (src1 ^ src2).
 void
-MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpTeq, SetCC, c);
+MacroAssemblerARM::ma_teq(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpTeq, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_teq(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
 
 void
 MacroAssemblerARM::ma_teq(Register src1, Operand op, Condition c)
 {
     as_teq(src1, op.toOp2(), c);
 }
 
 // Test (src1 & src2).
 void
-MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, Condition c)
-{
-    ma_alu(src1, imm, InvalidReg, OpTst, SetCC, c);
+MacroAssemblerARM::ma_tst(Register src1, Imm32 imm, AutoRegisterScope& scratch, Condition c)
+{
+    ma_alu(src1, imm, InvalidReg, scratch, OpTst, SetCC, c);
 }
 
 void
 MacroAssemblerARM::ma_tst(Register src1, Register src2, Condition c)
 {
     as_tst(src1, O2Reg(src2), c);
 }
 
@@ -854,81 +854,79 @@ MacroAssemblerARM::ma_tst(Register src1,
 
 void
 MacroAssemblerARM::ma_mul(Register src1, Register src2, Register dest)
 {
     as_mul(dest, src1, src2);
 }
 
 void
-MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_mul(Register src1, Imm32 imm, Register dest, AutoRegisterScope& scratch)
+{
     ma_mov(imm, scratch);
     as_mul(dest, src1, scratch);
 }
 
 Assembler::Condition
-MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest, Condition cond)
-{
-    ScratchRegisterScope scratch(asMasm());
-
-    // TODO: this operation is illegal on armv6 and earlier if src2 ==
-    // ScratchRegister or src2 == dest.
+MacroAssemblerARM::ma_check_mul(Register src1, Register src2, Register dest,
+                                AutoRegisterScope& scratch, Condition cond)
+{
+    // TODO: this operation is illegal on armv6 and earlier
+    // if src2 == scratch or src2 == dest.
     if (cond == Equal || cond == NotEqual) {
         as_smull(scratch, dest, src1, src2, SetCC);
         return cond;
     }
 
     if (cond == Overflow) {
         as_smull(scratch, dest, src1, src2);
         as_cmp(scratch, asr(dest, 31));
         return NotEqual;
     }
 
     MOZ_CRASH("Condition NYI");
 }
 
 Assembler::Condition
-MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest, Condition cond)
-{
-    ScratchRegisterScope scratch(asMasm());
-
+MacroAssemblerARM::ma_check_mul(Register src1, Imm32 imm, Register dest,
+                                AutoRegisterScope& scratch, Condition cond)
+{
     ma_mov(imm, scratch);
+
     if (cond == Equal || cond == NotEqual) {
         as_smull(scratch, dest, scratch, src1, SetCC);
         return cond;
     }
 
     if (cond == Overflow) {
         as_smull(scratch, dest, scratch, src1);
         as_cmp(scratch, asr(dest, 31));
         return NotEqual;
     }
 
     MOZ_CRASH("Condition NYI");
 }
 
 void
-MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_umull(Register src1, Imm32 imm, Register destHigh, Register destLow,
+                            AutoRegisterScope& scratch)
+{
     ma_mov(imm, scratch);
     as_umull(destHigh, destLow, src1, scratch);
 }
 
 void
 MacroAssemblerARM::ma_umull(Register src1, Register src2, Register destHigh, Register destLow)
 {
     as_umull(destHigh, destLow, src1, src2);
 }
 
 void
 MacroAssemblerARM::ma_mod_mask(Register src, Register dest, Register hold, Register tmp,
-                               int32_t shift)
+                               AutoRegisterScope& scratch, AutoRegisterScope& scratch2, int32_t shift)
 {
     // We wish to compute x % (1<<y) - 1 for a known constant, y.
     //
     // 1. Let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit dividend as
     // a number in base b, namely c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
     //
     // 2. Since both addition and multiplication commute with modulus:
     //   x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
@@ -939,78 +937,70 @@ MacroAssemblerARM::ma_mod_mask(Register 
     //
     // Each c_n can easily be computed by a shift/bitextract, and the modulus
     // can be maintained by simply subtracting by C whenever the number gets
     // over C.
     int32_t mask = (1 << shift) - 1;
     Label head;
 
     // Register 'hold' holds -1 if the value was negative, 1 otherwise. The
-    // ScratchRegister holds the remaining bits that have not been processed lr
+    // scratch reg holds the remaining bits that have not been processed lr
     // serves as a temporary location to store extracted bits into as well as
     // holding the trial subtraction as a temp value dest is the accumulator
     // (and holds the final result)
     //
     // Move the whole value into tmp, setting the codition codes so we can muck
     // with them later.
-    //
-    // Note that we cannot use ScratchRegister in place of tmp here, as ma_and
-    // below on certain architectures move the mask into ScratchRegister before
-    // performing the bitwise and.
     as_mov(tmp, O2Reg(src), SetCC);
     // Zero out the dest.
     ma_mov(Imm32(0), dest);
     // Set the hold appropriately.
     ma_mov(Imm32(1), hold);
     ma_mov(Imm32(-1), hold, Signed);
-    ma_rsb(Imm32(0), tmp, SetCC, Signed);
+    as_rsb(tmp, tmp, Imm8(0), SetCC, Signed);
 
     // Begin the main loop.
     bind(&head);
     {
-        AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-
-        // Extract the bottom bits into lr.
-        ma_and(Imm32(mask), tmp, scratch2);
+        // Extract the bottom bits.
+        ma_and(Imm32(mask), tmp, scratch, scratch2);
         // Add those bits to the accumulator.
-        ma_add(scratch2, dest, dest);
+        ma_add(scratch, dest, dest);
         // Do a trial subtraction, this is the same operation as cmp, but we store
         // the dest.
-        ma_sub(dest, Imm32(mask), scratch2, SetCC);
+        ma_sub(dest, Imm32(mask), scratch, scratch2, SetCC);
         // If (sum - C) > 0, store sum - C back into sum, thus performing a modulus.
-        ma_mov(scratch2, dest, LeaveCC, NotSigned);
+        ma_mov(scratch, dest, LeaveCC, NotSigned);
         // Get rid of the bits that we extracted before, and set the condition codes.
         as_mov(tmp, lsr(tmp, shift), SetCC);
         // If the shift produced zero, finish, otherwise, continue in the loop.
         ma_b(&head, NonZero);
     }
 
     // Check the hold to see if we need to negate the result. Hold can only be
     // 1 or -1, so this will never set the 0 flag.
-    ma_cmp(hold, Imm32(0));
+    as_cmp(hold, Imm8(0));
     // If the hold was non-zero, negate the result to be in line with what JS
     // wants this will set the condition codes if we try to negate.
-    ma_rsb(Imm32(0), dest, SetCC, Signed);
+    as_rsb(dest, dest, Imm8(0), SetCC, Signed);
     // Since the Zero flag is not set by the compare, we can *only* set the Zero
     // flag in the rsb, so Zero is set iff we negated zero (e.g. the result of
     // the computation was -0.0).
 }
 
 void
-MacroAssemblerARM::ma_smod(Register num, Register div, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_smod(Register num, Register div, Register dest, AutoRegisterScope& scratch)
+{
     as_sdiv(scratch, num, div);
     as_mls(dest, num, scratch, div);
 }
 
 void
-MacroAssemblerARM::ma_umod(Register num, Register div, Register dest)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_umod(Register num, Register div, Register dest, AutoRegisterScope& scratch)
+{
     as_udiv(scratch, num, div);
     as_mls(dest, num, scratch, div);
 }
 
 // Division
 void
 MacroAssemblerARM::ma_sdiv(Register num, Register div, Register dest, Condition cond)
 {
@@ -1026,60 +1016,52 @@ MacroAssemblerARM::ma_udiv(Register num,
 // Miscellaneous instructions.
 void
 MacroAssemblerARM::ma_clz(Register src, Register dest, Condition cond)
 {
     as_clz(dest, src, cond);
 }
 
 void
-MacroAssemblerARM::ma_ctz(Register src, Register dest)
+MacroAssemblerARM::ma_ctz(Register src, Register dest, AutoRegisterScope& scratch)
 {
     // int c = __clz(a & -a);
     // return a ? 31 - c : c;
-
-    ScratchRegisterScope scratch(asMasm());
     as_rsb(scratch, src, Imm8(0), SetCC);
     as_and(dest, src, O2Reg(scratch), LeaveCC);
     as_clz(dest, dest);
     as_rsb(dest, dest, Imm8(0x1F), LeaveCC, Assembler::NotEqual);
 }
 
 // Memory.
 // Shortcut for when we know we're transferring 32 bits of data.
 void
 MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Imm32 offset, Register rt,
-                          Index mode, Assembler::Condition cc)
-{
-    ma_dataTransferN(ls, 32, true, rn, offset, rt, mode, cc);
-}
-
-void
-MacroAssemblerARM::ma_dtr(LoadStore ls, Register rn, Register rm, Register rt,
-                          Index mode, Assembler::Condition cc)
-{
-    MOZ_CRASH("Feature NYI");
+                          AutoRegisterScope& scratch, Index mode, Assembler::Condition cc)
+{
+    ma_dataTransferN(ls, 32, true, rn, offset, rt, scratch, mode, cc);
+}
+
+void
+MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr,
+                          AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_str(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 32, mode, rt, addr, cc);
 }
 
 void
-MacroAssemblerARM::ma_dtr(LoadStore ls, Register rt, const Address& addr, Index mode, Condition cc)
-{
-    ma_dataTransferN(ls, 32, true, addr.base, Imm32(addr.offset), rt, mode, cc);
-}
-
-void
-MacroAssemblerARM::ma_str(Register rt, const Address& addr, Index mode, Condition cc)
-{
-    ma_dtr(IsStore, rt, addr, mode, cc);
+MacroAssemblerARM::ma_str(Register rt, const Address& addr, AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dtr(IsStore, rt, addr, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_strd(Register rt, DebugOnly<Register> rt2, EDtrAddr addr, Index mode, Condition cc)
 {
     MOZ_ASSERT((rt.code() & 1) == 0);
     MOZ_ASSERT(rt2.value.code() == rt.code() + 1);
     as_extdtr(IsStore, 64, true, mode, rt, addr, cc);
@@ -1087,19 +1069,19 @@ MacroAssemblerARM::ma_strd(Register rt, 
 
 void
 MacroAssemblerARM::ma_ldr(DTRAddr addr, Register rt, Index mode, Condition cc)
 {
     as_dtr(IsLoad, 32, mode, rt, addr, cc);
 }
 
 void
-MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, Index mode, Condition cc)
-{
-    ma_dtr(IsLoad, rt, addr, mode, cc);
+MacroAssemblerARM::ma_ldr(const Address& addr, Register rt, AutoRegisterScope& scratch, Index mode, Condition cc)
+{
+    ma_dtr(IsLoad, rt, addr, scratch, mode, cc);
 }
 
 void
 MacroAssemblerARM::ma_ldrb(DTRAddr addr, Register rt, Index mode, Condition cc)
 {
     as_dtr(IsLoad, 8, mode, rt, addr, cc);
 }
 
@@ -1142,50 +1124,64 @@ void
 MacroAssemblerARM::ma_strb(Register rt, DTRAddr addr, Index mode, Condition cc)
 {
     as_dtr(IsStore, 8, mode, rt, addr, cc);
 }
 
 // Specialty for moving N bits of data, where n == 8,16,32,64.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                    Register rn, Register rm, Register rt,
-                                    Index mode, Assembler::Condition cc, unsigned shiftAmount)
-{
+                                    Register rn, Register rm, Register rt, AutoRegisterScope& scratch,
+                                    Index mode, Assembler::Condition cc, Scale scale)
+{
+    MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
+
     if (size == 32 || (size == 8 && !IsSigned))
-        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, shiftAmount)), cc);
-
-    ScratchRegisterScope scratch(asMasm());
-
-    if (shiftAmount != 0) {
-        MOZ_ASSERT(rn != scratch);
-        MOZ_ASSERT(rt != scratch);
-        ma_lsl(Imm32(shiftAmount), rm, scratch);
+        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, scale)), cc);
+
+    if (scale != TimesOne) {
+        ma_lsl(Imm32(scale), rm, scratch);
         rm = scratch;
     }
 
     return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc);
 }
 
+// No scratch register is required if scale is TimesOne.
 BufferOffset
 MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
-                                    Register rn, Imm32 offset, Register rt,
+                                    Register rn, Register rm, Register rt,
                                     Index mode, Assembler::Condition cc)
 {
+    MOZ_ASSERT(size == 8 || size == 16 || size == 32 || size == 64);
+    if (size == 32 || (size == 8 && !IsSigned))
+        return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(rm, LSL, TimesOne)), cc);
+    return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(rm)), cc);
+}
+
+
+BufferOffset
+MacroAssemblerARM::ma_dataTransferN(LoadStore ls, int size, bool IsSigned,
+                                    Register rn, Imm32 offset, Register rt, AutoRegisterScope& scratch,
+                                    Index mode, Assembler::Condition cc)
+{
+    MOZ_ASSERT(!(ls == IsLoad && mode == PostIndex && rt == pc),
+               "Large-offset PostIndex loading into PC requires special logic: see ma_popn_pc().");
+
     int off = offset.value;
 
     // We can encode this as a standard ldr.
     if (size == 32 || (size == 8 && !IsSigned) ) {
         if (off < 4096 && off > -4096) {
             // This encodes as a single instruction, Emulating mode's behavior
             // in a multi-instruction sequence is not necessary.
             return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrOffImm(off)), cc);
         }
 
-        // We cannot encode this offset in a a single ldr. For mode == index,
+        // We cannot encode this offset in a single ldr. For mode == index,
         // try to encode it as |add scratch, base, imm; ldr dest, [scratch, +offset]|.
         // This does not wark for mode == PreIndex or mode == PostIndex.
         // PreIndex is simple, just do the add into the base register first,
         // then do a PreIndex'ed load. PostIndexed loads can be tricky.
         // Normally, doing the load with an index of 0, then doing an add would
         // work, but if the destination is the PC, you don't get to execute the
         // instruction after the branch, which will lead to the base register
         // not being updated correctly. Explicitly handle this case, without
@@ -1193,56 +1189,23 @@ MacroAssemblerARM::ma_dataTransferN(Load
 
         // mode == Offset
         //  add   scratch, base, offset_hi
         //  ldr   dest, [scratch, +offset_lo]
         //
         // mode == PreIndex
         //  add   base, base, offset_hi
         //  ldr   dest, [base, +offset_lo]!
-        //
-        // mode == PostIndex, dest == pc
-        //  ldr   scratch, [base]
-        //  add   base, base, offset_hi
-        //  add   base, base, offset_lo
-        //  mov   dest, scratch
-        // PostIndex with the pc as the destination needs to be handled
-        // specially, since in the code below, the write into 'dest' is going to
-        // alter the control flow, so the following instruction would never get
-        // emitted.
-        //
-        // mode == PostIndex, dest != pc
-        //  ldr   dest, [base], offset_lo
-        //  add   base, base, offset_hi
-
-        if (rt == pc && mode == PostIndex && ls == IsLoad) {
-            ScratchRegisterScope scratch(asMasm());
-            ma_mov(rn, scratch);
-            ma_alu(rn, offset, rn, OpAdd);
-            return as_dtr(IsLoad, size, Offset, pc, DTRAddr(scratch, DtrOffImm(0)), cc);
-        }
-
-        // Often this code is called with rt as the ScratchRegister.
-        // The register is logically owned by the caller, so we cannot ask
-        // for exclusive ownership here. If full checking is desired,
-        // this function should take an explicit scratch register argument.
-        const Register& scratch = ScratchRegister;
-        MOZ_ASSERT(rn != scratch);
 
         int bottom = off & 0xfff;
         int neg_bottom = 0x1000 - bottom;
-        // For a regular offset, base == ScratchRegister does what we want.
-        // Modify the scratch register, leaving the actual base unscathed.
-        Register base = scratch;
-        // For the preindex case, we want to just re-use rn as the base
-        // register, so when the base register is updated *before* the load, rn
-        // is updated.
-        if (mode == PreIndex)
-            base = rn;
+
+        MOZ_ASSERT(rn != scratch);
         MOZ_ASSERT(mode != PostIndex);
+
         // At this point, both off - bottom and off + neg_bottom will be
         // reasonable-ish quantities.
         //
         // Note a neg_bottom of 0x1000 can not be encoded as an immediate
         // negative offset in the instruction and this occurs when bottom is
         // zero, so this case is guarded against below.
         if (off < 0) {
             Operand2 sub_off = Imm8(-(off - bottom)); // sub_off = bottom - off
@@ -1279,18 +1242,16 @@ MacroAssemblerARM::ma_dataTransferN(Load
                 as_add(scratch, rn, sub_off, LeaveCC,  cc);
                 return as_dtr(ls, size, Offset, rt, DTRAddr(scratch, DtrOffImm(-neg_bottom)), cc);
             }
         }
 
         ma_mov(offset, scratch);
         return as_dtr(ls, size, mode, rt, DTRAddr(rn, DtrRegImmShift(scratch, LSL, 0)));
     } else {
-        ScratchRegisterScope scratch(asMasm());
-
         // Should attempt to use the extended load/store instructions.
         if (off < 256 && off > -256)
             return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffImm(off)), cc);
 
         // We cannot encode this offset in a single extldr. Try to encode it as
         // an add scratch, base, imm; extldr dest, [scratch, +offset].
         int bottom = off & 0xff;
         int neg_bottom = 0x100 - bottom;
@@ -1346,31 +1307,48 @@ MacroAssemblerARM::ma_dataTransferN(Load
         ma_mov(offset, scratch);
         return as_extdtr(ls, size, IsSigned, mode, rt, EDtrAddr(rn, EDtrOffReg(scratch)), cc);
     }
 }
 
 void
 MacroAssemblerARM::ma_pop(Register r)
 {
-    ma_dtr(IsLoad, sp, Imm32(4), r, PostIndex);
+    as_dtr(IsLoad, 32, PostIndex, r, DTRAddr(sp, DtrOffImm(4)));
+}
+
+void
+MacroAssemblerARM::ma_popn_pc(Imm32 n, AutoRegisterScope& scratch, AutoRegisterScope& scratch2)
+{
+    // pc <- [sp]; sp += n
+    int32_t nv = n.value;
+
+    if (nv < 4096 && nv >= -4096) {
+        as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(nv)));
+    } else {
+        ma_mov(sp, scratch);
+        ma_add(Imm32(n), sp, scratch2);
+        as_dtr(IsLoad, 32, Offset, pc, DTRAddr(scratch, DtrOffImm(0)));
+    }
 }
 
 void
 MacroAssemblerARM::ma_push(Register r)
 {
-    // Pushing sp is not well defined: use two instructions.
-    if (r == sp) {
-        ScratchRegisterScope scratch(asMasm());
-        ma_mov(sp, scratch);
-        ma_dtr(IsStore, sp, Imm32(-4), scratch, PreIndex);
-        return;
-    }
-
-    ma_dtr(IsStore, sp, Imm32(-4), r, PreIndex);
+    MOZ_ASSERT(r != sp, "Use ma_push_sp().");
+    as_dtr(IsStore, 32, PreIndex, r, DTRAddr(sp, DtrOffImm(-4)));
+}
+
+void
+MacroAssemblerARM::ma_push_sp(Register r, AutoRegisterScope& scratch)
+{
+    // Pushing sp is not well-defined: use two instructions.
+    MOZ_ASSERT(r == sp);
+    ma_mov(sp, scratch);
+    as_dtr(IsStore, 32, PreIndex, scratch, DTRAddr(sp, DtrOffImm(-4)));
 }
 
 void
 MacroAssemblerARM::ma_vpop(VFPRegister r)
 {
     startFloatTransferM(IsLoad, sp, IA, WriteBack);
     transferFloatReg(r);
     finishFloatTransfer();
@@ -1720,26 +1698,25 @@ MacroAssemblerARM::ma_vxfer(Register src
 
 void
 MacroAssemblerARM::ma_vxfer(Register src1, Register src2, FloatRegister dest, Condition cc)
 {
     as_vxfer(src1, src2, VFPRegister(dest), CoreToFloat, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt, Condition cc)
+MacroAssemblerARM::ma_vdtr(LoadStore ls, const Address& addr, VFPRegister rt,
+                           AutoRegisterScope& scratch, Condition cc)
 {
     int off = addr.offset;
     MOZ_ASSERT((off & 3) == 0);
     Register base = addr.base;
     if (off > -1024 && off < 1024)
         return as_vdtr(ls, rt, Operand(addr).toVFPAddr(), cc);
 
-    ScratchRegisterScope scratch(asMasm());
-
     // We cannot encode this offset in a a single ldr. Try to encode it as an
     // add scratch, base, imm; ldr dest, [scratch, +offset].
     int bottom = off & (0xff << 2);
     int neg_bottom = (0x100 << 2) - bottom;
     // At this point, both off - bottom and off + neg_bottom will be
     // reasonable-ish quantities.
     //
     // Note a neg_bottom of 0x400 can not be encoded as an immediate negative
@@ -1775,60 +1752,70 @@ MacroAssemblerARM::ma_vdtr(LoadStore ls,
         if (!sub_off.invalid && bottom != 0) {
             // Guarded against by: bottom != 0
             MOZ_ASSERT(neg_bottom < 0x400);
             // sub_off = neg_bottom + off
             as_add(scratch, base, sub_off, LeaveCC, cc);
             return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(-neg_bottom)), cc);
         }
     }
-    ma_add(base, Imm32(off), scratch, LeaveCC, cc);
+
+    // Safe to use scratch as dest, since ma_add() overwrites dest at the end
+    // and can't use it as internal scratch since it may also == base.
+    ma_add(base, Imm32(off), scratch, scratch, LeaveCC, cc);
     return as_vdtr(ls, rt, VFPAddr(scratch, VFPOffImm(0)), cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vldr(VFPAddr addr, VFPRegister dest, Condition cc)
 {
     return as_vdtr(IsLoad, dest, addr, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, Condition cc)
-{
-    return ma_vdtr(IsLoad, addr, dest, cc);
+MacroAssemblerARM::ma_vldr(const Address& addr, VFPRegister dest, AutoRegisterScope& scratch, Condition cc)
+{
+    return ma_vdtr(IsLoad, addr, dest, scratch, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, int32_t shift,
-                           Condition cc)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_vldr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           int32_t shift, Condition cc)
+{
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
-    return ma_vldr(Address(scratch, 0), src, cc);
+    return as_vdtr(IsLoad, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
 }
 
 BufferOffset
 MacroAssemblerARM::ma_vstr(VFPRegister src, VFPAddr addr, Condition cc)
 {
     return as_vdtr(IsStore, src, addr, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, Condition cc)
-{
-    return ma_vdtr(IsStore, addr, src, cc);
+MacroAssemblerARM::ma_vstr(VFPRegister src, const Address& addr, AutoRegisterScope& scratch, Condition cc)
+{
+    return ma_vdtr(IsStore, addr, src, scratch, cc);
 }
 
 BufferOffset
-MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
-                           int32_t offset, Condition cc)
-{
-    ScratchRegisterScope scratch(asMasm());
+MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           AutoRegisterScope& scratch2, int32_t shift, int32_t offset, Condition cc)
+{
     as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
-    return ma_vstr(src, Address(scratch, offset), cc);
+    return ma_vstr(src, Address(scratch, offset), scratch2, cc);
+}
+
+// Without an offset, no second scratch register is necessary.
+BufferOffset
+MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, AutoRegisterScope& scratch,
+                           int32_t shift, Condition cc)
+{
+    as_add(scratch, base, lsl(index, shift), LeaveCC, cc);
+    return as_vdtr(IsStore, src, Operand(Address(scratch, 0)).toVFPAddr(), cc);
 }
 
 bool
 MacroAssemblerARMCompat::buildOOLFakeExitFrame(void* fakeReturnAddr)
 {
     DebugOnly<uint32_t> initialDepth = asMasm().framePushed();
     uint32_t descriptor = MakeFrameDescriptor(asMasm().framePushed(), JitFrame_IonJS,
                                               ExitFrameLayout::Size());
@@ -1892,117 +1879,126 @@ MacroAssemblerARMCompat::movePtr(wasm::S
 {
     append(AsmJSAbsoluteAddress(CodeOffset(currentOffset()), imm));
     ma_movPatchable(Imm32(-1), dest, Always);
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 8, false, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     if (src.offset == 0) {
         ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
     } else {
-        ScratchRegisterScope scratch(asMasm());
-        ma_add(base, Imm32(src.offset), scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
     }
 }
 
 void
 MacroAssemblerARMCompat::load8SignExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load8SignExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // ARMv7 does not have LSL on an index register with an extended load.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrsb(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load16ZeroExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 16, false, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load16ZeroExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // ARMv7 does not have LSL on an index register with an extended load.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load16SignExtend(const Address& address, Register dest)
 {
-    ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsLoad, 16, true, address.base, Imm32(address.offset), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::load16SignExtend(const BaseIndex& src, Register dest)
 {
     Register index = src.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // We don't have LSL on index register yet.
     if (src.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(src.scale), index, scratch);
         index = scratch;
     }
 
     if (src.offset != 0) {
         if (index != scratch) {
             ma_mov(index, scratch);
             index = scratch;
         }
-        ma_add(Imm32(src.offset), index);
+        ma_add(Imm32(src.offset), index, scratch2);
     }
     ma_ldrsh(EDtrAddr(src.base, EDtrOffReg(index)), dest);
 }
 
 void
 MacroAssemblerARMCompat::load32(const Address& address, Register dest)
 {
     loadPtr(address, dest);
@@ -2018,34 +2014,35 @@ void
 MacroAssemblerARMCompat::load32(AbsoluteAddress address, Register dest)
 {
     loadPtr(address, dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(const Address& address, Register dest)
 {
-    ma_ldr(address, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(address, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(const BaseIndex& src, Register dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     if (src.offset != 0) {
-        ScratchRegisterScope scratch(asMasm());
-        ma_mov(base, scratch);
-        ma_add(Imm32(src.offset), scratch);
+        ma_add(base, Imm32(src.offset), scratch, scratch2);
         ma_ldr(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
-        return;
+    } else {
+        ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
     }
-
-    ma_ldr(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPtr(AbsoluteAddress address, Register dest)
 {
     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
     movePtr(ImmWord(uintptr_t(address.addr)), dest);
     loadPtr(Address(dest, 0), dest);
@@ -2057,160 +2054,202 @@ MacroAssemblerARMCompat::loadPtr(wasm::S
     MOZ_ASSERT(dest != pc); // Use dest as a scratch register.
     movePtr(address, dest);
     loadPtr(Address(dest, 0), dest);
 }
 
 void
 MacroAssemblerARMCompat::loadPrivate(const Address& address, Register dest)
 {
-    ma_ldr(ToPayload(address), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(ToPayload(address), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadDouble(const Address& address, FloatRegister dest)
 {
-    ma_vldr(address, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(address, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadDouble(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), dest);
+    ma_vldr(Address(scratch, offset), dest, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::loadFloatAsDouble(const Address& address, FloatRegister dest)
 {
+    ScratchRegisterScope scratch(asMasm());
+
     VFPRegister rt = dest;
-    ma_vldr(address, rt.singleOverlay());
+    ma_vldr(address, rt.singleOverlay(), scratch);
     as_vcvt(rt, rt.singleOverlay());
 }
 
 void
 MacroAssemblerARMCompat::loadFloatAsDouble(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
     VFPRegister rt = dest;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), rt.singleOverlay());
+    ma_vldr(Address(scratch, offset), rt.singleOverlay(), scratch2);
     as_vcvt(rt, rt.singleOverlay());
 }
 
 void
 MacroAssemblerARMCompat::loadFloat32(const Address& address, FloatRegister dest)
 {
-    ma_vldr(address, VFPRegister(dest).singleOverlay());
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(address, VFPRegister(dest).singleOverlay(), scratch);
 }
 
 void
 MacroAssemblerARMCompat::loadFloat32(const BaseIndex& src, FloatRegister dest)
 {
     // VFP instructions don't even support register Base + register Index modes,
     // so just add the index, then handle the offset like normal.
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
     as_add(scratch, base, lsl(index, scale));
-    ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay());
+    ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay(), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_mov(imm, scratch2);
     store8(scratch2, address);
 }
 
 void
 MacroAssemblerARMCompat::store8(Register src, const Address& address)
 {
-    ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 8, false, address.base, Imm32(address.offset), src, scratch);
 }
 
 void
 MacroAssemblerARMCompat::store8(Imm32 imm, const BaseIndex& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    ma_mov(imm, scratch2);
-    store8(scratch2, dest);
+    Register base = dest.base;
+    uint32_t scale = Imm32::ShiftOf(dest.scale).value;
+
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    if (dest.offset != 0) {
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_mov(imm, scratch2);
+        ma_strb(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_mov(imm, scratch2);
+        ma_strb(scratch2, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
+    }
 }
 
 void
 MacroAssemblerARMCompat::store8(Register src, const BaseIndex& dest)
 {
     Register base = dest.base;
     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (dest.offset != 0) {
-        ma_add(base, Imm32(dest.offset), scratch);
-        base = scratch;
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_strb(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
     }
-    ma_strb(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 void
 MacroAssemblerARMCompat::store16(Imm32 imm, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_mov(imm, scratch2);
     store16(scratch2, address);
 }
 
 void
 MacroAssemblerARMCompat::store16(Register src, const Address& address)
 {
-    ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 16, false, address.base, Imm32(address.offset), src, scratch);
 }
 
 void
 MacroAssemblerARMCompat::store16(Imm32 imm, const BaseIndex& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    Register index = dest.index;
+
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    // We don't have LSL on index register yet.
+    if (dest.scale != TimesOne) {
+        ma_lsl(Imm32::ShiftOf(dest.scale), index, scratch);
+        index = scratch;
+    }
+
+    if (dest.offset != 0) {
+        ma_add(index, Imm32(dest.offset), scratch, scratch2);
+        index = scratch;
+    }
+
     ma_mov(imm, scratch2);
-    store16(scratch2, dest);
+    ma_strh(scratch2, EDtrAddr(dest.base, EDtrOffReg(index)));
 }
 
 void
 MacroAssemblerARMCompat::store16(Register src, const BaseIndex& address)
 {
     Register index = address.index;
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     // We don't have LSL on index register yet.
     if (address.scale != TimesOne) {
         ma_lsl(Imm32::ShiftOf(address.scale), index, scratch);
         index = scratch;
     }
 
     if (address.offset != 0) {
-        ma_add(index, Imm32(address.offset), scratch);
+        ma_add(index, Imm32(address.offset), scratch, scratch2);
         index = scratch;
     }
     ma_strh(src, EDtrAddr(address.base, EDtrOffReg(index)));
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, AbsoluteAddress address)
 {
@@ -2221,42 +2260,57 @@ void
 MacroAssemblerARMCompat::store32(Register src, const Address& address)
 {
     storePtr(src, address);
 }
 
 void
 MacroAssemblerARMCompat::store32(Imm32 src, const Address& address)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    move32(src, scratch2);
-    storePtr(scratch2, address);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    move32(src, scratch);
+    ma_str(scratch, address, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest)
 {
+    Register base = dest.base;
+    uint32_t scale = Imm32::ShiftOf(dest.scale).value;
+
     ScratchRegisterScope scratch(asMasm());
-    ma_mov(imm, scratch);
-    store32(scratch, dest);
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    if (dest.offset != 0) {
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_mov(imm, scratch2);
+        ma_str(scratch2, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_mov(imm, scratch);
+        ma_str(scratch, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
+    }
+
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest)
 {
     Register base = dest.base;
     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     if (dest.offset != 0) {
-        ma_add(base, Imm32(dest.offset), scratch2);
-        base = scratch2;
+        ma_add(base, Imm32(dest.offset), scratch, scratch2);
+        ma_str(src, DTRAddr(scratch, DtrRegImmShift(dest.index, LSL, scale)));
+    } else {
+        ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
     }
-    ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 template <typename T>
 void
 MacroAssemblerARMCompat::storePtr(ImmWord imm, T address)
 {
     ScratchRegisterScope scratch(asMasm());
     movePtr(imm, scratch);
@@ -2286,31 +2340,32 @@ MacroAssemblerARMCompat::storePtr(ImmGCP
 }
 
 template void MacroAssemblerARMCompat::storePtr<Address>(ImmGCPtr imm, Address address);
 template void MacroAssemblerARMCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIndex address);
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const Address& address)
 {
-    ma_str(src, address);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_str(src, address, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, const BaseIndex& address)
 {
     store32(src, address);
 }
 
 void
 MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest)
 {
     ScratchRegisterScope scratch(asMasm());
     movePtr(ImmWord(uintptr_t(dest.addr)), scratch);
-    storePtr(src, Address(scratch, 0));
+    ma_str(src, DTRAddr(scratch, DtrOffImm(0)));
 }
 
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     if (HasVFPv3()) {
         Label notSplit;
@@ -2326,33 +2381,32 @@ MacroAssembler::clampDoubleToUint8(Float
         }
 
         // Move the fixed point value into an integer register.
         {
             ScratchFloat32Scope scratchFloat(*this);
             as_vxfer(output, InvalidReg, scratchFloat.uintOverlay(), FloatToCore);
         }
 
+        ScratchRegisterScope scratch(*this);
+
         // See if this value *might* have been an exact integer after adding
         // 0.5. This tests the 1/2 through 1/16,777,216th places, but 0.5 needs
         // to be tested out to the 1/140,737,488,355,328th place.
-        ma_tst(output, Imm32(0x00ffffff));
+        ma_tst(output, Imm32(0x00ffffff), scratch);
         // Convert to a uint8 by shifting out all of the fraction bits.
         ma_lsr(Imm32(24), output, output);
         // If any of the bottom 24 bits were non-zero, then we're good, since
         // this number can't be exactly XX.0
         ma_b(&notSplit, NonZero);
-        {
-            ScratchRegisterScope scratch(*this);
-            as_vxfer(scratch, InvalidReg, input, FloatToCore);
-            ma_cmp(scratch, Imm32(0));
-        }
+        as_vxfer(scratch, InvalidReg, input, FloatToCore);
+        as_cmp(scratch, Imm8(0));
         // If the lower 32 bits of the double were 0, then this was an exact number,
         // and it should be even.
-        ma_bic(Imm32(1), output, LeaveCC, Zero);
+        as_bic(output, output, Imm8(1), LeaveCC, Zero);
         bind(&notSplit);
     } else {
         ScratchDoubleScope scratchDouble(*this);
         MOZ_ASSERT(input != scratchDouble);
         loadConstantDouble(0.5, scratchDouble);
 
         Label outOfRange;
         ma_vcmpz(input);
@@ -2360,115 +2414,120 @@ MacroAssembler::clampDoubleToUint8(Float
         ma_vadd(input, scratchDouble, input);
         // Do the conversion to an integer.
         as_vcvt(VFPRegister(scratchDouble).uintOverlay(), VFPRegister(input));
         // Copy the converted value out.
         as_vxfer(output, InvalidReg, scratchDouble, FloatToCore);
         as_vmrs(pc);
         ma_mov(Imm32(0), output, Overflow);  // NaN => 0
         ma_b(&outOfRange, Overflow);  // NaN
-        ma_cmp(output, Imm32(0xff));
+        as_cmp(output, Imm8(0xff));
         ma_mov(Imm32(0xff), output, Above);
         ma_b(&outOfRange, Above);
         // Convert it back to see if we got the same value back.
         as_vcvt(scratchDouble, VFPRegister(scratchDouble).uintOverlay());
         // Do the check.
         as_vcmp(scratchDouble, input);
         as_vmrs(pc);
-        ma_bic(Imm32(1), output, LeaveCC, Zero);
+        as_bic(output, output, Imm8(1), LeaveCC, Zero);
         bind(&outOfRange);
     }
 }
 
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Imm32 rhs)
 {
-    MOZ_ASSERT(lhs != ScratchRegister);
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(lhs, rhs, scratch);
 }
 
 void
 MacroAssemblerARMCompat::cmp32(Register lhs, Register rhs)
 {
     ma_cmp(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmWord rhs)
 {
-    MOZ_ASSERT(lhs != ScratchRegister);
-    ma_cmp(lhs, Imm32(rhs.value));
+    cmp32(lhs, Imm32(rhs.value));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmPtr rhs)
 {
-    return cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
+    cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, Register rhs)
 {
     ma_cmp(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, ImmGCPtr rhs)
 {
-    ma_cmp(lhs, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(lhs, rhs, scratch);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(Register lhs, Imm32 rhs)
 {
-    ma_cmp(lhs, rhs);
+    cmp32(lhs, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Register rhs)
 {
     ScratchRegisterScope scratch(asMasm());
-    loadPtr(lhs, scratch);
-    cmpPtr(scratch, rhs);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmWord rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, Imm32(rhs.value));
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, Imm32(rhs.value), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmPtr rhs)
 {
     cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, ImmGCPtr rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::cmpPtr(const Address& lhs, Imm32 rhs)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    loadPtr(lhs, scratch2);
-    ma_cmp(scratch2, rhs);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(lhs, scratch, scratch2);
+    ma_cmp(scratch, rhs, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::setStackArg(Register reg, uint32_t arg)
 {
-    ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg);
+    ScratchRegisterScope scratch(asMasm());
+    ma_dataTransferN(IsStore, 32, true, sp, Imm32(arg * sizeof(intptr_t)), reg, scratch);
 }
 
 void
 MacroAssemblerARMCompat::minMaxDouble(FloatRegister srcDest, FloatRegister second, bool canBeNaN,
                                       bool isMax)
 {
     FloatRegister first = srcDest;
 
@@ -2605,17 +2664,18 @@ MacroAssemblerARMCompat::testBoolean(Ass
     return cond;
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     Assembler::Condition actual = (cond == Equal) ? Below : AboveOrEqual;
-    ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR));
+    ScratchRegisterScope scratch(asMasm());
+    ma_cmp(value.typeReg(), ImmTag(JSVAL_TAG_CLEAR), scratch);
     return actual;
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testNull(Assembler::Condition cond, const ValueOperand& value)
 {
     MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     ma_cmp(value.typeReg(), ImmType(JSVAL_TYPE_NULL));
@@ -2965,40 +3025,43 @@ MacroAssemblerARMCompat::unboxNonDouble(
 {
     if (operand.payloadReg() != dest)
         ma_mov(operand.payloadReg(), dest);
 }
 
 void
 MacroAssemblerARMCompat::unboxNonDouble(const Address& src, Register dest)
 {
-    ma_ldr(ToPayload(src), dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_ldr(ToPayload(src), dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::unboxNonDouble(const BaseIndex& src, Register dest)
 {
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
     ma_alu(src.base, lsl(src.index, src.scale), scratch, OpAdd);
-    ma_ldr(Address(scratch, src.offset), dest);
+    ma_ldr(Address(scratch, src.offset), dest, scratch2);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const ValueOperand& operand, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
     as_vxfer(operand.payloadReg(), operand.typeReg(),
              VFPRegister(dest), CoreToFloat);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const Address& src, FloatRegister dest)
 {
     MOZ_ASSERT(dest.isDouble());
-    ma_vldr(src, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_vldr(src, dest, scratch);
 }
 
 void
 MacroAssemblerARMCompat::unboxValue(const ValueOperand& src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
         asMasm().branchTestInt32(Assembler::NotEqual, src, &notInt32);
@@ -3031,17 +3094,17 @@ MacroAssemblerARMCompat::boxNonDouble(JS
     ma_mov(ImmType(type), dest.typeReg());
 }
 
 void
 MacroAssemblerARMCompat::boolValueToDouble(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister d = VFPRegister(dest);
     loadConstantDouble(1.0, dest);
-    ma_cmp(operand.payloadReg(), Imm32(0));
+    as_cmp(operand.payloadReg(), Imm8(0));
     // If the source is 0, then subtract the dest from itself, producing 0.
     as_vsub(d, d, d, Equal);
 }
 
 void
 MacroAssemblerARMCompat::int32ValueToDouble(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister vfpdest = VFPRegister(dest);
@@ -3053,17 +3116,17 @@ MacroAssemblerARMCompat::int32ValueToDou
     as_vcvt(vfpdest, scratch.sintOverlay());
 }
 
 void
 MacroAssemblerARMCompat::boolValueToFloat32(const ValueOperand& operand, FloatRegister dest)
 {
     VFPRegister d = VFPRegister(dest).singleOverlay();
     loadConstantFloat32(1.0, dest);
-    ma_cmp(operand.payloadReg(), Imm32(0));
+    as_cmp(operand.payloadReg(), Imm8(0));
     // If the source is 0, then subtract the dest from itself, producing 0.
     as_vsub(d, d, d, Equal);
 }
 
 void
 MacroAssemblerARMCompat::int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest)
 {
     // Transfer the integral value to a floating point register.
@@ -3089,27 +3152,31 @@ MacroAssemblerARMCompat::loadConstantFlo
 void
 MacroAssemblerARMCompat::loadInt32OrDouble(const Address& src, FloatRegister dest)
 {
     Label notInt32, end;
 
     // If it's an int, convert to a double.
     {
         ScratchRegisterScope scratch(asMasm());
-
-        ma_ldr(ToType(src), scratch);
+        SecondScratchRegisterScope scratch2(asMasm());
+
+        ma_ldr(ToType(src), scratch, scratch2);
         asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
-        ma_ldr(ToPayload(src), scratch);
+        ma_ldr(ToPayload(src), scratch, scratch2);
         convertInt32ToDouble(scratch, dest);
         ma_b(&end);
     }
 
     // Not an int, just load as double.
     bind(&notInt32);
-    ma_vldr(src, dest);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_vldr(src, dest, scratch);
+    }
     bind(&end);
 }
 
 void
 MacroAssemblerARMCompat::loadInt32OrDouble(Register base, Register index,
                                            FloatRegister dest, int32_t shift)
 {
     Label notInt32, end;
@@ -3118,30 +3185,30 @@ MacroAssemblerARMCompat::loadInt32OrDoub
 
     ScratchRegisterScope scratch(asMasm());
 
     // If it's an int, convert it to double.
     ma_alu(base, lsl(index, shift), scratch, OpAdd);
 
     // Since we only have one scratch register, we need to stomp over it with
     // the tag.
-    ma_ldr(Address(scratch, NUNBOX32_TYPE_OFFSET), scratch);
+    ma_ldr(DTRAddr(scratch, DtrOffImm(NUNBOX32_TYPE_OFFSET)), scratch);
     asMasm().branchTestInt32(Assembler::NotEqual, scratch, &notInt32);
 
     // Implicitly requires NUNBOX32_PAYLOAD_OFFSET == 0: no offset provided
     ma_ldr(DTRAddr(base, DtrRegImmShift(index, LSL, shift)), scratch);
     convertInt32ToDouble(scratch, dest);
     ma_b(&end);
 
     // Not an int, just load as double.
     bind(&notInt32);
     // First, recompute the offset that had been stored in the scratch register
     // since the scratch register was overwritten loading in the type.
     ma_alu(base, lsl(index, shift), scratch, OpAdd);
-    ma_vldr(Address(scratch, 0), dest);
+    ma_vldr(VFPAddr(scratch, VFPOffImm(0)), dest);
     bind(&end);
 }
 
 void
 MacroAssemblerARMCompat::loadConstantDouble(double dp, FloatRegister dest)
 {
     loadConstantDouble(wasm::RawF64(dp), dest);
 }
@@ -3174,24 +3241,26 @@ MacroAssemblerARMCompat::testDoubleTruth
     as_vmrs(pc);
     as_cmp(r0, O2Reg(r0), Overflow);
     return truthy ? NonZero : Zero;
 }
 
 Register
 MacroAssemblerARMCompat::extractObject(const Address& address, Register scratch)
 {
-    ma_ldr(ToPayload(address), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(ToPayload(address), scratch, scratch2);
     return scratch;
 }
 
 Register
 MacroAssemblerARMCompat::extractTag(const Address& address, Register scratch)
 {
-    ma_ldr(ToType(address), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_ldr(ToType(address), scratch, scratch2);
     return scratch;
 }
 
 Register
 MacroAssemblerARMCompat::extractTag(const BaseIndex& address, Register scratch)
 {
     ma_alu(address.base, lsl(address.index, address.scale), scratch, OpAdd, LeaveCC);
     return extractTag(Address(scratch, address.offset), scratch);
@@ -3215,18 +3284,19 @@ MacroAssemblerARMCompat::moveValue(const
 }
 
 /////////////////////////////////////////////////////////////////
 // X86/X64-common (ARM too now) interface.
 /////////////////////////////////////////////////////////////////
 void
 MacroAssemblerARMCompat::storeValue(ValueOperand val, const Address& dst)
 {
-    ma_str(val.payloadReg(), ToPayload(dst));
-    ma_str(val.typeReg(), ToType(dst));
+    SecondScratchRegisterScope scratch2(asMasm());
+    ma_str(val.payloadReg(), ToPayload(dst), scratch2);
+    ma_str(val.typeReg(), ToType(dst), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storeValue(ValueOperand val, const BaseIndex& dest)
 {
     ScratchRegisterScope scratch(asMasm());
 
     if (isValueDTRDCandidate(val) && Abs(dest.offset) <= 255) {
@@ -3321,21 +3391,23 @@ MacroAssemblerARMCompat::loadValue(Addre
             transferReg(val.typeReg());
             finishDataTransfer();
             return;
         }
     }
     // Ensure that loading the payload does not erase the pointer to the Value
     // in memory.
     if (type.base != val.payloadReg()) {
-        ma_ldr(payload, val.payloadReg());
-        ma_ldr(type, val.typeReg());
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(payload, val.payloadReg(), scratch2);
+        ma_ldr(type, val.typeReg(), scratch2);
     } else {
-        ma_ldr(type, val.typeReg());
-        ma_ldr(payload, val.payloadReg());
+        SecondScratchRegisterScope scratch2(asMasm());
+        ma_ldr(type, val.typeReg(), scratch2);
+        ma_ldr(payload, val.payloadReg(), scratch2);
     }
 }
 
 void
 MacroAssemblerARMCompat::tagValue(JSValueType type, Register payload, ValueOperand dest)
 {
     MOZ_ASSERT(dest.typeReg() != dest.payloadReg());
     if (payload != dest.payloadReg())
@@ -3349,131 +3421,135 @@ MacroAssemblerARMCompat::pushValue(Value
     ma_push(val.typeReg());
     ma_push(val.payloadReg());
 }
 
 void
 MacroAssemblerARMCompat::pushValue(const Address& addr)
 {
     ScratchRegisterScope scratch(asMasm());
-    ma_ldr(ToType(addr), scratch);
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_ldr(ToType(addr), scratch, scratch2);
     ma_push(scratch);
-    ma_ldr(ToPayloadAfterStackPush(addr), scratch);
+    ma_ldr(ToPayloadAfterStackPush(addr), scratch, scratch2);
     ma_push(scratch);
 }
 
 void
 MacroAssemblerARMCompat::popValue(ValueOperand val)
 {
     ma_pop(val.payloadReg());
     ma_pop(val.typeReg());
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const Address& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
-        ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch2);
+        ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch);
     else
-        ma_mov(Imm32(jv.s.payload.i32), scratch2);
-    ma_str(scratch2, ToPayload(dest));
+        ma_mov(Imm32(jv.s.payload.i32), scratch);
+    ma_str(scratch, ToPayload(dest), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const Address& dest)
 {
-    ma_str(src, ToPayload(dest));
+    ScratchRegisterScope scratch(asMasm());
+    ma_str(src, ToPayload(dest), scratch);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(const Value& val, const BaseIndex& dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
 
     jsval_layout jv = JSVAL_TO_IMPL(val);
     if (val.isMarkable())
         ma_mov(ImmGCPtr((gc::Cell*)jv.s.payload.ptr), scratch);
     else
         ma_mov(Imm32(jv.s.payload.i32), scratch);
 
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
     // If an offset is used, modify the base so that a [base + index << shift]
     // instruction format can be used.
     if (dest.offset != 0)
-        ma_add(dest.base, Imm32(dest.offset), dest.base);
+        ma_add(dest.base, Imm32(dest.offset), dest.base, scratch2);
 
     as_dtr(IsStore, 32, Offset, scratch,
            DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
 
     // Restore the original value of the base, if necessary.
     if (dest.offset != 0)
-        ma_sub(dest.base, Imm32(dest.offset), dest.base);
+        ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
 }
 
 void
 MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex& dest)
 {
     unsigned shift = ScaleToShift(dest.scale);
     MOZ_ASSERT(shift < 32);
 
+    ScratchRegisterScope scratch(asMasm());
+
     // If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
     // << shift + imm] cannot be encoded into a single instruction, and cannot
     // be integrated into the as_dtr call.
     JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
 
     // Save/restore the base if the BaseIndex has an offset, as above.
     if (dest.offset != 0)
-        ma_add(dest.base, Imm32(dest.offset), dest.base);
+        ma_add(dest.base, Imm32(dest.offset), dest.base, scratch);
 
     // Technically, shift > -32 can be handle by changing LSL to ASR, but should
     // never come up, and this is one less code path to get wrong.
     as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
 
     if (dest.offset != 0)
-        ma_sub(dest.base, Imm32(dest.offset), dest.base);
+        ma_sub(dest.base, Imm32(dest.offset), dest.base, scratch);
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const Address& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    ma_mov(tag, scratch2);
-    ma_str(scratch2, ToType(dest));
+    ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_mov(tag, scratch);
+    ma_str(scratch, ToType(dest), scratch2);
 }
 
 void
 MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex& dest)
 {
     Register base = dest.base;
     Register index = dest.index;
     unsigned shift = ScaleToShift(dest.scale);
-    MOZ_ASSERT(base != ScratchRegister);
-    MOZ_ASSERT(index != ScratchRegister);
-
-    // A value needs to be store a value int base + index << shift + 4.
-    // ARM cannot handle this in a single operand, so a temp register is
-    // required. However, the scratch register is presently in use to hold the
-    // immediate that is being stored into said memory location. Work around
-    // this by modifying the base so the valid [base + index << shift] format
-    // can be used, then restore it.
-    ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
 
     ScratchRegisterScope scratch(asMasm());
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    MOZ_ASSERT(base != scratch && base != scratch2);
+    MOZ_ASSERT(index != scratch && index != scratch2);
+
+    ma_add(base, Imm32(dest.offset + NUNBOX32_TYPE_OFFSET), scratch2, scratch);
     ma_mov(tag, scratch);
-    ma_str(scratch, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
-    ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
+    ma_str(scratch, DTRAddr(scratch2, DtrRegImmShift(index, LSL, shift)));
 }
 
 void
 MacroAssemblerARM::ma_call(ImmPtr dest)
 {
     ma_movPatchable(dest, CallReg, Always);
     as_blx(CallReg);
 }
@@ -3523,78 +3599,94 @@ MacroAssemblerARMCompat::checkStackAlign
 }
 
 void
 MacroAssemblerARMCompat::handleFailureWithHandlerTail(void* handler)
 {
     // Reserve space for exception information.
     int size = (sizeof(ResumeFromException) + 7) & ~7;
 
-    ma_sub(Imm32(size), sp);
+    Imm8 size8(size);
+    as_sub(sp, sp, size8);
     ma_mov(sp, r0);
 
     // Call the handler.
     asMasm().setupUnalignedABICall(r1);
     asMasm().passABIArg(r0);
     asMasm().callWithABI(handler);
 
     Label entryFrame;
     Label catch_;
     Label finally;
     Label return_;
     Label bailout;
 
-    ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, kind)), r0, scratch);
+    }
+
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME),
                       &entryFrame);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN),
                       &return_);
     asMasm().branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout);
 
     breakpoint(); // Invalid kind.
 
     // No exception handler. Load the error value, load the new stack pointer
     // and return from the entry frame.
     bind(&entryFrame);
     moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
 
     // We're going to be returning by the ion calling convention, which returns
     // by ??? (for now, I think ldr pc, [sp]!)
     as_dtr(IsLoad, 32, PostIndex, pc, DTRAddr(sp, DtrOffImm(4)));
 
     // If we found a catch handler, this must be a baseline frame. Restore state
     // and jump to the catch block.
     bind(&catch_);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
     jump(r0);
 
     // If we found a finally block, this must be a baseline frame. Push two
     // values expected by JSOP_RETSUB: BooleanValue(true) and the exception.
     bind(&finally);
     ValueOperand exception = ValueOperand(r1, r2);
     loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception);
-
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r0, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
 
     pushValue(BooleanValue(true));
     pushValue(exception);
     jump(r0);
 
     // Only used in debug mode. Return BaselineFrame->returnValue() to the
     // caller.
     bind(&return_);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, framePointer)), r11, scratch);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp, scratch);
+    }
     loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand);
     ma_mov(r11, sp);
     pop(r11);
 
     // If profiling is enabled, then update the lastProfilingFrame to refer to caller
     // frame before returning.
     {
         Label skipProfilingInstrumentation;
@@ -3606,29 +3698,34 @@ MacroAssemblerARMCompat::handleFailureWi
         bind(&skipProfilingInstrumentation);
     }
 
     ret();
 
     // If we are bailing out to baseline to handle an exception, jump to the
     // bailout tail stub.
     bind(&bailout);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2);
-    ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
-    ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1);
+    {
+        ScratchRegisterScope scratch(asMasm());
+        ma_ldr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), r2, scratch);
+        ma_mov(Imm32(BAILOUT_RETURN_OK), r0);
+        ma_ldr(Address(sp, offsetof(ResumeFromException, target)), r1, scratch);
+    }
     jump(r1);
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testStringTruthy(bool truthy, const ValueOperand& value)
 {
     Register string = value.payloadReg();
     ScratchRegisterScope scratch(asMasm());
-    ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch);
-    ma_cmp(scratch, Imm32(0));
+    SecondScratchRegisterScope scratch2(asMasm());
+
+    ma_dtr(IsLoad, string, Imm32(JSString::offsetOfLength()), scratch, scratch2);
+    as_cmp(scratch, Imm8(0));
     return truthy ? Assembler::NotEqual : Assembler::Equal;
 }
 
 void
 MacroAssemblerARMCompat::floor(FloatRegister input, Register output, Label* bail)
 {
     Label handleZero;
     Label handleNeg;
@@ -3651,31 +3748,31 @@ MacroAssemblerARMCompat::floor(FloatRegi
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0.
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.
     ma_vneg(input, input);
     ma_vcvt_F64_U32(input, scratchDouble.uintOverlay());
     ma_vxfer(scratchDouble.uintOverlay(), output);
     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
     compareDouble(scratchDouble, input);
-    ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
     // Flip the negated input back to its original value.
     ma_vneg(input, input);
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required. Zero is also caught
     // by this case, but floor of a negative number should never be zero.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3705,34 +3802,34 @@ MacroAssemblerARMCompat::floorf(FloatReg
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0.
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing.
     {
         ScratchFloat32Scope scratch(asMasm());
         ma_vneg_f32(input, input);
         ma_vcvt_F32_U32(input, scratch.uintOverlay());
         ma_vxfer(VFPRegister(scratch).uintOverlay(), output);
         ma_vcvt_U32_F32(scratch.uintOverlay(), scratch);
         compareFloat(scratch, input);
-        ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+        as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     }
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
     // Flip the negated input back to its original value.
     ma_vneg_f32(input, input);
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required. Zero is also caught
     // by this case, but floor of a negative number should never be zero.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3768,28 +3865,28 @@ MacroAssemblerARMCompat::ceil(FloatRegis
     ma_neg(output, output, SetCC);
     ma_b(bail, NotSigned);
     ma_b(&fin);
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
     ma_vcvt_F64_U32(input, ScratchUIntReg);
     ma_vxfer(ScratchUIntReg, output);
     ma_vcvt_U32_F64(ScratchUIntReg, scratchDouble);
     compareDouble(scratchDouble, input);
-    ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+    as_add(output, output, Imm8(1), LeaveCC, NotEqual);
     // Bail out if the add overflowed or the result is non positive.
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(bail, Zero);
 
     bind(&fin);
 }
 
@@ -3829,33 +3926,33 @@ MacroAssemblerARMCompat::ceilf(FloatRegi
         ma_b(bail, NotSigned);
         ma_b(&fin);
     }
 
     // Test for 0.0 / -0.0: if the top word of the input double is not zero,
     // then it was -0 and we need to bail out.
     bind(&handleZero);
     as_vxfer(output, InvalidReg, VFPRegister(input).singleOverlay(), FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     // We are in the ]0; +inf] range: truncate integer values, maybe add 1 for
     // non integer values, maybe bail if overflow.
     bind(&handlePos);
     {
         ScratchDoubleScope scratchDouble(asMasm());
         FloatRegister scratchFloat = scratchDouble.asSingle();
         FloatRegister scratchUInt = scratchDouble.uintOverlay();
 
         ma_vcvt_F32_U32(input, scratchUInt);
         ma_vxfer(scratchUInt, output);
         ma_vcvt_U32_F32(scratchUInt, scratchFloat);
         compareFloat(scratchFloat, input);
-        ma_add(output, Imm32(1), output, LeaveCC, NotEqual);
+        as_add(output, output, Imm8(1), LeaveCC, NotEqual);
 
         // Bail on overflow or non-positive result.
         ma_mov(output, output, SetCC);
         ma_b(bail, Signed);
         ma_b(bail, Zero);
     }
 
     bind(&fin);
@@ -3921,17 +4018,17 @@ MacroAssemblerARMCompat::round(FloatRegi
     ma_mov(output, output, SetCC);
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
     // Move the top word of the double into the output reg, if it is non-zero,
     // then the original value was -0.0
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 1);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
     // Negative case, negate, then start dancing. This number may be positive,
     // since we added 0.5.
 
     // Add 0.5 to negative numbers, store the result into tmp
@@ -3941,20 +4038,20 @@ MacroAssemblerARMCompat::round(FloatRegi
     ma_vcvt_F64_U32(tmp, scratchDouble.uintOverlay());
     ma_vxfer(VFPRegister(scratchDouble).uintOverlay(), output);
 
     // -output is now a correctly rounded value, unless the original value was
     // exactly halfway between two integers, at which point, it has been rounded
     // away from zero, when it should be rounded towards \infty.
     ma_vcvt_U32_F64(scratchDouble.uintOverlay(), scratchDouble);
     compareDouble(scratchDouble, tmp);
-    ma_sub(output, Imm32(1), output, LeaveCC, Equal);
+    as_sub(output, output, Imm8(1), LeaveCC, Equal);
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
 
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required, or it was zero,
     // which means the result is actually -0.0 which also requires special
     // handling.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -3998,17 +4095,17 @@ MacroAssemblerARMCompat::roundf(FloatReg
     ma_b(bail, Signed);
     ma_b(&fin);
 
     bind(&handleZero);
 
     // Move the whole float32 into the output reg, if it is non-zero, then the
     // original value was -0.0.
     as_vxfer(output, InvalidReg, input, FloatToCore, Always, 0);
-    ma_cmp(output, Imm32(0));
+    as_cmp(output, Imm8(0));
     ma_b(bail, NonZero);
     ma_b(&fin);
 
     bind(&handleNeg);
 
     // Add 0.5 to negative numbers, storing the result into tmp.
     ma_vneg_f32(input, tmp);
     loadConstantFloat32(0.5f, scratchFloat);
@@ -4029,22 +4126,22 @@ MacroAssemblerARMCompat::roundf(FloatReg
     Label flipSign;
     ma_b(&flipSign, Equal);
 
     // -output is now a correctly rounded value, unless the original value was
     // exactly halfway between two integers, at which point, it has been rounded
     // away from zero, when it should be rounded towards \infty.
     ma_vcvt_U32_F32(tmp.uintOverlay(), tmp);
     compareFloat(tmp, scratchFloat);
-    ma_sub(output, Imm32(1), output, LeaveCC, Equal);
+    as_sub(output, output, Imm8(1), LeaveCC, Equal);
 
     // Negate the output. Since INT_MIN < -INT_MAX, even after adding 1, the
     // result will still be a negative number.
     bind(&flipSign);
-    ma_rsb(output, Imm32(0), output, SetCC);
+    as_rsb(output, output, Imm8(0), SetCC);
 
     // If the result looks non-negative, then this value didn't actually fit
     // into the int range, and special handling is required, or it was zero,
     // which means the result is actually -0.0 which also requires special
     // handling.
     ma_b(bail, NotSigned);
 
     bind(&fin);
@@ -4067,29 +4164,33 @@ namespace jit {
 template<>
 Register
 MacroAssemblerARMCompat::computePointer<BaseIndex>(const BaseIndex& src, Register r)
 {
     Register base = src.base;
     Register index = src.index;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
+
+    ScratchRegisterScope scratch(asMasm());
+
     as_add(r, base, lsl(index, scale));
     if (offset != 0)
-        ma_add(r, Imm32(offset), r);
+        ma_add(r, Imm32(offset), r, scratch);
     return r;
 }
 
 template<>
 Register
 MacroAssemblerARMCompat::computePointer<Address>(const Address& src, Register r)
 {
+    ScratchRegisterScope scratch(asMasm());
     if (src.offset == 0)
         return src.base;
-    ma_add(src.base, Imm32(src.offset), r);
+    ma_add(src.base, Imm32(src.offset), r, scratch);
     return r;
 }
 
 } // namespace jit
 } // namespace js
 
 template<typename T>
 void
@@ -4132,17 +4233,17 @@ template<typename T>
 void
 MacroAssemblerARMCompat::compareExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                               Register oldval, Register newval, Register output)
 {
     Label again;
     Label done;
     ma_dmb(BarrierST);
 
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
+    SecondScratchRegisterScope scratch2(asMasm());
     Register ptr = computePointer(mem, scratch2);
 
     ScratchRegisterScope scratch(asMasm());
 
     bind(&again);
     switch (nbytes) {
       case 1:
         as_ldrexb(output, ptr);
@@ -4228,17 +4329,17 @@ template<typename T>
 void
 MacroAssemblerARMCompat::atomicExchangeARMv7(int nbytes, bool signExtend, const T& mem,
                                              Register value, Register output)
 {
     Label again;
     Label done;
     ma_dmb(BarrierST