Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 03 Apr 2016 21:39:40 -0400
changeset 291521 cfd51e67b26e
parent 291496 d50240348923 (current diff)
parent 291520 57cc6679cc66 (diff)
child 291523 fc4a988ca8d6
child 291528 600f96d25f8c
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
Merge inbound to m-c. a=merge
js/src/jit-test/tests/atomics/inline-fence.js
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -160,17 +160,17 @@ nsCoreUtils::DispatchTouchEvent(EventMes
   WidgetTouchEvent event(true, aMessage, aRootWidget);
 
   event.mTime = PR_IntervalNow();
 
   // XXX: Touch has an identifier of -1 to hint that it is synthesized.
   RefPtr<dom::Touch> t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY),
                                         LayoutDeviceIntPoint(1, 1), 0.0f, 1.0f);
   t->SetTarget(aContent);
-  event.touches.AppendElement(t);
+  event.mTouches.AppendElement(t);
   nsEventStatus status = nsEventStatus_eIgnore;
   aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status);
 }
 
 uint32_t
 nsCoreUtils::GetAccessKeyFor(nsIContent* aContent)
 {
   // Accesskeys are registered by @accesskey attribute only. At first check
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -833,17 +833,17 @@ var SessionStoreInternal = {
         // monitor the progress of network loads.
         if (gDebuggingEnabled) {
           Services.obs.notifyObservers(browser, NOTIFY_TAB_RESTORED, null);
         }
 
         SessionStoreInternal._resetLocalTabRestoringState(tab);
         SessionStoreInternal.restoreNextTab();
 
-        this._sendTabRestoredNotification(tab);
+        this._sendTabRestoredNotification(tab, data.isRemotenessUpdate);
         break;
       case "SessionStore:crashedTabRevived":
         // The browser was revived by navigating to a different page
         // manually, so we remove it from the ignored browser set.
         this._crashedBrowsers.delete(browser.permanentKey);
         break;
       case "SessionStore:error":
         this.reportInternalError(data);
@@ -3281,16 +3281,19 @@ var SessionStoreInternal = {
 
   /**
    * Kicks off restoring the given tab.
    *
    * @param aTab
    *        the tab to restore
    * @param aLoadArguments
    *        optional load arguments used for loadURI()
+   * @param aRemotenessSwitch
+   *        true if we're restoring a tab's content because we flipped
+   *        its remoteness (out-of-process) state.
    */
   restoreTabContent: function (aTab, aLoadArguments = null) {
     if (aTab.hasAttribute("customizemode")) {
       return;
     }
 
     let browser = aTab.linkedBrowser;
     let window = aTab.ownerDocument.defaultView;
@@ -3304,17 +3307,18 @@ var SessionStoreInternal = {
     }
 
     // We have to mark this tab as restoring first, otherwise
     // the "pending" attribute will be applied to the linked
     // browser, which removes it from the display list. We cannot
     // flip the remoteness of any browser that is not being displayed.
     this.markTabAsRestoring(aTab);
 
-    if (tabbrowser.updateBrowserRemotenessByURL(browser, uri)) {
+    let isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(browser, uri);
+    if (isRemotenessUpdate) {
       // We updated the remoteness, so we need to send the history down again.
       //
       // Start a new epoch to discard all frame script messages relating to a
       // previous epoch. All async messages that are still on their way to chrome
       // will be ignored and don't override any tab data set when restoring.
       let epoch = this.startNextEpoch(browser);
 
       browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory", {
@@ -3327,17 +3331,17 @@ var SessionStoreInternal = {
 
     // If the restored browser wants to show view source content, start up a
     // view source browser that will load the required frame script.
     if (uri && ViewSourceBrowser.isViewSource(uri)) {
       new ViewSourceBrowser(browser);
     }
 
     browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
-      {loadArguments: aLoadArguments});
+      {loadArguments: aLoadArguments, isRemotenessUpdate});
   },
 
   /**
    * Marks a given pending tab as restoring.
    *
    * @param aTab
    *        the pending tab to mark as restoring
    */
@@ -3972,21 +3976,27 @@ var SessionStoreInternal = {
   _sendWindowStateEvent: function ssi_sendWindowStateEvent(aWindow, aType) {
     let event = aWindow.document.createEvent("Events");
     event.initEvent("SSWindowState" + aType, true, false);
     aWindow.dispatchEvent(event);
   },
 
   /**
    * Dispatch the SSTabRestored event for the given tab.
-   * @param aTab the which has been restored
+   * @param aTab
+   *        The tab which has been restored
+   * @param aIsRemotenessUpdate
+   *        True if this tab was restored due to flip from running from
+   *        out-of-main-process to in-main-process or vice-versa.
    */
-  _sendTabRestoredNotification: function ssi_sendTabRestoredNotification(aTab) {
-    let event = aTab.ownerDocument.createEvent("Events");
-    event.initEvent("SSTabRestored", true, false);
+  _sendTabRestoredNotification(aTab, aIsRemotenessUpdate) {
+    let event = aTab.ownerDocument.createEvent("CustomEvent");
+    event.initCustomEvent("SSTabRestored", true, false, {
+      isRemotenessUpdate: aIsRemotenessUpdate,
+    });
     aTab.dispatchEvent(event);
   },
 
   /**
    * @param aWindow
    *        Window reference
    * @returns whether this window's data is still cached in _statesToRestore
    *          because it's not fully loaded yet
--- a/browser/components/sessionstore/StartupPerformance.jsm
+++ b/browser/components/sessionstore/StartupPerformance.jsm
@@ -194,19 +194,26 @@ this.StartupPerformance = {
           this._totalNumberOfWindows += 1;
 
           // Observe the restoration of all tabs. We assume that all tabs of this
           // window will have been restored before `COLLECT_RESULTS_AFTER_MS`.
           // The last call to `observer` will let us determine how long it took
           // to reach that point.
           let win = subject;
 
-          let observer = () => {
-            this._latestRestoredTimeStamp = Date.now();
-            this._totalNumberOfEagerTabs += 1;
+          let observer = (event) => {
+            // We don't care about tab restorations that are due to
+            // a browser flipping from out-of-main-process to in-main-process
+            // or vice-versa. We only care about restorations that are due
+            // to the user switching to a lazily restored tab, or for tabs
+            // that are restoring eagerly.
+            if (!event.detail.isRemotenessUpdate) {
+              this._latestRestoredTimeStamp = Date.now();
+              this._totalNumberOfEagerTabs += 1;
+            }
           };
           win.gBrowser.tabContainer.addEventListener("SSTabRestored", observer);
           this._totalNumberOfTabs += win.gBrowser.tabContainer.itemCount;
 
           // Once we have finished collecting the results, clean up the observers.
           this._promiseFinished.then(() => {
             if (!win.gBrowser.tabContainer) {
               // May be undefined during shutdown and/or some tests.
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -162,31 +162,31 @@ var MessageListener = {
     // SessionStore.jsm so that it can run SSTabRestoring. Users of
     // SSTabRestoring seem to get confused if chrome and content are out of
     // sync about the state of the restore (particularly regarding
     // docShell.currentURI). Using a synchronous message is the easiest way
     // to temporarily synchronize them.
     sendSyncMessage("SessionStore:restoreHistoryComplete", {epoch});
   },
 
-  restoreTabContent({loadArguments}) {
+  restoreTabContent({loadArguments, isRemotenessUpdate}) {
     let epoch = gCurrentEpoch;
 
     // We need to pass the value of didStartLoad back to SessionStore.jsm.
     let didStartLoad = gContentRestore.restoreTabContent(loadArguments, () => {
       // Tell SessionStore.jsm that it may want to restore some more tabs,
       // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time.
-      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch});
+      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
     });
 
     sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch});
 
     if (!didStartLoad) {
       // Pretend that the load succeeded so that event handlers fire correctly.
-      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch});
+      sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate});
     }
   },
 
   flush({id}) {
     // Flush the message queue, send the latest updates.
     MessageQueue.send({flushID: id});
   }
 };
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -299,17 +299,22 @@ nsScriptSecurityManager::AppStatusForPri
                       nsIPrincipal::APP_STATUS_NOT_INSTALLED);
     nsCOMPtr<nsIURI> appURI;
     NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
                       nsIPrincipal::APP_STATUS_NOT_INSTALLED);
 
     // The app could contain a cross-origin iframe - make sure that the content
     // is actually same-origin with the app.
     MOZ_ASSERT(inIsolatedMozBrowser == false, "Checked this above");
-    PrincipalOriginAttributes attrs(appId, false);
+    nsAutoCString suffix;
+    PrincipalOriginAttributes attrs;
+    NS_ENSURE_TRUE(attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(appOrigin), suffix),
+                   nsIPrincipal::APP_STATUS_NOT_INSTALLED);
+    attrs.mAppId = appId;
+    attrs.mInIsolatedMozBrowser = false;
     nsCOMPtr<nsIPrincipal> appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs);
     NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
     return aPrin->Equals(appPrin) ? status
                                   : nsIPrincipal::APP_STATUS_NOT_INSTALLED;
 }
 
 /*
  * GetChannelResultPrincipal will return the principal that the resource
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
@@ -18,18 +18,19 @@
 //  ]
 var testData = [
   ["d", {}, "display", 1, 3, false],
   ["VK_TAB", {}, "", -1, 10, true],
   ["VK_DOWN", {}, "-moz-box", 0, 10, true],
   ["n", {}, "none", -1, 0, true],
   ["VK_TAB", {shiftKey: true}, "display", -1, 0, true],
   ["VK_BACK_SPACE", {}, "", -1, 0, false],
-  ["c", {}, "color", 5, 10, false],
-  ["o", {}, "color", 0, 7, false],
+  ["o", {}, "opacity", 6, 10, false],
+  ["u", {}, "outline", 0, 5, false],
+  ["VK_DOWN", {}, "outline-color", 1, 5, false],
   ["VK_TAB", {}, "none", -1, 0, true],
   ["r", {}, "rebeccapurple", 0, 6, true],
   ["VK_DOWN", {}, "red", 1, 6, true],
   ["VK_DOWN", {}, "rgb", 2, 6, true],
   ["VK_DOWN", {}, "rgba", 3, 6, true],
   ["VK_DOWN", {}, "rosybrown", 4, 6, true],
   ["VK_DOWN", {}, "royalblue", 5, 6, true],
   ["VK_RIGHT", {}, "royalblue", -1, 0, false],
--- a/devtools/client/sourceeditor/test/css_autocompletion_tests.json
+++ b/devtools/client/sourceeditor/test/css_autocompletion_tests.json
@@ -13,18 +13,21 @@
   [[5,  8], ['-moz-animation', '-moz-animation-delay', '-moz-animation-direction',
              '-moz-animation-duration', '-moz-animation-fill-mode',
              '-moz-animation-iteration-count', '-moz-animation-name',
              '-moz-animation-play-state', '-moz-animation-timing-function',
              '-moz-appearance']],
   [[12, 20], ['none', 'number-input']],
   [[12, 22], ['none']],
   [[17, 22], ['hsl', 'hsla']],
+  [[19, 10], ['background', 'background-attachment', 'background-blend-mode',
+              'background-clip', 'background-color', 'background-image',
+              'background-origin', 'background-position', 'background-repeat',
+              'background-size']],
   [[21,  9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]],
-  [[22,  5], ['color', 'color-interpolation', 'color-interpolation-filters']],
   [[25, 26], ['.devtools-toolbarbutton > tab',
               '.devtools-toolbarbutton > hbox',
               '.devtools-toolbarbutton > .toolbarbutton-menubutton-button']],
   [[25, 31], ['.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button']],
   [[29, 20], ['.devtools-menulist:after', '.devtools-menulist:active']],
   [[30, 10], ['#devtools-anotherone', '#devtools-itjustgoeson', '#devtools-menu',
               '#devtools-okstopitnow', '#devtools-toolbarbutton', '#devtools-yetagain']],
   [[39, 39], ['.devtools-toolbarbutton:not([label]) > tab']],
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -188,16 +188,18 @@ skip-if = e10s && debug && (os == 'win' 
 [browser_longstring_hang.js]
 [browser_output_breaks_after_console_dir_uninspectable.js]
 [browser_output_longstring_expand.js]
 [browser_repeated_messages_accuracy.js]
 [browser_result_format_as_string.js]
 [browser_warn_user_about_replaced_api.js]
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
 tags = mcb
+[browser_webconsole_script_errordoc_urls.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
 [browser_webconsole_assert.js]
 [browser_webconsole_block_mixedcontent_securityerrors.js]
 tags = mcb
 [browser_webconsole_bug_579412_input_focus.js]
 [browser_webconsole_bug_580001_closing_after_completion.js]
 [browser_webconsole_bug_580030_errors_after_page_reload.js]
 [browser_webconsole_bug_580454_timestamp_l10n.js]
 [browser_webconsole_bug_582201_duplicate_errors.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js
@@ -0,0 +1,67 @@
+/* -*- 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/ */
+
+// Ensure that [Learn More] links appear alongside any errors listed
+// in "errordocs.js". Note: this only tests script execution.
+
+"use strict";
+
+const ErrorDocs = require("devtools/server/actors/errordocs");
+
+function makeURIData(script) {
+  return `data:text/html;charset=utf8,<script>${script}</script>`
+}
+
+const TestData = [
+  {
+    jsmsg: "JSMSG_READ_ONLY",
+    script: "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;",
+    isException: true,
+  },
+  {
+    jsmsg: "JSMSG_STMT_AFTER_RETURN",
+    script: "function a() { return; 1 + 1; };",
+    isException: false,
+  }
+]
+
+add_task(function* () {
+  yield loadTab("data:text/html;charset=utf8,errordoc tests");
+
+  let hud = yield openConsole();
+
+  for (let i = 0; i < TestData.length; i++) {
+    yield testScriptError(hud, TestData[i]);
+  }
+});
+
+function* testScriptError(hud, testData) {
+  if (testData.isException === true) {
+    expectUncaughtException();
+  }
+
+  BrowserTestUtils.loadURI(gBrowser.selectedBrowser, makeURIData(testData.script));
+
+  yield waitForMessages({
+    webconsole: hud,
+    messages: [
+      {
+        category: CATEGORY_JS
+      }
+    ]
+  });
+
+  // grab the most current error doc URL
+  let url = ErrorDocs.GetURL(testData.jsmsg);
+
+  let hrefs = {};
+  for (let link of hud.jsterm.outputNode.querySelectorAll("a")) {
+    hrefs[link.href] = true;
+  }
+
+  ok(url in hrefs, `Expected a link to ${url}.`);
+
+  hud.jsterm.clearOutput();
+}
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -11,16 +11,17 @@ const {Cc, Ci, Cu} = require("chrome");
 const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} =
   require("devtools/shared/webconsole/utils");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const BrowserLoaderModule = {};
 Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
 
 const promise = require("promise");
 const Services = require("Services");
+const ErrorDocs = require("devtools/server/actors/errordocs");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
@@ -1484,16 +1485,17 @@ WebConsoleFrame.prototype = {
       private: scriptError.private,
       filterDuplicates: true
     });
 
     let node = msg.init(this.output).render().element;
 
     // Select the body of the message node that is displayed in the console
     let msgBody = node.getElementsByClassName("message-body")[0];
+
     // Add the more info link node to messages that belong to certain categories
     this.addMoreInfoLink(msgBody, scriptError);
 
     if (objectActors.size > 0) {
       node._objectActors = objectActors;
     }
 
     return node;
@@ -1692,21 +1694,24 @@ WebConsoleFrame.prototype = {
         break;
       case "SHA-1 Signature":
         url = WEAK_SIGNATURE_ALGORITHM_LEARN_MORE;
         break;
       case "Tracking Protection":
         url = TRACKING_PROTECTION_LEARN_MORE;
         break;
       default:
-        // Unknown category. Return without adding more info node.
-        return;
+        // If all else fails check for an error doc URL.
+        url = ErrorDocs.GetURL(scriptError.errorMessageName);
+        break;
     }
 
-    this.addLearnMoreWarningNode(node, url);
+    if (url) {
+      this.addLearnMoreWarningNode(node, url);
+    }
   },
 
   /*
    * Appends a clickable warning node to the node passed
    * as a parameter to the function. When a user clicks on the appended
    * warning node, the browser navigates to the provided url.
    *
    * @param node
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/errordocs.js
@@ -0,0 +1,23 @@
+/* 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/. */
+
+/**
+ * A mapping of error message names to external documentation. Any error message
+ * included here will be displayed alongside its link in the web console.
+ */
+
+"use strict";
+
+const ErrorDocs = {
+    JSMSG_READ_ONLY: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Read-only",
+    JSMSG_BAD_ARRAY_LENGTH: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length",
+    JSMSG_NEGATIVE_REPETITION_COUNT: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Negative_repetition_count",
+    JSMSG_RESULTING_STRING_TOO_LARGE: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Resulting_string_too_large",
+    JSMSG_BAD_RADIX: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Bad_radix",
+    JSMSG_PRECISION_RANGE: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Precision_range",
+    JSMSG_BAD_FORMAL: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Malformed_formal_parameter",
+    JSMSG_STMT_AFTER_RETURN: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Stmt_after_return",
+}
+
+exports.GetURL = (errorName) => ErrorDocs[errorName];
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -20,16 +20,17 @@ DevToolsModules(
     'childtab.js',
     'chrome.js',
     'common.js',
     'csscoverage.js',
     'device.js',
     'director-manager.js',
     'director-registry.js',
     'environment.js',
+    'errordocs.js',
     'eventlooplag.js',
     'frame.js',
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.css',
     'highlighters.js',
     'inspector.js',
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1387,16 +1387,17 @@ WebConsoleActor.prototype =
     }
     let lineText = aPageError.sourceLine;
     if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
       lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
     }
 
     return {
       errorMessage: this._createStringGrip(aPageError.errorMessage),
+      errorMessageName: aPageError.errorMessageName,
       sourceName: aPageError.sourceName,
       lineText: lineText,
       lineNumber: aPageError.lineNumber,
       columnNumber: aPageError.columnNumber,
       category: aPageError.category,
       timeStamp: aPageError.timeStamp,
       warning: !!(aPageError.flags & aPageError.warningFlag),
       error: !!(aPageError.flags & aPageError.errorFlag),
--- a/devtools/shared/webconsole/test/test_page_errors.html
+++ b/devtools/shared/webconsole/test/test_page_errors.html
@@ -13,50 +13,134 @@
 
 <script class="testbody" type="text/javascript;version=1.8">
 SimpleTest.waitForExplicitFinish();
 
 let expectedPageErrors = [];
 
 function doPageErrors()
 {
-  expectedPageErrors = [
-    {
+  expectedPageErrors = {
+    "document.body.style.color = 'fooColor';": {
       errorMessage: /fooColor/,
+      errorMessageName: undefined,
       sourceName: /test_page_errors/,
       category: "CSS Parser",
       timeStamp: /^\d+$/,
       error: false,
       warning: true,
       exception: false,
       strict: false,
     },
-    {
+   "document.doTheImpossible();": {
       errorMessage: /doTheImpossible/,
+      errorMessageName: undefined,
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+      strict: false,
+    },
+    "(42).toString(0);": {
+      errorMessage: /radix/,
+      errorMessageName: "JSMSG_BAD_RADIX",
       sourceName: /test_page_errors/,
       category: "chrome javascript",
       timeStamp: /^\d+$/,
       error: false,
       warning: false,
       exception: true,
       strict: false,
     },
-  ];
+     "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;": {
+      errorMessage: /read.only/,
+      errorMessageName: "JSMSG_READ_ONLY",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+     "([]).length = -1": {
+      errorMessage: /array length/,
+      errorMessageName: "JSMSG_BAD_ARRAY_LENGTH",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+     "'abc'.repeat(-1);": {
+      errorMessage: /repeat count.*non-negative/,
+      errorMessageName: "JSMSG_NEGATIVE_REPETITION_COUNT",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+    "'a'.repeat(2**28);": {
+      errorMessage: /repeat count.*less than infinity/,
+      errorMessageName: "JSMSG_RESULTING_STRING_TOO_LARGE",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+    "77.1234.toExponential(-1);": {
+      errorMessage: /out of range/,
+      errorMessageName: "JSMSG_PRECISION_RANGE",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+    "var f = Function('x y', 'return x + y;');": {
+      errorMessage: /malformed formal/,
+      errorMessageName: "JSMSG_BAD_FORMAL",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: false,
+      exception: true,
+    },
+    "function a() { return; 1 + 1; }": {
+      errorMessage: /unreachable code/,
+      errorMessageName: "JSMSG_STMT_AFTER_RETURN",
+      sourceName: /test_page_errors/,
+      category: "chrome javascript",
+      timeStamp: /^\d+$/,
+      error: false,
+      warning: true,
+      exception: false,
+    },
+  };
 
   let container = document.createElement("script");
-  document.body.appendChild(container);
-  container.textContent = "document.body.style.color = 'fooColor';";
-  document.body.removeChild(container);
-
-  SimpleTest.expectUncaughtException();
-
-  container = document.createElement("script");
-  document.body.appendChild(container);
-  container.textContent = "document.doTheImpossible();";
-  document.body.removeChild(container);
+  for (let stmt of Object.keys(expectedPageErrors)) {
+      if (expectedPageErrors[stmt].exception) {
+          SimpleTest.expectUncaughtException();
+      }
+      info("starting stmt: " + stmt);
+      container = document.createElement("script");
+      document.body.appendChild(container);
+      container.textContent = stmt;
+      document.body.removeChild(container);
+      info("ending stmt: " + stmt);
+  }
 }
 
 function startTest()
 {
   removeEventListener("load", startTest);
 
   attachConsole(["PageError"], onAttach);
 }
@@ -75,25 +159,25 @@ function onPageError(aState, aType, aPac
   if (!aPacket.pageError.sourceName.includes("test_page_errors")) {
     info("Ignoring error from unknown source: " + aPacket.pageError.sourceName);
     return;
   }
 
   is(aPacket.from, aState.actor, "page error actor");
 
   pageErrors.push(aPacket.pageError);
-  if (pageErrors.length != expectedPageErrors.length) {
+  if (pageErrors.length != Object.keys(expectedPageErrors).length) {
     return;
   }
 
   aState.dbgClient.removeListener("pageError", onPageError);
 
-  expectedPageErrors.forEach(function(aMessage, aIndex) {
+  Object.values(expectedPageErrors).forEach(function(aMessage, aIndex) {
     info("checking received page error #" + aIndex);
-    checkObject(pageErrors[aIndex], expectedPageErrors[aIndex]);
+    checkObject(pageErrors[aIndex], Object.values(expectedPageErrors)[aIndex]);
   });
 
   closeDebugger(aState, function() {
     SimpleTest.finish();
   });
 }
 
 addEventListener("load", startTest);
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -769,17 +769,21 @@ nsContentIterator::NextNode(nsINode* aNo
     }
 
     // else next sibling is next
     return GetNextSibling(node, aIndexes);
   }
 
   // post-order
   nsINode* parent = node->GetParentNode();
-  NS_WARN_IF(!parent);
+  if (NS_WARN_IF(!parent)) {
+    MOZ_ASSERT(parent, "The node is the root node but not the last node");
+    mIsDone = true;
+    return node;
+  }
   nsIContent* sibling = nullptr;
   int32_t indx = 0;
 
   // get the cached index
   NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
                "ContentIterator stack underflow");
   if (aIndexes && !aIndexes->IsEmpty()) {
     // use the last entry on the Indexes array for the current index
@@ -834,17 +838,21 @@ nsContentIterator::NextNode(nsINode* aNo
 nsINode*
 nsContentIterator::PrevNode(nsINode* aNode, nsTArray<int32_t>* aIndexes)
 {
   nsINode* node = aNode;
 
   // if we are a Pre-order iterator, use pre-order
   if (mPre) {
     nsINode* parent = node->GetParentNode();
-    NS_WARN_IF(!parent);
+    if (NS_WARN_IF(!parent)) {
+      MOZ_ASSERT(parent, "The node is the root node but not the first node");
+      mIsDone = true;
+      return aNode;
+    }
     nsIContent* sibling = nullptr;
     int32_t indx = 0;
 
     // get the cached index
     NS_ASSERTION(!aIndexes || !aIndexes->IsEmpty(),
                  "ContentIterator stack underflow");
     if (aIndexes && !aIndexes->IsEmpty()) {
       // use the last entry on the Indexes array for the current index
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -940,29 +940,29 @@ nsDOMWindowUtils::SendTouchEventCommon(c
   event.mModifiers = nsContentUtils::GetWidgetModifiers(aModifiers);
   event.widget = widget;
   event.mTime = PR_Now();
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return NS_ERROR_FAILURE;
   }
-  event.touches.SetCapacity(aCount);
+  event.mTouches.SetCapacity(aCount);
   for (uint32_t i = 0; i < aCount; ++i) {
     LayoutDeviceIntPoint pt =
       nsContentUtils::ToWidgetPoint(CSSPoint(aXs[i], aYs[i]), offset, presContext);
     LayoutDeviceIntPoint radius =
       LayoutDeviceIntPoint::FromAppUnitsRounded(
         CSSPoint::ToAppUnits(CSSPoint(aRxs[i], aRys[i])),
         presContext->AppUnitsPerDevPixel());
 
     RefPtr<Touch> t =
       new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]);
 
-    event.touches.AppendElement(t);
+    event.mTouches.AppendElement(t);
   }
 
   nsEventStatus status;
   if (aToWindow) {
     nsCOMPtr<nsIPresShell> presShell;
     nsView* view = nsContentUtils::GetViewToDispatchEvent(presContext, getter_AddRefs(presShell));
     if (!presShell || !view) {
       return NS_ERROR_FAILURE;
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1561,18 +1561,23 @@ nsXMLHttpRequest::Open(const nsACString&
   if (mBaseURI) {
     baseURI = mBaseURI;
   }
   else if (doc) {
     baseURI = doc->GetBaseURI();
   }
 
   rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
-  if (NS_FAILED(rv)) return rv;
-
+
+  if (NS_FAILED(rv)) {
+    if (rv ==  NS_ERROR_MALFORMED_URI) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+    return rv;
+  }
   rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // XXXbz this is wrong: we should only be looking at whether
   // user/password were passed, not at the values!  See bug 759624.
   if (user.WasPassed() && !user.Value().IsEmpty()) {
     nsAutoCString userpass;
     CopyUTF16toUTF8(user.Value(), userpass);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1309,17 +1309,17 @@ EventStateManager::HandleCrossProcessEve
     // This is a touch event with possibly multiple touch points.
     // Each touch point may have its own target.  So iterate through
     // all of them and collect the unique set of targets for event
     // forwarding.
     //
     // This loop is similar to the one used in
     // PresShell::DispatchTouchEvent().
     const WidgetTouchEvent::TouchArray& touches =
-      aEvent->AsTouchEvent()->touches;
+      aEvent->AsTouchEvent()->mTouches;
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       Touch* touch = touches[i];
       // NB: the |mChanged| check is an optimization, subprocesses can
       // compute this for themselves.  If the touch hasn't changed, we
       // may be able to avoid forwarding the event entirely (which is
       // not free).
       if (!touch || !touch->mChanged) {
         continue;
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -64,18 +64,18 @@ TouchEvent::TouchEvent(EventTarget* aOwn
                        WidgetTouchEvent* aEvent)
   : UIEvent(aOwner, aPresContext,
             aEvent ? aEvent :
                      new WidgetTouchEvent(false, eVoidEvent, nullptr))
 {
   if (aEvent) {
     mEventIsInternal = false;
 
-    for (uint32_t i = 0; i < aEvent->touches.Length(); ++i) {
-      Touch* touch = aEvent->touches[i];
+    for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
+      Touch* touch = aEvent->mTouches[i];
       touch->InitializePoints(mPresContext, aEvent);
     }
   } else {
     mEventIsInternal = true;
     mEvent->mTime = PR_Now();
   }
 }
 
@@ -113,39 +113,39 @@ TouchEvent::InitTouchEvent(const nsAStri
 }
 
 TouchList*
 TouchEvent::Touches()
 {
   if (!mTouches) {
     WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
     if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) {
-      // for touchend events, remove any changed touches from the touches array
+      // for touchend events, remove any changed touches from mTouches
       WidgetTouchEvent::AutoTouchArray unchangedTouches;
-      const WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
+      const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
       for (uint32_t i = 0; i < touches.Length(); ++i) {
         if (!touches[i]->mChanged) {
           unchangedTouches.AppendElement(touches[i]);
         }
       }
       mTouches = new TouchList(ToSupports(this), unchangedTouches);
     } else {
-      mTouches = new TouchList(ToSupports(this), touchEvent->touches);
+      mTouches = new TouchList(ToSupports(this), touchEvent->mTouches);
     }
   }
   return mTouches;
 }
 
 TouchList*
 TouchEvent::TargetTouches()
 {
   if (!mTargetTouches) {
     WidgetTouchEvent::AutoTouchArray targetTouches;
     WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
-    const WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
+    const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       // for touchend/cancel events, don't append to the target list if this is a
       // touch that is ending
       if ((mEvent->mMessage != eTouchEnd && mEvent->mMessage != eTouchCancel) ||
           !touches[i]->mChanged) {
         if (touches[i]->mTarget == mEvent->originalTarget) {
           targetTouches.AppendElement(touches[i]);
         }
@@ -157,17 +157,17 @@ TouchEvent::TargetTouches()
 }
 
 TouchList*
 TouchEvent::ChangedTouches()
 {
   if (!mChangedTouches) {
     WidgetTouchEvent::AutoTouchArray changedTouches;
     WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent();
-    const WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
+    const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       if (touches[i]->mChanged) {
         changedTouches.AppendElement(touches[i]);
       }
     }
     mChangedTouches = new TouchList(ToSupports(this), changedTouches);
   }
   return mChangedTouches;
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -146,17 +146,17 @@ support-files = bug1017086_inner.html
 support-files = bug1017086_inner.html
 [test_bug1079236.html]
 [test_bug1145910.html]
 [test_bug1150308.html]
 [test_bug1248459.html]
 [test_clickevent_on_input.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_continuous_wheel_events.html]
-skip-if = buildapp == 'b2g' || e10s # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop)
+skip-if = buildapp == 'b2g' # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop)
 [test_dblclick_explicit_original_target.html]
 [test_dom_keyboard_event.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dom_mouse_event.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dom_storage_event.html]
 [test_dom_wheel_event.html]
 skip-if = buildapp == 'b2g' # b2g(456 failed out of 19873, mousewheel test) b2g-debug(456 failed out of 19873, mousewheel test) b2g-desktop(456 failed out of 19873, mousewheel test)
--- a/dom/events/test/test_continuous_wheel_events.html
+++ b/dom/events/test/test_continuous_wheel_events.html
@@ -1,14 +1,15 @@
 <!DOCTYPE HTML>
 <html style="font-size: 32px;">
 <head>
   <title>Test for D3E WheelEvent</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="scrollable" style="font-family:monospace; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;">
   <div id="scrolled" style="font-size: 64px; width: 5000px; height: 5000px;">
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
@@ -34,80 +35,85 @@
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
     Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
   </div>
 </div>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests, window);
 
 var gScrollableElement = document.getElementById("scrollable");
 var gScrolledElement = document.getElementById("scrolled");
 
 var gLineHeight = 0;
 var gHorizontalLine = 0;
 var gPageHeight = 0;
 var gPageWidth  = 0;
 
-function prepareScrollUnits()
+function sendWheelAndWait(aX, aY, aEvent)
+{
+  sendWheelAndPaint(gScrollableElement, aX, aY, aEvent, continueTest);
+}
+
+function* prepareScrollUnits()
 {
   var result = -1;
   function handler(aEvent)
   {
     result = aEvent.detail;
     aEvent.preventDefault();
   }
   window.addEventListener("MozMousePixelScroll", handler, true);
 
-  synthesizeWheel(gScrollableElement, 10, 10,
-                  { deltaMode: WheelEvent.DOM_DELTA_LINE,
-                    deltaY: 1.0, lineOrPageDeltaY: 1 });
+  yield sendWheelAndWait(10, 10,
+                         { deltaMode: WheelEvent.DOM_DELTA_LINE,
+                           deltaY: 1.0, lineOrPageDeltaY: 1 });
   gLineHeight = result;
   ok(gLineHeight > 10 && gLineHeight < 25, "prepareScrollUnits: gLineHeight may be illegal value, got " + gLineHeight);
 
   result = -1;
-  synthesizeWheel(gScrollableElement, 10, 10,
-                  { deltaMode: WheelEvent.DOM_DELTA_LINE,
-                    deltaX: 1.0, lineOrPageDeltaX: 1 });
+  yield sendWheelAndWait(10, 10,
+                         { deltaMode: WheelEvent.DOM_DELTA_LINE,
+                           deltaX: 1.0, lineOrPageDeltaX: 1 });
   gHorizontalLine = result;
   ok(gHorizontalLine > 5 && gHorizontalLine < 16, "prepareScrollUnits: gHorizontalLine may be illegal value, got " + gHorizontalLine);
 
   result = -1;
-  synthesizeWheel(gScrollableElement, 10, 10,
-                  { deltaMode: WheelEvent.DOM_DELTA_PAGE,
-                    deltaY: 1.0, lineOrPageDeltaY: 1 });
+  yield sendWheelAndWait(10, 10,
+                         { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+                           deltaY: 1.0, lineOrPageDeltaY: 1 });
   gPageHeight = result;
   // XXX Cannot we know the actual scroll port size?
   ok(gPageHeight >= 150 && gPageHeight <= 200,
      "prepareScrollUnits: gPageHeight is strange value, got " + gPageHeight);
 
   result = -1;
-  synthesizeWheel(gScrollableElement, 10, 10,
-                  { deltaMode: WheelEvent.DOM_DELTA_PAGE,
-                    deltaX: 1.0, lineOrPageDeltaX: 1 });
+  yield sendWheelAndWait(10, 10,
+                         { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+                           deltaX: 1.0, lineOrPageDeltaX: 1 });
   gPageWidth = result;
   ok(gPageWidth >= 150 && gPageWidth <= 200,
      "prepareScrollUnits: gPageWidth is strange value, got " + gPageWidth);
 
   window.removeEventListener("MozMousePixelScroll", handler, true);
 }
 
 // Tests continuous trusted wheel events. Trusted wheel events should cause
 // legacy mouse scroll events when its lineOrPageDelta value is not zero or
 // accumulated delta values of pixel scroll events of pixel only device
 // become over the line height.
-function testContinuousTrustedEvents()
+function* testContinuousTrustedEvents()
 {
   const kSynthesizedWheelEventTests = [
     { description: "Simple horizontal wheel event by pixels (16.0 - 1) #1",
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
                lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
@@ -2656,18 +2662,19 @@ function testContinuousTrustedEvents()
       MozMousePixelScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: gPageWidth },
         vertical:   { expected: true,  preventDefault: false, detail: gPageHeight } },
     },
 
     // Tests for accumulation delta when delta_multiplier_is customized.
     { description: "lineOrPageDelta should be recomputed by ESM (pixel) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 300);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+                                           ["mousewheel.default.delta_multiplier_y", 300]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: gHorizontalLine / 4, deltaY: gLineHeight / 8, deltaZ: 0,
                lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -2709,25 +2716,27 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: false,  preventDefault: false, detail: 0 },
         vertical:   { expected: true,   preventDefault: false, detail: 1 } },
       MozMousePixelScroll: {
         horizontal: { expected: true,   preventDefault: false, detail: Math.floor((gHorizontalLine / 4 + 1) * 2) },
         vertical:   { expected: true,   preventDefault: false, detail: Math.floor((gLineHeight / 8 + 1) * 3) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
 
     { description: "lineOrPageDelta should be recomputed by ESM (pixel, negative, shift) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 200);
-        SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 300);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 200],
+                                           ["mousewheel.with_shift.delta_multiplier_y", 300]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
                deltaX: -gHorizontalLine / 4, deltaY: -gLineHeight / 8, deltaZ: 0,
                lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -2769,25 +2778,27 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: false,  preventDefault: false, detail: 0 },
         vertical:   { expected: true,   preventDefault: false, detail: -1 } },
       MozMousePixelScroll: {
         horizontal: { expected: true,   preventDefault: false, detail: Math.ceil(-(gHorizontalLine / 4 + 1) * 2) },
         vertical:   { expected: true,   preventDefault: false, detail: Math.ceil(-(gLineHeight / 8 + 1) * 3) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 100],
+                                           ["mousewheel.with_shift.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
 
     { description: "lineOrPageDelta should be recomputed by ESM (line) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
                lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -2829,25 +2840,27 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: false, preventDefault: false, detail: 0 },
         vertical:   { expected: true,  preventDefault: false, detail: 1 } },
       MozMousePixelScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: Math.floor(gHorizontalLine * 0.6) },
         vertical:   { expected: true,  preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
 
     { description: "lineOrPageDelta should be recomputed by ESM (line, negative) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", -100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+                                           ["mousewheel.default.delta_multiplier_y", -100]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
                deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
                lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -2889,25 +2902,27 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: false, preventDefault: false, detail: 0 },
         vertical:   { expected: true,  preventDefault: false, detail: 1 } },
       MozMousePixelScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: Math.ceil(gHorizontalLine * -0.6) },
         vertical:   { expected: true,  preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
 
     { description: "lineOrPageDelta should be recomputed by ESM (page) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 200);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 200]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
                lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -2949,25 +2964,27 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
         vertical:   { expected: true,  preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
       MozMousePixelScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: Math.floor(gPageWidth * 0.4) },
         vertical:   { expected: true,  preventDefault: false, detail: Math.floor(gPageHeight * 0.8) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
 
     { description: "lineOrPageDelta should be recomputed by ESM (page, negative) #1",
       prepare: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 200);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 200]]},
+                                  continueTest);
       },
       event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
                deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
                lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
                isCustomizedByPrefs: false,
                shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
       wheel: {
         expected: true, preventDefault: false,
@@ -3009,18 +3026,19 @@ function testContinuousTrustedEvents()
       },
       DOMMouseScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
         vertical:   { expected: true,  preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
       MozMousePixelScroll: {
         horizontal: { expected: true,  preventDefault: false, detail: Math.ceil(gPageWidth * -0.4) },
         vertical:   { expected: true,  preventDefault: false, detail: Math.ceil(gPageHeight * -0.8) } },
       finished: function () {
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-        SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
+        SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                           ["mousewheel.default.delta_multiplier_y", 100]]},
+                                  continueTest);
       },
     },
   ];
 
   var currentWheelEventTest;
   var calledHandlers = { wheel: false,
                          DOMMouseScroll: { horizontal: false, vertical: false },
                          MozMousePixelScroll: { horizontal: false, vertical: false } };
@@ -3140,23 +3158,23 @@ function testContinuousTrustedEvents()
   window.addEventListener("MozMousePixelScroll", legacyEventHandler, true);
 
   for (var i = 0; i < kSynthesizedWheelEventTests.length; i++) {
     gScrollableElement.scrollTop = gScrollableElement.scrollBottom = 1000;
 
     currentWheelEventTest = kSynthesizedWheelEventTests[i];
 
     if (currentWheelEventTest.prepare) {
-      currentWheelEventTest.prepare();
+      yield currentWheelEventTest.prepare();
     }
 
-    synthesizeWheel(gScrollableElement, 10, 10, currentWheelEventTest.event);
+    yield sendWheelAndWait(10, 10, currentWheelEventTest.event);
 
     if (currentWheelEventTest.finished) {
-      currentWheelEventTest.finished();
+      yield currentWheelEventTest.finished();
     }
 
     var description = "testContinuousTrustedEvents, " +
       currentWheelEventTest.description + ": ";
     is(calledHandlers.wheel, currentWheelEventTest.wheel.expected,
        description + "wheel event was fired or not fired");
     is(calledHandlers.DOMMouseScroll.horizontal,
        currentWheelEventTest.DOMMouseScroll.horizontal.expected,
@@ -3176,66 +3194,55 @@ function testContinuousTrustedEvents()
                        MozMousePixelScroll: { horizontal: false, vertical: false } };
   }
 
   window.removeEventListener("wheel", wheelEventHandler, true);
   window.removeEventListener("DOMMouseScroll", legacyEventHandler, true);
   window.removeEventListener("MozMousePixelScroll", legacyEventHandler, true);
 }
 
+var gTestContinuation = null;
+
+function continueTest()
+{
+  if (!gTestContinuation) {
+    gTestContinuation = testBody();
+  }
+  var ret = gTestContinuation.next();
+  if (ret.done) {
+    SimpleTest.finish();
+  }
+}
+
+function* testBody()
+{
+  yield* prepareScrollUnits();
+  yield* testContinuousTrustedEvents();
+}
+
 function runTests()
 {
-  SpecialPowers.setIntPref("mousewheel.transaction.timeout", 100000);
-
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_z", 100);
-
-  prepareScrollUnits();
-  testContinuousTrustedEvents();
-
-  clearPrefs();
-  SimpleTest.finish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["mousewheel.transaction.timeout", 100000],
+    ["mousewheel.default.delta_multiplier_x", 100],
+    ["mousewheel.default.delta_multiplier_y", 100],
+    ["mousewheel.default.delta_multiplier_z", 100],
+    ["mousewheel.with_alt.delta_multiplier_x", 100],
+    ["mousewheel.with_alt.delta_multiplier_y", 100],
+    ["mousewheel.with_alt.delta_multiplier_z", 100],
+    ["mousewheel.with_control.delta_multiplier_x", 100],
+    ["mousewheel.with_control.delta_multiplier_y", 100],
+    ["mousewheel.with_control.delta_multiplier_z", 100],
+    ["mousewheel.with_meta.delta_multiplier_x", 100],
+    ["mousewheel.with_meta.delta_multiplier_y", 100],
+    ["mousewheel.with_meta.delta_multiplier_z", 100],
+    ["mousewheel.with_shift.delta_multiplier_x", 100],
+    ["mousewheel.with_shift.delta_multiplier_y", 100],
+    ["mousewheel.with_shift.delta_multiplier_z", 100],
+    ["mousewheel.with_win.delta_multiplier_x", 100],
+    ["mousewheel.with_win.delta_multiplier_y", 100],
+    ["mousewheel.with_win.delta_multiplier_z", 100]
+  ]}, continueTest);
 }
-
-function clearPrefs()
-{
-  SpecialPowers.clearUserPref("mousewheel.transaction.timeout");
-
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_z");
-}
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4237,17 +4237,17 @@ HTMLInputElement::PostHandleEventForRang
       if (aVisitor.mEvent->mMessage == eMouseDown) {
         if (aVisitor.mEvent->AsMouseEvent()->buttons ==
               WidgetMouseEvent::eLeftButtonFlag) {
           StartRangeThumbDrag(inputEvent);
         } else if (mIsDraggingRange) {
           CancelRangeThumbDrag();
         }
       } else {
-        if (aVisitor.mEvent->AsTouchEvent()->touches.Length() == 1) {
+        if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) {
           StartRangeThumbDrag(inputEvent);
         } else if (mIsDraggingRange) {
           CancelRangeThumbDrag();
         }
       }
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
     } break;
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2487,16 +2487,20 @@ ContentChild::RecvAddPermission(const IP
   nsPermissionManager* permissionManager =
     static_cast<nsPermissionManager*>(permissionManagerIface.get());
   MOZ_ASSERT(permissionManager,
          "We have no permissionManager in the Content process !");
 
   nsAutoCString originNoSuffix;
   PrincipalOriginAttributes attrs;
   attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
+  // we're doing this because we currently don't support isolating permissions
+  // by userContextId.
+  MOZ_ASSERT(attrs.mUserContextId == nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID,
+      "permission user context should be set to default!");
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
   NS_ENSURE_SUCCESS(rv, true);
 
   nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 
   // child processes don't care about modification time.
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1653,35 +1653,35 @@ bool TabParent::SendRealTouchEvent(Widge
     return false;
   }
 
   // PresShell::HandleEventInternal adds touches on touch end/cancel.  This
   // confuses remote content and the panning and zooming logic into thinking
   // that the added touches are part of the touchend/cancel, when actually
   // they're not.
   if (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) {
-    for (int i = event.touches.Length() - 1; i >= 0; i--) {
-      if (!event.touches[i]->mChanged) {
-        event.touches.RemoveElementAt(i);
+    for (int i = event.mTouches.Length() - 1; i >= 0; i--) {
+      if (!event.mTouches[i]->mChanged) {
+        event.mTouches.RemoveElementAt(i);
       }
     }
   }
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
   nsEventStatus apzResponse;
   ApzAwareEventRoutingToChild(&guid, &blockId, &apzResponse);
 
   if (mIsDestroyed) {
     return false;
   }
 
   LayoutDeviceIntPoint offset = GetChildProcessOffset();
-  for (uint32_t i = 0; i < event.touches.Length(); i++) {
-    event.touches[i]->mRefPoint += offset;
+  for (uint32_t i = 0; i < event.mTouches.Length(); i++) {
+    event.mTouches[i]->mRefPoint += offset;
   }
 
   return (event.mMessage == eTouchMove) ?
     PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId, apzResponse) :
     PBrowserParent::SendRealTouchEvent(event, guid, blockId, apzResponse);
 }
 
 bool
@@ -2773,17 +2773,17 @@ TabParent::InjectTouchEvent(const nsAStr
   }
 
   nsIDocument* doc = content->OwnerDoc();
   if (!doc || !doc->GetShell()) {
     return NS_ERROR_FAILURE;
   }
   nsPresContext* presContext = doc->GetShell()->GetPresContext();
 
-  event.touches.SetCapacity(aCount);
+  event.mTouches.SetCapacity(aCount);
   for (uint32_t i = 0; i < aCount; ++i) {
     LayoutDeviceIntPoint pt =
       LayoutDeviceIntPoint::FromAppUnitsRounded(
         CSSPoint::ToAppUnits(CSSPoint(aXs[i], aYs[i])),
         presContext->AppUnitsPerDevPixel());
 
     LayoutDeviceIntPoint radius =
       LayoutDeviceIntPoint::FromAppUnitsRounded(
@@ -2792,17 +2792,17 @@ TabParent::InjectTouchEvent(const nsAStr
 
     RefPtr<Touch> t =
       new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]);
 
     // Consider all injected touch events as changedTouches. For more details
     // about the meaning of changedTouches for each event, see
     // https://developer.mozilla.org/docs/Web/API/TouchEvent.changedTouches
     t->mChanged = true;
-    event.touches.AppendElement(t);
+    event.mTouches.AppendElement(t);
   }
 
   SendRealTouchEvent(event);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -615,17 +615,19 @@ AudioCallbackDriver::Init()
   input = output;
   input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream = nullptr;
   CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
   // We have to translate the deviceID values to cubeb devid's since those can be
   // freed whenever enumerate is called.
   {
+#ifdef MOZ_WEBRTC
     StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
+#endif
     if ((!mGraphImpl->mInputWanted
 #ifdef MOZ_WEBRTC
          || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id)
 #endif
          ) &&
         (mGraphImpl->mOutputDeviceID == -1 // pass nullptr for ID for default output
 #ifdef MOZ_WEBRTC
          // XXX we should figure out how we would use a deviceID for output without webrtc.
@@ -640,17 +642,19 @@ AudioCallbackDriver::Init()
                           "AudioCallbackDriver",
                           input_id,
                           mGraphImpl->mInputWanted ? &input : nullptr,
                           output_id,
                           mGraphImpl->mOutputWanted ? &output : nullptr, latency,
                           DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
       mAudioStream.own(stream);
     } else {
+#ifdef MOZ_WEBRTC
       StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex());
+#endif
       NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
       // Fall back to a driver using a normal thread.
       MonitorAutoLock lock(GraphImpl()->GetMonitor());
       SetNextDriver(new SystemClockDriver(GraphImpl()));
       NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
       mGraphImpl->SetCurrentDriver(NextDriver());
       NextDriver()->Start();
       return;
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -346,18 +346,16 @@ nsSpeechTask::DispatchStart()
   }
 
   return DispatchStartInner();
 }
 
 nsresult
 nsSpeechTask::DispatchStartInner()
 {
-  CreateAudioChannelAgent();
-
   nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true);
   return DispatchStartImpl();
 }
 
 nsresult
 nsSpeechTask::DispatchStartImpl()
 {
   return DispatchStartImpl(mChosenVoiceURI);
@@ -368,16 +366,18 @@ nsSpeechTask::DispatchStartImpl(const ns
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchStart"));
 
   MOZ_ASSERT(mUtterance);
   if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING))) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
+  CreateAudioChannelAgent();
+
   mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
   mUtterance->mChosenVoiceURI = aUri;
   mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0, 0,
                                            EmptyString());
 
   return NS_OK;
 }
 
@@ -390,30 +390,30 @@ nsSpeechTask::DispatchEnd(float aElapsed
   }
 
   return DispatchEndInner(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchEndInner(float aElapsedTime, uint32_t aCharIndex)
 {
-  DestroyAudioChannelAgent();
-
   if (!mPreCanceled) {
     nsSynthVoiceRegistry::GetInstance()->SpeakNext();
   }
 
   return DispatchEndImpl(aElapsedTime, aCharIndex);
 }
 
 nsresult
 nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
 {
   LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n"));
 
+  DestroyAudioChannelAgent();
+
   MOZ_ASSERT(mUtterance);
   if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // XXX: This should not be here, but it prevents a crash in MSG.
   if (mStream) {
     mStream->Destroy();
--- a/editor/libeditor/nsHTMLEditRules.cpp
+++ b/editor/libeditor/nsHTMLEditRules.cpp
@@ -5020,26 +5020,26 @@ nsHTMLEditRules::CheckForEmptyBlock(nsIN
       if (aAction == nsIEditor::eNext) {
         // Adjust selection to be right after it.
         res = aSelection->Collapse(blockParent, offset + 1);
         NS_ENSURE_SUCCESS(res, res);
 
         // Move to the start of the next node if it's a text.
         nsCOMPtr<nsIContent> nextNode = mHTMLEditor->GetNextNode(blockParent,
                                                                  offset + 1, true);
-        if (mHTMLEditor->IsTextNode(nextNode)) {
+        if (nextNode && mHTMLEditor->IsTextNode(nextNode)) {
           res = aSelection->Collapse(nextNode, 0);
           NS_ENSURE_SUCCESS(res, res);
         }
       } else {
         // Move to the end of the previous node if it's a text.
         nsCOMPtr<nsIContent> priorNode = mHTMLEditor->GetPriorNode(blockParent,
                                                                    offset,
                                                                    true);
-        if (mHTMLEditor->IsTextNode(priorNode)) {
+        if (priorNode && mHTMLEditor->IsTextNode(priorNode)) {
           res = aSelection->Collapse(priorNode, priorNode->TextLength());
           NS_ENSURE_SUCCESS(res, res);
         } else {
           res = aSelection->Collapse(blockParent, offset + 1);
           NS_ENSURE_SUCCESS(res, res);
         }
       }
     }
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -708,16 +708,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mUseDisplayPortMargins);
     WriteParam(aMsg, aParam.mAllowVerticalScrollWithWheel);
     WriteParam(aMsg, aParam.mIsLayersIdRoot);
     WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.mIsScrollInfoLayer);
+    WriteParam(aMsg, aParam.mForceDisableApz);
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
     }
@@ -767,17 +768,18 @@ struct ParamTraits<mozilla::layers::Fram
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsRootContent) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetHasScrollgrab) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUpdateScrollOffset) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetDoSmoothScroll) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUseDisplayPortMargins) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetAllowVerticalScrollWithWheel) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsLayersIdRoot) &&
             ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUsesContainerScrolling) &&
-            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsScrollInfoLayer));
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsScrollInfoLayer) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetForceDisableApz));
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::ScrollSnapInfo>
 {
   typedef mozilla::layers::ScrollSnapInfo paramType;
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -69,16 +69,17 @@ public:
     , mHasScrollgrab(false)
     , mUpdateScrollOffset(false)
     , mDoSmoothScroll(false)
     , mUseDisplayPortMargins(false)
     , mAllowVerticalScrollWithWheel(false)
     , mIsLayersIdRoot(false)
     , mUsesContainerScrolling(false)
     , mIsScrollInfoLayer(false)
+    , mForceDisableApz(false)
   {
   }
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     // Put mScrollId at the top since it's the most likely one to fail.
@@ -108,17 +109,18 @@ public:
            mIsRootContent == aOther.mIsRootContent &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
            mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
            mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
            mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
-           mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
+           mIsScrollInfoLayer == aOther.mIsScrollInfoLayer &&
+           mForceDisableApz == aOther.mForceDisableApz;
   }
 
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsScrollable() const
@@ -549,16 +551,23 @@ public:
 
   void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
     mIsScrollInfoLayer = aIsScrollInfoLayer;
   }
   bool IsScrollInfoLayer() const {
     return mIsScrollInfoLayer;
   }
 
+  void SetForceDisableApz(bool aForceDisable) {
+    mForceDisableApz = aForceDisable;
+  }
+  bool IsApzForceDisabled() const {
+    return mForceDisableApz;
+  }
+
 private:
   // A unique ID assigned to each scrollable frame.
   ViewID mScrollId;
 
   // The ViewID of the scrollable frame to which overscroll should be handed off.
   ViewID mScrollParentId;
 
   // The pres-shell resolution that has been induced on the document containing
@@ -737,16 +746,20 @@ private:
 
   // True if scrolling using containers, false otherwise. This can be removed
   // when containerful scrolling is eliminated.
   bool mUsesContainerScrolling:1;
 
   // Whether or not this frame has a "scroll info layer" to capture events.
   bool mIsScrollInfoLayer:1;
 
+  // Whether or not the compositor should actually do APZ-scrolling on this
+  // scrollframe.
+  bool mForceDisableApz:1;
+
   // WARNING!!!!
   //
   // When adding new fields to FrameMetrics, the following places should be
   // updated to include them (as needed):
   //    FrameMetrics::operator ==
   //    AsyncPanZoomController::NotifyLayersUpdated
   //    The ParamTraits specialization in GfxMessageUtils.h
   //
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1147,20 +1147,21 @@ APZCTreeManager::ReceiveInputEvent(Widge
     case eTouchEventClass: {
       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
       MultiTouchInput touchInput(touchEvent);
       nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       // touchInput was modified in-place to possibly remove some
       // touch points (if we are overscrolled), and the coordinates were
       // modified using the APZ untransform. We need to copy these changes
       // back into the WidgetInputEvent.
-      touchEvent.touches.Clear();
-      touchEvent.touches.SetCapacity(touchInput.mTouches.Length());
+      touchEvent.mTouches.Clear();
+      touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length());
       for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
-        *touchEvent.touches.AppendElement() = touchInput.mTouches[i].ToNewDOMTouch();
+        *touchEvent.mTouches.AppendElement() =
+          touchInput.mTouches[i].ToNewDOMTouch();
       }
       touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ;
       return result;
     }
     case eWheelEventClass: {
       WidgetWheelEvent& wheelEvent = *aEvent.AsWheelEvent();
       if (WillHandleWheelEvent(&wheelEvent)) {
         return ProcessWheelEvent(wheelEvent, aOutTargetGuid, aOutInputBlockId);
@@ -1929,17 +1930,17 @@ APZCTreeManager::GetScreenToApzcTransfor
 
   // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
   result = ancestorUntransform;
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
     ancestorUntransform = parent->GetAncestorTransform().Inverse();
     // asyncUntransform is updated to PA.Inverse() when parent == P
-    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
+    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
     // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
     Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
 
     // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
     result = untransformSinceLastApzc * result;
 
     // The above value for result when parent == P matches the required output
     // as explained in the comment above this method. Note that any missing
@@ -1961,17 +1962,17 @@ APZCTreeManager::GetApzcToGeckoTransform
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // asyncUntransform is LA.Inverse()
-  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix();
+  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix();
 
   // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
   result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
     result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -151,16 +151,24 @@ using mozilla::gfx::PointTyped;
  * goes from y=0...1000 and the visible portion is y=250...750 then
  * we're far from checkerboarding. If we get to y=490...990 though then we're
  * only 10 pixels away from showing checkerboarding so we are probably in
  * a state where we can't keep up with scrolling. The danger zone prefs specify
  * how wide this margin is; in the above example a y-axis danger zone of 10
  * pixels would make us drop to low-res at y=490...990.\n
  * This value is in layer pixels.
  *
+ * \li\b apz.disable_for_scroll_linked_effects
+ * Setting this pref to true will disable APZ scrolling on documents where
+ * scroll-linked effects are detected. A scroll linked effect is detected if
+ * positioning or transform properties are updated inside a scroll event
+ * dispatch; we assume that such an update is in response to the scroll event
+ * and is therefore a scroll-linked effect which will be laggy with APZ
+ * scrolling.
+ *
  * \li\b apz.displayport_expiry_ms
  * While a scrollable frame is scrolling async, we set a displayport on it
  * to make sure it is layerized. However this takes up memory, so once the
  * scrolling stops we want to remove the displayport. This pref controls how
  * long after scrolling stops the displayport is removed. A value of 0 will
  * disable the expiry behavior entirely.
  * Units: milliseconds
  *
@@ -3025,18 +3033,25 @@ bool AsyncPanZoomController::UpdateAnima
       RequestContentRepaint();
     }
     UpdateSharedCompositorFrameMetrics();
     return true;
   }
   return false;
 }
 
-AsyncTransformComponentMatrix AsyncPanZoomController::GetOverscrollTransform() const {
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetOverscrollTransform(AsyncMode aMode) const
+{
   ReentrantMonitorAutoEnter lock(mMonitor);
+
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return AsyncTransformComponentMatrix();
+  }
+
   if (!IsOverscrolled()) {
     return AsyncTransformComponentMatrix();
   }
 
   // The overscroll effect is a uniform stretch along the overscrolled axis,
   // with the edge of the content where we have reached the end of the
   // scrollable area pinned into place.
 
@@ -3119,28 +3134,38 @@ bool AsyncPanZoomController::AdvanceAnim
 
   // One of the deferred tasks may have started a new animation. In this case,
   // we want to ask the compositor to schedule a new composite.
   requestAnimationFrame |= (mAnimation != nullptr);
 
   return requestAnimationFrame;
 }
 
-void AsyncPanZoomController::SampleContentTransformForFrame(AsyncTransform* aOutTransform,
-                                                            ParentLayerPoint& aScrollOffset)
+ParentLayerPoint
+AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncMode aMode) const
 {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
-  aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom();
-  *aOutTransform = GetCurrentAsyncTransform();
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom();
+  }
+
+  return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset)
+      * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
 }
 
-AsyncTransform AsyncPanZoomController::GetCurrentAsyncTransform() const {
+AsyncTransform
+AsyncPanZoomController::GetCurrentAsyncTransform(AsyncMode aMode) const
+{
   ReentrantMonitorAutoEnter lock(mMonitor);
 
+  if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) {
+    return AsyncTransform();
+  }
+
   CSSPoint lastPaintScrollOffset;
   if (mLastContentPaintMetrics.IsScrollable()) {
     lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset();
   }
 
   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
     mTestAsyncScrollOffset;
 
@@ -3165,19 +3190,21 @@ AsyncTransform AsyncPanZoomController::G
   ParentLayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
                                * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale;
 
   return AsyncTransform(
     LayerToParentLayerScale(mFrameMetrics.GetAsyncZoom().scale * mTestAsyncZoom.scale),
     -translation);
 }
 
-AsyncTransformComponentMatrix AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const {
-  return AsyncTransformComponentMatrix(GetCurrentAsyncTransform())
-       * GetOverscrollTransform();
+AsyncTransformComponentMatrix
+AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const
+{
+  return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode))
+       * GetOverscrollTransform(aMode);
 }
 
 Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   LayerPoint scrollChange =
     (mLastContentPaintMetrics.GetScrollOffset() - mExpectedGeckoMetrics.GetScrollOffset())
     * mLastContentPaintMetrics.GetDevPixelsPerCSSPixel()
@@ -3431,16 +3458,17 @@ void AsyncPanZoomController::NotifyLayer
     mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount());
     mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount());
     mScrollMetadata.SetSnapInfo(ScrollSnapInfo(aScrollMetadata.GetSnapInfo()));
     mScrollMetadata.SetClipRect(aScrollMetadata.GetClipRect());
     mScrollMetadata.SetMaskLayerIndex(aScrollMetadata.GetMaskLayerIndex());
     mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot());
     mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling());
     mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer());
+    mFrameMetrics.SetForceDisableApz(aLayerMetrics.IsApzForceDisabled());
 
     if (scrollOffsetUpdated) {
       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
         ToString(aLayerMetrics.GetScrollOffset()).c_str());
 
       // Send an acknowledgement with the new scroll generation so that any
       // repaint requests later in this function go through.
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -158,31 +158,16 @@ public:
    * should continue. If true, the compositor should schedule another composite.
    */
   bool AdvanceAnimations(const TimeStamp& aSampleTime);
 
   bool UpdateAnimation(const TimeStamp& aSampleTime,
                        Vector<Task*>* aOutDeferredTasks);
 
   /**
-   * Query the transforms that should be applied to the layer corresponding
-   * to this APZC due to asynchronous panning and zooming.
-   * This function returns the async transform via the |aOutTransform|
-   * out parameter.
-   */
-  void SampleContentTransformForFrame(AsyncTransform* aOutTransform,
-                                      ParentLayerPoint& aScrollOffset);
-
-  /**
-   * Return a visual effect that reflects this apzc's
-   * overscrolled state, if any.
-   */
-  AsyncTransformComponentMatrix GetOverscrollTransform() const;
-
-  /**
    * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
    * for the container layer corresponding to this APZC.
    * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the scroll metadata being sent with this call are
    * the initial metadata and the initial paint of the frame has just happened.
    */
   void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
                            bool aThisLayerTreeUpdated);
@@ -218,30 +203,16 @@ public:
   void Destroy();
 
   /**
    * Returns true if Destroy() has already been called on this APZC instance.
    */
   bool IsDestroyed() const;
 
   /**
-   * Returns the incremental transformation corresponding to the async pan/zoom
-   * in progress. That is, when this transform is multiplied with the layer's
-   * existing transform, it will make the layer appear with the desired pan/zoom
-   * amount.
-   */
-  AsyncTransform GetCurrentAsyncTransform() const;
-
-  /**
-   * Returns the same transform as GetCurrentAsyncTransform(), but includes
-   * any transform due to axis over-scroll.
-   */
-  AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll() const;
-
-  /**
    * Returns the transform to take something from the coordinate space of the
    * last thing we know gecko painted, to the coordinate space of the last thing
    * we asked gecko to paint. In cases where that last request has not yet been
    * processed, this is needed to transform input events properly into a space
    * gecko will understand.
    */
   Matrix4x4 GetTransformToLastDispatchedPaint() const;
 
@@ -732,16 +703,63 @@ private:
   // to allow panning by moving multiple fingers (thus moving the focus point).
   ParentLayerPoint mLastZoomFocus;
 
   RefPtr<AsyncPanZoomAnimation> mAnimation;
 
   friend class Axis;
 
 
+  /* ===================================================================
+   * The functions and members in this section are used to expose
+   * the current async transform state to callers.
+   */
+public:
+  /**
+   * Allows callers to specify which type of async transform they want:
+   * NORMAL provides the actual async transforms of the APZC, whereas
+   * RESPECT_FORCE_DISABLE will provide empty async transforms if and only if
+   * the metrics has the mForceDisableApz flag set. In general the latter should
+   * only be used by call sites that are applying the transform to update
+   * a layer's position.
+   */
+  enum AsyncMode {
+    NORMAL,
+    RESPECT_FORCE_DISABLE,
+  };
+
+  /**
+   * Query the transforms that should be applied to the layer corresponding
+   * to this APZC due to asynchronous panning and zooming.
+   * This function returns the async transform via the |aOutTransform|
+   * out parameter.
+   */
+  ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncMode aMode) const;
+
+  /**
+   * Return a visual effect that reflects this apzc's
+   * overscrolled state, if any.
+   */
+  AsyncTransformComponentMatrix GetOverscrollTransform(AsyncMode aMode) const;
+
+  /**
+   * Returns the incremental transformation corresponding to the async pan/zoom
+   * in progress. That is, when this transform is multiplied with the layer's
+   * existing transform, it will make the layer appear with the desired pan/zoom
+   * amount.
+   */
+  AsyncTransform GetCurrentAsyncTransform(AsyncMode aMode) const;
+
+  /**
+   * Returns the same transform as GetCurrentAsyncTransform(), but includes
+   * any transform due to axis over-scroll.
+   */
+  AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const;
+
+
 
   /* ===================================================================
    * The functions and members in this section are used to manage
    * the state that tracks what this APZC is doing with the input events.
    */
 protected:
   enum PanZoomState {
     NOTHING,                  /* no touch-start events received */
@@ -1101,19 +1119,19 @@ public:
   }
 
   uint64_t GetLayersId() const
   {
     return mLayersId;
   }
 
 private:
-  // Extra offset to add in SampleContentTransformForFrame for testing
+  // Extra offset to add to the async scroll position for testing
   CSSPoint mTestAsyncScrollOffset;
-  // Extra zoom to include in SampleContentTransformForFrame for testing
+  // Extra zoom to include in the aync zoom for testing
   LayerToParentLayerScale mTestAsyncZoom;
   // Flag to track whether or not the APZ transform is not used. This
   // flag is recomputed for every composition frame.
   bool mAsyncTransformAppliedToContent;
 
 
   /* ===================================================================
    * The functions and members in this section are used for checkerboard
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -236,17 +236,17 @@ HitTestingTreeNode::IsOutsideClip(const 
 
 Maybe<LayerPoint>
 HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
 {
   // convert into Layer coordinate space
   LayerToParentLayerMatrix4x4 transform = mTransform *
       CompleteAsyncTransform(
         mApzc
-      ? mApzc->GetCurrentAsyncTransformWithOverscroll()
+      ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
       : AsyncTransformComponentMatrix());
   return UntransformBy(transform.Inverse(), aPoint);
 }
 
 HitTestResult
 HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
 {
   // This should only ever get called if the point is inside the clip region
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -91,17 +91,17 @@ protected:
     ParentLayerPoint pointOut;
     AsyncTransform viewTransformOut;
     while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
       // The reported scroll offset should be the same throughout.
       EXPECT_EQ(aExpectedScrollOffset, pointOut);
 
       // Trigger computation of the overscroll tranform, to make sure
       // no assetions fire during the calculation.
-      apzc->GetOverscrollTransform();
+      apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL);
 
       if (!apzc->IsOverscrolled()) {
         recoveredFromOverscroll = true;
       }
 
       mcc->AdvanceBy(increment);
     }
     EXPECT_TRUE(recoveredFromOverscroll);
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -247,18 +247,20 @@ public:
     }
   }
 
   bool SampleContentTransformForFrame(AsyncTransform* aOutTransform,
                                       ParentLayerPoint& aScrollOffset,
                                       const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
     mcc->AdvanceBy(aIncrement);
     bool ret = AdvanceAnimations(mcc->Time());
-    AsyncPanZoomController::SampleContentTransformForFrame(
-      aOutTransform, aScrollOffset);
+    if (aOutTransform) {
+      *aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL);
+    }
+    aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL);
     return ret;
   }
 
   void SetWaitForMainThread() {
     mWaitForMainThread = true;
   }
 
 private:
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -53,16 +53,22 @@ protected:
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
 
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
     SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
   }
 
+  void DisableApzOn(Layer* aLayer) {
+    ScrollMetadata m = aLayer->GetScrollMetadata(0);
+    m.GetMetrics().SetForceDisableApz(true);
+    aLayer->SetScrollMetadata(m);
+  }
+
   void CreateComplexMultiLayerTree() {
     const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
     // LayerID                     0 12 3 45 6 7 89
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0,0,300,400)),      // root(0)
       nsIntRegion(IntRect(0,0,100,100)),      // thebes(1) in top-left
       nsIntRegion(IntRect(50,50,200,300)),    // container(2) centered in root(0)
       nsIntRegion(IntRect(50,50,200,300)),    // thebes(3) fully occupying parent container(2)
@@ -448,16 +454,58 @@ TEST_F(APZHitTestingTester, TestRepaintF
     EXPECT_EQ((i + 1) * 10, point.y);
     EXPECT_EQ(0, viewTransform.mTranslation.x);
     EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
 
     mcc->AdvanceByMillis(5);
   }
 }
 
+TEST_F(APZHitTestingTester, TestForceDisableApz) {
+  CreateSimpleScrollingLayer();
+  DisableApzOn(root);
+  ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
+  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+  ScreenPoint origin(100, 50);
+  ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+    ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+    origin, 0, 10, false);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+  EXPECT_EQ(origin, swi.mOrigin);
+
+  AsyncTransform viewTransform;
+  ParentLayerPoint point;
+  apzcroot->SampleContentTransformForFrame(&viewTransform, point);
+  // Since APZ is force-disabled, we expect to see the async transform via
+  // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
+  EXPECT_EQ(0, point.x);
+  EXPECT_EQ(10, point.y);
+  EXPECT_EQ(0, viewTransform.mTranslation.x);
+  EXPECT_EQ(-10, viewTransform.mTranslation.y);
+  viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+  point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+  EXPECT_EQ(0, point.x);
+  EXPECT_EQ(0, point.y);
+  EXPECT_EQ(0, viewTransform.mTranslation.x);
+  EXPECT_EQ(0, viewTransform.mTranslation.y);
+
+  mcc->AdvanceByMillis(10);
+
+  // With untransforming events we should get normal behaviour (in this case,
+  // no noticeable untransform, because the repaint request already got
+  // flushed).
+  swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
+    ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+    origin, 0, 0, false);
+  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+  EXPECT_EQ(origin, swi.mOrigin);
+}
+
 TEST_F(APZHitTestingTester, Bug1148350) {
   CreateBug1148350LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -496,19 +496,19 @@ APZCCallbackHelper::ApplyCallbackTransfo
 
 void
 APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent,
                                            const ScrollableLayerGuid& aGuid,
                                            const CSSToLayoutDeviceScale& aScale)
 {
   if (aEvent.AsTouchEvent()) {
     WidgetTouchEvent& event = *(aEvent.AsTouchEvent());
-    for (size_t i = 0; i < event.touches.Length(); i++) {
-      event.touches[i]->mRefPoint = ApplyCallbackTransform(
-          event.touches[i]->mRefPoint, aGuid, aScale);
+    for (size_t i = 0; i < event.mTouches.Length(); i++) {
+      event.mTouches[i]->mRefPoint = ApplyCallbackTransform(
+          event.mTouches[i]->mRefPoint, aGuid, aScale);
     }
   } else {
     aEvent.refPoint = ApplyCallbackTransform(
         aEvent.refPoint, aGuid, aScale);
   }
 }
 
 nsEventStatus
@@ -801,19 +801,19 @@ APZCCallbackHelper::SendSetTargetAPZCNot
   }
   sLastTargetAPZCNotificationInputBlock = aInputBlockId;
   if (nsIPresShell* shell = aDocument->GetShell()) {
     if (nsIFrame* rootFrame = shell->GetRootFrame()) {
       bool waitForRefresh = false;
       nsTArray<ScrollableLayerGuid> targets;
 
       if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
-        for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
+        for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
           waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
-              rootFrame, touchEvent->touches[i]->mRefPoint, &targets);
+              rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets);
         }
       } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
         waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
             rootFrame, wheelEvent->refPoint, &targets);
       }
       // TODO: Do other types of events need to be handled?
 
       if (!targets.IsEmpty()) {
@@ -831,18 +831,20 @@ APZCCallbackHelper::SendSetTargetAPZCNot
 void
 APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
         nsIWidget* aWidget,
         const WidgetTouchEvent& aEvent,
         uint64_t aInputBlockId,
         const SetAllowedTouchBehaviorCallback& aCallback)
 {
   nsTArray<TouchBehaviorFlags> flags;
-  for (uint32_t i = 0; i < aEvent.touches.Length(); i++) {
-    flags.AppendElement(widget::ContentHelper::GetAllowedTouchBehavior(aWidget, aEvent.touches[i]->mRefPoint));
+  for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
+    flags.AppendElement(
+      widget::ContentHelper::GetAllowedTouchBehavior(
+                               aWidget, aEvent.mTouches[i]->mRefPoint));
   }
   aCallback(aInputBlockId, flags);
 }
 
 void
 APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
   nsCOMPtr<nsIContent> targetContent = nsLayoutUtils::FindContentFor(aScrollId);
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -255,18 +255,18 @@ APZEventState::ProcessLongTap(const nsCO
 
 void
 APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
                                  const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  nsEventStatus aApzResponse,
                                  nsEventStatus aContentResponse)
 {
-  if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() > 0) {
-    mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget());
+  if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) {
+    mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget());
   }
 
   bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault;
   bool sentContentResponse = false;
   APZES_LOG("Handling event type %d\n", aEvent.mMessage);
   switch (aEvent.mMessage) {
   case eTouchStart: {
     mTouchEndCancelled = false;
@@ -317,18 +317,18 @@ APZEventState::ProcessTouchEvent(const W
   }
 
   if (sentContentResponse &&
         aApzResponse == nsEventStatus_eConsumeDoDefault &&
         gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
     cancelEvent.mMessage = eTouchCancel;
     cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
-    for (uint32_t i = 0; i < cancelEvent.touches.Length(); ++i) {
-      if (mozilla::dom::Touch* touch = cancelEvent.touches[i]) {
+    for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
+      if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
         touch->convertToPointer = true;
       }
     }
     nsEventStatus status;
     cancelEvent.widget->DispatchEvent(&cancelEvent, status);
   }
 }
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -840,21 +840,20 @@ AsyncCompositionManager::ApplyAsyncConte
   for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
 
     hasAsyncTransform = true;
 
-    AsyncTransform asyncTransformWithoutOverscroll;
-    ParentLayerPoint scrollOffset;
-    controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
-                                               scrollOffset);
-    AsyncTransformComponentMatrix overscrollTransform = controller->GetOverscrollTransform();
+    AsyncTransform asyncTransformWithoutOverscroll =
+        controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+    AsyncTransformComponentMatrix overscrollTransform =
+        controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
     AsyncTransformComponentMatrix asyncTransform =
         AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll)
       * overscrollTransform;
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
@@ -877,16 +876,18 @@ AsyncCompositionManager::ApplyAsyncConte
         CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
         if (mIsFirstPaint) {
           LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
           mContentRect = metrics.GetScrollableRect();
           SetFirstPaintViewport(scrollOffsetLayerPixels,
                                 geckoZoom,
                                 mContentRect);
         } else {
+          ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(
+              AsyncPanZoomController::RESPECT_FORCE_DISABLE);
           // Compute the painted displayport in document-relative CSS pixels.
           CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
               metrics.GetDisplayPort() :
               metrics.GetCriticalDisplayPort());
           displayPort += metrics.GetScrollOffset();
           SyncFrameMetrics(scrollOffset,
               geckoZoom * asyncTransformWithoutOverscroll.mScale,
               metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
@@ -1027,17 +1028,18 @@ ApplyAsyncTransformToScrollbarForContent
   // disparity between scrollbars and visible content.
   if (aContent.IsScrollInfoLayer()) {
     return;
   }
 
   const FrameMetrics& metrics = aContent.Metrics();
   AsyncPanZoomController* apzc = aContent.GetApzc();
 
-  AsyncTransformComponentMatrix asyncTransform = apzc->GetCurrentAsyncTransform();
+  AsyncTransformComponentMatrix asyncTransform =
+    apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
 
   // |asyncTransform| represents the amount by which we have scrolled and
   // zoomed since the last paint. Because the scrollbar was sized and positioned based
   // on the painted content, we need to adjust it based on asyncTransform so that
   // it reflects what the user is actually seeing now.
   AsyncTransformComponentMatrix scrollbarTransform;
   if (aScrollbar->GetScrollbarDirection() == Layer::VERTICAL) {
     const ParentLayerCoord asyncScrollY = asyncTransform._42;
@@ -1142,17 +1144,19 @@ ApplyAsyncTransformToScrollbarForContent
   // transform, and without this adjustment the scrollbar will end up in the
   // wrong place.
   //
   // Note that since the async transform is applied on top of the content's
   // regular transform, we need to make sure to unapply the async transform in
   // the same coordinate space. This requires applying the content transform
   // and then unapplying it after unapplying the async transform.
   if (aScrollbarIsDescendant) {
-    Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse().ToUnknownMatrix();
+    AsyncTransformComponentMatrix overscroll =
+        apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+    Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
     Matrix4x4 contentTransform = aContent.GetTransform();
     Matrix4x4 contentUntransform = contentTransform.Inverse();
 
     AsyncTransformComponentMatrix asyncCompensation =
         ViewAs<AsyncTransformComponentMatrix>(
             contentTransform
           * asyncUntransform
           * contentUntransform);
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -483,20 +483,17 @@ RenderMinimap(ContainerT* aContainer, La
     return;
   }
 
   AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
   if (!controller) {
     return;
   }
 
-  AsyncTransform asyncTransformWithoutOverscroll;
-  ParentLayerPoint scrollOffset;
-  controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
-                                           scrollOffset);
+  ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
 
   // Options
   const int verticalPadding = 10;
   const int horizontalPadding = 5;
   gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
   gfx::Color tileActiveColor(1, 1, 1, 0.4f);
   gfx::Color tileBorderColor(0, 0, 0, 0.1f);
   gfx::Color pageBorderColor(0, 0, 0);
@@ -660,18 +657,19 @@ RenderLayers(ContainerT* aContainer,
         // Since the composition bounds are in the parent layer's coordinates,
         // use the parent's effective transform rather than the layer's own.
         ParentLayerRect compositionBounds = layer->GetFrameMetrics(i - 1).GetCompositionBounds();
         aManager->GetCompositor()->DrawDiagnostics(DiagnosticFlags::CONTAINER,
                                                    compositionBounds.ToUnknownRect(),
                                                    gfx::Rect(aClipRect.ToUnknownRect()),
                                                    asyncTransform * aContainer->GetEffectiveTransform());
         if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) {
-          asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll().ToUnknownMatrix()
-                         * asyncTransform;
+          asyncTransform =
+              apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix()
+            * asyncTransform;
         }
       }
     }
 
     if (gfxPrefs::APZMinimap()) {
       RenderMinimap(aContainer, aManager, aClipRect, layer);
     }
 
@@ -810,17 +808,17 @@ ContainerRender(ContainerT* aContainer,
   // enabled).
   if (gfxPrefs::LayersDrawFPS() && aContainer->IsScrollInfoLayer()) {
     // Since aContainer doesn't have any children we can just iterate from the top metrics
     // on it down to the bottom using GetFirstChild and not worry about walking onto another
     // underlying layer.
     for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) {
       if (AsyncPanZoomController* apzc = i.GetApzc()) {
         if (!apzc->GetAsyncTransformAppliedToContent()
-            && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
+            && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) {
           aManager->UnusedApzTransformWarning();
           break;
         }
       }
     }
   }
 }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -143,16 +143,17 @@ private:
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle",          APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold",      APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
   DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle",        APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.lock_angle",              APZAxisLockAngle, float, float(M_PI / 6.0) /* 30 degrees */);
   DECL_GFX_PREF(Live, "apz.axis_lock.mode",                    APZAxisLockMode, int32_t, 0);
   DECL_GFX_PREF(Live, "apz.content_response_timeout",          APZContentResponseTimeout, int32_t, 300);
   DECL_GFX_PREF(Live, "apz.danger_zone_x",                     APZDangerZoneX, int32_t, 50);
   DECL_GFX_PREF(Live, "apz.danger_zone_y",                     APZDangerZoneY, int32_t, 100);
+  DECL_GFX_PREF(Live, "apz.disable_for_scroll_linked_effects", APZDisableForScrollLinkedEffects, bool, false);
   DECL_GFX_PREF(Live, "apz.displayport_expiry_ms",             APZDisplayPortExpiryTime, uint32_t, 15000);
   DECL_GFX_PREF(Live, "apz.drag.enabled",                      APZDragEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped",  APZEnlargeDisplayPortWhenClipped, bool, false);
   DECL_GFX_PREF(Live, "apz.fling_accel_base_mult",             APZFlingAccelBaseMultiplier, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms",           APZFlingAccelInterval, int32_t, 500);
   DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult",     APZFlingAccelSupplementalMultiplier, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x1",           APZCurveFunctionX1, float, 0.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x2",           APZCurveFunctionX2, float, 1.0f);
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
+ #include "mozilla/ChaosMode.h"
 
 #include "ImageLogging.h"
 #include "nsPrintfCString.h"
 #include "imgLoader.h"
 #include "imgRequestProxy.h"
 
 #include "nsCOMPtr.h"
 
@@ -1767,16 +1768,22 @@ imgLoader::ValidateEntry(imgCacheEntry* 
   void *key = (void*) aCX;
   if (request->LoadId() != key) {
     // If we would need to revalidate this entry, but we're being told to
     // bypass the cache, we don't allow this entry to be used.
     if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
       return false;
     }
 
+    if (MOZ_UNLIKELY(ChaosMode::isActive(ChaosFeature::ImageCache))) {
+      if (ChaosMode::randomUint32LessThan(4) < 1) {
+        return false;
+      }
+    }
+
     // Determine whether the cache aEntry must be revalidated...
     validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
 
     MOZ_LOG(gImgLog, LogLevel::Debug,
            ("imgLoader::ValidateEntry validating cache entry. "
             "validateRequest = %d", validateRequest));
   } else if (!key && MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString spec;
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -619,17 +619,17 @@ struct ClassExtension
      */
     JSObjectMovedOp objectMovedOp;
 };
 
 inline ClassObjectCreationOp DELEGATED_CLASSSPEC(const ClassSpec* spec) {
     return reinterpret_cast<ClassObjectCreationOp>(const_cast<ClassSpec*>(spec));
 }
 
-#define JS_NULL_CLASS_SPEC  {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr}
+#define JS_NULL_CLASS_SPEC  nullptr
 #define JS_NULL_CLASS_EXT   {false,nullptr}
 
 struct ObjectOps
 {
     LookupPropertyOp lookupProperty;
     DefinePropertyOp defineProperty;
     HasPropertyOp    hasProperty;
     GetPropertyOp    getProperty;
@@ -649,17 +649,17 @@ struct ObjectOps
 
 // Classes, objects, and properties.
 
 typedef void (*JSClassInternal)();
 
 struct JSClass {
     JS_CLASS_MEMBERS(JSFinalizeOp);
 
-    void*               reserved[12];
+    void*               reserved[5];
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_DELAY_METADATA_CALLBACK (1<<1)  // class's initialization code
                                                 // will call
                                                 // SetNewObjectMetadata itself
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports*)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
@@ -751,17 +751,17 @@ struct JSClass {
 #define JSCLASS_NO_INTERNAL_MEMBERS     {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
 #define JSCLASS_NO_OPTIONAL_MEMBERS     0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
 
 namespace js {
 
 struct Class
 {
     JS_CLASS_MEMBERS(FinalizeOp);
-    ClassSpec          spec;
+    const ClassSpec*    spec;
     ClassExtension      ext;
     const ObjectOps*    ops;
 
     /*
      * Objects of this class aren't native objects. They don't have Shapes that
      * describe their properties and layout. Classes using this flag must
      * provide their own property behavior, either by being proxy classes (do
      * this) or by overriding all the ObjectOps except getElements, watch and
@@ -799,16 +799,36 @@ struct Class
     }
 
     bool shouldDelayMetadataCallback() const {
         return flags & JSCLASS_DELAY_METADATA_CALLBACK;
     }
 
     static size_t offsetOfFlags() { return offsetof(Class, flags); }
 
+    bool specDefined()         const { return spec ? spec->defined()   : false; }
+    bool specDependent()       const { return spec ? spec->dependent() : false; }
+    JSProtoKey specParentKey() const { return spec ? spec->parentKey() : JSProto_Null; }
+    bool specShouldDefineConstructor()
+                               const { return spec ? spec->shouldDefineConstructor() : true; }
+    ClassObjectCreationOp specCreateConstructorHook()
+                               const { return spec ? spec->createConstructorHook()   : nullptr; }
+    ClassObjectCreationOp specCreatePrototypeHook()
+                               const { return spec ? spec->createPrototypeHook()     : nullptr; }
+    const JSFunctionSpec* specConstructorFunctions()
+                               const { return spec ? spec->constructorFunctions()    : nullptr; }
+    const JSPropertySpec* specConstructorProperties()
+                               const { return spec ? spec->constructorProperties()   : nullptr; }
+    const JSFunctionSpec* specPrototypeFunctions()
+                               const { return spec ? spec->prototypeFunctions()      : nullptr; }
+    const JSPropertySpec* specPrototypeProperties()
+                               const { return spec ? spec->prototypeProperties()     : nullptr; }
+    FinishClassInitOp specFinishInitHook()
+                               const { return spec ? spec->finishInitHook()          : nullptr; }
+
     LookupPropertyOp getOpsLookupProperty() const { return ops ? ops->lookupProperty : nullptr; }
     DefinePropertyOp getOpsDefineProperty() const { return ops ? ops->defineProperty : nullptr; }
     HasPropertyOp    getOpsHasProperty()    const { return ops ? ops->hasProperty    : nullptr; }
     GetPropertyOp    getOpsGetProperty()    const { return ops ? ops->getProperty    : nullptr; }
     SetPropertyOp    getOpsSetProperty()    const { return ops ? ops->setProperty    : nullptr; }
     GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
                                             const { return ops ? ops->getOwnPropertyDescriptor
                                                                                      : nullptr; }
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -91,17 +91,16 @@ enum AsmJSMathBuiltinFunction
 
 // The asm.js spec will recognize this set of builtin Atomics functions.
 enum AsmJSAtomicsBuiltinFunction
 {
     AsmJSAtomicsBuiltin_compareExchange,
     AsmJSAtomicsBuiltin_exchange,
     AsmJSAtomicsBuiltin_load,
     AsmJSAtomicsBuiltin_store,
-    AsmJSAtomicsBuiltin_fence,
     AsmJSAtomicsBuiltin_add,
     AsmJSAtomicsBuiltin_sub,
     AsmJSAtomicsBuiltin_and,
     AsmJSAtomicsBuiltin_or,
     AsmJSAtomicsBuiltin_xor,
     AsmJSAtomicsBuiltin_isLockFree
 };
 
@@ -1730,17 +1729,16 @@ class MOZ_STACK_CLASS ModuleValidator
             return false;
         }
 
         if (!standardLibraryAtomicsNames_.init() ||
             !addStandardLibraryAtomicsName("compareExchange", AsmJSAtomicsBuiltin_compareExchange) ||
             !addStandardLibraryAtomicsName("exchange", AsmJSAtomicsBuiltin_exchange) ||
             !addStandardLibraryAtomicsName("load", AsmJSAtomicsBuiltin_load) ||
             !addStandardLibraryAtomicsName("store", AsmJSAtomicsBuiltin_store) ||
-            !addStandardLibraryAtomicsName("fence", AsmJSAtomicsBuiltin_fence) ||
             !addStandardLibraryAtomicsName("add", AsmJSAtomicsBuiltin_add) ||
             !addStandardLibraryAtomicsName("sub", AsmJSAtomicsBuiltin_sub) ||
             !addStandardLibraryAtomicsName("and", AsmJSAtomicsBuiltin_and) ||
             !addStandardLibraryAtomicsName("or", AsmJSAtomicsBuiltin_or) ||
             !addStandardLibraryAtomicsName("xor", AsmJSAtomicsBuiltin_xor) ||
             !addStandardLibraryAtomicsName("isLockFree", AsmJSAtomicsBuiltin_isLockFree))
         {
             return false;
@@ -4102,26 +4100,16 @@ CheckSharedArrayAtomicAccess(FunctionVal
       default:
         return f.failf(viewName, "not an integer array");
     }
 
     return true;
 }
 
 static bool
-CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type)
-{
-    if (CallArgListLength(call) != 0)
-        return f.fail(call, "Atomics.fence must be passed 0 arguments");
-
-    *type = Type::Void;
-    return f.encoder().writeExpr(Expr::AtomicsFence);
-}
-
-static bool
 WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt)
 {
     return f.encoder().writeExpr(opcode) &&
            f.encoder().writePatchableFixedU8(viewTypeAt);
 }
 
 static bool
 CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
@@ -4304,18 +4292,16 @@ CheckAtomicsBuiltinCall(FunctionValidato
       case AsmJSAtomicsBuiltin_compareExchange:
         return CheckAtomicsCompareExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_exchange:
         return CheckAtomicsExchange(f, callNode, type);
       case AsmJSAtomicsBuiltin_load:
         return CheckAtomicsLoad(f, callNode, type);
       case AsmJSAtomicsBuiltin_store:
         return CheckAtomicsStore(f, callNode, type);
-      case AsmJSAtomicsBuiltin_fence:
-        return CheckAtomicsFence(f, callNode, type);
       case AsmJSAtomicsBuiltin_add:
         return CheckAtomicsBinop(f, callNode, type, AtomicFetchAddOp);
       case AsmJSAtomicsBuiltin_sub:
         return CheckAtomicsBinop(f, callNode, type, AtomicFetchSubOp);
       case AsmJSAtomicsBuiltin_and:
         return CheckAtomicsBinop(f, callNode, type, AtomicFetchAndOp);
       case AsmJSAtomicsBuiltin_or:
         return CheckAtomicsBinop(f, callNode, type, AtomicFetchOrOp);
@@ -7366,17 +7352,16 @@ ValidateAtomicsBuiltinFunction(JSContext
         return false;
 
     Native native = nullptr;
     switch (global.atomicsBuiltinFunction()) {
       case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
       case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
       case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
       case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
-      case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break;
       case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
       case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
       case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
       case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
       case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
       case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
     }
 
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -270,17 +270,16 @@ enum class Expr
     F64Acos,
     F64Atan,
     F64Exp,
     F64Log,
     F64Pow,
     F64Atan2,
 
     // Atomics
-    AtomicsFence,
     I32AtomicsCompareExchange,
     I32AtomicsExchange,
     I32AtomicsLoad,
     I32AtomicsStore,
     I32AtomicsBinOp,
 
     // SIMD
 #define SIMD_OPCODE(TYPE, OP) TYPE##OP,
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -572,24 +572,16 @@ class FunctionCompiler
             return;
 
         MOZ_ASSERT(Scalar::isSimdType(access.accessType()),
                    "storeSimdHeap can only load from a SIMD view");
         MAsmJSStoreHeap* store = MAsmJSStoreHeap::New(alloc(), base, access, v);
         curBlock_->add(store);
     }
 
-    void memoryBarrier(MemoryBarrierBits type)
-    {
-        if (inDeadCode())
-            return;
-        MMemoryBarrier* ins = MMemoryBarrier::New(alloc(), type);
-        curBlock_->add(ins);
-    }
-
     MDefinition* atomicLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access)
     {
         if (inDeadCode())
             return nullptr;
 
         MAsmJSLoadHeap* load = MAsmJSLoadHeap::New(alloc(), base, access);
         curBlock_->add(load);
         return load;
@@ -3068,20 +3060,16 @@ EmitExpr(FunctionCompiler& f, MDefinitio
       case Expr::I32x4greaterThanOrEqualU:
         return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_greaterThanOrEqual,
                           SimdSign::Unsigned, def);
       case Expr::I32x4fromFloat32x4U:
         return EmitSimdOp(f, ValType::I32x4, SimdOperation::Fn_fromFloat32x4,
                           SimdSign::Unsigned, def);
 
       // Atomics
-      case Expr::AtomicsFence:
-        *def = nullptr;
-        f.memoryBarrier(MembarFull);
-        return true;
       case Expr::I32AtomicsCompareExchange:
         return EmitAtomicsCompareExchange(f, def);
       case Expr::I32AtomicsExchange:
         return EmitAtomicsExchange(f, def);
       case Expr::I32AtomicsLoad:
         return EmitAtomicsLoad(f, def);
       case Expr::I32AtomicsStore:
         return EmitAtomicsStore(f, def);
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -47,16 +47,17 @@
 
 #include "builtin/AtomicsObject.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "jsnum.h"
 
 #include "asmjs/WasmModule.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
 #include "vm/GlobalObject.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
@@ -75,17 +76,20 @@ ReportBadArrayType(JSContext* cx)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_ARRAY);
     return false;
 }
 
 static bool
 ReportOutOfRange(JSContext* cx)
 {
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_INDEX);
+    // Use JSMSG_BAD_INDEX here even if it is generic, since that is
+    // the message used by ToIntegerIndex for its initial range
+    // checking.
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
     return false;
 }
 
 static bool
 ReportCannotWait(JSContext* cx)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
     return false;
@@ -103,32 +107,22 @@ GetSharedTypedArray(JSContext* cx, Handl
     if (!viewp->isSharedMemory())
         return ReportBadArrayType(cx);
     return true;
 }
 
 static bool
 GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle<TypedArrayObject*> view, uint32_t* offset)
 {
-    RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, v, &id))
+    uint64_t index;
+    if (!js::ToIntegerIndex(cx, v, &index))
         return false;
-    uint64_t index;
-    if (!IsTypedArrayIndex(id, &index) || index >= view->length())
+    if (index >= view->length())
         return ReportOutOfRange(cx);
-    *offset = (uint32_t)index;
-    return true;
-}
-
-bool
-js::atomics_fence(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    jit::AtomicOperations::fenceSeqCst();
-    args.rval().setUndefined();
+    *offset = uint32_t(index);
     return true;
 }
 
 static int32_t
 CompareExchange(Scalar::Type viewType, int32_t oldCandidate, int32_t newCandidate,
                 SharedMem<void*> viewData, uint32_t offset, bool* badArrayType = nullptr)
 {
     switch (viewType) {
@@ -749,17 +743,17 @@ class AutoUnlockFutexAPI
     ~AutoUnlockFutexAPI() {
         FutexRuntime::lock();
     }
 };
 
 } // namespace js
 
 bool
-js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp)
+js::atomics_wait(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
     HandleValue valv = args.get(2);
     HandleValue timeoutv = args.get(3);
     MutableHandleValue r = args.rval();
 
@@ -792,17 +786,17 @@ js::atomics_futexWait(JSContext* cx, uns
         return ReportCannotWait(cx);
 
     // This lock also protects the "waiters" field on SharedArrayRawBuffer,
     // and it provides the necessary memory fence.
     AutoLockFutexAPI lock;
 
     SharedMem<int32_t*>(addr) = view->viewDataShared().cast<int32_t*>() + offset;
     if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
-        r.setInt32(AtomicsObject::FutexNotequal);
+        r.setString(cx->names().futexNotEqual);
         return true;
     }
 
     Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
     SharedArrayRawBuffer* sarb = sab->rawBufferObject();
 
     FutexWaiter w(offset, rt);
     if (FutexWaiter* waiters = sarb->waiters()) {
@@ -810,54 +804,66 @@ js::atomics_futexWait(JSContext* cx, uns
         w.back = waiters->back;
         waiters->back->lower_pri = &w;
         waiters->back = &w;
     } else {
         w.lower_pri = w.back = &w;
         sarb->setWaiters(&w);
     }
 
-    AtomicsObject::FutexWaitResult result = AtomicsObject::FutexOK;
+    FutexRuntime::WaitResult result = FutexRuntime::FutexOK;
     bool retval = rt->fx.wait(cx, timeout_ms, &result);
-    if (retval)
-        r.setInt32(result);
+    if (retval) {
+        switch (result) {
+          case FutexRuntime::FutexOK:
+            r.setString(cx->names().futexOK);
+            break;
+          case FutexRuntime::FutexTimedOut:
+            r.setString(cx->names().futexTimedOut);
+            break;
+        }
+    }
 
     if (w.lower_pri == &w) {
         sarb->setWaiters(nullptr);
     } else {
         w.lower_pri->back = w.back;
         w.back->lower_pri = w.lower_pri;
         if (sarb->waiters() == &w)
             sarb->setWaiters(w.lower_pri);
     }
     return retval;
 }
 
 bool
-js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp)
+js::atomics_wake(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     HandleValue objv = args.get(0);
     HandleValue idxv = args.get(1);
     HandleValue countv = args.get(2);
     MutableHandleValue r = args.rval();
 
     Rooted<TypedArrayObject*> view(cx, nullptr);
     if (!GetSharedTypedArray(cx, objv, &view))
         return false;
     if (view->type() != Scalar::Int32)
         return ReportBadArrayType(cx);
     uint32_t offset;
     if (!GetTypedArrayIndex(cx, idxv, view, &offset))
         return false;
     double count;
-    if (!ToInteger(cx, countv, &count))
-        return false;
-    if (count < 0)
-        count = 0;
+    if (countv.isUndefined()) {
+        count = mozilla::PositiveInfinity<double>();
+    } else {
+        if (!ToInteger(cx, countv, &count))
+            return false;
+        if (count < 0.0)
+            count = 0.0;
+    }
 
     AutoLockFutexAPI lock;
 
     Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
     SharedArrayRawBuffer* sarb = sab->rawBufferObject();
     int32_t woken = 0;
 
     FutexWaiter* waiters = sarb->waiters();
@@ -873,130 +879,16 @@ js::atomics_futexWake(JSContext* cx, uns
             --count;
         } while (count > 0 && iter != waiters);
     }
 
     r.setInt32(woken);
     return true;
 }
 
-bool
-js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    HandleValue objv = args.get(0);
-    HandleValue idx1v = args.get(1);
-    HandleValue countv = args.get(2);
-    HandleValue idx2v = args.get(3);
-    HandleValue valv = args.get(4);
-    MutableHandleValue r = args.rval();
-
-    Rooted<TypedArrayObject*> view(cx, nullptr);
-    if (!GetSharedTypedArray(cx, objv, &view))
-        return false;
-    if (view->type() != Scalar::Int32)
-        return ReportBadArrayType(cx);
-    uint32_t offset1;
-    if (!GetTypedArrayIndex(cx, idx1v, view, &offset1))
-        return false;
-    double count;
-    if (!ToInteger(cx, countv, &count))
-        return false;
-    if (count < 0)
-        count = 0;
-    int32_t value;
-    if (!ToInt32(cx, valv, &value))
-        return false;
-    uint32_t offset2;
-    if (!GetTypedArrayIndex(cx, idx2v, view, &offset2))
-        return false;
-
-    AutoLockFutexAPI lock;
-
-    SharedMem<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset1;
-    if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
-        r.setInt32(AtomicsObject::FutexNotequal);
-        return true;
-    }
-
-    Rooted<SharedArrayBufferObject*> sab(cx, view->bufferShared());
-    SharedArrayRawBuffer* sarb = sab->rawBufferObject();
-
-    // Walk the list of waiters looking for those waiting on offset1.
-    // Wake some and requeue the others.  There may already be other
-    // waiters on offset2, so those that are requeued must be moved to
-    // the back of the list.  Offset1 may equal offset2.  The list's
-    // first node may change, and the list may be emptied out by the
-    // operation.
-
-    FutexWaiter* waiters = sarb->waiters();
-    if (!waiters) {
-        r.setInt32(0);
-        return true;
-    }
-
-    int32_t woken = 0;
-    FutexWaiter whead((uint32_t)-1, nullptr); // Header node for waiters
-    FutexWaiter* first = waiters;
-    FutexWaiter* last = waiters->back;
-    whead.lower_pri = first;
-    whead.back = last;
-    first->back = &whead;
-    last->lower_pri = &whead;
-
-    FutexWaiter rhead((uint32_t)-1, nullptr); // Header node for requeued
-    rhead.lower_pri = rhead.back = &rhead;
-
-    FutexWaiter* iter = whead.lower_pri;
-    while (iter != &whead) {
-        FutexWaiter* c = iter;
-        iter = iter->lower_pri;
-        if (c->offset != offset1 || !c->rt->fx.isWaiting())
-            continue;
-        if (count > 0) {
-            c->rt->fx.wake(FutexRuntime::WakeExplicit);
-            ++woken;
-            --count;
-        } else {
-            c->offset = offset2;
-
-            // Remove the node from the waiters list.
-            c->back->lower_pri = c->lower_pri;
-            c->lower_pri->back = c->back;
-
-            // Insert the node at the back of the requeuers list.
-            c->lower_pri = &rhead;
-            c->back = rhead.back;
-            rhead.back->lower_pri = c;
-            rhead.back = c;
-        }
-    }
-
-    // If there are any requeuers, append them to the waiters.
-    if (rhead.lower_pri != &rhead) {
-        whead.back->lower_pri = rhead.lower_pri;
-        rhead.lower_pri->back = whead.back;
-
-        whead.back = rhead.back;
-        rhead.back->lower_pri = &whead;
-    }
-
-    // Make the final list and install it.
-    waiters = nullptr;
-    if (whead.lower_pri != &whead) {
-        whead.back->lower_pri = whead.lower_pri;
-        whead.lower_pri->back = whead.back;
-        waiters = whead.lower_pri;
-    }
-    sarb->setWaiters(waiters);
-
-    r.setInt32(woken);
-    return true;
-}
-
 /* static */ bool
 js::FutexRuntime::initialize()
 {
     MOZ_ASSERT(!lock_);
     lock_ = PR_NewLock();
     return lock_ != nullptr;
 }
 
@@ -1065,17 +957,17 @@ js::FutexRuntime::isWaiting()
     // wakes up and goes into WaitingInterrupted.  In those states the
     // worker is still waiting, and if an explicit wake arrives the
     // worker transitions to Woken.  See further comments in
     // FutexRuntime::wait().
     return state_ == Waiting || state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt;
 }
 
 bool
-js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWaitResult* result)
+js::FutexRuntime::wait(JSContext* cx, double timeout_ms, WaitResult* result)
 {
     MOZ_ASSERT(&cx->runtime()->fx == this);
     MOZ_ASSERT(cx->runtime()->fx.canWait());
     MOZ_ASSERT(lockHolder_ == PR_GetCurrentThread());
     MOZ_ASSERT(state_ == Idle || state_ == WaitingInterrupted);
 
     // Disallow waiting when a runtime is processing an interrupt.
     // See explanation below.
@@ -1122,39 +1014,39 @@ js::FutexRuntime::wait(JSContext* cx, do
         lockHolder_ = holder;
 #endif
         switch (state_) {
           case FutexRuntime::Waiting:
             // Timeout or spurious wakeup.
             if (timed) {
                 uint64_t now = PRMJ_Now();
                 if (now >= finalEnd) {
-                    *result = AtomicsObject::FutexTimedout;
+                    *result = FutexTimedOut;
                     goto finished;
                 }
             }
             break;
 
           case FutexRuntime::Woken:
-            *result = AtomicsObject::FutexOK;
+            *result = FutexOK;
             goto finished;
 
           case FutexRuntime::WaitingNotifiedForInterrupt:
             // The interrupt handler may reenter the engine.  In that case
             // there are two complications:
             //
             // - The waiting thread is not actually waiting on the
             //   condition variable so we have to record that it
             //   should be woken when the interrupt handler returns.
             //   To that end, we flag the thread as interrupted around
             //   the interrupt and check state_ when the interrupt
-            //   handler returns.  A futexWake() call that reaches the
+            //   handler returns.  A wake() call that reaches the
             //   runtime during the interrupt sets state_ to Woken.
             //
-            // - It is in principle possible for futexWait() to be
+            // - It is in principle possible for wait() to be
             //   reentered on the same thread/runtime and waiting on the
             //   same location and to yet again be interrupted and enter
             //   the interrupt handler.  In this case, it is important
             //   that when another agent wakes waiters, all waiters using
             //   the same runtime on the same location are woken in LIFO
             //   order; FIFO may be the required order, but FIFO would
             //   fail to wake up the innermost call.  Interrupts are
             //   outside any spec anyway.  Also, several such suspended
@@ -1170,17 +1062,17 @@ js::FutexRuntime::wait(JSContext* cx, do
             state_ = WaitingInterrupted;
             {
                 AutoUnlockFutexAPI unlock;
                 retval = cx->runtime()->handleInterrupt(cx);
             }
             if (!retval)
                 goto finished;
             if (state_ == Woken) {
-                *result = AtomicsObject::FutexOK;
+                *result = FutexOK;
                 goto finished;
             }
             break;
 
           default:
             MOZ_CRASH();
         }
     }
@@ -1214,52 +1106,43 @@ js::FutexRuntime::wake(WakeReason reason
     PR_NotifyCondVar(cond_);
 }
 
 const JSFunctionSpec AtomicsMethods[] = {
     JS_INLINABLE_FN("compareExchange",    atomics_compareExchange,    4,0, AtomicsCompareExchange),
     JS_INLINABLE_FN("load",               atomics_load,               2,0, AtomicsLoad),
     JS_INLINABLE_FN("store",              atomics_store,              3,0, AtomicsStore),
     JS_INLINABLE_FN("exchange",           atomics_exchange,           3,0, AtomicsExchange),
-    JS_INLINABLE_FN("fence",              atomics_fence,              0,0, AtomicsFence),
     JS_INLINABLE_FN("add",                atomics_add,                3,0, AtomicsAdd),
     JS_INLINABLE_FN("sub",                atomics_sub,                3,0, AtomicsSub),
     JS_INLINABLE_FN("and",                atomics_and,                3,0, AtomicsAnd),
     JS_INLINABLE_FN("or",                 atomics_or,                 3,0, AtomicsOr),
     JS_INLINABLE_FN("xor",                atomics_xor,                3,0, AtomicsXor),
     JS_INLINABLE_FN("isLockFree",         atomics_isLockFree,         1,0, AtomicsIsLockFree),
-    JS_FN("futexWait",                    atomics_futexWait,          4,0),
-    JS_FN("futexWake",                    atomics_futexWake,          3,0),
-    JS_FN("futexWakeOrRequeue",           atomics_futexWakeOrRequeue, 5,0),
+    JS_FN("wait",                         atomics_wait,               4,0),
+    JS_FN("futexWait",                    atomics_wait,               4,0),
+    JS_FN("wake",                         atomics_wake,               3,0),
+    JS_FN("futexWake",                    atomics_wake,               3,0),
     JS_FS_END
 };
 
-static const JSConstDoubleSpec AtomicsConstants[] = {
-    {"OK",       AtomicsObject::FutexOK},
-    {"TIMEDOUT", AtomicsObject::FutexTimedout},
-    {"NOTEQUAL", AtomicsObject::FutexNotequal},
-    {0,          0}
-};
-
 JSObject*
 AtomicsObject::initClass(JSContext* cx, Handle<GlobalObject*> global)
 {
     // Create Atomics Object.
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
         return nullptr;
     RootedObject Atomics(cx, NewObjectWithGivenProto(cx, &AtomicsObject::class_, objProto,
                                                      SingletonObject));
     if (!Atomics)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods))
         return nullptr;
-    if (!JS_DefineConstDoubles(cx, Atomics, AtomicsConstants))
-        return nullptr;
 
     RootedValue AtomicsValue(cx, ObjectValue(*Atomics));
 
     // Everything is set up, install Atomics on the global object.
     if (!DefineProperty(cx, global, cx->names().Atomics, AtomicsValue, nullptr, nullptr,
                         JSPROP_RESOLVING))
     {
         return nullptr;
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -13,41 +13,30 @@
 namespace js {
 
 class AtomicsObject : public JSObject
 {
   public:
     static const Class class_;
     static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
     static bool toString(JSContext* cx, unsigned int argc, Value* vp);
-
-    // Defined return values for futexWait.
-    // The error values must be negative because APIs such as futexWaitOrRequeue
-    // return a value that is either the number of tasks woken or an error code.
-    enum FutexWaitResult : int32_t {
-        FutexOK = 0,
-        FutexNotequal = -1,
-        FutexTimedout = -2
-    };
 };
 
 bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_load(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_store(JSContext* cx, unsigned argc, Value* vp);
-bool atomics_fence(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_add(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_sub(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_and(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_or(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_xor(JSContext* cx, unsigned argc, Value* vp);
 bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp);
-bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp);
-bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp);
-bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp);
+bool atomics_wait(JSContext* cx, unsigned argc, Value* vp);
+bool atomics_wake(JSContext* cx, unsigned argc, Value* vp);
 
 /* asm.js callouts */
 int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value);
 int32_t atomics_sub_asm_callout(int32_t vt, int32_t offset, int32_t value);
 int32_t atomics_and_asm_callout(int32_t vt, int32_t offset, int32_t value);
 int32_t atomics_or_asm_callout(int32_t vt, int32_t offset, int32_t value);
 int32_t atomics_xor_asm_callout(int32_t vt, int32_t offset, int32_t value);
 int32_t atomics_cmpxchg_asm_callout(int32_t vt, int32_t offset, int32_t oldval, int32_t newval);
@@ -67,49 +56,55 @@ public:
     void destroyInstance();
 
     // Parameters to wake().
     enum WakeReason {
         WakeExplicit,           // Being asked to wake up by another thread
         WakeForJSInterrupt      // Interrupt requested
     };
 
+    // Result code from wait().
+    enum WaitResult {
+        FutexOK,
+        FutexTimedOut
+    };
+
     // Block the calling thread and wait.
     //
     // The futex lock must be held around this call.
     //
     // The timeout is the number of milliseconds, with fractional
     // times allowed; specify positive infinity for an indefinite wait.
     //
     // wait() will not wake up spuriously.  It will return true and
     // set *result to a return code appropriate for
-    // Atomics.futexWait() on success, and return false on error.
-    bool wait(JSContext* cx, double timeout, AtomicsObject::FutexWaitResult* result);
+    // Atomics.wait() on success, and return false on error.
+    bool wait(JSContext* cx, double timeout, WaitResult* result);
 
     // Wake the thread represented by this Runtime.
     //
     // The futex lock must be held around this call.  (The sleeping
-    // thread will not wake up until the caller of futexWake()
+    // thread will not wake up until the caller of Atomics.wake()
     // releases the lock.)
     //
     // If the thread is not waiting then this method does nothing.
     //
-    // If the thread is waiting in a call to futexWait() and the
-    // reason is WakeExplicit then the futexWait() call will return
+    // If the thread is waiting in a call to wait() and the
+    // reason is WakeExplicit then the wait() call will return
     // with Woken.
     //
-    // If the thread is waiting in a call to futexWait() and the
-    // reason is WakeForJSInterrupt then the futexWait() will return
+    // If the thread is waiting in a call to wait() and the
+    // reason is WakeForJSInterrupt then the wait() will return
     // with WaitingNotifiedForInterrupt; in the latter case the caller
-    // of futexWait() must handle the interrupt.
+    // of wait() must handle the interrupt.
     void wake(WakeReason reason);
 
     bool isWaiting();
 
-    // If canWait() returns false (the default) then futexWait is disabled
+    // If canWait() returns false (the default) then wait() is disabled
     // on the runtime to which the FutexRuntime belongs.
     bool canWait() {
         return canWait_;
     }
 
     void setCanWait(bool flag) {
         canWait_ = flag;
     }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1206,35 +1206,37 @@ FinishObjectClassInit(JSContext* cx, JS:
     Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
     if (global->shouldSplicePrototype(cx)) {
         if (!global->splicePrototype(cx, global->getClass(), tagged))
             return false;
     }
     return true;
 }
 
+static const ClassSpec PlainObjectClassSpec = {
+    CreateObjectConstructor,
+    CreateObjectPrototype,
+    object_static_methods,
+    nullptr,
+    object_methods,
+    object_properties,
+    FinishObjectClassInit
+};
+
 const Class PlainObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     nullptr,  /* addProperty */
     nullptr,  /* delProperty */
     nullptr,  /* getProperty */
     nullptr,  /* setProperty */
     nullptr,  /* enumerate */
     nullptr,  /* resolve */
     nullptr,  /* mayResolve */
     nullptr,  /* finalize */
     nullptr,  /* call */
     nullptr,  /* hasInstance */
     nullptr,  /* construct */
     nullptr,  /* trace */
-    {
-        CreateObjectConstructor,
-        CreateObjectPrototype,
-        object_static_methods,
-        nullptr,
-        object_methods,
-        object_properties,
-        FinishObjectClassInit
-    }
+    &PlainObjectClassSpec
 };
 
 const Class* const js::ObjectClassPtr = &PlainObject::class_;
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -392,39 +392,52 @@ PromiseObject::reject(JSContext* cx, Han
 } // namespace js
 
 static JSObject*
 CreatePromisePrototype(JSContext* cx, JSProtoKey key)
 {
     return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
 }
 
+static const ClassSpec PromiseObjectClassSpec = {
+    GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
+    CreatePromisePrototype,
+    promise_static_methods,
+    promise_static_properties,
+    promise_methods
+};
+
 const Class PromiseObject::class_ = {
     "Promise",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) |
     JSCLASS_HAS_XRAYED_CONSTRUCTOR,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
-    {
-        GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
-        CreatePromisePrototype,
-        promise_static_methods,
-        promise_static_properties,
-        promise_methods
-    }
+    &PromiseObjectClassSpec
+};
+
+static const ClassSpec PromiseObjectProtoClassSpec = {
+    DELEGATED_CLASSSPEC(PromiseObject::class_.spec),
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    ClassSpec::IsDelegated
 };
 
 const Class PromiseObject::protoClass_ = {
     "PromiseProto",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Promise),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -432,19 +445,10 @@ const Class PromiseObject::protoClass_ =
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace  */
-    {
-        DELEGATED_CLASSSPEC(&PromiseObject::class_.spec),
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        ClassSpec::IsDelegated
-    }
+    &PromiseObjectProtoClassSpec
 };
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -13,16 +13,17 @@
 
 #include "builtin/SIMD.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/IntegerTypeTraits.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "jsnum.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/InlinableNatives.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
@@ -1249,88 +1250,24 @@ Select(JSContext* cx, unsigned argc, Val
 
     Elem result[V::lanes];
     for (unsigned i = 0; i < V::lanes; i++)
         result[i] = mask[i] ? tv[i] : fv[i];
 
     return StoreResult<V>(cx, args, result);
 }
 
-// Get an integer array index from a function argument. Coerce if necessary.
-//
-// When a JS function argument represents an integer index into an array, it is
-// laundered like this:
-//
-//   1. numericIndex = ToNumber(argument)            (may throw TypeError)
-//   2. intIndex = ToInteger(numericIndex)
-//   3. if intIndex != numericIndex throw RangeError
-//
-// This function additionally bounds the range to the non-negative contiguous
-// integers:
-//
-//   4. if intIndex < 0 or intIndex > 2^53 throw RangeError
-//
-// Return true and set |*index| to the integer value if |argument| is a valid
-// array index argument. Otherwise report an TypeError or RangeError and return
-// false.
-//
-// The returned index will always be in the range 0 <= *index <= 2^53.
-static bool
-ArgumentToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
-{
-    // Fast common case.
-    if (v.isInt32()) {
-        int32_t i = v.toInt32();
-        if (i >= 0) {
-            *index = i;
-            return true;
-        }
-    }
-
-    // Slow case. Use ToNumber() to coerce. This may throw a TypeError.
-    double d;
-    if (!ToNumber(cx, v, &d))
-        return false;
-
-    // Check that |d| is an integer in the valid range.
-    //
-    // Not all floating point integers fit in the range of a uint64_t, so we
-    // need a rough range check before the real range check in our caller. We
-    // could limit indexes to UINT64_MAX, but this would mean that our callers
-    // have to be very careful about integer overflow. The contiguous integer
-    // floating point numbers end at 2^53, so make that our upper limit. If we
-    // ever support arrays with more than 2^53 elements, this will need to
-    // change.
-    //
-    // Reject infinities, NaNs, and numbers outside the contiguous integer range
-    // with a RangeError.
-
-    // Write relation so NaNs throw a RangeError.
-    if (!(0 <= d && d <= (uint64_t(1) << 53)))
-        return ErrorBadIndex(cx);
-
-    // Check that d is an integer, throw a RangeError if not.
-    // Note that this conversion could invoke undefined behaviour without the
-    // range check above.
-    uint64_t i(d);
-    if (d != double(i))
-        return ErrorBadIndex(cx);
-
-    *index = i;
-    return true;
-}
-
 // Extract an integer lane index from a function argument.
 //
 // Register an exception and return false if the argument is not suitable.
 static bool
 ArgumentToLaneIndex(JSContext* cx, JS::HandleValue v, unsigned limit, unsigned* lane)
 {
     uint64_t arg;
-    if (!ArgumentToIntegerIndex(cx, v, &arg))
+    if (!ToIntegerIndex(cx, v, &arg))
         return false;
     if (arg >= limit)
         return ErrorBadIndex(cx);
 
     *lane = unsigned(arg);
     return true;
 }
 
@@ -1347,17 +1284,17 @@ TypedArrayFromArgs(JSContext* cx, const 
 
     JSObject& argobj = args[0].toObject();
     if (!argobj.is<TypedArrayObject>())
         return ErrorBadArgs(cx);
 
     typedArray.set(&argobj);
 
     uint64_t index;
-    if (!ArgumentToIntegerIndex(cx, args[1], &index))
+    if (!ToIntegerIndex(cx, args[1], &index))
         return false;
 
     // Do the range check in 64 bits even when size_t is 32 bits.
     // This can't overflow because index <= 2^53.
     uint64_t bytes = index * typedArray->as<TypedArrayObject>().bytesPerElement();
     // Keep in sync with AsmJS OnOutOfBounds function.
     if ((bytes + accessBytes) > typedArray->as<TypedArrayObject>().byteLength())
         return ErrorBadIndex(cx);
--- a/js/src/jit-test/tests/asm.js/testAtomics.js
+++ b/js/src/jit-test/tests/asm.js/testAtomics.js
@@ -6,33 +6,28 @@ if (!this.SharedArrayBuffer || !this.Ato
 // The code duplication below is very far from elegant but provides
 // flexibility that comes in handy several places.
 
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
 var loadModule_int32_code =
     USE_ASM + `
-    var atomic_fence = stdlib.Atomics.fence;
     var atomic_load = stdlib.Atomics.load;
     var atomic_store = stdlib.Atomics.store;
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
     var i32a = new stdlib.Int32Array(heap);
 
-    function do_fence() {
-        atomic_fence();
-    }
-
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i32a, 0);
         return v|0;
     }
 
     // Load element i
@@ -199,18 +194,17 @@ var loadModule_int32_code =
     // CAS element i: -1 -> 0x5A5A5A5A
     function do_cas2_i(i) {
         i = i|0;
         var v = 0;
         v = atomic_cmpxchg(i32a, i>>2, -1, 0x5A5A5A5A);
         return v|0;
     }
 
-    return { fence: do_fence,
-        load: do_load,
+    return { load: do_load,
         load_i: do_load_i,
         store: do_store,
         store_i: do_store_i,
         xchg: do_xchg,
         xchg_i: do_xchg_i,
         xchg_intish: do_xchg_intish,
         add: do_add,
         add_i: do_add_i,
@@ -233,18 +227,16 @@ var loadModule_int32_code =
 var loadModule_int32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int32_code);
 
 function test_int32(heap) {
     var i32a = new Int32Array(heap);
     var i32m = asmLink(loadModule_int32, this, {}, heap);
 
     var size = Int32Array.BYTES_PER_ELEMENT;
 
-    i32m.fence();
-
     i32a[0] = 12345;
     assertEq(i32m.load(), 12345);
     assertEq(i32m.load_i(size*0), 12345);
 
     assertEq(i32m.store(), 37);
     assertEq(i32a[0], 37);
     assertEq(i32m.store_i(size*0), 37);
 
@@ -323,17 +315,16 @@ function test_int32(heap) {
     assertEq(i32m.store_i((i32a.length-1)*4), 37);
     assertEq(i32m.add_i((i32a.length-1)*4), 37);
     assertEq(i32m.load_i((i32a.length-1)*4), 37+37);
     i32a[i32a.length-1] = 0;
 }
 
 var loadModule_uint32_code =
     USE_ASM + `
-    var atomic_fence = stdlib.Atomics.fence;
     var atomic_load = stdlib.Atomics.load;
     var atomic_store = stdlib.Atomics.store;
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
@@ -604,33 +595,28 @@ function test_uint32(heap) {
     assertEq(i32m.store_i((i32a.length-1)*4), 37);
     assertEq(i32m.add_i((i32a.length-1)*4), 37);
     assertEq(i32m.load_i((i32a.length-1)*4), 37+37);
     i32a[i32a.length-1] = 0;
 }
 
 var loadModule_int16_code =
     USE_ASM + `
-    var atomic_fence = stdlib.Atomics.fence;
     var atomic_load = stdlib.Atomics.load;
     var atomic_store = stdlib.Atomics.store;
     var atomic_cmpxchg = stdlib.Atomics.compareExchange;
     var atomic_exchange = stdlib.Atomics.exchange;
     var atomic_add = stdlib.Atomics.add;
     var atomic_sub = stdlib.Atomics.sub;
     var atomic_and = stdlib.Atomics.and;
     var atomic_or = stdlib.Atomics.or;
     var atomic_xor = stdlib.Atomics.xor;
 
     var i16a = new stdlib.Int16Array(heap);
 
-    function do_fence() {
-        atomic_fence();
-    }
-
     // Load element 0
     function do_load() {
         var v = 0;
         v = atomic_load(i16a, 0);
         return v|0;
     }
 
     // Load element i
@@ -771,18 +757,17 @@ var loadModule_int16_code =
     // CAS element i: -1 -> 0x5A5A
     function do_cas2_i(i) {
         i = i|0;
         var v = 0;
         v = atomic_cmpxchg(i16a, i>>1, -1, 0x5A5A);
         return v|0;
     }
 
-    return { fence: do_fence,
-        load: do_load,
+    return { load: do_load,
         load_i: do_load_i,
         store: do_store,
         store_i: do_store_i,
         xchg: do_xchg,
         xchg_i: do_xchg_i,
         add: do_add,
         add_i: do_add_i,
         sub: do_sub,
@@ -802,18 +787,16 @@ var loadModule_int16_code =
 var loadModule_int16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int16_code);
 
 function test_int16(heap) {
     var i16a = new Int16Array(heap);
     var i16m = loadModule_int16(this, {}, heap);
 
     var size = Int16Array.BYTES_PER_ELEMENT;
 
-    i16m.fence();
-
     i16a[0] = 12345;
     assertEq(i16m.load(), 12345);
     assertEq(i16m.load_i(size*0), 12345);
 
     i16a[0] = -38;
     assertEq(i16m.load(), -38);
     assertEq(i16m.load_i(size*0), -38);
 
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -1,14 +1,14 @@
 // Basic functional tests for the Atomics primitives.
 //
 // These do not test atomicity, just that calling and coercions and
 // indexing and exception behavior all work right.
 //
-// These do not test the futex operations.
+// These do not test the wait/wake operations.
 
 load(libdir + "asserts.js");
 
 var DEBUG = false;		// Set to true for useful printouts
 
 function dprint(...xs) {
     if (!DEBUG)
 	return;
@@ -61,18 +61,16 @@ function testMethod(a, ...indices) {
 	// val = 9
 	assertEq(Atomics.store(a, x, 14), 14); // What about coercion?
 	// val = 14
 	assertEq(Atomics.load(a, x), 14);
 	// val = 14
 	Atomics.store(a, x, 0);
 	// val = 0
 
-	Atomics.fence();
-
 	// val = 0
 	assertEq(Atomics.add(a, x, 3), 0);
 	// val = 3
 	assertEq(Atomics.sub(a, x, 2), 3);
 	// val = 1
 	assertEq(Atomics.or(a, x, 6), 1);
 	// val = 7
 	assertEq(Atomics.and(a, x, 14), 7);
@@ -132,18 +130,16 @@ function testFunction(a, ...indices) {
 	// val = 9
 	assertEq(gAtomics_store(a, x, 14), 14); // What about coercion?
 	// val = 14
 	assertEq(gAtomics_load(a, x), 14);
 	// val = 14
 	gAtomics_store(a, x, 0);
 	// val = 0
 
-	gAtomics_fence();
-
 	// val = 0
 	assertEq(gAtomics_add(a, x, 3), 0);
 	// val = 3
 	assertEq(gAtomics_sub(a, x, 2), 3);
 	// val = 1
 	assertEq(gAtomics_or(a, x, 6), 1);
 	// val = 7
 	assertEq(gAtomics_and(a, x, 14), 7);
@@ -206,17 +202,17 @@ function testTypeBinop(a, op) {
     op(a, 0);
 }
 
 var globlength = 0;		// Will be set later
 
 function testRangeCAS(a) {
     dprint("Range: " + a.constructor.name);
 
-    var msg = /out-of-range index for atomic access/;
+    var msg = /out-of-range index/; // A generic message
 
     assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg);
     assertEq(a[0], 0);
 
     assertErrorMessage(() => Atomics.compareExchange(a, "hi", 0, 1), RangeError, msg);
     assertEq(a[0], 0);
 
     assertErrorMessage(() => Atomics.compareExchange(a, a.length + 5, 0, 1), RangeError, msg);
@@ -375,17 +371,17 @@ function adHocExchange() {
     assertEq(exchangeLoop(a), -100000);
 }
 
 // isLockFree(n) may return true only if there is an integer array
 // on which atomic operations is allowed whose byte size is n,
 // ie, it must return false for n=8.
 //
 // SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(4) on all
-// supported platforms, though this is not guaranteed by the spec.
+// supported platforms, only the last is guaranteed by the spec.
 
 var sizes   = [    1,     2,     3,     4,     5,     6,     7,  8,
                    9,    10,    11,    12];
 var answers = [ true,  true, false,  true, false, false, false, false,
 	       false, false, false, false];
 
 function testIsLockFree() {
     // This ought to defeat most compile-time resolution.
@@ -451,16 +447,23 @@ function testUint8Clamped(sab) {
     }
     catch (e) {
 	thrown = true;
 	assertEq(e instanceof TypeError, true);
     }
     assertEq(thrown, true);
 }
 
+function testWeirdIndices() {
+    var a = new Int8Array(new SharedArrayBuffer(16));
+    a[3] = 10;
+    assertEq(Atomics.load(a, "0x03"), 10);
+    assertEq(Atomics.load(a, {valueOf: () => 3}), 10);
+}
+
 function isLittleEndian() {
     var xxx = new ArrayBuffer(2);
     var xxa = new Int16Array(xxx);
     var xxb = new Int8Array(xxx);
     xxa[0] = 37;
     var is_little = xxb[0] == 37;
     return is_little;
 }
@@ -492,17 +495,16 @@ function runTests() {
     CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023);
     CLONE(testMethod)(new Uint32Array(sab), 0, 42, 1023);
 
     // Test that invoking as v = Atomics.whatever; v() works, on correct arguments.
     gAtomics_compareExchange = Atomics.compareExchange;
     gAtomics_exchange = Atomics.exchange;
     gAtomics_load = Atomics.load;
     gAtomics_store = Atomics.store;
-    gAtomics_fence = Atomics.fence;
     gAtomics_add = Atomics.add;
     gAtomics_sub = Atomics.sub;
     gAtomics_and = Atomics.and;
     gAtomics_or = Atomics.or;
     gAtomics_xor = Atomics.xor;
 
     CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095);
     CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095);
@@ -546,12 +548,13 @@ function runTests() {
     testUint8Clamped(sab);
 
     // Misc ad-hoc tests
     adHocExchange();
 
     // Misc
     testIsLockFree();
     testIsLockFree2();
+    testWeirdIndices();
 }
 
 if (this.Atomics && this.SharedArrayBuffer)
     runTests();
deleted file mode 100644
--- a/js/src/jit-test/tests/atomics/inline-fence.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// |jit-test| slow;
-//
-// This is intended to be run manually with IONFLAGS=logs and
-// postprocessing by iongraph to verify manually (by inspecting the
-// MIR) that:
-//
-//  - the fence operation is inlined as it should be
-//  - loads and stores are not moved across the fence
-//
-// Be sure to run with --ion-eager --ion-offthread-compile=off.
-
-function fence(ta) {
-    var x = ta[0];
-    Atomics.fence();
-    var y = ta[1];
-    var z = y + 1;
-    var w = x + z;
-    return w;
-}
-
-if (!this.SharedArrayBuffer || !this.Atomics)
-    quit(0);
-
-var sab = new SharedArrayBuffer(4096);
-var ia = new Int32Array(sab);
-for ( var i=0, limit=ia.length ; i < limit ; i++ )
-    ia[i] = 37;
-var v = 0;
-for ( var i=0 ; i < 1000 ; i++ )
-    v += fence(ia);
-//print(v);
--- a/js/src/jit/AtomicOperations.h
+++ b/js/src/jit/AtomicOperations.h
@@ -294,18 +294,21 @@ class RegionLock
 
 inline bool
 AtomicOperations::isLockfree(int32_t size)
 {
     // Keep this in sync with visitAtomicIsLockFree() in jit/CodeGenerator.cpp.
 
     switch (size) {
       case 1:
+        return true;
       case 2:
+        return true;
       case 4:
+        // The spec requires Atomics.isLockFree(4) to return true.
         return true;
       case 8:
         // The spec requires Atomics.isLockFree(n) to return false
         // unless n is the BYTES_PER_ELEMENT value of some integer
         // TypedArray that admits atomic operations.  At the time of
         // writing (February 2016) there is no such array with n=8.
         // return AtomicOperations::isLockfree8();
         return false;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10002,17 +10002,20 @@ CodeGenerator::visitStoreTypedArrayEleme
 
 void
 CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir)
 {
     Register value = ToRegister(lir->value());
     Register output = ToRegister(lir->output());
 
     // Keep this in sync with isLockfree() in jit/AtomicOperations.h.
-    MOZ_ASSERT(!AtomicOperations::isLockfree(8));
+    MOZ_ASSERT(AtomicOperations::isLockfree(1));  // Implementation artifact
+    MOZ_ASSERT(AtomicOperations::isLockfree(2));  // Implementation artifact
+    MOZ_ASSERT(AtomicOperations::isLockfree(4));  // Spec requirement
+    MOZ_ASSERT(!AtomicOperations::isLockfree(8)); // Implementation invariant, for now
 
     Label Ldone, Lfailed;
     masm.move32(Imm32(1), output);
     masm.branch32(Assembler::Equal, value, Imm32(4), &Ldone);
     masm.branch32(Assembler::Equal, value, Imm32(2), &Ldone);
     masm.branch32(Assembler::Equal, value, Imm32(1), &Ldone);
     masm.move32(Imm32(0), output);
     masm.bind(&Ldone);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -17,17 +17,16 @@
     _(ArrayConcat)                  \
     _(ArraySlice)                   \
     _(ArraySplice)                  \
                                     \
     _(AtomicsCompareExchange)       \
     _(AtomicsExchange)              \
     _(AtomicsLoad)                  \
     _(AtomicsStore)                 \
-    _(AtomicsFence)                 \
     _(AtomicsAdd)                   \
     _(AtomicsSub)                   \
     _(AtomicsAnd)                   \
     _(AtomicsOr)                    \
     _(AtomicsXor)                   \
     _(AtomicsIsLockFree)            \
                                     \
     _(MathAbs)                      \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -827,17 +827,16 @@ class IonBuilder
     InliningStatus inlineObjectCreate(CallInfo& callInfo);
     InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
 
     // Atomics natives.
     InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
     InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
     InliningStatus inlineAtomicsLoad(CallInfo& callInfo);
     InliningStatus inlineAtomicsStore(CallInfo& callInfo);
-    InliningStatus inlineAtomicsFence(CallInfo& callInfo);
     InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target);
     InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo);
 
     // Slot intrinsics.
     InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo);
     InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo,
                                                MIRType knownValueType);
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4200,23 +4200,16 @@ void
 LIRGenerator::visitRecompileCheck(MRecompileCheck* ins)
 {
     LRecompileCheck* lir = new(alloc()) LRecompileCheck(temp());
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
-LIRGenerator::visitMemoryBarrier(MMemoryBarrier* ins)
-{
-    LMemoryBarrier* lir = new(alloc()) LMemoryBarrier(ins->type());
-    add(lir, ins);
-}
-
-void
 LIRGenerator::visitSimdBox(MSimdBox* ins)
 {
     MOZ_ASSERT(IsSimdType(ins->input()->type()));
     LUse in = useRegister(ins->input());
     LSimdBox* lir = new(alloc()) LSimdBox(in, temp());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -281,17 +281,16 @@ class LIRGenerator : public LIRGenerator
     void visitAsmJSReturn(MAsmJSReturn* ins);
     void visitAsmJSVoidReturn(MAsmJSVoidReturn* ins);
     void visitAsmJSPassStackArg(MAsmJSPassStackArg* ins);
     void visitAsmJSCall(MAsmJSCall* ins);
     void visitSetDOMProperty(MSetDOMProperty* ins);
     void visitGetDOMProperty(MGetDOMProperty* ins);
     void visitGetDOMMember(MGetDOMMember* ins);
     void visitRecompileCheck(MRecompileCheck* ins);
-    void visitMemoryBarrier(MMemoryBarrier* ins);
     void visitSimdBox(MSimdBox* ins);
     void visitSimdUnbox(MSimdUnbox* ins);
     void visitSimdExtractElement(MSimdExtractElement* ins);
     void visitSimdInsertElement(MSimdInsertElement* ins);
     void visitSimdSwizzle(MSimdSwizzle* ins);
     void visitSimdGeneralShuffle(MSimdGeneralShuffle* ins);
     void visitSimdShuffle(MSimdShuffle* ins);
     void visitSimdUnaryArith(MSimdUnaryArith* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -88,18 +88,16 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::AtomicsCompareExchange:
         return inlineAtomicsCompareExchange(callInfo);
       case InlinableNative::AtomicsExchange:
         return inlineAtomicsExchange(callInfo);
       case InlinableNative::AtomicsLoad:
         return inlineAtomicsLoad(callInfo);
       case InlinableNative::AtomicsStore:
         return inlineAtomicsStore(callInfo);
-      case InlinableNative::AtomicsFence:
-        return inlineAtomicsFence(callInfo);
       case InlinableNative::AtomicsAdd:
       case InlinableNative::AtomicsSub:
       case InlinableNative::AtomicsAnd:
       case InlinableNative::AtomicsOr:
       case InlinableNative::AtomicsXor:
         return inlineAtomicsBinop(callInfo, inlNative);
       case InlinableNative::AtomicsIsLockFree:
         return inlineAtomicsIsLockFree(callInfo);
@@ -2911,40 +2909,16 @@ IonBuilder::inlineAtomicsStore(CallInfo&
 
     if (!resumeAfter(store))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineAtomicsFence(CallInfo& callInfo)
-{
-    if (callInfo.argc() != 0 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
-    if (!JitSupportsAtomics())
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MMemoryBarrier* fence = MMemoryBarrier::New(alloc());
-    current->add(fence);
-    pushConstant(UndefinedValue());
-
-    // Fences are considered effectful (they execute a memory barrier).
-    if (!resumeAfter(fence))
-        return InliningStatus_Error;
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target)
 {
     if (callInfo.argc() != 3 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     MDefinition* value = callInfo.getArg(2);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9990,25 +9990,39 @@ class MArrayJoin
     virtual AliasSet getAliasSet() const override {
         // Array.join might coerce the elements of the Array to strings.  This
         // coercion might cause the evaluation of the some JavaScript code.
         return AliasSet::Store(AliasSet::Any);
     }
     MDefinition* foldsTo(TempAllocator& alloc) override;
 };
 
-// See comments above MMemoryBarrier, below.
+// All barriered operations - MCompareExchangeTypedArrayElement,
+// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as
+// well as MLoadUnboxedScalar and MStoreUnboxedScalar when they are
+// marked as requiring a memory barrer - have the following
+// attributes:
+//
+// - Not movable
+// - Not removable
+// - Not congruent with any other instruction
+// - Effectful (they alias every TypedArray store)
+//
+// The intended effect of those constraints is to prevent all loads
+// and stores preceding the barriered operation from being moved to
+// after the barriered operation, and vice versa, and to prevent the
+// barriered operation from being removed or hoisted.
 
 enum MemoryBarrierRequirement
 {
     DoesNotRequireMemoryBarrier,
     DoesRequireMemoryBarrier
 };
 
-// Also see comments above MMemoryBarrier, below.
+// Also see comments at MMemoryBarrierRequirement, above.
 
 // Load an unboxed scalar value from a typed array or other object.
 class MLoadUnboxedScalar
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     Scalar::Type storageType_;
     Scalar::Type readType_;
@@ -13643,59 +13657,16 @@ class MRecompileCheck : public MNullaryI
         return increaseWarmUpCounter_;
     }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
-// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement,
-// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as well as
-// MLoadUnboxedScalar and MStoreUnboxedScalar when they are marked as requiring
-// a memory barrer - have the following attributes:
-//
-// - Not movable
-// - Not removable
-// - Not congruent with any other instruction
-// - Effectful (they alias every TypedArray store)
-//
-// The intended effect of those constraints is to prevent all loads
-// and stores preceding the barriered operation from being moved to
-// after the barriered operation, and vice versa, and to prevent the
-// barriered operation from being removed or hoisted.
-
-class MMemoryBarrier
-  : public MNullaryInstruction
-{
-    // The type is a combination of the memory barrier types in AtomicOp.h.
-    const MemoryBarrierBits type_;
-
-    explicit MMemoryBarrier(MemoryBarrierBits type)
-      : type_(type)
-    {
-        MOZ_ASSERT((type_ & ~MembarAllbits) == MembarNobits);
-        setGuard();             // Not removable
-    }
-
-  public:
-    INSTRUCTION_HEADER(MemoryBarrier)
-
-    static MMemoryBarrier* New(TempAllocator& alloc, MemoryBarrierBits type = MembarFull) {
-        return new(alloc) MMemoryBarrier(type);
-    }
-    MemoryBarrierBits type() const {
-        return type_;
-    }
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::UnboxedElement);
-    }
-};
-
 class MAtomicIsLockFree
   : public MUnaryInstruction,
     public ConvertToInt32Policy<0>::Data
 {
     explicit MAtomicIsLockFree(MDefinition* value)
       : MUnaryInstruction(value)
     {
         setResultType(MIRType_Boolean);
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -275,17 +275,16 @@ namespace jit {
     _(AsmJSParameter)                                                       \
     _(AsmJSVoidReturn)                                                      \
     _(AsmJSPassStackArg)                                                    \
     _(AsmJSCall)                                                            \
     _(AsmSelect)                                                            \
     _(AsmReinterpret)                                                       \
     _(NewDerivedTypedObject)                                                \
     _(RecompileCheck)                                                       \
-    _(MemoryBarrier)                                                        \
     _(AsmJSCompareExchangeHeap)                                             \
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
     _(ThrowRuntimeLexicalError)                                             \
     _(GlobalNameConflictsCheck)                                             \
     _(Debugger)                                                             \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7958,20 +7958,16 @@ class LMemoryBarrier : public LInstructi
     explicit LMemoryBarrier(MemoryBarrierBits type) : type_(type)
     {
         MOZ_ASSERT((type_ & ~MembarAllbits) == MembarNobits);
     }
 
     MemoryBarrierBits type() const {
         return type_;
     }
-
-    const MMemoryBarrier* mir() const {
-        return mir_->toMemoryBarrier();
-    }
 };
 
 class LDebugger : public LCallInstructionHelper<0, 0, 2>
 {
   public:
     LIR_HEADER(Debugger)
 
     LDebugger(const LDefinition& temp1, const LDefinition& temp2) {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -492,17 +492,16 @@ MSG_DEF(JSMSG_BAD_PARSE_NODE,          0
 // Symbol
 MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0, JSEXN_TYPEERR, "can't convert symbol to string")
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
 // Atomics and futexes
 MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY,         0, JSEXN_TYPEERR, "invalid array type for the operation")
 MSG_DEF(JSMSG_ATOMICS_TOO_LONG,          0, JSEXN_RANGEERR, "timeout value too large")
 MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED,  0, JSEXN_ERR, "waiting is not allowed on this thread")
-MSG_DEF(JSMSG_ATOMICS_BAD_INDEX,         0, JSEXN_RANGEERR, "out-of-range index for atomic access")
 
 // XPConnect wrappers and DOM bindings
 MSG_DEF(JSMSG_CANT_SET_INTERPOSED,       1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
 MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
 MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,   0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
 MSG_DEF(JSMSG_NO_NAMED_SETTER,           2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3257,40 +3257,42 @@ array_proto_finish(JSContext* cx, JS::Ha
         return false;
     }
 
     RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
     value.setObject(*unscopables);
     return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY);
 }
 
+static const ClassSpec ArrayObjectClassSpec = {
+    GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
+    CreateArrayPrototype,
+    array_static_methods,
+    nullptr,
+    array_methods,
+    nullptr,
+    array_proto_finish
+};
+
 const Class ArrayObject::class_ = {
     "Array",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_CALLBACK,
     array_addProperty,
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
-    {
-        GenericCreateConstructor<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
-        CreateArrayPrototype,
-        array_static_methods,
-        nullptr,
-        array_methods,
-        nullptr,
-        array_proto_finish
-    }
+    &ArrayObjectClassSpec
 };
 
 /*
  * Array allocation functions.
  */
 
 static inline bool
 EnsureNewArrayElements(ExclusiveContext* cx, ArrayObject* obj, uint32_t length)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -3267,41 +3267,54 @@ FinishDateClassInit(JSContext* cx, Handl
     RootedValue toUTCStringFun(cx);
     RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
     RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
     return NativeGetProperty(cx, proto.as<NativeObject>(), toUTCStringId, &toUTCStringFun) &&
            NativeDefineProperty(cx, proto.as<NativeObject>(), toGMTStringId, toUTCStringFun,
                                 nullptr, nullptr, 0);
 }
 
+static const ClassSpec DateObjectClassSpec = {
+    GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
+    CreateDatePrototype,
+    date_static_methods,
+    nullptr,
+    date_methods,
+    nullptr,
+    FinishDateClassInit
+};
+
 const Class DateObject::class_ = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
-    {
-        GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
-        CreateDatePrototype,
-        date_static_methods,
-        nullptr,
-        date_methods,
-        nullptr,
-        FinishDateClassInit
-    }
+    &DateObjectClassSpec
+};
+
+static const ClassSpec DateObjectProtoClassSpec = {
+    DELEGATED_CLASSSPEC(DateObject::class_.spec),
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    nullptr,
+    ClassSpec::IsDelegated
 };
 
 const Class DateObject::protoClass_ = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -3309,26 +3322,17 @@ const Class DateObject::protoClass_ = {
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace  */
-    {
-        DELEGATED_CLASSSPEC(&DateObject::class_.spec),
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        nullptr,
-        ClassSpec::IsDelegated
-    }
+    &DateObjectProtoClassSpec
 };
 
 JSObject*
 js::NewDateObjectMsec(JSContext* cx, ClippedTime t, HandleObject proto /* = nullptr */)
 {
     JSObject* obj = NewObjectWithClassProto(cx, &DateObject::class_, proto);
     if (!obj)
         return nullptr;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -60,87 +60,86 @@ static const JSPropertySpec exception_pr
 static const JSFunctionSpec exception_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, exn_toSource, 0, 0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0),
     JS_FS_END
 };
 
-#define IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(name, extraClassSpecFlags)  \
+#define IMPLEMENT_ERROR_CLASS(name, classSpecPtr) \
     { \
         js_Error_str, /* yes, really */ \
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
         JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
         nullptr,                 /* addProperty */ \
         nullptr,                 /* delProperty */ \
         nullptr,                 /* getProperty */ \
         nullptr,                 /* setProperty */ \
         nullptr,                 /* enumerate */ \
         nullptr,                 /* resolve */ \
         nullptr,                 /* mayResolve */ \
         exn_finalize, \
         nullptr,                 /* call        */ \
         nullptr,                 /* hasInstance */ \
         nullptr,                 /* construct   */ \
         nullptr,                 /* trace       */ \
-        { \
-            ErrorObject::createConstructor, \
-            ErrorObject::createProto, \
-            nullptr, \
-            nullptr, \
-            exception_methods, \
-            exception_properties, \
-            nullptr, \
-            JSProto_Error | extraClassSpecFlags \
-        } \
+        classSpecPtr \
     }
 
-#define IMPLEMENT_ERROR_SUBCLASS(name) \
-    IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(name, 0)
+const ClassSpec
+ErrorObject::errorClassSpec_ = {
+    ErrorObject::createConstructor,
+    ErrorObject::createProto,
+    nullptr,
+    nullptr,
+    exception_methods,
+    exception_properties,
+    nullptr,
+    0
+};
+
+const ClassSpec
+ErrorObject::subErrorClassSpec_ = {
+    ErrorObject::createConstructor,
+    ErrorObject::createProto,
+    nullptr,
+    nullptr,
+    exception_methods,
+    exception_properties,
+    nullptr,
+    JSProto_Error
+};
+
+const ClassSpec
+ErrorObject::debuggeeWouldRunClassSpec_ = {
+    ErrorObject::createConstructor,
+    ErrorObject::createProto,
+    nullptr,
+    nullptr,
+    exception_methods,
+    exception_properties,
+    nullptr,
+    JSProto_Error | ClassSpec::DontDefineConstructor
+};
 
 const Class
 ErrorObject::classes[JSEXN_LIMIT] = {
-    {
-        js_Error_str,
-        JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
-        JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
-        nullptr,                 /* addProperty */
-        nullptr,                 /* delProperty */
-        nullptr,                 /* getProperty */
-        nullptr,                 /* setProperty */
-        nullptr,                 /* enumerate */
-        nullptr,                 /* resolve */
-        nullptr,                 /* mayResolve */
-        exn_finalize,
-        nullptr,                 /* call        */
-        nullptr,                 /* hasInstance */
-        nullptr,                 /* construct   */
-        nullptr,                 /* trace       */
-        {
-            ErrorObject::createConstructor,
-            ErrorObject::createProto,
-            nullptr,
-            nullptr,
-            exception_methods,
-            exception_properties,
-            nullptr
-        }
-    },
-    IMPLEMENT_ERROR_SUBCLASS(InternalError),
-    IMPLEMENT_ERROR_SUBCLASS(EvalError),
-    IMPLEMENT_ERROR_SUBCLASS(RangeError),
-    IMPLEMENT_ERROR_SUBCLASS(ReferenceError),
-    IMPLEMENT_ERROR_SUBCLASS(SyntaxError),
-    IMPLEMENT_ERROR_SUBCLASS(TypeError),
-    IMPLEMENT_ERROR_SUBCLASS(URIError),
+    IMPLEMENT_ERROR_CLASS(Error,          &ErrorObject::errorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(InternalError,  &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(EvalError,      &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(RangeError,     &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(ReferenceError, &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(SyntaxError,    &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(TypeError,      &ErrorObject::subErrorClassSpec_),
+    IMPLEMENT_ERROR_CLASS(URIError,       &ErrorObject::subErrorClassSpec_),
 
     // DebuggeeWouldRun is a subclass of Error but is accessible via the
     // Debugger constructor, not the global.
-    IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(DebuggeeWouldRun, ClassSpec::DontDefineConstructor),
+    IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun, &ErrorObject::debuggeeWouldRunClassSpec_)
 };
 
 JSErrorReport*
 js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
 {
     /*
      * We use a single malloc block to make a deep copy of JSErrorReport with
      * the following layout:
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -638,17 +638,17 @@ ProtoKeyToClass(JSProtoKey key);
 // Returns true if the standard class identified by |key| inherits from
 // another standard class (in addition to Object) along its proto chain.
 //
 // In practice, this only returns true for Error subtypes.
 inline bool
 StandardClassIsDependent(JSProtoKey key)
 {
     const Class* clasp = ProtoKeyToClass(key);
-    return clasp && clasp->spec.defined() && clasp->spec.dependent();
+    return clasp && clasp->specDefined() && clasp->specDependent();
 }
 
 // Returns the key for the class inherited by a given standard class (that
 // is to say, the prototype of this standard class's prototype).
 //
 // You must be sure that this corresponds to a standard class with a cached
 // JSProtoKey before calling this function. In general |key| will match the
 // cached proto key, except in cases where multiple JSProtoKeys share a
@@ -657,17 +657,17 @@ inline JSProtoKey
 ParentKeyForStandardClass(JSProtoKey key)
 {
     // [Object] has nothing to inherit from.
     if (key == JSProto_Object)
         return JSProto_Null;
 
     // If we're dependent, return the key of the class we depend on.
     if (StandardClassIsDependent(key))
-        return ProtoKeyToClass(key)->spec.parentKey();
+        return ProtoKeyToClass(key)->specParentKey();
 
     // Otherwise, we inherit [Object].
     return JSProto_Object;
 }
 
 JS_FRIEND_API(bool)
 IsFunctionObject(JSObject* obj);
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -829,39 +829,41 @@ CreateFunctionPrototype(JSContext* cx, J
     if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
         return nullptr;
 
     self->setThrowTypeError(throwTypeError);
 
     return functionProto;
 }
 
+static const ClassSpec JSFunctionClassSpec = {
+    CreateFunctionConstructor,
+    CreateFunctionPrototype,
+    nullptr,
+    nullptr,
+    function_methods,
+    function_properties
+};
+
 const Class JSFunction::class_ = {
     js_Function_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     nullptr,                 /* addProperty */
     nullptr,                 /* delProperty */
     nullptr,                 /* getProperty */
     nullptr,                 /* setProperty */
     fun_enumerate,
     fun_resolve,
     fun_mayResolve,
     nullptr,                 /* finalize    */
     nullptr,                 /* call        */
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace,
-    {
-        CreateFunctionConstructor,
-        CreateFunctionPrototype,
-        nullptr,
-        nullptr,
-        function_methods,
-        function_properties
-    }
+    &JSFunctionClassSpec
 };
 
 const Class* const js::FunctionClassPtr = &JSFunction::class_;
 
 /* Find the body of a function (not including braces). */
 bool
 js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
              size_t* bodyEnd)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1743,16 +1743,65 @@ js::ToLengthClamped(T* cx, HandleValue v
     return true;
 }
 
 template bool
 js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
 template bool
 js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*);
 
+bool
+js::ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
+{
+    // Fast common case.
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        if (i >= 0) {
+            *index = i;
+            return true;
+        }
+    }
+
+    // Slow case. Use ToNumber() to coerce. This may throw a TypeError.
+    double d;
+    if (!ToNumber(cx, v, &d))
+        return false;
+
+    // Check that |d| is an integer in the valid range.
+    //
+    // Not all floating point integers fit in the range of a uint64_t, so we
+    // need a rough range check before the real range check in our caller. We
+    // could limit indexes to UINT64_MAX, but this would mean that our callers
+    // have to be very careful about integer overflow. The contiguous integer
+    // floating point numbers end at 2^53, so make that our upper limit. If we
+    // ever support arrays with more than 2^53 elements, this will need to
+    // change.
+    //
+    // Reject infinities, NaNs, and numbers outside the contiguous integer range
+    // with a RangeError.
+
+    // Write relation so NaNs throw a RangeError.
+    if (!(0 <= d && d <= (uint64_t(1) << 53))) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+        return false;
+    }
+
+    // Check that d is an integer, throw a RangeError if not.
+    // Note that this conversion could invoke undefined behaviour without the
+    // range check above.
+    uint64_t i(d);
+    if (d != double(i)) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+        return false;
+    }
+
+    *index = i;
+    return true;
+}
+
 template <typename CharT>
 bool
 js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd,
           double* d)
 {
     const CharT* s = SkipSpace(begin, end);
     size_t length = end - s;
 
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -270,16 +270,36 @@ ToInteger(JSContext* cx, HandleValue v, 
  * return value is false then *overflow will be true iff the value was
  * not clampable to uint32_t range.
  *
  * For JSContext and ExclusiveContext.
  */
 template<typename T>
 bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow);
 
+/* Convert and range check an index value as for DataView, SIMD, and Atomics
+ * operations, eg ES7 24.2.1.1, DataView's GetViewValue():
+ *
+ *   1. numericIndex = ToNumber(argument)            (may throw TypeError)
+ *   2. intIndex = ToInteger(numericIndex)
+ *   3. if intIndex != numericIndex throw RangeError
+ *
+ * This function additionally bounds the range to the non-negative contiguous
+ * integers:
+ *
+ *   4. if intIndex < 0 or intIndex > 2^53 throw RangeError
+ *
+ * Return true and set |*index| to the integer value if |argument| is a valid
+ * array index argument. Otherwise report an TypeError or RangeError and return
+ * false.
+ *
+ * The returned index will always be in the range 0 <= *index <= 2^53.
+ */
+bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index);
+
 inline bool
 SafeAdd(int32_t one, int32_t two, int32_t* res)
 {
 #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow)
     // Using compiler's builtin function.
     return !__builtin_sadd_overflow(one, two, res);
 #else
     // Use unsigned for the 32-bit operation since signed overflow gets
--- a/js/src/tests/shell/futex-apis.js
+++ b/js/src/tests/shell/futex-apis.js
@@ -5,17 +5,17 @@
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!(this.SharedArrayBuffer && this.Atomics)) {
     reportCompare(true,true);
     quit(0);
 }
 
-// Checks for parameter validation of futex API.  ALl of these test
+// Checks for parameter validation of wait/wake API.  All of these test
 // cases should throw exceptions during parameter validation, before
 // we check whether any waiting should be done.
 
 let ab = new ArrayBuffer(16);
 let sab = new SharedArrayBuffer(16);
 
 //////////////////////////////////////////////////////////////////////
 //
@@ -52,48 +52,45 @@ let sab = new SharedArrayBuffer(16);
 		  Object,
 		  Int32Array,
 		  Date,
 		  Math,
 		  Atomics ];
 
     for ( let i=0 ; i < values.length ; i++ ) {
 	let view = values[i];
-	assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
     }
 }
 
 // Check against TypedArray on non-shared memory cases.
 
 {
     let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array];
 
     for ( let View of views ) {
 	let view = new View(ab);
 
-	assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
     }
 }
 
 // Check against TypedArray on shared memory, but wrong view type
 
 {
     let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Uint32Array,
 		 Uint8ClampedArray, Float32Array, Float64Array];
 
     for ( let View of views ) {
 	let view = new View(sab);
 
-	assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
-	assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+	assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
     }
 }
 
 //////////////////////////////////////////////////////////////////////
 //
 // The indices must be in the range of the array
 
 {
@@ -103,16 +100,14 @@ let sab = new SharedArrayBuffer(16);
 		    (view) => view.length,
 		    (view) => view.length*2,
 		    (view) => undefined,
 		    (view) => '3.5',
 		    (view) => { password: "qumquat" } ];
 
     for ( let iidx=0 ; iidx < indices.length ; iidx++ ) {
 	let Idx = indices[iidx](view);
-	assertThrowsInstanceOf(() => Atomics.futexWait(view, Idx, 10), RangeError);
-	assertThrowsInstanceOf(() => Atomics.futexWake(view, Idx), RangeError);
-	assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, Idx, 5, 0, 0), RangeError);
-	assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 5, Idx, 0), RangeError);
+	assertThrowsInstanceOf(() => Atomics.wait(view, Idx, 10), RangeError);
+	assertThrowsInstanceOf(() => Atomics.wake(view, Idx), RangeError);
     }
 }
 
 reportCompare(true,true);
--- a/js/src/tests/shell/futex.js
+++ b/js/src/tests/shell/futex.js
@@ -12,17 +12,17 @@ if (!(this.SharedArrayBuffer && this.get
 
 var DEBUG = false;
 
 function dprint(s) {
     if (DEBUG) print(s);
 }
 
 // Tests the SharedArrayBuffer mailbox in the shell.
-// Tests the futex functionality in the shell.
+// Tests the wait/wake functionality in the shell.
 
 var sab = new SharedArrayBuffer(12);
 var mem = new Int32Array(sab);
 
 // SharedArrayBuffer mailbox tests
 
 assertEq(getSharedArrayBuffer(), null); // Mbx starts empty
 
@@ -56,67 +56,103 @@ assertThrowsInstanceOf(() => setSharedAr
 assertThrowsInstanceOf(() => setSharedArrayBuffer(false), Error);
 assertThrowsInstanceOf(() => setSharedArrayBuffer(3.14), Error);
 assertThrowsInstanceOf(() => setSharedArrayBuffer(mem), Error);
 assertThrowsInstanceOf(() => setSharedArrayBuffer("abracadabra"), Error);
 assertThrowsInstanceOf(() => setSharedArrayBuffer(() => 37), Error);
 
 // Futex test
 
+if (helperThreadCount() === 0) {
+  // Abort if there is no helper thread.
+  reportCompare(true,true);
+  quit();
+}
+
+////////////////////////////////////////////////////////////
+
+// wait() returns "not-equal" if the value is not the expected one.
+
+mem[0] = 42;
+
+assertEq(Atomics.wait(mem, 0, 33), "not-equal");
+
+// wait() returns "timed-out" if it times out
+
+assertEq(Atomics.wait(mem, 0, 42, 100), "timed-out");
+
+////////////////////////////////////////////////////////////
+
 // Main is sharing the buffer with the worker; the worker is clearing
 // the buffer.
 
 mem[0] = 42;
 mem[1] = 37;
 mem[2] = DEBUG;
+
 setSharedArrayBuffer(mem.buffer);
 
-if (helperThreadCount() === 0) {
-  // Abort if there is no helper thread.
-  reportCompare(true,true);
-  quit();
-}
-
 evalInWorker(`
 var mem = new Int32Array(getSharedArrayBuffer());
 function dprint(s) {
     if (mem[2]) print(s);
 }
 assertEq(mem[0], 42);		// what was written in the main thread
 assertEq(mem[1], 37);		//   is read in the worker
 mem[1] = 1337;
-dprint("Sleeping for 3 seconds");
-sleep(3);
+dprint("Sleeping for 2 seconds");
+sleep(2);
 dprint("Waking the main thread now");
 setSharedArrayBuffer(null);
-Atomics.futexWake(mem, 0, 1);
+assertEq(Atomics.wake(mem, 0, 1), 1); // Can fail spuriously but very unlikely
 `);
 
 var then = Date.now();
-assertEq(Atomics.futexWait(mem, 0, 42), Atomics.OK);
+assertEq(Atomics.wait(mem, 0, 42), "ok");
 dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s");
 assertEq(mem[1], 1337); // what was written in the worker is read in the main thread
 assertEq(getSharedArrayBuffer(), null); // The worker's clearing of the mbx is visible
 
+////////////////////////////////////////////////////////////
+
+// Test the default argument to atomics.wake()
+
+setSharedArrayBuffer(mem.buffer);
+
+evalInWorker(`
+var mem = new Int32Array(getSharedArrayBuffer());
+sleep(2);				// Probably long enough to avoid a spurious error next
+assertEq(Atomics.wake(mem, 0), 1);	// Last argument to wake should default to +Infinity
+`);
+
+var then = Date.now();
+dprint("Main thread waiting on wakeup (2s)");
+assertEq(Atomics.wait(mem, 0, 42), "ok");
+dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s");
+
+////////////////////////////////////////////////////////////
+
 // A tricky case: while in the wait there will be an interrupt, and in
-// the interrupt handler we will execute a futexWait.  This is
+// the interrupt handler we will execute a wait.  This is
 // explicitly prohibited (for now), so there should be a catchable exception.
 
 timeout(2, function () {
-    dprint("In the interrupt, starting inner wait");
-    Atomics.futexWait(mem, 0, 42); // Should throw and propagate all the way out
+    dprint("In the interrupt, starting inner wait with timeout 2s");
+    Atomics.wait(mem, 0, 42); // Should throw and propagate all the way out
 });
 var exn = false;
 try {
     dprint("Starting outer wait");
-    assertEq(Atomics.futexWait(mem, 0, 42, 5000), Atomics.OK);
+    assertEq(Atomics.wait(mem, 0, 42, 5000), "ok");
 }
 catch (e) {
-    dprint("Got the exception!");
+    dprint("Got the timeout exception!");
     exn = true;
 }
 finally {
     timeout(-1);
 }
 assertEq(exn, true);
-dprint("Done");
+
+////////////////////////////////////////////////////////////
 
+dprint("Done");
 reportCompare(true,true);
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -1012,16 +1012,17 @@ ArrayBufferViewObject::trace(JSTracer* t
     TraceEdge(trc, &bufSlot, "typedarray.buffer");
 
     // Update obj's data pointer if it moved.
     if (bufSlot.isObject()) {
         if (IsArrayBuffer(&bufSlot.toObject())) {
             ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
             uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());
             MOZ_ASSERT(buf.dataPointer() != nullptr);
+            MOZ_ASSERT(offset <= INT32_MAX);
 
             if (buf.forInlineTypedObject()) {
                 // The data is inline with an InlineTypedObject associated with the
                 // buffer. Get a new address for the typed object if it moved.
                 JSObject* view = buf.firstView();
 
                 // Mark the object to move it into the tenured space.
                 TraceManuallyBarrieredEdge(trc, &view, "typed array nursery owner");
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -107,16 +107,19 @@
     macro(float64, float64, "float64") \
     macro(Float64x2, Float64x2, "Float64x2") \
     macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
     macro(forEach, forEach, "forEach") \
     macro(format, format, "format") \
     macro(formatToParts, formatToParts, "formatToParts") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
+    macro(futexOK, futexOK, "ok") \
+    macro(futexNotEqual, futexNotEqual, "not-equal") \
+    macro(futexTimedOut, futexTimedOut, "timed-out") \
     macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
     macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(global, global, "global") \
--- a/js/src/vm/ErrorObject.h
+++ b/js/src/vm/ErrorObject.h
@@ -36,16 +36,20 @@ class ErrorObject : public NativeObject
     static bool
     init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
          ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName, HandleObject stack,
          uint32_t lineNumber, uint32_t columnNumber, HandleString message);
 
     static bool checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName,
                                    MutableHandle<ErrorObject*> error);
 
+    static const ClassSpec errorClassSpec_;
+    static const ClassSpec subErrorClassSpec_;
+    static const ClassSpec debuggeeWouldRunClassSpec_;
+
   protected:
     static const uint32_t EXNTYPE_SLOT          = 0;
     static const uint32_t STACK_SLOT            = EXNTYPE_SLOT + 1;
     static const uint32_t ERROR_REPORT_SLOT     = STACK_SLOT + 1;
     static const uint32_t FILENAME_SLOT         = ERROR_REPORT_SLOT + 1;
     static const uint32_t LINENUMBER_SLOT       = FILENAME_SLOT + 1;
     static const uint32_t COLUMNNUMBER_SLOT     = LINENUMBER_SLOT + 1;
     static const uint32_t MESSAGE_SLOT          = COLUMNNUMBER_SLOT + 1;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -157,17 +157,17 @@ GlobalObject::resolveConstructor(JSConte
         return true;
 
     // Some classes have no init routine, which means that they're disabled at
     // compile-time. We could try to enforce that callers never pass such keys
     // to resolveConstructor, but that would cramp the style of consumers like
     // GlobalObject::initStandardClasses that want to just carpet-bomb-call
     // ensureConstructor with every JSProtoKey. So it's easier to just handle
     // it here.
-    bool haveSpec = clasp && clasp->spec.defined();
+    bool haveSpec = clasp && clasp->specDefined();
     if (!init && !haveSpec)
         return true;
 
     // See if there's an old-style initialization hook.
     if (init) {
         MOZ_ASSERT(!haveSpec);
         return init(cx, global);
     }
@@ -190,77 +190,79 @@ GlobalObject::resolveConstructor(JSConte
     // Function will also be resolved before we return.
     if (key == JSProto_Function && global->getPrototype(JSProto_Object).isUndefined())
         return resolveConstructor(cx, global, JSProto_Object);
 
     // We don't always have a prototype (i.e. Math and JSON). If we don't,
     // |createPrototype|, |prototypeFunctions|, and |prototypeProperties|
     // should all be null.
     RootedObject proto(cx);
-    if (clasp->spec.createPrototypeHook()) {
-        proto = clasp->spec.createPrototypeHook()(cx, key);
+    if (ClassObjectCreationOp createPrototype = clasp->specCreatePrototypeHook()) {
+        proto = createPrototype(cx, key);
         if (!proto)
             return false;
 
         // Make sure that creating the prototype didn't recursively resolve our
         // own constructor. We can't just assert that there's no prototype; OOMs
         // can result in incomplete resolutions in which the prototype is saved
         // but not the constructor. So use the same criteria that protects entry
         // into this function.
         MOZ_ASSERT(!global->isStandardClassResolved(key));
 
         global->setPrototype(key, ObjectValue(*proto));
     }
 
     // Create the constructor.
-    RootedObject ctor(cx, clasp->spec.createConstructorHook()(cx, key));
+    RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, key));
     if (!ctor)
         return false;
 
     RootedId id(cx, NameToId(ClassName(key, cx)));
-    if (clasp->spec.shouldDefineConstructor()) {
+    if (clasp->specShouldDefineConstructor()) {
         if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0))
             return false;
     }
 
     global->setConstructor(key, ObjectValue(*ctor));
     global->setConstructorPropertySlot(key, ObjectValue(*ctor));
 
     // Define any specified functions and properties, unless we're a dependent
     // standard class (in which case they live on the prototype), or we're
     // operating on the self-hosting global, in which case we don't want any
     // functions and properties on the builtins and their prototypes.
     if (!StandardClassIsDependent(key) && !cx->runtime()->isSelfHostingGlobal(global)) {
-        if (const JSFunctionSpec* funs = clasp->spec.prototypeFunctions()) {
+        if (const JSFunctionSpec* funs = clasp->specPrototypeFunctions()) {
             if (!JS_DefineFunctions(cx, proto, funs))
                 return false;
         }
-        if (const JSPropertySpec* props = clasp->spec.prototypeProperties()) {
+        if (const JSPropertySpec* props = clasp->specPrototypeProperties()) {
             if (!JS_DefineProperties(cx, proto, props))
                 return false;
         }
-        if (const JSFunctionSpec* funs = clasp->spec.constructorFunctions()) {
+        if (const JSFunctionSpec* funs = clasp->specConstructorFunctions()) {
             if (!JS_DefineFunctions(cx, ctor, funs))
                 return false;
         }
-        if (const JSPropertySpec* props = clasp->spec.constructorProperties()) {
+        if (const JSPropertySpec* props = clasp->specConstructorProperties()) {
             if (!JS_DefineProperties(cx, ctor, props))
                 return false;
         }
     }
 
     // If the prototype exists, link it with the constructor.
     if (proto && !LinkConstructorAndPrototype(cx, ctor, proto))
         return false;
 
     // Call the post-initialization hook, if provided.
-    if (clasp->spec.finishInitHook() && !clasp->spec.finishInitHook()(cx, ctor, proto))
-        return false;
+    if (FinishClassInitOp finishInit = clasp->specFinishInitHook()) {
+        if (!finishInit(cx, ctor, proto))
+            return false;
+    }
 
-    if (clasp->spec.shouldDefineConstructor()) {
+    if (clasp->specShouldDefineConstructor()) {
         // Stash type information, so that what we do here is equivalent to
         // initBuiltinConstructor.
         AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     }
 
     return true;
 }
 
@@ -411,21 +413,21 @@ GlobalObject::initStandardClasses(JSCont
  * self-hosted builtins.
  */
 static bool
 InitBareBuiltinCtor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey protoKey)
 {
     MOZ_ASSERT(cx->runtime()->isSelfHostingGlobal(global));
     const Class* clasp = ProtoKeyToClass(protoKey);
     RootedObject proto(cx);
-    proto = clasp->spec.createPrototypeHook()(cx, protoKey);
+    proto = clasp->specCreatePrototypeHook()(cx, protoKey);
     if (!proto)
         return false;
 
-    RootedObject ctor(cx, clasp->spec.createConstructorHook()(cx, protoKey));
+    RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, protoKey));
     if (!ctor)
         return false;
 
     return GlobalObject::initBuiltinConstructor(cx, global, protoKey, ctor, proto);
 }
 
 /**
  * The self-hosting global only gets a small subset of all standard classes.
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -180,16 +180,25 @@ RegExpObject::trace(JSTracer* trc, JSObj
         !obj->asTenured().zone()->isPreservingCode())
     {
         obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
     } else {
         shared->trace(trc);
     }
 }
 
+static const ClassSpec RegExpObjectClassSpec = {
+    GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
+    CreateRegExpPrototype,
+    nullptr,
+    js::regexp_static_props,
+    js::regexp_methods,
+    js::regexp_properties
+};
+
 const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
@@ -197,26 +206,17 @@ const Class RegExpObject::class_ = {
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     RegExpObject::trace,
-
-    // ClassSpec
-    {
-        GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
-        CreateRegExpPrototype,
-        nullptr,
-        js::regexp_static_props,
-        js::regexp_methods,
-        js::regexp_properties
-    }
+    &RegExpObjectClassSpec
 };
 
 RegExpObject*
 RegExpObject::create(ExclusiveContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
                      TokenStream* tokenStream, LifoAlloc& alloc)
 {
     RootedAtom source(cx, AtomizeChars(cx, chars, length));
     if (!source)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -647,17 +647,17 @@ JSRuntime::requestInterrupt(InterruptMod
     interrupt_ = true;
     jitStackLimit_ = UINTPTR_MAX;
 
     if (mode == JSRuntime::RequestInterruptUrgent) {
         // If this interrupt is urgent (slow script dialog and garbage
         // collection among others), take additional steps to
         // interrupt corner cases where the above fields are not
         // regularly polled.  Wake both ilooping JIT code and
-        // futexWait.
+        // Atomics.wait().
         fx.lock();
         if (fx.isWaiting())
             fx.wake(FutexRuntime::WakeForJSInterrupt);
         fx.unlock();
         InterruptRunningJitCode(this);
     }
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -956,17 +956,17 @@ struct JSRuntime : public JS::shadow::Ru
     const JSLocaleCallbacks* localeCallbacks;
 
     /* Default locale for Internationalization API */
     char* defaultLocale;
 
     /* Default JSVersion. */
     JSVersion defaultVersion_;
 
-    /* Futex state, used by futexWait and futexWake on the Atomics object */
+    /* Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics object */
     js::FutexRuntime fx;
 
   private:
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
     void* ownerThread_;
     size_t ownerThreadNative_;
     friend bool js::CurrentThreadCanAccessRuntime(JSRuntime* rt);
   public:
--- a/js/src/vm/SavedFrame.h
+++ b/js/src/vm/SavedFrame.h
@@ -13,16 +13,18 @@
 #include "js/UbiNode.h"
 
 namespace js {
 
 class SavedFrame : public NativeObject {
     friend class SavedStacks;
     friend struct ::JSStructuredCloneReader;
 
+    static const ClassSpec      classSpec_;
+
   public:
     static const Class          class_;
     static const JSPropertySpec protoAccessors[];
     static const JSFunctionSpec protoFunctions[];
     static const JSFunctionSpec staticFunctions[];
 
     // Prototype methods and properties to be exposed to JS.
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -284,16 +284,27 @@ SavedFrame::finishSavedFrameInit(JSConte
 {
     // The only object with the SavedFrame::class_ that doesn't have a source
     // should be the prototype.
     proto->as<NativeObject>().setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
 
     return FreezeObject(cx, proto);
 }
 
+const ClassSpec SavedFrame::classSpec_ = {
+    GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
+    GenericCreatePrototype,
+    SavedFrame::staticFunctions,
+    nullptr,
+    SavedFrame::protoFunctions,
+    SavedFrame::protoAccessors,
+    SavedFrame::finishSavedFrameInit,
+    ClassSpec::DontDefineConstructor
+};
+
 /* static */ const Class SavedFrame::class_ = {
     "SavedFrame",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
     JSCLASS_IS_ANONYMOUS,
     nullptr,                    // addProperty
     nullptr,                    // delProperty
@@ -302,28 +313,17 @@ SavedFrame::finishSavedFrameInit(JSConte
     nullptr,                    // enumerate
     nullptr,                    // resolve
     nullptr,                    // mayResolve
     SavedFrame::finalize,       // finalize
     nullptr,                    // call
     nullptr,                    // hasInstance
     nullptr,                    // construct
     nullptr,                    // trace
-
-    // ClassSpec
-    {
-        GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
-        GenericCreatePrototype,
-        SavedFrame::staticFunctions,
-        nullptr,
-        SavedFrame::protoFunctions,
-        SavedFrame::protoAccessors,
-        SavedFrame::finishSavedFrameInit,
-        ClassSpec::DontDefineConstructor
-    }
+    &SavedFrame::classSpec_
 };
 
 /* static */ const JSFunctionSpec
 SavedFrame::staticFunctions[] = {
     JS_FS_END
 };
 
 /* static */ const JSFunctionSpec
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -863,17 +863,17 @@ with_GetOwnPropertyDescriptor(JSContext*
 static bool
 with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return DeleteProperty(cx, actual, id, result);
 }
 
-const ObjectOps DynamicWithObject::objectOps_ = {
+static const ObjectOps DynamicWithObjectObjectOps = {
     with_LookupProperty,
     with_DefineProperty,
     with_HasProperty,
     with_GetProperty,
     with_SetProperty,
     with_GetOwnPropertyDescriptor,
     with_DeleteProperty,
     nullptr, nullptr,    /* watch/unwatch */
@@ -895,17 +895,17 @@ const Class DynamicWithObject::class_ = 
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
-    &DynamicWithObject::objectOps_
+    &DynamicWithObjectObjectOps
 };
 
 /* static */ StaticEvalScope*
 StaticEvalScope::create(JSContext* cx, HandleObject enclosing)
 {
     StaticEvalScope* obj =
         NewObjectWithNullTaggedProto<StaticEvalScope>(cx, TenuredObject, BaseShape::DELEGATE);
     if (!obj)
@@ -1119,30 +1119,16 @@ ClonedBlockObject::thisValue() const
         return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
     }
     return v;
 }
 
 static_assert(StaticBlockScope::RESERVED_SLOTS == ClonedBlockObject::RESERVED_SLOTS,
               "static block scopes and dynamic block environments share a Class");
 
-const ObjectOps ClonedBlockObject::objectOps_ = {
-    nullptr,          /* lookupProperty */
-    nullptr,          /* defineProperty */
-    nullptr,          /* hasProperty */
-    nullptr,          /* getProperty */
-    nullptr,          /* setProperty */
-    nullptr,          /* getOwnPropertyDescriptor */
-    nullptr,          /* deleteProperty */
-    nullptr, nullptr, /* watch/unwatch */
-    nullptr,          /* getElements */
-    nullptr,          /* enumerate (native enumeration of target doesn't work) */
-    nullptr,
-};
-
 const Class ClonedBlockObject::class_ = {
     "Block",
     JSCLASS_HAS_RESERVED_SLOTS(ClonedBlockObject::RESERVED_SLOTS) |
     JSCLASS_IS_ANONYMOUS,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
@@ -1151,17 +1137,17 @@ const Class ClonedBlockObject::class_ = 
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
-    &ClonedBlockObject::objectOps_
+    JS_NULL_OBJECT_OPS
 };
 
 template<XDRMode mode>
 bool
 js::XDRStaticBlockScope(XDRState<mode>* xdr, HandleObject enclosingScope,
                         MutableHandle<StaticBlockScope*> objp)
 {
     /* NB: Keep this in sync with CloneStaticBlockScope. */
@@ -1392,17 +1378,17 @@ lexicalError_GetOwnPropertyDescriptor(JS
 
 static bool
 lexicalError_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
     return false;
 }
 
-const ObjectOps RuntimeLexicalErrorObject::objectOps_ = {
+static const ObjectOps RuntimeLexicalErrorObjectObjectOps = {
     lexicalError_LookupProperty,
     nullptr,             /* defineProperty */
     lexicalError_HasProperty,
     lexicalError_GetProperty,
     lexicalError_SetProperty,
     lexicalError_GetOwnPropertyDescriptor,
     lexicalError_DeleteProperty,
     nullptr, nullptr,    /* watch/unwatch */
@@ -1424,17 +1410,17 @@ const Class RuntimeLexicalErrorObject::c
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
     nullptr, /* trace */
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
-    &RuntimeLexicalErrorObject::objectOps_
+    &RuntimeLexicalErrorObjectObjectOps
 };
 
 /*****************************************************************************/
 
 // Any name atom for a function which will be added as a DeclEnv object to the
 // scope chain above call objects for fun.
 static inline JSAtom*
 CallObjectLambdaName(JSFunction& fun)
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -811,18 +811,19 @@ class CallObject : public LexicalScopeBa
         return CALLEE_SLOT;
     }
 };
 
 class ModuleEnvironmentObject : public LexicalScopeBase
 {
     static const uint32_t MODULE_SLOT = 1;
 
+    static const ObjectOps objectOps_;
+
   public:
-    static const ObjectOps objectOps_;
     static const Class class_;
 
     static const uint32_t RESERVED_SLOTS = 2;
 
     static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
     ModuleObject& module();
     IndirectBindingMap& importBindings();
 
@@ -907,17 +908,16 @@ class NestedScopeObject : public ScopeOb
 class DynamicWithObject : public NestedScopeObject
 {
     static const unsigned OBJECT_SLOT = 1;
     static const unsigned THIS_SLOT = 2;
     static const unsigned KIND_SLOT = 3;
 
   public:
     static const unsigned RESERVED_SLOTS = 4;
-    static const ObjectOps objectOps_;
     static const Class class_;
 
     enum WithKind {
         SyntacticWith,
         NonSyntacticWith
     };
 
     static DynamicWithObject*
@@ -1070,17 +1070,16 @@ class ClonedBlockObject : public NestedS
 // ES6 'const' bindings induce a runtime error when assigned to outside
 // of initialization, regardless of strictness.
 class RuntimeLexicalErrorObject : public ScopeObject
 {
     static const unsigned ERROR_SLOT = 1;
 
   public:
     static const unsigned RESERVED_SLOTS = 2;
-    static const ObjectOps objectOps_;
     static const Class class_;
 
     static RuntimeLexicalErrorObject* create(JSContext* cx, HandleObject enclosing,
                                              unsigned errorNumber);
 
     unsigned errorNumber() {
         return getReservedSlot(ERROR_SLOT).toInt32();
     }
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -856,16 +856,28 @@ TypedArrayObject::protoFunctions[] = {
 
 /* static */ const JSFunctionSpec
 TypedArrayObject::staticFunctions[] = {
     JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
     JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
     JS_FS_END
 };
 
+static const ClassSpec
+TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
+    GenericCreateConstructor<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
+    GenericCreatePrototype,
+    TypedArrayObject::staticFunctions,
+    nullptr,
+    TypedArrayObject::protoFunctions,
+    TypedArrayObject::protoAccessors,
+    nullptr,
+    ClassSpec::DontDefineConstructor
+};
+
 /* static */ const Class
 TypedArrayObject::sharedTypedArrayPrototypeClass = {
     // Actually ({}).toString.call(%TypedArray%.prototype) should throw,
     // because %TypedArray%.prototype lacks the the typed array internal
     // slots.  (It's not clear this is desirable -- particularly applied to
     // the actual typed array prototypes, see below -- but it's what ES6
     // draft 20140824 requires.)  But this is about as much as we can do
     // until we implement @@toStringTag.
@@ -878,26 +890,17 @@ TypedArrayObject::sharedTypedArrayProtot
     nullptr,                /* enumerate */
     nullptr,                /* resolve */
     nullptr,                /* mayResolve */
     nullptr,                /* finalize */
     nullptr,                /* call */
     nullptr,                /* hasInstance */
     nullptr,                /* construct */
     nullptr,                /* trace */
-    {
-        GenericCreateConstructor<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
-        GenericCreatePrototype,
-        TypedArrayObject::staticFunctions,
-        nullptr,
-        TypedArrayObject::protoFunctions,
-        TypedArrayObject::protoAccessors,
-        nullptr,
-        ClassSpec::DontDefineConstructor
-    }
+    &TypedArrayObjectSharedTypedArrayPrototypeClassSpec
 };
 
 template<typename T>
 bool
 ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext* cx, const CallArgs& args)
 {
     typedef TypedArrayObjectTemplate<T> ArrayType;
     MOZ_ASSERT(IsArrayBuffer(args.thisv()));
@@ -1900,114 +1903,141 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uin
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float)
 IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
 
-#define TYPED_ARRAY_CLASS_SPEC(_typedArray)                                    \
+#define IMPL_TYPED_ARRAY_CLASS_SPEC(_type)                                     \
 {                                                                              \
-    _typedArray::createConstructor,                                            \
-    _typedArray::createPrototype,                                              \
+    _type##Array::createConstructor,                                           \
+    _type##Array::createPrototype,                                             \
     nullptr,                                                                   \
     nullptr,                                                                   \
     nullptr,                                                                   \
     nullptr,                                                                   \
-    _typedArray::finishClassInit,                                              \
+    _type##Array::finishClassInit,                                             \
     JSProto_TypedArray                                                         \
 }
 
-#define IMPL_TYPED_ARRAY_CLASS(_typedArray)                                    \
+static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = {
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Int8),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Int16),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Uint16),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Int32),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Float32),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Float64),
+    IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped)
+};
+
+#define IMPL_TYPED_ARRAY_CLASS(_type)                                          \
 {                                                                              \
-    #_typedArray,                                                              \
+    #_type "Array",                                                            \
     JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) |             \
     JSCLASS_HAS_PRIVATE |                                                      \
-    JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray) |                          \
+    JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) |                         \
     JSCLASS_DELAY_METADATA_CALLBACK,                                           \
     nullptr,                 /* addProperty */                                 \
     nullptr,                 /* delProperty */                                 \
     nullptr,                 /* getProperty */                                 \
     nullptr,                 /* setProperty */                                 \
     nullptr,                 /* enumerate   */                                 \
     nullptr,                 /* resolve     */                                 \
     nullptr,                 /* mayResolve  */                                 \
     nullptr,                 /* finalize    */                                 \
     nullptr,                 /* call        */                                 \
     nullptr,                 /* hasInstance */                                 \
     nullptr,                 /* construct   */                                 \
     TypedArrayObject::trace, /* trace  */                                      \
-    TYPED_ARRAY_CLASS_SPEC(_typedArray)                                        \
+    &TypedArrayObjectClassSpecs[Scalar::Type::_type]                           \
 }
 
 const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = {
-    IMPL_TYPED_ARRAY_CLASS(Int8Array),
-    IMPL_TYPED_ARRAY_CLASS(Uint8Array),
-    IMPL_TYPED_ARRAY_CLASS(Int16Array),
-    IMPL_TYPED_ARRAY_CLASS(Uint16Array),
-    IMPL_TYPED_ARRAY_CLASS(Int32Array),
-    IMPL_TYPED_ARRAY_CLASS(Uint32Array),
-    IMPL_TYPED_ARRAY_CLASS(Float32Array),
-    IMPL_TYPED_ARRAY_CLASS(Float64Array),
-    IMPL_TYPED_ARRAY_CLASS(Uint8ClampedArray)
+    IMPL_TYPED_ARRAY_CLASS(Int8),
+    IMPL_TYPED_ARRAY_CLASS(Uint8),
+    IMPL_TYPED_ARRAY_CLASS(Int16),
+    IMPL_TYPED_ARRAY_CLASS(Uint16),
+    IMPL_TYPED_ARRAY_CLASS(Int32),
+    IMPL_TYPED_ARRAY_CLASS(Uint32),
+    IMPL_TYPED_ARRAY_CLASS(Float32),
+    IMPL_TYPED_ARRAY_CLASS(Float64),
+    IMPL_TYPED_ARRAY_CLASS(Uint8Clamped)
+};
+
+#define IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(_type) \
+{ \
+    DELEGATED_CLASSSPEC(TypedArrayObject::classes[Scalar::Type::_type].spec), \
+    nullptr, \
+    nullptr, \
+    nullptr, \
+    nullptr, \
+    nullptr, \
+    nullptr, \
+    JSProto_TypedArray | ClassSpec::IsDelegated \
+}
+
+static const ClassSpec TypedArrayObjectProtoClassSpecs[Scalar::MaxTypedArrayViewType] = {
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int8),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int16),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint16),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float64),
+    IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8Clamped)
 };
 
 // The various typed array prototypes are supposed to 1) be normal objects,
 // 2) stringify to "[object <name of constructor>]", and 3) (Gecko-specific)
 // be xrayable.  The first and second requirements mandate (in the absence of
 // @@toStringTag) a custom class.  The third requirement mandates that each
 // prototype's class have the relevant typed array's cached JSProtoKey in them.
 // Thus we need one class with cached prototype per kind of typed array, with a
 // delegated ClassSpec.
-#define IMPL_TYPED_ARRAY_PROTO_CLASS(typedArray, i) \
+#define IMPL_TYPED_ARRAY_PROTO_CLASS(_type) \
 { \
     /*
      * Actually ({}).toString.call(Uint8Array.prototype) should throw, because
      * Uint8Array.prototype lacks the the typed array internal slots.  (Same as
      * with %TypedArray%.prototype.)  It's not clear this is desirable (see
      * above), but it's what we've always done, so keep doing it till we
      * implement @@toStringTag or ES6 changes.
      */ \
-    #typedArray "Prototype", \
-    JSCLASS_HAS_CACHED_PROTO(JSProto_##typedArray), \
+    #_type "ArrayPrototype", \
+    JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \
     nullptr, /* addProperty */ \
     nullptr, /* delProperty */ \
     nullptr, /* getProperty */ \
     nullptr, /* setProperty */ \
     nullptr, /* enumerate */ \
     nullptr, /* resolve */ \
     nullptr, /* mayResolve */ \
     nullptr, /* finalize */ \
     nullptr, /* call */ \
     nullptr, /* hasInstance */ \
     nullptr, /* construct */ \
     nullptr, /* trace  */ \
-    { \
-        DELEGATED_CLASSSPEC(&TypedArrayObject::classes[i].spec), \
-        nullptr, \
-        nullptr, \
-        nullptr, \
-        nullptr, \
-        nullptr, \
-        nullptr, \
-        JSProto_TypedArray | ClassSpec::IsDelegated \
-    } \
+    &TypedArrayObjectProtoClassSpecs[Scalar::Type::_type] \
 }
 
 const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = {
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array, 0),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array, 1),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array, 2),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array, 3),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array, 4),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array, 5),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array, 6),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array, 7),
-    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray, 8)
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Int8),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Int16),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Int32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Float32),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Float64),
+    IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped)
 };
 
 /* static */ bool
 TypedArrayObject::isOriginalLengthGetter(Native native)
 {
     return native == TypedArray_lengthGetter;
 }
 
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -115,17 +115,19 @@ class TypedArrayObject : public NativeOb
 
     inline Scalar::Type type() const;
     inline size_t bytesPerElement() const;
 
     static Value bufferValue(TypedArrayObject* tarr) {
         return tarr->getFixedSlot(BUFFER_SLOT);
     }
     static Value byteOffsetValue(TypedArrayObject* tarr) {
-        return tarr->getFixedSlot(BYTEOFFSET_SLOT);
+        Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
+        MOZ_ASSERT(v.toInt32() >= 0);
+        return v;
     }
     static Value byteLengthValue(TypedArrayObject* tarr) {
         return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
     }
     static Value lengthValue(TypedArrayObject* tarr) {
         return tarr->getFixedSlot(LENGTH_SLOT);
     }
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -900,17 +900,17 @@ UnboxedPlainObject::obj_enumerate(JSCont
     return true;
 }
 
 const Class UnboxedExpandoObject::class_ = {
     "UnboxedExpandoObject",
     0
 };
 
-const ObjectOps UnboxedPlainObject::objectOps_ = {
+static const ObjectOps UnboxedPlainObjectObjectOps = {
     UnboxedPlainObject::obj_lookupProperty,
     UnboxedPlainObject::obj_defineProperty,
     UnboxedPlainObject::obj_hasProperty,
     UnboxedPlainObject::obj_getProperty,
     UnboxedPlainObject::obj_setProperty,
     UnboxedPlainObject::obj_getOwnPropertyDescriptor,
     UnboxedPlainObject::obj_deleteProperty,
     UnboxedPlainObject::obj_watch,
@@ -934,17 +934,17 @@ const Class UnboxedPlainObject::class_ =
     nullptr,        /* mayResolve  */
     nullptr,        /* finalize    */
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     UnboxedPlainObject::trace,
     JS_NULL_CLASS_SPEC,
     JS_NULL_CLASS_EXT,
-    &UnboxedPlainObject::objectOps_
+    &UnboxedPlainObjectObjectOps
 };
 
 /////////////////////////////////////////////////////////////////////
 // UnboxedArrayObject
 /////////////////////////////////////////////////////////////////////
 
 template <JSValueType Type>
 DenseElementResult
@@ -1586,17 +1586,17 @@ UnboxedArrayObject::obj_enumerate(JSCont
     }
 
     if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
         return false;
 
     return true;
 }
 
-const ObjectOps UnboxedArrayObject::objectOps_ = {
+static const ObjectOps UnboxedArrayObjectObjectOps = {
     UnboxedArrayObject::obj_lookupProperty,
     UnboxedArrayObject::obj_defineProperty,
     UnboxedArrayObject::obj_hasProperty,
     UnboxedArrayObject::obj_getProperty,
     UnboxedArrayObject::obj_setProperty,
     UnboxedArrayObject::obj_getOwnPropertyDescriptor,
     UnboxedArrayObject::obj_deleteProperty,
     UnboxedArrayObject::obj_watch,
@@ -1624,17 +1624,17 @@ const Class UnboxedArrayObject::class_ =
     nullptr,        /* construct   */
     UnboxedArrayObject::trace,
     JS_NULL_CLASS_SPEC,
     {
         false,      /* isWrappedNative */
         nullptr,    /* weakmapKeyDelegateOp */
         UnboxedArrayObject::objectMoved
     },
-    &UnboxedArrayObject::objectOps_
+    &UnboxedArrayObjectObjectOps
 };
 
 /////////////////////////////////////////////////////////////////////
 // API
 /////////////////////////////////////////////////////////////////////
 
 static bool
 UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype)
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -233,17 +233,16 @@ class UnboxedPlainObject : public JSObje
     // not automatically barriered to avoid problems if the object is converted
     // to a native. See ensureExpando().
     UnboxedExpandoObject* expando_;
 
     // Start of the inline data, which immediately follows the group and extra properties.
     uint8_t data_[1];
 
   public:
-    static const ObjectOps objectOps_;
     static const Class class_;
 
     static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
                                    MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<PropertyDescriptor> desc,
@@ -370,17 +369,16 @@ class UnboxedArrayObject : public JSObje
             return length;
         return CapacityArray[index];
     }
 
     static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
     static uint32_t exactCapacityIndex(uint32_t capacity);
 
   public:
-    static const ObjectOps objectOps_;
     static const Class class_;
 
     static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                    HandleId id, MutableHandleObject objp,
                                    MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<PropertyDescriptor> desc,
--- a/js/xpconnect/idl/nsIScriptError.idl
+++ b/js/xpconnect/idl/nsIScriptError.idl
@@ -10,17 +10,17 @@
 
 #include "nsISupports.idl"
 #include "nsIConsoleMessage.idl"
 
 %{C++
 #include "nsStringGlue.h" // for nsDependentCString
 %}
 
-[scriptable, uuid(18bdefde-e57b-11e4-832a-000c29a57fff)]
+[scriptable, uuid(361be358-76f0-47aa-b37b-6ad833599e8d)]
 interface nsIScriptError : nsIConsoleMessage
 {
     /** pseudo-flag for default case */
     const unsigned long errorFlag = 0x0;
 
     /** message is warning */
     const unsigned long warningFlag = 0x1;
 
@@ -63,16 +63,23 @@ interface nsIScriptError : nsIConsoleMes
     /* Get the inner window id this was initialized with.  Zero will be
        returned if init() was used instead of initWithWindowID(). */
     readonly attribute unsigned long long innerWindowID;
 
     readonly attribute boolean isFromPrivateWindow;
 
     attribute jsval stack;
 
+    /**
+     * The name of a template string, as found in js.msg, associated with the
+     * error message.
+     */
+    attribute AString errorMessageName;
+
+
     void init(in AString message,
               in AString sourceName,
               in AString sourceLine,
               in uint32_t lineNumber,
               in uint32_t columnNumber,
               in uint32_t flags,
               in string category);
 
--- a/js/xpconnect/src/nsScriptError.cpp
+++ b/js/xpconnect/src/nsScriptError.cpp
@@ -16,16 +16,17 @@
 #include "nsGlobalWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsILoadContext.h"
 #include "nsIDocShell.h"
 #include "nsISensitiveInfoHiddenURI.h"
 
 nsScriptErrorBase::nsScriptErrorBase()
     :  mMessage(),
+       mMessageName(),
        mSourceName(),
        mLineNumber(0),
        mSourceLine(),
        mColumnNumber(0),
        mFlags(0),
        mCategory(),
        mOuterWindowID(0),
        mInnerWindowID(0),
@@ -148,16 +149,28 @@ nsScriptErrorBase::GetStack(JS::MutableH
 }
 
 NS_IMETHODIMP
 nsScriptErrorBase::SetStack(JS::HandleValue aStack) {
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) {
+    aErrorMessageName = mMessageName;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
+    mMessageName = aErrorMessageName;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsScriptErrorBase::Init(const nsAString& message,
                         const nsAString& sourceName,
                         const nsAString& sourceLine,
                         uint32_t lineNumber,
                         uint32_t columnNumber,
                         uint32_t flags,
                         const char* category)
 {
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -26,16 +26,18 @@
 
 #include "nsDOMMutationObserver.h"
 #include "nsICycleCollectorListener.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsScriptSecurityManager.h"
 
+#include "jsfriendapi.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace xpc;
 using namespace JS;
 
 NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect)
 
 nsXPConnect* nsXPConnect::gSelf = nullptr;
@@ -173,28 +175,34 @@ void
 xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage,
                        bool aIsChrome, uint64_t aWindowID)
 {
     mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
                           : NS_LITERAL_CSTRING("content javascript");
     mWindowID = aWindowID;
 
     ErrorReportToMessageString(aReport, mErrorMsg);
-
     if (mErrorMsg.IsEmpty() && aFallbackMessage) {
         mErrorMsg.AssignWithConversion(aFallbackMessage);
     }
 
     if (!aReport->filename) {
         mFileName.SetIsVoid(true);
     } else {
         mFileName.AssignWithConversion(aReport->filename);
     }
 
     mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength());
+    const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber);
+
+    if (efs == nullptr) {
+        mErrorMsgName.AssignASCII("");
+    } else {
+        mErrorMsgName.AssignASCII(efs->name);
+    }
 
     mLineNumber = aReport->lineno;
     mColumn = aReport->column;
     mFlags = aReport->flags;
     mIsMuted = aReport->isMuted;
 }
 
 static LazyLogModule gJSDiagnostics("JSDiagnostics");
@@ -243,16 +251,17 @@ xpc::ErrorReport::LogToConsoleWithStack(
       // Only set stack on messages related to a document
       // As we cache messages in the console service,
       // we have to ensure not leaking them after the related
       // context is destroyed and we only track document lifecycle for now.
       errorObject = new nsScriptErrorWithStack(aStack);
     } else {
       errorObject = new nsScriptError();
     }
+    errorObject->SetErrorMessageName(mErrorMsgName);
     NS_ENSURE_TRUE_VOID(consoleService && errorObject);
 
     nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,
                                                 mLineNumber, mColumn, mFlags,
                                                 mCategory, mWindowID);
     NS_ENSURE_SUCCESS_VOID(rv);
     consoleService->LogMessage(errorObject);
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2938,16 +2938,17 @@ public:
 
 protected:
     virtual ~nsScriptErrorBase();
 
     void
     InitializeOnMainThread();
 
     nsString mMessage;
+    nsString mMessageName;
     nsString mSourceName;
     uint32_t mLineNumber;
     nsString mSourceLine;
     uint32_t mColumnNumber;
     uint32_t mFlags;
     nsCString mCategory;
     // mOuterWindowID is set on the main thread from InitializeOnMainThread().
     uint64_t mOuterWindowID;
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -515,16 +515,17 @@ class ErrorReport {
     // that this may produce an empty string if aReport doesn't have a
     // message attached.
     static void ErrorReportToMessageString(JSErrorReport* aReport,
                                            nsAString& aString);
 
   public:
 
     nsCString mCategory;
+    nsString mErrorMsgName;
     nsString mErrorMsg;
     nsString mFileName;
     nsString mSourceLine;
     uint64_t mWindowID;
     uint32_t mLineNumber;
     uint32_t mColumn;
     uint32_t mFlags;
     bool mIsMuted;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -568,21 +568,21 @@ JSXrayTraits::resolveOwnProperty(JSConte
                             return false;
                         FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
                                                ObjectValue(*standardProto));
                         return true;
                     }
 
                     if (ShouldResolveStaticProperties(standardConstructor)) {
                         const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
-                        MOZ_ASSERT(clasp->spec.defined());
+                        MOZ_ASSERT(clasp->specDefined());
 
                         if (!TryResolvePropertyFromSpecs(cx, id, holder,
-                               clasp->spec.constructorFunctions(),
-                               clasp->spec.constructorProperties(), desc)) {
+                               clasp->specConstructorFunctions(),
+                               clasp->specConstructorProperties(), desc)) {
                             return false;
                         }
 
                         if (desc.object()) {
                             desc.object().set(wrapper);
                             return true;
                         }
                     }
@@ -651,23 +651,23 @@ JSXrayTraits::resolveOwnProperty(JSConte
     }
 
     // Handle the 'lastIndex' property for RegExp prototypes.
     if (key == JSProto_RegExp && id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX))
         return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
 
     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     const js::Class* clasp = js::GetObjectClass(target);
-    MOZ_ASSERT(clasp->spec.defined());
+    MOZ_ASSERT(clasp->specDefined());
 
     // Indexed array properties are handled above, so we can just work with the
     // class spec here.
     if (!TryResolvePropertyFromSpecs(cx, id, holder,
-                                     clasp->spec.prototypeFunctions(),
-                                     clasp->spec.prototypeProperties(),
+                                     clasp->specPrototypeFunctions(),
+                                     clasp->specPrototypeProperties(),
                                      desc)) {
         return false;
     }
 
     if (desc.object()) {
         desc.object().set(wrapper);
     }
 
@@ -859,21 +859,21 @@ JSXrayTraits::enumerateNames(JSContext* 
             // constructors.
             JSProtoKey standardConstructor = constructorFor(holder);
             if (standardConstructor != JSProto_Null) {
                 if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTOTYPE)))
                     return false;
 
                 if (ShouldResolveStaticProperties(standardConstructor)) {
                     const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
-                    MOZ_ASSERT(clasp->spec.defined());
+                    MOZ_ASSERT(clasp->specDefined());
 
                     if (!AppendNamesFromFunctionAndPropertySpecs(
-                           cx, clasp->spec.constructorFunctions(),
-                           clasp->spec.constructorProperties(), flags, props)) {
+                           cx, clasp->specConstructorFunctions(),
+                           clasp->specConstructorProperties(), flags, props)) {
                         return false;
                     }
                 }
             }
         } else if (IsErrorObjectKey(key)) {
             if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_FILENAME)) ||
                 !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LINENUMBER)) ||
                 !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_COLUMNNUMBER)) ||
@@ -900,21 +900,21 @@ JSXrayTraits::enumerateNames(JSContext* 
         return false;
 
     // For RegExp protoypes, add the 'lastIndex' property.
     if (key == JSProto_RegExp && !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_LASTINDEX)))
         return false;
 
     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     const js::Class* clasp = js::GetObjectClass(target);
-    MOZ_ASSERT(clasp->spec.defined());
+    MOZ_ASSERT(clasp->specDefined());
 
     return AppendNamesFromFunctionAndPropertySpecs(
-        cx, clasp->spec.prototypeFunctions(),
-        clasp->spec.prototypeProperties(), flags, props);
+        cx, clasp->specPrototypeFunctions(),
+        clasp->specPrototypeProperties(), flags, props);
 }
 
 bool
 JSXrayTraits::construct(JSContext* cx, HandleObject wrapper,
                         const JS::CallArgs& args, const js::Wrapper& baseInstance)
 {
     JSXrayTraits& self = JSXrayTraits::singleton;
     JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -540,25 +540,25 @@ AccessibleCaretEventHub::HandleMouseEven
   }
 
   return rv;
 }
 
 nsEventStatus
 AccessibleCaretEventHub::HandleTouchEvent(WidgetTouchEvent* aEvent)
 {
-  if (aEvent->touches.IsEmpty()) {
+  if (aEvent->mTouches.IsEmpty()) {
     AC_LOG("%s: Receive a touch event without any touch data!", __FUNCTION__);
     return nsEventStatus_eIgnore;
   }
 
   nsEventStatus rv = nsEventStatus_eIgnore;
 
   int32_t id =
-    (mActiveTouchId == kInvalidTouchId ? aEvent->touches[0]->Identifier()
+    (mActiveTouchId == kInvalidTouchId ? aEvent->mTouches[0]->Identifier()
                                        : mActiveTouchId);
   nsPoint point = GetTouchEventPosition(aEvent, id);
 
   switch (aEvent->mMessage) {
     case eTouchStart:
       AC_LOGV("Before eTouchStart, state: %s", mState->Name());
       rv = mState->OnPress(this, point, id);
       AC_LOGV("After eTouchStart, state: %s, consume: %d", mState->Name(), rv);
@@ -776,17 +776,17 @@ AccessibleCaretEventHub::NotifyBlur(bool
   AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
   mState->OnBlur(this, aIsLeavingDocument);
 }
 
 nsPoint
 AccessibleCaretEventHub::GetTouchEventPosition(WidgetTouchEvent* aEvent,
                                                int32_t aIdentifier) const
 {
-  for (dom::Touch* touch : aEvent->touches) {
+  for (dom::Touch* touch : aEvent->mTouches) {
     if (touch->Identifier() == aIdentifier) {
       LayoutDeviceIntPoint touchIntPoint = touch->mRefPoint;
 
       // Get event coordinate relative to root frame.
       nsIFrame* rootFrame = mPresShell->GetRootFrame();
       return nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, touchIntPoint,
                                                           rootFrame);
     }
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -62,17 +62,17 @@ EvictTouchPoint(RefPtr<dom::Touch>& aTou
         nsIFrame* frame = presShell->GetRootFrame();
         if (frame) {
           nsPoint pt(aTouch->mRefPoint.x, aTouch->mRefPoint.y);
           nsCOMPtr<nsIWidget> widget = frame->GetView()->GetNearestWidget(&pt);
           if (widget) {
             WidgetTouchEvent event(true, eTouchEnd, widget);
             event.widget = widget;
             event.mTime = PR_IntervalNow();
-            event.touches.AppendElement(aTouch);
+            event.mTouches.AppendElement(aTouch);
             nsEventStatus status;
             widget->DispatchEvent(&event, status);
             return;
           }
         }
       }
     }
   }
@@ -113,40 +113,40 @@ TouchManager::PreHandleEvent(WidgetEvent
 {
   switch (aEvent->mMessage) {
     case eTouchStart: {
       aIsHandlingUserInput = true;
       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
       // if there is only one touch in this touchstart event, assume that it is
       // the start of a new touch session and evict any old touches in the
       // queue
-      if (touchEvent->touches.Length() == 1) {
+      if (touchEvent->mTouches.Length() == 1) {
         WidgetTouchEvent::AutoTouchArray touches;
         AppendToTouchList(&touches);
         for (uint32_t i = 0; i < touches.Length(); ++i) {
           EvictTouchPoint(touches[i]);
         }
       }
       // Add any new touches to the queue
-      for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
-        dom::Touch* touch = touchEvent->touches[i];
+      for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+        dom::Touch* touch = touchEvent->mTouches[i];
         int32_t id = touch->Identifier();
         if (!gCaptureTouchList->Get(id, nullptr)) {
           // If it is not already in the queue, it is a new touch
           touch->mChanged = true;
         }
         touch->mMessage = aEvent->mMessage;
         gCaptureTouchList->Put(id, touch);
       }
       break;
     }
     case eTouchMove: {
       // Check for touches that changed. Mark them add to queue
       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
-      WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
+      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
       bool haveChanged = false;
       for (int32_t i = touches.Length(); i; ) {
         --i;
         dom::Touch* touch = touches[i];
         if (!touch) {
           continue;
         }
         int32_t id = touch->Identifier();
@@ -180,19 +180,19 @@ TouchManager::PreHandleEvent(WidgetEvent
       if (!haveChanged) {
         if (aTouchIsNew) {
           // however, if this is the first touchmove after a touchstart,
           // it is special in that preventDefault is allowed on it, so
           // we must dispatch it to content even if nothing changed. we
           // arbitrarily pick the first touch point to be the "changed"
           // touch because firing an event with no changed events doesn't
           // work.
-          for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
-            if (touchEvent->touches[i]) {
-              touchEvent->touches[i]->mChanged = true;
+          for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+            if (touchEvent->mTouches[i]) {
+              touchEvent->mTouches[i]->mChanged = true;
               break;
             }
           }
         } else {
           return false;
         }
       }
       break;
@@ -200,17 +200,17 @@ TouchManager::PreHandleEvent(WidgetEvent
     case eTouchEnd:
       aIsHandlingUserInput = true;
       // Fall through to touchcancel code
       MOZ_FALLTHROUGH;
     case eTouchCancel: {
       // Remove the changed touches
       // need to make sure we only remove touches that are ending here
       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
-      WidgetTouchEvent::TouchArray& touches = touchEvent->touches;
+      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
       for (uint32_t i = 0; i < touches.Length(); ++i) {
         dom::Touch* touch = touches[i];
         if (!touch) {
           continue;
         }
         touch->mMessage = aEvent->mMessage;
         touch->mChanged = true;
 
--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -69,17 +69,17 @@ public:
     mManager = MakeUnique<MockAccessibleCaretManager>();
     mInitialized = true;
   }
 
   virtual nsPoint GetTouchEventPosition(WidgetTouchEvent* aEvent,
                                         int32_t aIdentifier) const override
   {
     // Return the device point directly.
-    LayoutDeviceIntPoint touchIntPoint = aEvent->touches[0]->mRefPoint;
+    LayoutDeviceIntPoint touchIntPoint = aEvent->mTouches[0]->mRefPoint;
     return nsPoint(touchIntPoint.x, touchIntPoint.y);
   }
 
   virtual nsPoint GetMouseEventPosition(WidgetMouseEvent* aEvent) const override
   {
     // Return the device point directly.
     LayoutDeviceIntPoint mouseIntPoint = aEvent->AsGUIEvent()->refPoint;
     return nsPoint(mouseIntPoint.x, mouseIntPoint.y);
@@ -158,17 +158,17 @@ public:
     int32_t identifier = 0;
     LayoutDeviceIntPoint point(aX, aY);
     LayoutDeviceIntPoint radius(19, 19);
     float rotationAngle = 0;
     float force = 1;
 
     RefPtr<dom::Touch> touch(
       new dom::Touch(identifier, point, radius, rotationAngle, force));
-    event->touches.AppendElement(touch);
+    event->mTouches.AppendElement(touch);
 
     return Move(event);
   }
 
   static UniquePtr<WidgetEvent> CreateTouchStartEvent(nscoord aX, nscoord aY)
   {
     return CreateTouchEvent(eTouchStart, aX, aY);
   }
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1980,17 +1980,20 @@ nsCSSRendering::DetermineBackgroundColor
                                          nsStyleContext* aStyleContext,
                                          nsIFrame* aFrame,
                                          bool& aDrawBackgroundImage,
                                          bool& aDrawBackgroundColor)
 {
   aDrawBackgroundImage = true;
   aDrawBackgroundColor = true;
 
-  if (aFrame->HonorPrintBackgroundSettings()) {
+  const nsStyleVisibility* visibility = aStyleContext->StyleVisibility();
+
+  if (visibility->mColorAdjust != NS_STYLE_COLOR_ADJUST_EXACT &&
+      aFrame->HonorPrintBackgroundSettings()) {
     aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
     aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
   }
 
   const nsStyleBackground *bg = aStyleContext->StyleBackground();
   nscolor bgColor;
   if (aDrawBackgroundColor) {
     bgColor =
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1056,16 +1056,26 @@ GetDisplayPortFromMarginsData(nsIContent
   nsRect expandedScrollableRect =
     nsLayoutUtils::CalculateExpandedScrollableRect(frame);
   result = result.MoveInsideAndClamp(expandedScrollableRect - scrollPos);
 
   return result;
 }
 
 static bool
+ShouldDisableApzForElement(nsIContent* aContent)
+{
+  if (gfxPrefs::APZDisableForScrollLinkedEffects() && aContent) {
+    nsIDocument* doc = aContent->GetComposedDoc();
+    return (doc && doc->HasScrollLinkedEffect());
+  }
+  return false;
+}
+
+static bool
 GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier)
 {
   DisplayPortPropertyData* rectData =
     static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort));
   DisplayPortMarginsPropertyData* marginsData =
     static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
 
   if (!rectData && !marginsData) {
@@ -1089,17 +1099,17 @@ GetDisplayPortImpl(nsIContent* aContent,
   }
 
   NS_ASSERTION((rectData == nullptr) != (marginsData == nullptr),
                "Only one of rectData or marginsData should be set!");
 
   nsRect result;
   if (rectData) {
     result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier);
-  } else if (APZCCallbackHelper::IsDisplayportSuppressed()) {
+  } else if (APZCCallbackHelper::IsDisplayportSuppressed() || ShouldDisableApzForElement(aContent)) {
     DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1);
     result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier);
   } else {
     result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier);
   }
 
   if (!gfxPrefs::LayersTilesEnabled()) {
     // Either we should have gotten a valid rect directly from the displayport
@@ -8796,16 +8806,20 @@ nsLayoutUtils::ComputeScrollMetadata(nsI
       nsStyleContext* backgroundStyle;
       if (nsCSSRendering::FindBackground(aScrollFrame, &backgroundStyle)) {
         metrics.SetBackgroundColor(Color::FromABGR(
           backgroundStyle->StyleBackground()->mBackgroundColor));
       }
     }
   }
 
+  if (ShouldDisableApzForElement(aContent)) {
+    metrics.SetForceDisableApz(true);
+  }
+
   return metadata;
 }
 
 /* static */ bool
 nsLayoutUtils::ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId)
 {
   for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) {
     if (aLayer->GetFrameMetrics(i-1).GetScrollId() == aScrollId) {
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6886,18 +6886,18 @@ DispatchPointerFromMouseOrTouch(PresShel
       break;
     case eTouchCancel:
       pointerMessage = ePointerCancel;
       break;
     default:
       return NS_OK;
     }
 
-    for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
-      mozilla::dom::Touch* touch = touchEvent->touches[i];
+    for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+      mozilla::dom::Touch* touch = touchEvent->mTouches[i];
       if (!touch || !touch->convertToPointer) {
         continue;
       }
 
       WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
                                touchEvent->widget);
       event.isPrimary = i == 0;
       event.pointerId = touch->Identifier();
@@ -7539,34 +7539,35 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       uint32_t flags = 0;
       if (aEvent->mMessage == eTouchStart) {
         flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
         // if this is a continuing session, ensure that all these events are
         // in the same document by taking the target of the events already in
         // the capture list
         nsCOMPtr<nsIContent> anyTarget;
-        if (TouchManager::gCaptureTouchList->Count() > 0 && touchEvent->touches.Length() > 1) {
+        if (TouchManager::gCaptureTouchList->Count() > 0 &&
+            touchEvent->mTouches.Length() > 1) {
           for (auto iter = TouchManager::gCaptureTouchList->Iter();
                !iter.Done();
                iter.Next()) {
             RefPtr<dom::Touch>& touch = iter.Data();
             if (touch) {
               dom::EventTarget* target = touch->GetTarget();
               if (target) {
                 anyTarget = do_QueryInterface(target);
                 break;
               }
             }
           }
         }
 
-        for (int32_t i = touchEvent->touches.Length(); i; ) {
+        for (int32_t i = touchEvent->mTouches.Length(); i; ) {
           --i;
-          dom::Touch* touch = touchEvent->touches[i];
+          dom::Touch* touch = touchEvent->mTouches[i];
 
           int32_t id = touch->Identifier();
           if (!TouchManager::gCaptureTouchList->Get(id, nullptr)) {
             // find the target for this touch
             eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
                                                               touch->mRefPoint,
                                                               frame);
             nsIFrame* target = FindFrameTargetedByInputEvent(aEvent,
@@ -7590,20 +7591,20 @@ PresShell::HandleEvent(nsIFrame* aFrame,
                 // We must be in a subdocument so jump directly to the root frame.
                 // GetParentOrPlaceholderForCrossDoc gets called immediately to
                 // jump up to the containing document.
                 f = f->PresContext()->GetPresShell()->GetRootFrame();
               }
 
               // if we couldn't find a target frame in the same document as
               // anyTarget, remove the touch from the capture touch list, as
-              // well as the event->touches array. touchmove events that aren't
+              // well as the event->mTouches array. touchmove events that aren't
               // in the captured touch list will be discarded
               if (!newTargetFrame) {
-                touchEvent->touches.RemoveElementAt(i);
+                touchEvent->mTouches.RemoveElementAt(i);
               } else {
                 target = newTargetFrame;
                 nsCOMPtr<nsIContent> targetContent;
                 target->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
                 while (targetContent && !targetContent->IsElement()) {
                   targetContent = targetContent->GetParent();
                 }
                 touch->SetTarget(targetContent);
@@ -7723,17 +7724,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
     PresShell* shell =
         static_cast<PresShell*>(frame->PresContext()->PresShell());
     switch (aEvent->mMessage) {
       case eTouchMove:
       case eTouchCancel:
       case eTouchEnd: {
         // get the correct shell to dispatch to
         WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
-        for (dom::Touch* touch : touchEvent->touches) {
+        for (dom::Touch* touch : touchEvent->mTouches) {
           if (!touch) {
             break;
           }
 
           RefPtr<dom::Touch> oldTouch =
             TouchManager::gCaptureTouchList->GetWeak(touch->Identifier());
           if (!oldTouch) {
             break;
@@ -8336,17 +8337,17 @@ PresShell::DispatchTouchEventToDOM(Widge
   bool canPrevent = (aEvent->mMessage == eTouchStart) ||
                     (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
                     (aEvent->mMessage == eTouchEnd);
   bool preventDefault = false;
   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
 
   // loop over all touches and dispatch events on any that have changed
-  for (dom::Touch* touch : touchEvent->touches) {
+  for (dom::Touch* touch : touchEvent->mTouches) {
     if (!touch || !touch->mChanged) {
       continue;
     }
 
     nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
     nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
     if (!content) {
       continue;
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -530,19 +530,19 @@ nsRangeFrame::GetValueAtEventPoint(Widge
              "type=range should have a default maximum/minimum");
   if (maximum <= minimum) {
     return minimum;
   }
   Decimal range = maximum - minimum;
 
   LayoutDeviceIntPoint absPoint;
   if (aEvent->mClass == eTouchEventClass) {
-    MOZ_ASSERT(aEvent->AsTouchEvent()->touches.Length() == 1,
-               "Unexpected number of touches");
-    absPoint = aEvent->AsTouchEvent()->touches[0]->mRefPoint;
+    MOZ_ASSERT(aEvent->AsTouchEvent()->mTouches.Length() == 1,
+               "Unexpected number of mTouches");
+    absPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint;
   } else {
     absPoint = aEvent->refPoint;
   }
   nsPoint point =
     nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, absPoint, this);
 
   if (point == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
     // We don't want to change the current value for this error state.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/disable-apz-for-sle-pages-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+  <title>Check that the apz.disable_for_sle_pages pref behaves as expected</title>
+  <script>
+    addEventListener('load', function() {
+        window.scrollTo(0, 100);
+        setTimeout(done, 0);
+    }, false);
+
+    function done() {
+        document.documentElement.classList.remove('reftest-wait');
+    }
+  </script>
+ </head>
+ <body style="height: 5000px; background-image:url(repeatable-diagonal-gradient.png);">
+  <div id="fake-fixed" style="position: absolute; top: 100px; left: 100px; width: 100px; height: 100px; background-color: green"></div>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/disable-apz-for-sle-pages.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html class="reftest-wait" reftest-async-scroll
+      reftest-async-scroll-y="200">
+ <head>
+  <title>Check that the apz.disable_for_sle_pages pref behaves as expected</title>
+  <script>
+    // Upon load, this page scrolls to (0, 100). This triggers a scroll event,
+    // which runs the scroll listener below. The scroll listener updates the
+    // position of the div to simulate position:fixed using a scroll-linked
+    // effect. The scroll-linked effect detector should then report that the
+    // document contains such an effect, which will disable APZ on the page.
+    // That in turn will cause the reftest-async-scroll-y to get ignored, and
+    // that's what the reftest checks for.
+
+    addEventListener('scroll', function() {
+        document.getElementById('fake-fixed').style.top = window.scrollY + "px";
+    }, false);
+
+    addEventListener('load', function() {
+        window.scrollTo(0, 100);
+        setTimeout(done, 0);
+    }, false);
+
+    function done() {
+        document.documentElement.classList.remove('reftest-wait');
+    }
+  </script>
+ </head>
+ <body style="height: 5000px; background-image:url(repeatable-diagonal-gradient.png);">
+  <div id="fake-fixed" style="position: absolute; top: 0; left: 100px; width: 100px; height: 100px; background-color: green"></div>
+ </body>
+</html>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -34,16 +34,17 @@ skip-if(!asyncPan) == position-sticky-tr
 skip-if(!asyncPan) == offscreen-prerendered-active-opacity.html offscreen-prerendered-active-opacity-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-1.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-2.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip == offscreen-clipped-blendmode-3.html offscreen-clipped-blendmode-ref.html # bug 1251588 - wrong AGR on mix-blend-mode item
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-4.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-1.html perspective-scrolling-1-ref.html
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-2.html perspective-scrolling-2-ref.html
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-3.html perspective-scrolling-3-ref.html
+pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages-ref.html
 
 # for the following tests, we want to disable the low-precision buffer
 # as it will expand the displayport beyond what the test specifies in
 # its reftest-displayport attributes, and interfere with where we expect
 # checkerboarding to occur
 default-preferences pref(layers.low-precision-buffer,false)
 skip-if(!asyncPan) == checkerboard-1.html checkerboard-1-ref.html
 skip-if(!asyncPan) == checkerboard-2.html checkerboard-2-ref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -243,26 +243,28 @@ CSS_KEY(double-circle, double_circle)
 CSS_KEY(double-struck, double_struck)
 CSS_KEY(drag, drag)
 CSS_KEY(drop-shadow, drop_shadow)
 CSS_KEY(e-resize, e_resize)
 CSS_KEY(ease, ease)
 CSS_KEY(ease-in, ease_in)
 CSS_KEY(ease-in-out, ease_in_out)
 CSS_KEY(ease-out, ease_out)
+CSS_KEY(economy, economy)
 CSS_KEY(element, element)
 CSS_KEY(elements, elements)
 CSS_KEY(ellipse, ellipse)
 CSS_KEY(ellipsis, ellipsis)
 CSS_KEY(em, em)
 CSS_KEY(embed, embed)
 CSS_KEY(enabled, enabled)
 CSS_KEY(end, end)
 CSS_KEY(ethiopic-numeric, ethiopic_numeric)
 CSS_KEY(ex, ex)
+CSS_KEY(exact, exact)
 CSS_KEY(exclude, exclude)
 CSS_KEY(exclusion, exclusion)
 CSS_KEY(expanded, expanded)
 CSS_KEY(extends, extends)
 CSS_KEY(extra-condensed, extra_condensed)
 CSS_KEY(extra-expanded, extra_expanded)
 CSS_KEY(ew-resize, ew_resize)
 CSS_KEY(fallback, fallback)
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -1383,16 +1383,26 @@ CSS_PROP_COLOR(
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
         CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED |
         CSS_PROPERTY_HASHLESS_COLOR_QUIRK,
     "",
     VARIANT_HC,
     nullptr,
     offsetof(nsStyleColor, mColor),
     eStyleAnimType_Color)
+CSS_PROP_VISIBILITY(
+    color-adjust,
+    color_adjust,
+    ColorAdjust,
+    CSS_PROPERTY_PARSE_VALUE,
+    "layout.css.color-adjust.enabled",
+    VARIANT_HK,
+    kColorAdjustKTable,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
     -moz-columns,
     _moz_columns,
     CSS_PROP_DOMPROP_PREFIXED(Columns),
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_COLUMN(
     -moz-column-count,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -2361,16 +2361,22 @@ const KTableEntry nsCSSProps::kTextRende
 };
 
 const KTableEntry nsCSSProps::kVectorEffectKTable[] = {
   { eCSSKeyword_none, NS_STYLE_VECTOR_EFFECT_NONE },
   { eCSSKeyword_non_scaling_stroke, NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
+const KTableEntry nsCSSProps::kColorAdjustKTable[] = {
+  { eCSSKeyword_economy, NS_STYLE_COLOR_ADJUST_ECONOMY },
+  { eCSSKeyword_exact, NS_STYLE_COLOR_ADJUST_EXACT },
+  { eCSSKeyword_UNKNOWN, -1 }
+};
+
 const KTableEntry nsCSSProps::kColorInterpolationKTable[] = {
   { eCSSKeyword_auto, NS_STYLE_COLOR_INTERPOLATION_AUTO },
   { eCSSKeyword_srgb, NS_STYLE_COLOR_INTERPOLATION_SRGB },
   { eCSSKeyword_linearrgb, NS_STYLE_COLOR_INTERPOLATION_LINEARRGB },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kColumnFillKTable[] = {
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -702,16 +702,17 @@ public:
   static const KTableEntry kImageRenderingKTable[];
   static const KTableEntry kShapeRenderingKTable[];
   static const KTableEntry kStrokeLinecapKTable[];
   static const KTableEntry kStrokeLinejoinKTable[];
   static const KTableEntry kStrokeContextValueKTable[];
   static const KTableEntry kVectorEffectKTable[];
   static const KTableEntry kTextAnchorKTable[];
   static const KTableEntry kTextRenderingKTable[];
+  static const KTableEntry kColorAdjustKTable[];
   static const KTableEntry kColorInterpolationKTable[];
   static const KTableEntry kColumnFillKTable[];
   static const KTableEntry kBoxPropSourceKTable[];
   static const KTableEntry kBoxShadowTypeKTable[];
   static const KTableEntry kBoxSizingKTable[];
   static const KTableEntry kCaptionSideKTable[];
   // Not const because we modify its entries when the pref
   // "layout.css.float-logical-values.enabled" changes:
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -975,16 +975,26 @@ already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetColor()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   SetToRGBAColor(val, StyleColor()->mColor);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
+nsComputedDOMStyle::DoGetColorAdjust()
+{
+  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
+  val->SetIdent(
+    nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mColorAdjust,
+                                   nsCSSProps::kColorAdjustKTable));
+  return val.forget();
+}
+
+already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetOpacity()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetNumber(StyleDisplay()->mOpacity);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -423,16 +423,17 @@ private:
   already_AddRefed<CSSValue> DoGetWordBreak();
   already_AddRefed<CSSValue> DoGetWordWrap();
   already_AddRefed<CSSValue> DoGetHyphens();
   already_AddRefed<CSSValue> DoGetTabSize();
   already_AddRefed<CSSValue> DoGetTextSizeAdjust();
   already_AddRefed<CSSValue> DoGetWebkitTextFillColor();
 
   /* Visibility properties */
+  already_AddRefed<CSSValue> DoGetColorAdjust();
   already_AddRefed<CSSValue> DoGetOpacity();
   already_AddRefed<CSSValue> DoGetPointerEvents();
   already_AddRefed<CSSValue> DoGetVisibility();
   already_AddRefed<CSSValue> DoGetWritingMode();
 
   /* Direction properties */
   already_AddRefed<CSSValue> DoGetDirection();
   already_AddRefed<CSSValue> DoGetUnicodeBidi();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -97,16 +97,17 @@ COMPUTED_STYLE_PROP(border_top_width,   
 COMPUTED_STYLE_PROP(bottom,                        Bottom)
 COMPUTED_STYLE_PROP(box_decoration_break,          BoxDecorationBreak)
 COMPUTED_STYLE_PROP(box_shadow,                    BoxShadow)
 COMPUTED_STYLE_PROP(box_sizing,                    BoxSizing)
 COMPUTED_STYLE_PROP(caption_side,                  CaptionSide)
 COMPUTED_STYLE_PROP(clear,                         Clear)
 COMPUTED_STYLE_PROP(clip,                          Clip)
 COMPUTED_STYLE_PROP(color,                         Color)
+COMPUTED_STYLE_PROP(color_adjust,                  ColorAdjust)
 COMPUTED_STYLE_PROP(contain,                       Contain)
 COMPUTED_STYLE_PROP(content,                       Content)
 COMPUTED_STYLE_PROP(counter_increment,             CounterIncrement)
 COMPUTED_STYLE_PROP(counter_reset,                 CounterReset)
 COMPUTED_STYLE_PROP(cursor,                        Cursor)
 COMPUTED_STYLE_PROP(direction,                     Direction)
 COMPUTED_STYLE_PROP(display,                       Display)
 COMPUTED_STYLE_PROP(empty_cells,                   EmptyCells)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6456,16 +6456,22 @@ nsRuleNode::ComputeVisibilityData(void* 
         break;
       default:
         NS_NOTREACHED("Invalid image-orientation enumerated value");
     }
   } else {
     MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit");
   }
 
+  SetDiscrete(*aRuleData->ValueForColorAdjust(), visibility->mColorAdjust,
+              conditions,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentVisibility->mColorAdjust,
+              NS_STYLE_COLOR_ADJUST_ECONOMY, 0, 0, 0, 0);
+
   COMPUTE_END_INHERITED(Visibility, visibility)
 }
 
 const void*
 nsRuleNode::ComputeColorData(void* aStartStruct,
                              const nsRuleData* aRuleData,
                              nsStyleContext* aContext,
                              nsRuleNode* aHighestNode,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -1116,16 +1116,20 @@ enum class FillMode : uint32_t;
 #define NS_STYLE_TEXT_EMPHASIS_STYLE_STRING         255
 
 // text-rendering
 #define NS_STYLE_TEXT_RENDERING_AUTO               0
 #define NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED      1
 #define NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY 2
 #define NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION 3
 
+// adjust-color
+#define NS_STYLE_COLOR_ADJUST_ECONOMY               0
+#define NS_STYLE_COLOR_ADJUST_EXACT                 1
+
 // color-interpolation and color-interpolation-filters
 #define NS_STYLE_COLOR_INTERPOLATION_AUTO           0
 #define NS_STYLE_COLOR_INTERPOLATION_SRGB           1
 #define NS_STYLE_COLOR_INTERPOLATION_LINEARRGB      2
 
 // vector-effect
 #define NS_STYLE_VECTOR_EFFECT_NONE                 0
 #define NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE   1
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3167,27 +3167,29 @@ nsStyleVisibility::nsStyleVisibility(nsP
     mDirection = NS_STYLE_DIRECTION_RTL;
   else
     mDirection = NS_STYLE_DIRECTION_LTR;
 
   mVisible = NS_STYLE_VISIBILITY_VISIBLE;
   mPointerEvents = NS_STYLE_POINTER_EVENTS_AUTO;
   mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
   mTextOrientation = NS_STYLE_TEXT_ORIENTATION_MIXED;
+  mColorAdjust = NS_STYLE_COLOR_ADJUST_ECONOMY;
 }
 
 nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource)
 {
   MOZ_COUNT_CTOR(nsStyleVisibility);
   mImageOrientation = aSource.mImageOrientation;
   mDirection = aSource.mDirection;
   mVisible = aSource.mVisible;
   mPointerEvents = aSource.mPointerEvents;
   mWritingMode = aSource.mWritingMode;
   mTextOrientation = aSource.mTextOrientation;
+  mColorAdjust = aSource.mColorAdjust;
 } 
 
 nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (mDirection != aOther.mDirection || mWritingMode != aOther.mWritingMode) {
     // It's important that a change in mWritingMode results in frame
@@ -3212,16 +3214,20 @@ nsChangeHint nsStyleVisibility::CalcDiff
     }
     if (mPointerEvents != aOther.mPointerEvents) {
       // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value
       // of pointer-events. See nsSVGPathGeometryFrame::ReflowSVG's use of
       // GetHitTestFlags. (Only a reflow, no visual change.)
       NS_UpdateHint(hint, nsChangeHint_NeedReflow);
       NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085
     }
+    if (mColorAdjust != aOther.mColorAdjust) {
+      // color-adjust only affects media where dynamic changes can't happen.
+      NS_UpdateHint(hint, nsChangeHint_NeutralChange);
+    }
   }
   return hint;
 }
 
 nsStyleContentData::~nsStyleContentData()
 {
   MOZ_ASSERT(!mImageTracked,
              "nsStyleContentData being destroyed while still tracking image!");
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2104,32 +2104,34 @@ struct nsStyleVisibility
   void Destroy(nsPresContext* aContext) {
     this->~nsStyleVisibility();
     aContext->PresShell()->
       FreeByObjectID(mozilla::eArenaObjectID_nsStyleVisibility, this);
   }
 
   nsChangeHint CalcDifference(const nsStyleVisibility& aOther) const;
   static nsChangeHint MaxDifference() {
-    return NS_STYLE_HINT_FRAMECHANGE;
+    return NS_STYLE_HINT_FRAMECHANGE |
+           nsChangeHint_NeutralChange;
   }
   static nsChangeHint DifferenceAlwaysHandledForDescendants() {
     // CalcDifference never returns the reflow hints that are sometimes
     // handled for descendants as hints not handled for descendants.
     return nsChangeHint_NeedReflow |
            nsChangeHint_ReflowChangesSizeOrPosition |
            nsChangeHint_ClearAncestorIntrinsics;
   }
 
   nsStyleImageOrientation mImageOrientation;  // [inherited]
   uint8_t mDirection;                  // [inherited] see nsStyleConsts.h NS_STYLE_DIRECTION_*
   uint8_t mVisible;                    // [inherited]
   uint8_t mPointerEvents;              // [inherited] see nsStyleConsts.h
   uint8_t mWritingMode;                // [inherited] see nsStyleConsts.h
   uint8_t mTextOrientation;            // [inherited] see nsStyleConsts.h
+  uint8_t mColorAdjust;                // [inherited] see nsStyleConsts.h
 
   bool IsVisible() const {
     return (mVisible == NS_STYLE_VISIBILITY_VISIBLE);
   }
 
   bool IsVisibleOrCollapsed() const {
     return ((mVisible == NS_STYLE_VISIBILITY_VISIBLE) ||
             (mVisible == NS_STYLE_VISIBILITY_COLLAPSE));
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1363,16 +1363,24 @@ var gCSSProperties = {
   },
   "-moz-box-sizing": {
     domProp: "MozBoxSizing",
     inherited: false,
     type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
     alias_for: "box-sizing",
     subproperties: [ "box-sizing" ],
   },
+  "color-adjust": {
+    domProp: "colorAdjust",
+    inherited: true,
+    type: CSS_TYPE_LONGHAND,
+    initial_values: [ "economy" ],
+    other_values: [ "exact" ],
+    invalid_values: []
+  },
   "-moz-columns": {
     domProp: "MozColumns",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "-moz-column-count", "-moz-column-width" ],
     initial_values: [ "auto", "auto auto" ],
     other_values: [ "3", "20px", "2 10px", "10px 2", "2 auto", "auto 2", "auto 50px", "50px auto" ],
     invalid_values: [ "5%", "-1px", "-1", "3 5", "10px 4px", "10 2px 5in", "30px -1",
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -2088,21 +2088,21 @@ nsBoxFrame::GetEventPoint(WidgetGUIEvent
 bool
 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) {
   NS_ENSURE_TRUE(aEvent, false);
 
   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
   if (touchEvent) {
     // return false if there is more than one touch on the page, or if
     // we can't find a touch point
-    if (touchEvent->touches.Length() != 1) {
+    if (touchEvent->mTouches.Length() != 1) {
       return false;
     }
 
-    dom::Touch* touch = touchEvent->touches.SafeElementAt(0);
+    dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
     if (!touch) {
       return false;
     }
     aPoint = touch->mRefPoint;
   } else {
     aPoint = aEvent->refPoint;
   }
   return true;
--- a/mfbt/ChaosMode.h
+++ b/mfbt/ChaosMode.h
@@ -22,16 +22,18 @@ enum ChaosFeature {
   // Altering network request scheduling.
   NetworkScheduling = 0x2,
   // Altering timer scheduling.
   TimerScheduling = 0x4,
   // Read and write less-than-requested amounts.
   IOAmounts = 0x8,
   // Iterate over hash tables in random order.
   HashTableIteration = 0x10,
+  // Randomly refuse to use cached version of image (when allowed by spec).
+  ImageCache = 0x20,
   Any = 0xffffffff,
 };
 
 namespace detail {
 extern MFBT_DATA Atomic<uint32_t> gChaosModeCounter;
 extern MFBT_DATA ChaosFeature gChaosFeatures;
 } // namespace detail
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -562,16 +562,17 @@ pref("apz.axis_lock.mode", 0);
 pref("apz.axis_lock.lock_angle", "0.5235987");        // PI / 6 (30 degrees)
 pref("apz.axis_lock.breakout_threshold", "0.03125");  // 1/32 inches
 pref("apz.axis_lock.breakout_angle", "0.3926991");    // PI / 8 (22.5 degrees)
 pref("apz.axis_lock.direct_pan_angle", "1.047197");   // PI / 3 (60 degrees)
 pref("apz.content_response_timeout", 300);
 pref("apz.drag.enabled", false);
 pref("apz.danger_zone_x", 50);
 pref("apz.danger_zone_y", 100);
+pref("apz.disable_for_scroll_linked_effects", false);
 pref("apz.displayport_expiry_ms", 15000);
 pref("apz.enlarge_displayport_when_clipped", false);
 pref("apz.fling_accel_base_mult", "1.0");
 pref("apz.fling_accel_interval_ms", 500);
 pref("apz.fling_accel_supplemental_mult", "1.0");
 pref("apz.fling_curve_function_x1", "0.0");
 pref("apz.fling_curve_function_y1", "0.0");
 pref("apz.fling_curve_function_x2", "1.0");
@@ -5262,8 +5263,11 @@ pref("webextensions.tests", false);
 // Allow customization of the fallback directory for file uploads
 pref("dom.input.fallbackUploadDir", "");
 
 // Turn rewriting of youtube embeds on/off
 pref("plugins.rewrite_youtube_embeds", true);
 
 // Disable browser frames by default
 pref("dom.mozBrowserFramesEnabled", false);
+
+// Is support for 'color-adjust' CSS property enabled?
+pref("layout.css.color-adjust.enabled", true);
--- a/testing/firefox-ui/mach_commands.py
+++ b/testing/firefox-ui/mach_commands.py
@@ -24,21 +24,24 @@ def setup_argument_parser_functional():
 
 
 def setup_argument_parser_update():
     from firefox_ui_harness.arguments.update import UpdateArguments
     return UpdateArguments()
 
 
 def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs):
-    import argparse
-
     from mozlog.structured import commandline
     import firefox_ui_harness
 
+    if testtype == 'functional':
+        parser = setup_argument_parser_functional()
+    else:
+        parser = setup_argument_parser_update()
+
     test_types = {
         'functional': {
             'default_tests': [
                 os.path.join('puppeteer', 'manifest.ini'),
                 os.path.join('functional', 'manifest.ini'),
             ],
             'cli_module': firefox_ui_harness.cli_functional,
         },
@@ -52,24 +55,34 @@ def run_firefox_ui_test(testtype=None, t
 
     fxui_dir = os.path.join(topsrcdir, 'testing', 'firefox-ui')
 
     # Set the resources path which is used to serve test data via wptserve
     if not kwargs['server_root']:
         kwargs['server_root'] = os.path.join(fxui_dir, 'resources')
 
     # If no tests have been selected, set default ones
-    if not kwargs.get('tests'):
-        kwargs['tests'] = [os.path.join(fxui_dir, 'tests', test)
+    if kwargs.get('tests'):
+        tests = kwargs.get('tests')
+    else:
+        tests = [os.path.join(fxui_dir, 'tests', test)
                            for test in test_types[testtype]['default_tests']]
 
     kwargs['logger'] = commandline.setup_logging('Firefox UI - {} Tests'.format(testtype),
                                                  {"mach": sys.stdout})
 
-    failed = test_types[testtype]['cli_module'].cli(args=kwargs)
+    args = parser.parse_args(args=tests)
+
+    for k, v in kwargs.iteritems():
+        setattr(args, k, v)
+
+    parser.verify_usage(args)
+
+    failed = test_types[testtype]['cli_module'].cli(args=vars(args))
+
     if failed > 0:
         return 1
     else:
         return 0
 
 
 @CommandProvider
 class MachCommands(MachCommandBase):
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -572,17 +572,17 @@ function sendWheelAndPaint(aTarget, aOff
     // just wait for the paint to complete.
     aWindow.waitForAllPaintsFlushed(function() {
       sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWindow);
     });
     return;
   }
 
   var onwheel = function() {
-    window.removeEventListener("wheel", onwheel);
+    SpecialPowers.removeSystemEventListener(window, "wheel", onwheel);
 
     // Wait one frame since the wheel event has not caused a refresh observer
     // to be added yet.
     setTimeout(function() {
       utils.advanceTimeAndRefresh(1000);
 
       if (!aCallback) {
         utils.advanceTimeAndRefresh(0);
@@ -599,17 +599,19 @@ function sendWheelAndPaint(aTarget, aOff
 
       SpecialPowers.Services.obs.addObserver(waitForPaints, "apz-repaints-flushed", false);
       if (!utils.flushApzRepaints(aWindow)) {
         waitForPaints();
       }
     }, 0);
   };
 
-  aWindow.addEventListener("wheel", onwheel);
+  // Listen for the system wheel event, because it happens after all of
+  // the other wheel events, including legacy events.
+  SpecialPowers.addSystemEventListener(aWindow, "wheel", onwheel);
   synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
 }
 
 function synthesizeNativeMouseMove(aTarget, aOffsetX, aOffsetY, aCallback, aWindow = window) {
   var utils = _getDOMWindowUtils(aWindow);
   if (!utils)
     return;
 
--- a/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
+++ b/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
@@ -1,8 +1,5 @@
 [open-url-bogus.htm]
   type: testharness
   [XMLHttpRequest: open() - bogus URLs (http:)]
     expected: FAIL
 
-  [XMLHttpRequest: open() - bogus URLs (http://a a/)]
-    expected: FAIL
-
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -189,18 +189,18 @@ MultiTouchInput::MultiTouchInput(const W
     case eTouchCancel:
       mType = MULTITOUCH_CANCEL;
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Did not assign a type to a MultiTouchInput");
       break;
   }
 
-  for (size_t i = 0; i < aTouchEvent.touches.Length(); i++) {
-    const Touch* domTouch = aTouchEvent.touches[i];
+  for (size_t i = 0; i < aTouchEvent.mTouches.Length(); i++) {
+    const Touch* domTouch = aTouchEvent.mTouches[i];
 
     // Extract data from weird interfaces.
     int32_t identifier = domTouch->Identifier();
     int32_t radiusX = domTouch->RadiusX();
     int32_t radiusY = domTouch->RadiusY();
     float rotationAngle = domTouch->RotationAngle();
     float force = domTouch->Force();
 
@@ -246,17 +246,17 @@ MultiTouchInput::ToWidgetTouchEvent(nsIW
   }
 
   event.mModifiers = this->modifiers;
   event.mTime = this->mTime;
   event.mTimeStamp = this->mTimeStamp;
   event.mFlags.mHandledByAPZ = mHandledByAPZ;
 
   for (size_t i = 0; i < mTouches.Length(); i++) {
-    *event.touches.AppendElement() = mTouches[i].ToNewDOMTouch();
+    *event.mTouches.AppendElement() = mTouches[i].ToNewDOMTouch();
   }
 
   return event;
 }
 
 WidgetMouseEvent
 MultiTouchInput::ToWidgetMouseEvent(nsIWidget* aWidget) const
 {
--- a/widget/TouchEvents.h
+++ b/widget/TouchEvents.h
@@ -169,17 +169,17 @@ public:
   WidgetTouchEvent(const WidgetTouchEvent& aOther)
     : WidgetInputEvent(aOther.IsTrusted(), aOther.mMessage, aOther.widget,
                        eTouchEventClass)
   {
     MOZ_COUNT_CTOR(WidgetTouchEvent);
     mModifiers = aOther.mModifiers;
     mTime = aOther.mTime;
     mTimeStamp = aOther.mTimeStamp;
-    touches.AppendElements(aOther.touches);
+    mTouches.AppendElements(aOther.mTouches);
     mFlags.mCancelable = mMessage != eTouchCancel;
     mFlags.mHandledByAPZ = aOther.mFlags.mHandledByAPZ;
   }
 
   WidgetTouchEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget)
     : WidgetInputEvent(aIsTrusted, aMessage, aWidget, eTouchEventClass)
   {
     MOZ_COUNT_CTOR(WidgetTouchEvent);
@@ -197,23 +197,23 @@ public:
                "Duplicate() must be overridden by sub class");
     // Not copying widget, it is a weak reference.
     WidgetTouchEvent* result = new WidgetTouchEvent(false, mMessage, nullptr);
     result->AssignTouchEventData(*this, true);
     result->mFlags = mFlags;
     return result;
   }
 
-  TouchArray touches;
+  TouchArray mTouches;
 
   void AssignTouchEventData(const WidgetTouchEvent& aEvent, bool aCopyTargets)
   {
     AssignInputEventData(aEvent, aCopyTargets);
 
     // Assign*EventData() assume that they're called only new instance.
-    MOZ_ASSERT(touches.IsEmpty());
-    touches.AppendElements(aEvent.touches);
+    MOZ_ASSERT(mTouches.IsEmpty());
+    mTouches.AppendElements(aEvent.mTouches);
   }
 };
 
 } // namespace mozilla
 
 #endif // mozilla_TouchEvents_h__
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -606,17 +606,17 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWid
         // An event we don't know about
         return event;
     }
 
     event.mModifiers = DOMModifiers();
     event.mTime = Time();
 
     const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset();
-    event.touches.SetCapacity(endIndex - startIndex);
+    event.mTouches.SetCapacity(endIndex - startIndex);
     for (int i = startIndex; i < endIndex; i++) {
         // In this code branch, we are dispatching this event directly
         // into Gecko (as opposed to going through the AsyncPanZoomController),
         // and the Points() array has points in CSS pixels, which we need
         // to convert.
         CSSToLayoutDeviceScale scale = widget->GetDefaultScale();
         LayoutDeviceIntPoint pt(
             (Points()[i].x * scale.scale) - offset.x,
@@ -624,17 +624,17 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWid
         LayoutDeviceIntPoint radius(
             PointRadii()[i].x * scale.scale,
             PointRadii()[i].y * scale.scale);
         RefPtr<Touch> t = new Touch(PointIndicies()[i],
                                     pt,
                                     radius,
                                     Orientations()[i],
                                     Pressures()[i]);
-        event.touches.AppendElement(t);
+        event.mTouches.AppendElement(t);
     }
 
     return event;
 }
 
 MultiTouchInput
 AndroidGeckoEvent::MakeMultiTouchInput(nsIWidget* widget)
 {
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2002,24 +2002,24 @@ nsWindow::OnLongTapEvent(AndroidGeckoEve
     event.ignoreRootScrollFrame = true;
 
     DispatchEvent(&event);
 }
 
 void
 nsWindow::DispatchHitTest(const WidgetTouchEvent& aEvent)
 {
-    if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() == 1) {
+    if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() == 1) {
         // Since touch events don't get retargeted by PositionedEventTargeting.cpp
         // code on Fennec, we dispatch a dummy mouse event that *does* get
         // retargeted. The Fennec browser.js code can use this to activate the
         // highlight element in case the this touchstart is the start of a tap.
         WidgetMouseEvent hittest(true, eMouseHitTest, this,
                                  WidgetMouseEvent::eReal);
-        hittest.refPoint = aEvent.touches[0]->mRefPoint;
+        hittest.refPoint = aEvent.mTouches[0]->mRefPoint;
         hittest.ignoreRootScrollFrame = true;
         hittest.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
         nsEventStatus status;
         DispatchEvent(&hittest, status);
 
         if (mAPZEventState && hittest.hitCluster) {
             mAPZEventState->ProcessClusterHit();
         }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -3441,21 +3441,21 @@ nsWindow::OnTouchEvent(GdkEventTouch* aE
     WidgetTouchEvent event(true, msg, this);
     KeymapWrapper::InitInputEvent(event, aEvent->state);
     event.mTime = aEvent->time;
 
     if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) {
         mTouches.Put(aEvent->sequence, touch.forget());
         // add all touch points to event object
         for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) {
-            event.touches.AppendElement(new dom::Touch(*iter.UserData()));
+            event.mTouches.AppendElement(new dom::Touch(*iter.UserData()));
         }
     } else if (aEvent->type == GDK_TOUCH_END ||
                aEvent->type == GDK_TOUCH_CANCEL) {
-        *event.touches.AppendElement() = touch.forget();
+        *event.mTouches.AppendElement() = touch.forget();
     }
 
     DispatchInputEvent(&event);
     return TRUE;
 }
 #endif
 
 static void
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -298,18 +298,18 @@ template<>
 struct ParamTraits<mozilla::WidgetTouchEvent>
 {
   typedef mozilla::WidgetTouchEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, static_cast<const mozilla::WidgetInputEvent&>(aParam));
     // Sigh, Touch bites us again!  We want to be able to do
-    //   WriteParam(aMsg, aParam.touches);
-    const paramType::TouchArray& touches = aParam.touches;
+    //   WriteParam(aMsg, aParam.mTouches);
+    const paramType::TouchArray& touches = aParam.mTouches;
     WriteParam(aMsg, touches.Length());
     for (uint32_t i = 0; i < touches.Length(); ++i) {
       mozilla::dom::Touch* touch = touches[i];
       WriteParam(aMsg, touch->mIdentifier);
       WriteParam(aMsg, touch->mRefPoint);
       WriteParam(aMsg, touch->mRadius);
       WriteParam(aMsg, touch->mRotationAngle);
       WriteParam(aMsg, touch->mForce);
@@ -332,17 +332,17 @@ struct ParamTraits<mozilla::WidgetTouchE
         float force;
         if (!ReadParam(aMsg, aIter, &identifier) ||
             !ReadParam(aMsg, aIter, &refPoint) ||
             !ReadParam(aMsg, aIter, &radius) ||
             !ReadParam(aMsg, aIter, &rotationAngle) ||
             !ReadParam(aMsg, aIter, &force)) {
           return false;
         }
-        aResult->touches.AppendElement(
+        aResult->mTouches.AppendElement(
           new mozilla::dom::Touch(
             identifier, refPoint, radius, rotationAngle, force));
     }
     return true;
   }
 };
 
 template<>
--- a/widget/uikit/nsWindow.mm
+++ b/widget/uikit/nsWindow.mm
@@ -165,30 +165,30 @@ private:
 }
 
 - (void)sendTouchEvent:(EventMessage) aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow
 {
     WidgetTouchEvent event(true, aType, aWindow);
     //XXX: I think nativeEvent.timestamp * 1000 is probably usable here but
     // I don't care that much right now.
     event.mTime = PR_IntervalNow();
-    event.touches.SetCapacity(aTouches.count);
+    event.mTouches.SetCapacity(aTouches.count);
     for (UITouch* touch in aTouches) {
         LayoutDeviceIntPoint loc = UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]);
         LayoutDeviceIntPoint radius = UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]);
         void* value;
         if (!CFDictionaryGetValueIfPresent(mTouches, touch, (const void**)&value)) {
             // This shouldn't happen.
             NS_ASSERTION(false, "Got a touch that we didn't know about");
             continue;
         }
         int id = reinterpret_cast<int>(value);
         RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f);
         event.refPoint = loc;
-        event.touches.AppendElement(t);
+        event.mTouches.AppendElement(t);
     }
     aWindow->DispatchInputEvent(&event);
 }
 
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
     ALOG("[ChildView[%p] touchesBegan", self);
     if (!mGeckoChild)