Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 08 Sep 2016 12:51:52 +0200
changeset 313199 d00937cdf44d7cb55fc00b86f80c05b163b97115
parent 313198 e781ae7a5f8b34e41c24110a467543c9b06b2f11 (current diff)
parent 313183 938ce16be25f9c551c19ef8938e8717ed3d41ff5 (diff)
child 313200 316b7259747ba226d268dcc9d146986d11d4cccb
push id30674
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 22:03:48 +0000
treeherdermozilla-central@5d854c8d0765 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
browser/themes/linux/downloads/download-summary.png
browser/themes/osx/downloads/download-summary.png
browser/themes/osx/downloads/download-summary@2x.png
browser/themes/windows/downloads/download-summary.png
devtools/server/tests/unit/test_memory_footprint.js
testing/web-platform/meta/XMLHttpRequest/send-after-setting-document-domain.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-authentication-basic-cors-not-enabled.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-non-same-origin.sub.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-redirect-bogus-sync.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-redirect-infinite-sync.htm.ini
testing/web-platform/meta/XMLHttpRequest/xmlhttprequest-network-error-sync.htm.ini
toolkit/components/passwordmgr/content/passwordManagerCommon.js
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -268,35 +268,35 @@ function loadSnippets()
   let lastUpdate = gSnippetsMap.get("snippets-last-update");
   let updateURL = document.documentElement.getAttribute("snippetsURL");
   let shouldUpdate = !lastUpdate ||
                      Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS;
   if (updateURL && shouldUpdate) {
     // Try to update from network.
     let xhr = new XMLHttpRequest();
     xhr.timeout = 5000;
-    try {
-      xhr.open("GET", updateURL, true);
-    } catch (ex) {
-      showSnippets();
-      loadCompleted();
-      return;
-    }
     // Even if fetching should fail we don't want to spam the server, thus
     // set the last update time regardless its results.  Will retry tomorrow.
     gSnippetsMap.set("snippets-last-update", Date.now());
     xhr.onloadend = function (event) {
       if (xhr.status == 200) {
         gSnippetsMap.set("snippets", xhr.responseText);
         gSnippetsMap.set("snippets-cached-version", currentVersion);
       }
       showSnippets();
       loadCompleted();
     };
-    xhr.send(null);
+    try {
+      xhr.open("GET", updateURL, true);
+      xhr.send(null);
+    } catch (ex) {
+      showSnippets();
+      loadCompleted();
+      return;
+    }
   } else {
     showSnippets();
     loadCompleted();
   }
 }
 
 /**
  * Shows locally cached remote snippets, or default ones when not available.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3709,21 +3709,24 @@ const BrowserSearch = {
 
   /**
    * Returns the search bar element if it is present in the toolbar, null otherwise.
    */
   get searchBar() {
     return document.getElementById("searchbar");
   },
 
+  get searchEnginesURL() {
+    return formatURL("browser.search.searchEnginesURL", true);
+  },
+
   loadAddEngines: function BrowserSearch_loadAddEngines() {
     var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
     var where = newWindowPref == 3 ? "tab" : "window";
-    var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
-    openUILinkIn(searchEnginesURL, where);
+    openUILinkIn(this.searchEnginesURL, where);
   },
 
   get _isExtendedTelemetryEnabled() {
     return Services.prefs.getBoolPref("toolkit.telemetry.enabled");
   },
 
   _getSearchEngineId: function (engine) {
     if (engine && engine.identifier) {
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -17,18 +17,18 @@ body {
   display: -moz-box;
   position: relative;
   -moz-box-flex: 1;
   -moz-user-focus: normal;
   -moz-box-orient: vertical;
 }
 
 input {
-  font: message-box !important;
-  font-size: 16px !important;
+  font: message-box;
+  font-size: 16px;
 }
 
 input[type=button] {
   cursor: pointer;
 }
 
 /* UNDO */
 #newtab-undo-container {
--- a/browser/base/content/newtab/newTab.xhtml
+++ b/browser/base/content/newtab/newTab.xhtml
@@ -79,13 +79,15 @@
     <div id="newtab-horizontal-margin">
       <div class="newtab-side-margin"/>
       <div id="newtab-grid">
       </div>
       <div class="newtab-side-margin"/>
     </div>
     <div id="newtab-margin-bottom"/>
   </div>
-  <input id="newtab-customize-button" type="button" dir="&locale.dir;" title="&newtab.customize.title;"/>
+  <input id="newtab-customize-button" type="button" dir="&locale.dir;"
+         value="&#x2699;"
+         title="&newtab.customize.title;"/>
 </body>
 <script type="text/javascript;version=1.8" src="chrome://browser/content/contentSearchUI.js"/>
 <script type="text/javascript;version=1.8" src="chrome://browser/content/newtab/newTab.js"/>
 </html>
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -598,18 +598,19 @@ nsContextMenu.prototype = {
     if (this.isRemote) {
       aNode = gContextMenuContentData.event.target;
       aRangeParent = gContextMenuContentData.event.rangeParent;
       aRangeOffset = gContextMenuContentData.event.rangeOffset;
       editFlags = gContextMenuContentData.editFlags;
     }
 
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-    if (aNode.namespaceURI == xulNS ||
-        aNode.nodeType == Node.DOCUMENT_NODE) {
+    if (aNode.nodeType == Node.DOCUMENT_NODE ||
+        // Not display on XUL element but relax for <label class="text-link">
+        (aNode.namespaceURI == xulNS && !isXULTextLinkLabel(aNode))) {
       this.shouldDisplay = false;
       return;
     }
 
     // Initialize contextual info.
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
@@ -793,17 +794,18 @@ nsContextMenu.prototype = {
     const XMLNS = "http://www.w3.org/XML/1998/namespace";
     var elem = this.target;
     while (elem) {
       if (elem.nodeType == Node.ELEMENT_NODE) {
         // Link?
         if (!this.onLink &&
             // Be consistent with what hrefAndLinkNodeForClickEvent
             // does in browser.js
-             ((elem instanceof HTMLAnchorElement && elem.href) ||
+             (isXULTextLinkLabel(elem) ||
+              (elem instanceof HTMLAnchorElement && elem.href) ||
               (elem instanceof HTMLAreaElement && elem.href) ||
               elem instanceof HTMLLinkElement ||
               elem.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) {
 
           // Target is a link or a descendant of a link.
           this.onLink = true;
 
           // Remember corresponding element.
@@ -893,16 +895,23 @@ nsContextMenu.prototype = {
           InlineSpellCheckerUI.init(editingSession.getEditorForWindow(targetWin));
           InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
         }
         var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
         this.showItem("spell-check-enabled", canSpell);
         this.showItem("spell-separator", canSpell);
       }
     }
+
+    function isXULTextLinkLabel(node) {
+      return node.namespaceURI == xulNS &&
+             node.tagName == "label" &&
+             node.classList.contains('text-link') &&
+             node.href;
+    }
   },
 
   // Returns the computed style attribute for the given element.
   getComputedStyle: function(aElem, aProp) {
     return aElem.ownerDocument
                 .defaultView
                 .getComputedStyle(aElem, "").getPropertyValue(aProp);
   },
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -72,24 +72,32 @@ var gSyncUtils = {
     if (Weave.Utils.ensureMPUnlocked())
       this.openChange("UpdatePassphrase");
   },
 
   resetPassword: function () {
     this._openLink(Weave.Service.pwResetURL);
   },
 
-  openToS: function () {
+  get tosURL() {
     let root = this.fxAccountsEnabled ? "fxa." : "";
-    this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
+    return  Weave.Svc.Prefs.get(root + "termsURL");
+  },
+
+  openToS: function () {
+    this._openLink(this.tosURL);
+  },
+
+  get privacyPolicyURL() {
+    let root = this.fxAccountsEnabled ? "fxa." : "";
+    return  Weave.Svc.Prefs.get(root + "privacyURL");
   },
 
   openPrivacyPolicy: function () {
-    let root = this.fxAccountsEnabled ? "fxa." : "";
-    this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
+    this._openLink(this.privacyPolicyURL);
   },
 
   /**
    * Prepare an invisible iframe with the passphrase backup document.
    * Used by both the print and saving methods.
    *
    * @param elid : ID of the form element containing the passphrase.
    * @param callback : Function called once the iframe has loaded.
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -82,16 +82,17 @@ support-files =
   ssl_error_reports.sjs
   popup_blocker.html
   print_postdata.sjs
   searchSuggestionEngine.sjs
   searchSuggestionEngine.xml
   searchSuggestionEngine2.xml
   subtst_contextmenu.html
   subtst_contextmenu_input.html
+  subtst_contextmenu_xul.xul
   test-mixedcontent-securityerrors.html
   test_bug435035.html
   test_bug462673.html
   test_bug628179.html
   test_bug839103.html
   test_bug959531.html
   test_process_flags_chrome.html
   title_test.svg
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -5,28 +5,58 @@ let LOGIN_FILL_ITEMS = [
   "---", null,
   "fill-login", null,
     [
       "fill-login-no-logins", false,
       "---", null,
       "fill-login-saved-passwords", true
     ], null,
 ];
-
 let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
 let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
 
-add_task(function* test_setup() {
-  const example_base = "http://example.com/browser/browser/base/content/test/general/";
-  const url = example_base + "subtst_contextmenu.html";
+const example_base = "http://example.com/browser/browser/base/content/test/general/";
+const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
+
+Services.scriptloader.loadSubScript(chrome_base + "contextmenu_common.js", this);
+
+// Below are test cases for XUL element
+add_task(function* test_xul_text_link_label() {
+  let  url = chrome_base + "subtst_contextmenu_xul.xul";
+
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
-  const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
-  const contextmenu_common = chrome_base + "contextmenu_common.js";
-  Services.scriptloader.loadSubScript(contextmenu_common, this);
+  yield test_contextmenu("#test-xul-text-link-label",
+    ["context-openlinkintab", true,
+     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+     // We need a blank entry here because the containers submenu is
+     // dynamically generated with no ids.
+     ...(hasContainers ? ["", null] : []),
+     "context-openlink",      true,
+     "context-openlinkprivate", true,
+     "---",                   null,
+     "context-bookmarklink",  true,
+     "context-savelink",      true,
+     ...(hasPocket ? ["context-savelinktopocket", true] : []),
+     "context-copylink",      true,
+     "context-searchselect",  true
+    ]
+  );
+
+  // Clean up so won't affect HTML element test cases
+  lastElementSelector = null;
+  gBrowser.removeCurrentTab();
+});
+
+// Below are test cases for HTML element
+
+add_task(function* test_setup_html() {
+  let url = example_base + "subtst_contextmenu.html";
+
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
     let doc = content.document;
     let videoIframe = doc.querySelector("#test-video-in-iframe");
     let video = videoIframe.contentDocument.querySelector("video");
     let awaitPause = ContentTaskUtils.waitForEvent(video, "pause");
     video.pause();
     yield awaitPause;
@@ -921,17 +951,17 @@ add_task(function* test_link_sendlinktod
       *onContextMenuShown() {
         yield openMenuItemSubmenu("context-sendlinktodevice");
       }
     });
 
   restoreRemoteClients(oldGetter);
 });
 
-add_task(function* test_cleanup() {
+add_task(function* test_cleanup_html() {
   gBrowser.removeCurrentTab();
 });
 
 /**
  * Selects the text of the element that matches the provided `selector`
  *
  * @param {String} selector
  *        A selector passed to querySelector to find
--- a/browser/base/content/test/general/browser_permissions.js
+++ b/browser/base/content/test/general/browser_permissions.js
@@ -179,24 +179,27 @@ add_task(function* testPermissionIcons()
   let {gIdentityHandler} = gBrowser.ownerGlobal;
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   yield promiseTabLoadEvent(tab, PERMISSIONS_PAGE);
 
   SitePermissions.set(gBrowser.currentURI, "camera", SitePermissions.ALLOW);
   SitePermissions.set(gBrowser.currentURI, "geo", SitePermissions.BLOCK);
   SitePermissions.set(gBrowser.currentURI, "microphone", SitePermissions.SESSION);
 
-  let geoIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='geo']");
+  let geoIcon = gIdentityHandler._identityBox
+    .querySelector(".blocked-permission-icon[data-permission-id='geo']");
   ok(geoIcon.hasAttribute("showing"), "blocked permission icon is shown");
 
-  let cameraIcon = gIdentityHandler._identityBox.querySelector("[data-permission-id='camera']");
+  let cameraIcon = gIdentityHandler._identityBox
+    .querySelector(".blocked-permission-icon[data-permission-id='camera']");
   ok(!cameraIcon.hasAttribute("showing"),
     "allowed permission icon is not shown");
 
-  let microphoneIcon  = gIdentityHandler._identityBox.querySelector("[data-permission-id='microphone']");
+  let microphoneIcon  = gIdentityHandler._identityBox
+    .querySelector(".blocked-permission-icon[data-permission-id='microphone']");
   ok(!microphoneIcon.hasAttribute("showing"),
     "allowed permission icon is not shown");
 
   SitePermissions.remove(gBrowser.currentURI, "geo");
 
   ok(!geoIcon.hasAttribute("showing"),
     "blocked permission icon is not shown after reset");
 });
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/subtst_contextmenu_xul.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+      xmlns:html="http://www.w3.org/1999/xhtml">
+  <label id="test-xul-text-link-label" class="text-link" value="XUL text-link label" href="https://www.mozilla.com"/>
+</page>
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -820,23 +820,26 @@ function openNewWindowWith(aURL, aDocume
              { charset: originCharset,
                postData: aPostData,
                allowThirdPartyFixup: aAllowThirdPartyFixup,
                referrerURI: aReferrer,
                referrerPolicy: aReferrerPolicy,
              });
 }
 
-// aCalledFromModal is optional
-function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
+function getHelpLinkURL(aHelpTopic) {
   var url = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                       .getService(Components.interfaces.nsIURLFormatter)
                       .formatURLPref("app.support.baseURL");
-  url += aHelpTopic;
+  return url + aHelpTopic;
+}
 
+// aCalledFromModal is optional
+function openHelpLink(aHelpTopic, aCalledFromModal, aWhere) {
+  var url = getHelpLinkURL(aHelpTopic);
   var where = aWhere;
   if (!aWhere)
     where = aCalledFromModal ? "window" : "tab";
 
   openUILinkIn(url, where);
 }
 
 function openPrefsHelp() {
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -14,11 +14,14 @@ support-files =
   test_firstParty.html
   test_firstParty_cookie.html
   test_firstParty_html_redirect.html
   test_firstParty_http_redirect.html
   test_firstParty_http_redirect.html^headers^
   test_firstParty_iframe_http_redirect.html
   test_firstParty_postMessage.html
   window.html
+  worker_blobify.js
+  worker_deblobify.js
 
 [browser_firstPartyIsolation.js]
 [browser_localStorageIsolation.js]
+[browser_blobURLIsolation.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_blobURLIsolation.js
@@ -0,0 +1,97 @@
+/**
+ * Bug 1264573 - A test case for blob url isolation.
+ */
+
+const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/" +
+                  "originattributes/test/browser/file_firstPartyBasic.html";
+const SCRIPT_WORKER_BLOBIFY = "worker_blobify.js";
+const SCRIPT_WORKER_DEBLOBIFY = "worker_deblobify.js";
+
+function page_blobify(browser, input) {
+  return ContentTask.spawn(browser, input, function(input) {
+    return { blobURL: content.URL.createObjectURL(new content.Blob([input])) };
+  });
+}
+
+function page_deblobify(browser, blobURL) {
+  return ContentTask.spawn(browser, blobURL, function* (blobURL) {
+    if ("error" in blobURL) {
+      return blobURL;
+    }
+    blobURL = blobURL.blobURL;
+
+    function blobURLtoBlob(blobURL) {
+      return new content.Promise(function (resolve) {
+        let xhr = new content.XMLHttpRequest();
+        xhr.open("GET", blobURL, true);
+        xhr.onload = function () {
+          resolve(xhr.response);
+        };
+        xhr.onerror = function () {
+          resolve("xhr error");
+        };
+        xhr.responseType = "blob";
+        xhr.send();
+      });
+    }
+
+    function blobToString(blob) {
+      return new content.Promise(function (resolve) {
+        let fileReader = new content.FileReader();
+        fileReader.onload = function () {
+          resolve(fileReader.result);
+        };
+        fileReader.readAsText(blob);
+      });
+    }
+
+    let blob = yield blobURLtoBlob(blobURL);
+    if (blob == "xhr error") {
+      return "xhr error";
+    }
+
+    return yield blobToString(blob);
+  });
+}
+
+function workerIO(browser, scriptFile, message) {
+  return ContentTask.spawn(browser, {scriptFile, message}, function* (args) {
+    let worker = new content.Worker(args.scriptFile);
+    let promise = new content.Promise(function(resolve) {
+      let listenFunction = function(event) {
+        worker.removeEventListener("message", listenFunction, false);
+        worker.terminate();
+        resolve(event.data);
+      };
+      worker.addEventListener("message", listenFunction, false);
+    });
+    worker.postMessage(args.message);
+    return yield promise;
+  });
+}
+
+let worker_blobify = (browser, input) => workerIO(browser, SCRIPT_WORKER_BLOBIFY, input);
+let worker_deblobify = (browser, blobURL) => workerIO(browser, SCRIPT_WORKER_DEBLOBIFY, blobURL);
+
+function doTest(blobify, deblobify) {
+  let blobURL = null;
+  return function* (browser) {
+    if (blobURL === null) {
+      let input = Math.random().toString();
+      blobURL = yield blobify(browser, input);
+      return input;
+    }
+    let result = yield deblobify(browser, blobURL);
+    blobURL = null;
+    return result;
+  }
+}
+
+let tests = [];
+for (let blobify of [page_blobify, worker_blobify]) {
+  for (let deblobify of [page_deblobify, worker_deblobify]) {
+    tests.push(doTest(blobify, deblobify));
+  }
+}
+
+IsolationTestTools.runTests(TEST_PAGE, tests);
--- a/browser/components/originattributes/test/browser/head.js
+++ b/browser/components/originattributes/test/browser/head.js
@@ -254,41 +254,45 @@ this.IsolationTestTools = {
   /**
    * Run isolation tests. The framework will run tests with standard combinations
    * of prefs and tab settings, and checks whether the isolation is working.
    *
    * @param aURL
    *    The URL of the page that will be tested or an object contains 'url',
    *    the tested page, 'firstFrameSetting' for the frame setting of the first
    *    tab, and 'secondFrameSetting' for the second tab.
-   * @param aGetResultFunc
-   *    A function which is responsible for returning the isolation result back
-   *    to the framework for further checking. This function will be provided
-   *    the browser object of the tab, that allows modifying or fetching results
-   *    from the page content.
+   * @param aGetResultFuncs
+   *    An array of functions or a single function which are responsible for
+   *    returning the isolation result back to the framework for further checking.
+   *    Each of these functions will be provided the browser object of the tab,
+   *    that allows modifying or fetchings results from the page content.
    * @param aCompareResultFunc
    *    An optional function which allows modifying the way how does framework
    *    check results. This function will be provided a boolean to indicate
    *    the isolation is no or off and two results. This function should return
    *    a boolean to tell that whether isolation is working. If this function
    *    is not given, the framework will take case checking by itself.
    */
-  runTests(aURL, aGetResultFunc, aCompareResultFunc) {
+  runTests(aURL, aGetResultFuncs, aCompareResultFunc) {
     let pageURL;
     let firstFrameSetting;
     let secondFrameSetting;
 
     if (typeof aURL === "string") {
       pageURL = aURL;
     } else if (typeof aURL === "object") {
       pageURL = aURL.url;
       firstFrameSetting = aURL.firstFrameSetting;
       secondFrameSetting = aURL.secondFrameSetting;
     }
 
+    if (!Array.isArray(aGetResultFuncs)) {
+      aGetResultFuncs = [aGetResultFuncs];
+    }
+
     let tabSettings = [
                         { firstPartyDomain: "http://example.com", userContextId: 1},
                         { firstPartyDomain: "http://example.org", userContextId: 2}
                       ];
 
     this._add_task(function* (aMode) {
       let tabSettingA = 0;
 
@@ -298,37 +302,39 @@ this.IsolationTestTools = {
                                                         pageURL,
                                                         tabSettings[tabSettingA],
                                                         firstFrameSetting);
         let tabInfoB = yield IsolationTestTools._addTab(aMode,
                                                         pageURL,
                                                         tabSettings[tabSettingB],
                                                         secondFrameSetting);
 
-        // Fetch results from tabs.
-        let resultA = yield aGetResultFunc(tabInfoA.browser);
-        let resultB = yield aGetResultFunc(tabInfoB.browser);
+        for (let getResultFunc of aGetResultFuncs) {
+          // Fetch results from tabs.
+          let resultA = yield getResultFunc(tabInfoA.browser);
+          let resultB = yield getResultFunc(tabInfoB.browser);
+
+          // Compare results.
+          let result = false;
+          let shouldIsolate = (aMode !== TEST_MODE_NO_ISOLATION) &&
+                              tabSettingA !== tabSettingB;
+          if (aCompareResultFunc) {
+            result = yield aCompareResultFunc(shouldIsolate, resultA, resultB);
+          } else {
+            result = shouldIsolate ? resultA !== resultB :
+                                     resultA === resultB;
+          }
+
+          let msg = `Testing ${TEST_MODE_NAMES[aMode]} for ` +
+            `isolation ${shouldIsolate ? "on" : "off"} with TabSettingA ` +
+            `${tabSettingA} and tabSettingB ${tabSettingB}` +
+            `, resultA = ${resultA}, resultB = ${resultB}`;
+
+          ok(result, msg);
+        }
 
         // Close Tabs.
         yield BrowserTestUtils.removeTab(tabInfoA.tab);
         yield BrowserTestUtils.removeTab(tabInfoB.tab);
-
-        // Compare results.
-        let result = false;
-        let shouldIsolate = (aMode !== TEST_MODE_NO_ISOLATION) &&
-                            tabSettingA !== tabSettingB;
-        if (aCompareResultFunc) {
-          result = yield aCompareResultFunc(shouldIsolate, resultA, resultB);
-        } else {
-          result = shouldIsolate ? resultA !== resultB :
-                                   resultA === resultB;
-        }
-
-        let msg = `Testing ${TEST_MODE_NAMES[aMode]} for ` +
-                  `isolation ${shouldIsolate ? "on" : "off"} with TabSettingA ` +
-                  `${tabSettingA} and tabSettingB ${tabSettingB}`;
-
-        ok(result, msg);
       }
-
     });
   }
 };
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/worker_blobify.js
@@ -0,0 +1,11 @@
+// Wait for a string to be posted to this worker.
+// Create a blob containing this string, and then
+// post back a blob URL pointing to the blob.
+self.addEventListener("message", function (e) {
+  try {
+    var blobURL = URL.createObjectURL(new Blob([e.data]));
+    postMessage({ blobURL });
+  } catch (e) {
+    postMessage({ error: e.message });
+  }
+}, false);
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/worker_deblobify.js
@@ -0,0 +1,31 @@
+// Wait for a blob URL to be posted to this worker.
+// Obtain the blob, and read the string contained in it.
+// Post back the string.
+
+var postStringInBlob = function (blobObject) {
+  var fileReader = new FileReaderSync();
+  var result = fileReader.readAsText(blobObject);
+  postMessage(result);
+};
+
+self.addEventListener("message", function (e) {
+  if ("error" in e.data) {
+    postMessage(e.data);
+    return;
+  }
+  var blobURL = e.data.blobURL,
+      xhr = new XMLHttpRequest();
+  try {
+    xhr.open("GET", blobURL, true);
+    xhr.onload = function () {
+      postStringInBlob(xhr.response);
+    };
+    xhr.onerror = function () {
+      postMessage({ error: "xhr error" });
+    };
+    xhr.responseType = "blob";
+    xhr.send();
+  } catch (e) {
+    postMessage({ error: e.message });
+  }
+}, false);
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -115,18 +115,17 @@
 #endif
   <stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
 
 <hbox id="header-advanced"
       class="header"
       hidden="true"
       data-category="paneAdvanced">
   <label class="header-name" flex="1">&paneAdvanced.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <tabbox id="advancedPrefs"
         handleCtrlTab="false"
         handleCtrlPageUpDown="false"
         flex="1"
         data-category="paneAdvanced"
         hidden="true">
--- a/browser/components/preferences/in-content/applications.xul
+++ b/browser/components/preferences/in-content/applications.xul
@@ -53,23 +53,22 @@
 </preferences>
 
 <keyset>
   <!-- These <key>s have oncommand attributes because of bug 371900 -->
   <key key="&focusSearch1.key;" modifiers="accel" id="focusSearch1" oncommand=";"/>
   <key key="&focusSearch2.key;" modifiers="accel" id="focusSearch2" oncommand=";"/>
 </keyset>
 
-<hbox id="header-application"
+<hbox id="header-applications"
       class="header"
       hidden="true"
       data-category="paneApplications">
   <label class="header-name" flex="1">&paneApplications.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <vbox id="applicationsContent"
       data-category="paneApplications"
       hidden="true"
       flex="1">
   <hbox>
     <textbox id="filter" flex="1"
--- a/browser/components/preferences/in-content/content.xul
+++ b/browser/components/preferences/in-content/content.xul
@@ -32,18 +32,17 @@
 <script type="application/javascript"
         src="chrome://browser/content/preferences/in-content/content.js"/>
 
 <hbox id="header-content"
       class="header"
       hidden="true"
       data-category="paneContent">
   <label class="header-name" flex="1">&paneContent.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <groupbox id="drmGroup" data-category="paneContent" hidden="true">
   <caption><label>&drmContent.label;</label></caption>
   <grid id="contentGrid2">
     <columns>
       <column flex="1"/>
       <column/>
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -110,18 +110,17 @@
                 type="bool"/>
 </preferences>
 
 <hbox id="header-general"
       class="header"
       hidden="true"
       data-category="paneGeneral">
   <label class="header-name" flex="1">&paneGeneral.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- Startup -->
 <groupbox id="startupGroup"
           data-category="paneGeneral"
           hidden="true">
   <caption><label>&startup.label;</label></caption>
 
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -73,19 +73,23 @@ function init_all() {
   init_dynamic_padding();
 
   var initFinished = new CustomEvent("Initialized", {
     'bubbles': true,
     'cancelable': true
   });
   document.dispatchEvent(initFinished);
 
-  let helpCmds = document.querySelectorAll(".help-button");
-  for (let helpCmd of helpCmds)
-    helpCmd.addEventListener("command", helpButtonCommand);
+  categories = categories.querySelectorAll("richlistitem.category");
+  for (let category of categories) {
+    let name = internalPrefCategoryNameToFriendlyName(category.value);
+    let helpSelector = `#header-${name} > .help-button`;
+    let helpButton = document.querySelector(helpSelector);
+    helpButton.setAttribute("href", getHelpLinkURL(category.getAttribute("helpTopic")));
+  }
 
   // Wait until initialization of all preferences are complete before
   // notifying observers that the UI is now ready.
   Services.obs.notifyObservers(window, "advanced-pane-loaded", null);
 }
 
 // Make the space above the categories list shrink on low window heights
 function init_dynamic_padding() {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -75,18 +75,17 @@
               type="bool"/>
 </preferences>
 
 <hbox id="header-privacy"
       class="header"
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&panePrivacy.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- Tracking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true">
   <vbox id="trackingprotectionbox" hidden="true">
     <hbox align="start">
       <vbox>
         <caption><label>&trackingProtectionHeader.label;
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -31,16 +31,21 @@ var gSearchPane = {
   },
 
   init: function ()
   {
     gEngineView = new EngineView(new EngineStore());
     document.getElementById("engineList").view = gEngineView;
     this.buildDefaultEngineDropDown();
 
+    let addEnginesLink = document.getElementById("addEngines");
+    let searchEnginesURL = Services.wm.getMostRecentWindow('navigator:browser')
+                                      .BrowserSearch.searchEnginesURL;
+    addEnginesLink.setAttribute("href", searchEnginesURL);
+
     window.addEventListener("click", this, false);
     window.addEventListener("command", this, false);
     window.addEventListener("dragstart", this, false);
     window.addEventListener("keypress", this, false);
     window.addEventListener("select", this, false);
     window.addEventListener("blur", this, true);
 
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
@@ -118,20 +123,16 @@ var gSearchPane = {
           if (engineList.inputField.hidden && engineList.view) {
             let selection = engineList.view.selection;
             if (selection.count > 0) {
               selection.toggleSelect(selection.currentIndex);
             }
             engineList.blur();
           }
         }
-        if (aEvent.target.id == "addEngines" && aEvent.button == 0) {
-          Services.wm.getMostRecentWindow('navigator:browser')
-                     .BrowserSearch.loadAddEngines();
-        }
         break;
       case "command":
         switch (aEvent.target.id) {
           case "":
             if (aEvent.target.parentNode &&
                 aEvent.target.parentNode.parentNode &&
                 aEvent.target.parentNode.parentNode.id == "defaultEngine") {
               gSearchPane.setDefaultEngine();
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -23,18 +23,17 @@
 
     <stringbundle id="engineManagerBundle" src="chrome://browser/locale/engineManager.properties"/>
 
     <hbox id="header-search"
           class="header"
           hidden="true"
           data-category="paneSearch">
       <label class="header-name" flex="1">&paneSearch.title;</label>
-      <button class="help-button"
-              aria-label="&helpButton.label;"/>
+      <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
     </hbox>
 
     <!-- Default Search Engine -->
     <groupbox id="defaultEngineGroup" align="start" data-category="paneSearch">
       <caption label="&defaultSearchEngine.label;"/>
       <label>&chooseYourDefaultSearchEngine.label;</label>
       <menulist id="defaultEngine">
         <menupopup/>
--- a/browser/components/preferences/in-content/security.xul
+++ b/browser/components/preferences/in-content/security.xul
@@ -48,18 +48,17 @@
 
 </preferences>
 
 <hbox id="header-security"
       class="header"
       hidden="true"
       data-category="paneSecurity">
   <label class="header-name" flex="1">&paneSecurity.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <!-- addons, forgery (phishing) UI -->
 <groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
   <caption><label>&general.label;</label></caption>
 
   <hbox id="addonInstallBox">
     <checkbox id="warnAddonInstall"
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -137,18 +137,29 @@ var gSyncPane = {
     });
 
     XPCOMUtils.defineLazyGetter(this, '_accountsStringBundle', () => {
       return Services.strings.createBundle("chrome://browser/locale/accounts.properties");
     });
 
     let url = Services.prefs.getCharPref("identity.mobilepromo.android") + "sync-preferences";
     document.getElementById("fxaMobilePromo-android").setAttribute("href", url);
+    document.getElementById("fxaMobilePromo-android-hasFxaAccount").setAttribute("href", url);
     url = Services.prefs.getCharPref("identity.mobilepromo.ios") + "sync-preferences";
     document.getElementById("fxaMobilePromo-ios").setAttribute("href", url);
+    document.getElementById("fxaMobilePromo-ios-hasFxaAccount").setAttribute("href", url);
+
+    document.getElementById("tosPP-small-ToS").setAttribute("href", gSyncUtils.tosURL);
+    document.getElementById("tosPP-normal-ToS").setAttribute("href", gSyncUtils.tosURL);
+    document.getElementById("tosPP-small-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
+    document.getElementById("tosPP-normal-PP").setAttribute("href", gSyncUtils.privacyPolicyURL);
+
+    fxAccounts.promiseAccountsManageURI(this._getEntryPoint()).then(url => {
+      document.getElementById("verifiedManage").setAttribute("href", url);
+    });
 
     this.updateWeavePrefs();
 
     this._initProfileImageUI();
   },
 
   _toggleComputerNameControls: function(editMode) {
     let textbox = document.getElementById("fxaSyncComputerName");
@@ -235,18 +246,16 @@ var gSyncPane = {
       this._toggleComputerNameControls(false);
       this._updateComputerNameValue(true);
       this._focusAfterComputerNameTextbox();
     });
     setEventListener("unlinkDevice", "click", function () {
       gSyncPane.startOver(true);
       return false;
     });
-    setEventListener("tosPP-normal-ToS", "click", gSyncPane.openToS);
-    setEventListener("tosPP-normal-PP", "click", gSyncPane.openPrivacyPolicy);
     setEventListener("loginErrorUpdatePass", "click", function () {
       gSyncPane.updatePass();
       return false;
     });
     setEventListener("loginErrorResetPass", "click", function () {
       gSyncPane.resetPass();
       return false;
     });
@@ -271,18 +280,16 @@ var gSyncPane = {
       /* no warning as account can't have previously synced */
       gSyncPane.unlinkFirefoxAccount(false);
     });
     setEventListener("rejectReSignIn", "command",
       gSyncPane.reSignIn);
     setEventListener("rejectUnlinkFxaAccount", "command", function () {
       gSyncPane.unlinkFirefoxAccount(true);
     });
-    setEventListener("tosPP-small-ToS", "click", gSyncPane.openToS);
-    setEventListener("tosPP-small-PP", "click", gSyncPane.openPrivacyPolicy);
     setEventListener("fxaSyncComputerName", "keypress", function (e) {
       if (e.keyCode == KeyEvent.DOM_VK_RETURN) {
         document.getElementById("fxaSaveChangeDeviceName").click();
       } else if (e.keyCode == KeyEvent.DOM_VK_ESCAPE) {
         document.getElementById("fxaCancelChangeDeviceName").click();
       }
     });
   },
@@ -518,26 +525,16 @@ var gSyncPane = {
     let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIDocShell)
                         .chromeEventHandler;
     // And tell it to load our URL.
     browser.loadURI(url);
   },
 
-  openPrivacyPolicy: function(aEvent) {
-    aEvent.stopPropagation();
-    gSyncUtils.openPrivacyPolicy();
-  },
-
-  openToS: function(aEvent) {
-    aEvent.stopPropagation();
-    gSyncUtils.openToS();
-  },
-
   signUp: function() {
     this._openAboutAccounts("signup");
   },
 
   signIn: function() {
     this._openAboutAccounts("signin");
   },
 
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -30,18 +30,17 @@
 <script type="application/javascript"
         src="chrome://browser/content/sync/utils.js"/>
 
 <hbox id="header-sync"
       class="header"
       hidden="true"
       data-category="paneSync">
   <label class="header-name" flex="1">&paneSync.title;</label>
-  <button class="help-button"
-          aria-label="&helpButton.label;"/>
+  <html:a class="help-button text-link" target="_blank" aria-label="&helpButton.label;"></html:a>
 </hbox>
 
 <deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
   <!-- These panels are for the "legacy" sync provider -->
   <vbox id="noAccount" align="center">
     <spacer flex="1"/>
     <description id="syncDesc">
       &weaveDesc.label;
@@ -229,17 +228,16 @@
                     tooltiptext="&profilePicture.tooltip;"/>
               </vbox>
               <vbox flex="1" pack="center">
                 <label id="fxaDisplayName" hidden="true"/>
                 <label id="fxaEmailAddress1"/>
                 <hbox class="fxaAccountBoxButtons" align="center">
                   <button id="fxaUnlinkButton" label="&disconnect.label;"/>
                   <label id="verifiedManage" class="text-link"
-                         onclick="gSyncPane.openManageFirefoxAccount(event);"
                          onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
                   -->&verifiedManage.label;</label>
                 </hbox>
               </vbox>
             </hbox>
 
             <!-- logged in to an unverified account -->
             <hbox id="fxaLoginUnverified" class="fxaAccountBox">
@@ -335,22 +333,20 @@
           <button id="fxaSaveChangeDeviceName"
                   label="&saveChangeSyncDeviceName.label;"
                   hidden="true"/>
         </hbox>
       </hbox>
     </groupbox>
     <label class="fxaMobilePromo">
         &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
-        --><label class="androidLink text-link"
-                  href="https://www.mozilla.org/firefox/android/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
+        --><label class="androidLink text-link" id="fxaMobilePromo-android-hasFxaAccount"><!--
         -->&mobilePromo3.androidLink;</label><!--
         -->&mobilePromo3.iOSBefore;<!--
-        --><label class="iOSLink text-link"
-                  href="https://www.mozilla.org/firefox/ios/?utm_source=firefox-browser&amp;utm_medium=firefox-browser&amp;utm_campaign=sync-preferences"><!--
+        --><label class="iOSLink text-link" id="fxaMobilePromo-ios-hasFxaAccount"><!--
         -->&mobilePromo3.iOSLink;</label><!--
         -->&mobilePromo3.end;
     </label>
     <vbox id="tosPP-small" align="start">
       <label id="tosPP-small-ToS" class="text-link">
         &prefs.tosLink.label;
       </label>
       <label id="tosPP-small-PP" class="text-link">
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -935,22 +935,16 @@ Experiments.Experiments.prototype = {
 
   /*
    * Helper function to make HTTP GET requests. Returns a promise that is resolved with
    * the responseText when the request is complete.
    */
   _httpGetRequest: function (url) {
     this._log.trace("httpGetRequest(" + url + ")");
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
-    try {
-      xhr.open("GET", url);
-    } catch (e) {
-      this._log.error("httpGetRequest() - Error opening request to " + url + ": " + e);
-      return Promise.reject(new Error("Experiments - Error opening XHR for " + url));
-    }
 
     this._networkRequest = xhr;
     let deferred = Promise.defer();
 
     let log = this._log;
     let errorhandler = (evt) => {
       log.error("httpGetRequest::onError() - Error making request to " + url + ": " + evt.type);
       deferred.reject(new Error("Experiments - XHR error for " + url + " - " + evt.type));
@@ -967,22 +961,29 @@ Experiments.Experiments.prototype = {
         this._networkRequest = null;
         return;
       }
 
       deferred.resolve(xhr.responseText);
       this._networkRequest = null;
     };
 
-    if (xhr.channel instanceof Ci.nsISupportsPriority) {
-      xhr.channel.priority = Ci.nsISupportsPriority.PRIORITY_LOWEST;
+    try {
+      xhr.open("GET", url);
+
+      if (xhr.channel instanceof Ci.nsISupportsPriority) {
+        xhr.channel.priority = Ci.nsISupportsPriority.PRIORITY_LOWEST;
+      }
+
+      xhr.timeout = MANIFEST_FETCH_TIMEOUT_MSEC;
+      xhr.send(null);
+    } catch (e) {
+      this._log.error("httpGetRequest() - Error opening request to " + url + ": " + e);
+      return Promise.reject(new Error("Experiments - Error opening XHR for " + url));
     }
-
-    xhr.timeout = MANIFEST_FETCH_TIMEOUT_MSEC;
-    xhr.send(null);
     return deferred.promise;
   },
 
   /*
    * Path of the cache file we use in the profile.
    */
   get _cacheFilePath() {
     return OS.Path.join(OS.Constants.Path.profileDir, FILE_CACHE);
--- a/browser/extensions/pocket/content/panels/css/saved.css
+++ b/browser/extensions/pocket/content/panels/css/saved.css
@@ -401,17 +401,17 @@
     float: left;
     line-height: normal;
     height: auto;
     min-height: 0;
     min-width: 5em;
     padding: 3px 2px 1px;
     text-transform: none;
 }
-.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::-moz-placeholder {
+.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::placeholder {
     color: #a9a9a9;
     letter-spacing: normal;
     text-transform: none;
 }
 .pkt_ext_containersaved .input_disabled {
     cursor: default;
     opacity: 0.5;
 }
deleted file mode 100644
index c5d47545d9df54c39822adeb8f566a531b2fb8e4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -51,17 +51,16 @@ browser.jar:
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
 * skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
-  skin/classic/browser/downloads/download-summary.png (downloads/download-summary.png)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
 * skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
   skin/classic/browser/places/autocomplete-star.png   (places/autocomplete-star.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2325,17 +2325,17 @@ html|span.ac-emphasize-text-url {
   }
 
   #editBookmarkPanel .expander-up > .button-box > .button-icon,
   #editBookmarkPanel .expander-down > .button-box > .button-icon {
     width: 9px;
   }
 }
 
-#editBMPanel_tagsField > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input::-moz-placeholder {
+#editBMPanel_tagsField > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input::placeholder {
   opacity: 1.0;
   color: #bbb;
 }
 
 .editBMPanel_rowLabel {
   text-align: end;
 }
 
deleted file mode 100644
index 9150bcb976668c0460ceb3098e43e09afd2b5aaa..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 801a9bb7122e65104e5e17fac0ea9d69d2bc2006..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -75,18 +75,16 @@ browser.jar:
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
   skin/classic/browser/downloads/download-notification-finish.png  (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-finish@2x.png  (downloads/download-notification-finish@2x.png)
   skin/classic/browser/downloads/download-notification-start.png  (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-notification-start@2x.png  (downloads/download-notification-start@2x.png)
-  skin/classic/browser/downloads/download-summary.png       (downloads/download-summary.png)
-  skin/classic/browser/downloads/download-summary@2x.png    (downloads/download-summary@2x.png)
 * skin/classic/browser/downloads/downloads.css              (downloads/downloads.css)
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css               (feeds/subscribe-ui.css)
   skin/classic/browser/feeds/feedIcon.png                   (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                 (feeds/feedIcon16.png)
 * skin/classic/browser/newtab/newTab.css                    (newtab/newTab.css)
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/downloads/download-summary.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#fff" d="M26.2,31.4H6.8c-1.8,0-3.4-1.5-3.4-3.3V3.8c0-1.8,1.6-3.3,3.4-3.3H23l5.6,6.4V28C28.7,29.9,28,31.4,26.2,31.4z" />
+  <path fill="#939393" d="M26.2,31.9H6.8c-2,0-3.8-1.7-3.8-3.8V3.8C3,1.7,4.8,0,6.8,0h16.4L29,6.7v21.4C29,30.2,28.2,31.9,26.2,31.9z M6.8,1C5.2,1,4,2.3,4,3.8V28c0,1.6,1.4,3,2.9,3h19.2c1.6,0,2-1.5,2-3V7.1L22.7,1C22.7,1,6.8,1,6.8,1z" />
+  <path fill="#4c4c4c" d="M22.5,18.2L17,23.6c-0.3,0.3-0.6,0.4-1.1,0.4c-0.3,0-0.8-0.1-1.1-0.4l-5.5-5.4c-0.5-0.5-0.4-1.1,0.4-1.1H13 v-5.9c0-0.5,0.4-1,1-1h3.9c0.5,0,1,0.4,1,1v5.9h3.1C22.8,17.2,23,17.6,22.5,18.2z" />
+  <polygon fill="#939393" points="27,9 21,9 21,1.9 22,1.9 22,8 27,8" />
+</svg>
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -145,27 +145,19 @@ toolbarseparator.downloadsDropmarkerSpli
 
 #downloadsSummary {
   padding: 0 12px;
   cursor: pointer;
   -moz-user-focus: normal;
 }
 
 #downloadsSummary > .downloadTypeIcon {
-  list-style-image: url("chrome://browser/skin/downloads/download-summary.png");
+  list-style-image: url("chrome://browser/skin/downloads/download-summary.svg");
 }
 
-%ifdef XP_MACOSX
-@media (min-resolution: 2dppx) {
-  #downloadsSummary > .downloadTypeIcon {
-    list-style-image: url("chrome://browser/skin/downloads/download-summary@2x.png");
-  }
-}
-%endif
-
 #downloadsSummaryDescription {
   color: -moz-nativehyperlinktext;
 }
 
 /*** List items and similar elements in the summary ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -97,16 +97,25 @@ treecol {
 
 @media (max-width: 800px) {
   .category-name {
     display: none;
   }
 }
 
 /* header */
+.header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.header[hidden=true] {
+  display: none;
+}
 
 #header-advanced {
   border-bottom: none;
   padding-bottom: 0;
 }
 
 /* General Pane */
 
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -50,16 +50,17 @@
   skin/classic/browser/customizableui/subView-arrow-back-inverted@2x.png  (../shared/customizableui/subView-arrow-back-inverted@2x.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl.png  (../shared/customizableui/subView-arrow-back-inverted-rtl.png)
   skin/classic/browser/customizableui/subView-arrow-back-inverted-rtl@2x.png  (../shared/customizableui/subView-arrow-back-inverted-rtl@2x.png)
   skin/classic/browser/customizableui/whimsy.png               (../shared/customizableui/whimsy.png)
   skin/classic/browser/customizableui/whimsy@2x.png            (../shared/customizableui/whimsy@2x.png)
   skin/classic/browser/downloads/contentAreaDownloadsView.css  (../shared/downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-blocked.svg          (../shared/downloads/download-blocked.svg)
   skin/classic/browser/downloads/menubutton-dropmarker.svg     (../shared/downloads/menubutton-dropmarker.svg)
+  skin/classic/browser/downloads/download-summary.svg          (../shared/downloads/download-summary.svg)
   skin/classic/browser/drm-icon.svg                            (../shared/drm-icon.svg)
   skin/classic/browser/filters.svg                             (../shared/filters.svg)
   skin/classic/browser/fullscreen/insecure.svg                 (../shared/fullscreen/insecure.svg)
   skin/classic/browser/fullscreen/secure.svg                   (../shared/fullscreen/secure.svg)
   skin/classic/browser/heartbeat-icon.svg                      (../shared/heartbeat-icon.svg)
   skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
   skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -80,16 +80,23 @@
   border: none;
 }
 
 .newtab-customize {
   height: 28px;
   width: 28px;
 }
 
+#newtab-customize-button {
+  font-size: 28px;
+  padding: 0;
+  /* only display the text label when CSS backgrounds are disabled (e.g. in high contrast mode) */
+  color: transparent;
+}
+
 #newtab-customize-button:-moz-any(:hover, :active, [active]) {
   background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 64, 32, 32);
   background-color: #FFFFFF;
   border: solid 1px #CCCCCC;
   border-radius: 2px;
 }
 
 /* CELLS */
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1352,18 +1352,18 @@ toolbar[brighttext] #close-button {
   /* let urlbar-back-button-clip-path clip the urlbar's right side for RTL */
   transform: scaleX(-1);
 }
 
 @conditionalForwardWithUrlbar@:-moz-locale-dir(rtl) {
   -moz-box-direction: reverse;
 }
 
-html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
-.searchbar-textbox:-moz-lwtheme > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input::-moz-placeholder {
+html|*.urlbar-input:-moz-lwtheme::placeholder,
+.searchbar-textbox:-moz-lwtheme > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input::placeholder {
   opacity: 1.0;
   color: #777;
 }
 
 #urlbar-container {
   -moz-box-align: center;
 }
 
deleted file mode 100644
index 0240a58dfda31fc5ca81440df65708f73abbf41b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -87,17 +87,16 @@ browser.jar:
 * skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                   (downloads/buttons.png)
   skin/classic/browser/downloads/buttons-XP.png                (downloads/buttons-XP.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png   (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png   (downloads/download-glow-menuPanel-XPVista7.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
-  skin/classic/browser/downloads/download-summary.png          (downloads/download-summary.png)
 * skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
   skin/classic/browser/feeds/feedIcon-XP.png                   (feeds/feedIcon-XP.png)
   skin/classic/browser/feeds/feedIcon16-XP.png                 (feeds/feedIcon16-XP.png)
   skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
 * skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
--- a/devtools/client/debugger/new/index.html
+++ b/devtools/client/debugger/new/index.html
@@ -4,13 +4,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <link rel="stylesheet" type="text/css" href="resource://devtools/client/debugger/new/styles.css" />
   </head>
   <body>
     <div id="mount"></div>
     <script type="text/javascript">
-      var devtoolsRequire = Components.utils.import("resource://devtools/shared/Loader.jsm", {}).require;
+      const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {});
+      const { require: devtoolsRequire } = BrowserLoader({
+        baseURI: "resource://devtools/client/debugger/new/",
+        window,
+      });
     </script>
     <script type="text/javascript" src="resource://devtools/client/debugger/new/bundle.js"></script>
   </body>
 </html>
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -35,17 +35,17 @@
       <html:div id="inspector-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
         <html:button id="inspector-element-add-button"
           title="&inspectorAddNode.label;"
           class="devtools-button" />
         <html:div class="devtools-toolbar-spacer" />
         <html:span id="inspector-searchlabel" />
-        <html:div id="inspector-search" class="devtools-searchbox">
+        <html:div id="inspector-search" class="devtools-searchbox has-clear-btn">
           <html:input id="inspector-searchbox" class="devtools-searchinput"
                       type="search"
                       placeholder="&inspectorSearchHTML.label3;"/>
           <html:button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></html:button>
         </html:div>
         <html:button id="inspector-eyedropper-toggle"
                      title="&inspectorEyeDropper.label;"
                      class="devtools-button command-button-invertable" />
@@ -68,17 +68,17 @@
            hidden="true" />
     </vbox>
 
     <!-- Sidebar panel definitions -->
     <html:div xmlns="http://www.w3.org/1999/xhtml" id="tabpanels" style="visibility:collapse">
       <html:div id="sidebar-panel-ruleview" class="devtools-monospace theme-sidebar inspector-tabpanel">
         <html:div id="ruleview-toolbar-container" class="devtools-toolbar">
           <html:div id="ruleview-toolbar">
-            <html:div class="devtools-searchbox">
+            <html:div class="devtools-searchbox has-clear-btn">
               <html:input id="ruleview-searchbox"
                           class="devtools-filterinput devtools-rule-searchbox"
                           type="search"
                           placeholder="&filterStylesPlaceholder;"/>
               <html:button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
             </html:div>
             <html:div id="ruleview-command-toolbar">
               <html:button id="ruleview-add-rule-button" title="&addRuleButtonTooltip;" class="devtools-button"></html:button>
@@ -95,17 +95,17 @@
         <html:div id="ruleview-container" class="ruleview">
           <html:div id="ruleview-container-focusable" tabindex="-1">
           </html:div>
         </html:div>
       </html:div>
 
       <html:div id="sidebar-panel-computedview" class="devtools-monospace theme-sidebar inspector-tabpanel">
         <html:div id="computedview-toolbar" class="devtools-toolbar">
-          <html:div class="devtools-searchbox">
+          <html:div class="devtools-searchbox has-clear-btn">
             <html:input id="computedview-searchbox"
                         class="devtools-filterinput devtools-rule-searchbox"
                         type="search"
                         placeholder="&filterStylesPlaceholder;"/>
             <html:button id="computedview-searchinput-clear" class="devtools-searchinput-clear"></html:button>
           </html:div>
           <html:label id="browser-style-checkbox-label" for="browser-style-checkbox">
             <html:input id="browser-style-checkbox"
--- a/devtools/client/inspector/test/browser_inspector_highlighter-04.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-04.js
@@ -14,22 +14,22 @@ const ELEMENTS = ["box-model-root",
                   "box-model-margin",
                   "box-model-border",
                   "box-model-padding",
                   "box-model-content",
                   "box-model-guide-top",
                   "box-model-guide-right",
                   "box-model-guide-bottom",
                   "box-model-guide-left",
-                  "box-model-nodeinfobar-container",
-                  "box-model-nodeinfobar-tagname",
-                  "box-model-nodeinfobar-id",
-                  "box-model-nodeinfobar-classes",
-                  "box-model-nodeinfobar-pseudo-classes",
-                  "box-model-nodeinfobar-dimensions"];
+                  "box-model-infobar-container",
+                  "box-model-infobar-tagname",
+                  "box-model-infobar-id",
+                  "box-model-infobar-classes",
+                  "box-model-infobar-pseudo-classes",
+                  "box-model-infobar-dimensions"];
 
 add_task(function* () {
   let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
 
   info("Show the box-model highlighter");
   let divFront = yield getNodeFront("div", inspector);
   yield toolbox.highlighter.showBoxModel(divFront);
 
--- a/devtools/client/inspector/test/browser_inspector_highlighter-options.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-options.js
@@ -19,17 +19,17 @@ const TEST_URL = `
 // - checkHighlighter: a generator (async) function that should check the
 //   highlighter is correct.
 const TEST_DATA = [
   {
     desc: "Guides and infobar should be shown by default",
     options: {},
     checkHighlighter: function* (testActor) {
       let hidden = yield testActor.getHighlighterNodeAttribute(
-        "box-model-nodeinfobar-container", "hidden");
+        "box-model-infobar-container", "hidden");
       ok(!hidden, "Node infobar is visible");
 
       hidden = yield testActor.getHighlighterNodeAttribute(
         "box-model-elements", "hidden");
       ok(!hidden, "SVG container is visible");
 
       for (let side of ["top", "right", "bottom", "left"]) {
         hidden = yield testActor.getHighlighterNodeAttribute(
@@ -59,18 +59,18 @@ const TEST_DATA = [
       }
     }
   },
   {
     desc: "Infobar can be hidden",
     options: {hideInfoBar: true},
     checkHighlighter: function* (testActor) {
       let hidden = yield testActor.getHighlighterNodeAttribute(
-        "box-model-nodeinfobar-container", "hidden");
-      is(hidden, "true", "nodeinfobar has been hidden");
+        "box-model-infobar-container", "hidden");
+      is(hidden, "true", "infobar has been hidden");
     }
   },
   {
     desc: "One region only can be shown (1)",
     options: {showOnly: "content"},
     checkHighlighter: function* (testActor) {
       let {d} = yield testActor.getHighlighterRegionPath("margin");
       ok(!d, "margin region is hidden");
--- a/devtools/client/inspector/test/browser_inspector_infobar_01.js
+++ b/devtools/client/inspector/test/browser_inspector_infobar_01.js
@@ -59,31 +59,31 @@ add_task(function* () {
 });
 
 function* testPosition(test, inspector, testActor) {
   info("Testing " + test.selector);
 
   yield selectAndHighlightNode(test.selector, inspector);
 
   let position = yield testActor.getHighlighterNodeAttribute(
-    "box-model-nodeinfobar-container", "position");
+    "box-model-infobar-container", "position");
   is(position, test.position, "Node " + test.selector + ": position matches");
 
   let tag = yield testActor.getHighlighterNodeTextContent(
-    "box-model-nodeinfobar-tagname");
+    "box-model-infobar-tagname");
   is(tag, test.tag, "node " + test.selector + ": tagName matches.");
 
   if (test.id) {
     let id = yield testActor.getHighlighterNodeTextContent(
-      "box-model-nodeinfobar-id");
+      "box-model-infobar-id");
     is(id, "#" + test.id, "node " + test.selector + ": id matches.");
   }
 
   let classes = yield testActor.getHighlighterNodeTextContent(
-    "box-model-nodeinfobar-classes");
+    "box-model-infobar-classes");
   is(classes, test.classes, "node " + test.selector + ": classes match.");
 
   if (test.dims) {
     let dims = yield testActor.getHighlighterNodeTextContent(
-      "box-model-nodeinfobar-dimensions");
+      "box-model-infobar-dimensions");
     is(dims, test.dims, "node " + test.selector + ": dims match.");
   }
 }
--- a/devtools/client/inspector/test/browser_inspector_infobar_02.js
+++ b/devtools/client/inspector/test/browser_inspector_infobar_02.js
@@ -40,11 +40,11 @@ add_task(function* () {
 });
 
 function* testNode(test, inspector, testActor) {
   info("Testing " + test.selector);
 
   yield selectAndHighlightNode(test.selector, inspector);
 
   let tag = yield testActor.getHighlighterNodeTextContent(
-    "box-model-nodeinfobar-tagname");
+    "box-model-infobar-tagname");
   is(tag, test.tag, "node " + test.selector + ": tagName matches.");
 }
--- a/devtools/client/inspector/test/browser_inspector_infobar_03.js
+++ b/devtools/client/inspector/test/browser_inspector_infobar_03.js
@@ -21,21 +21,21 @@ add_task(function* () {
 });
 
 function* testPositionAndStyle(test, inspector, testActor) {
   info("Testing " + test.selector);
 
   yield selectAndHighlightNode(test.selector, inspector);
 
   let style = yield testActor.getHighlighterNodeAttribute(
-    "box-model-nodeinfobar-container", "style");
+    "box-model-infobar-container", "style");
 
   is(style.split(";")[0], test.style,
     "Infobar shows on top of the page when page isn't scrolled");
 
   yield testActor.scrollWindow(0, 500);
 
   style = yield testActor.getHighlighterNodeAttribute(
-    "box-model-nodeinfobar-container", "style");
+    "box-model-infobar-container", "style");
 
   is(style.split(";")[0], test.style,
     "Infobar shows on top of the page even if the page is scrolled");
 }
--- a/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js
+++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js
@@ -112,17 +112,17 @@ function* assertPseudoAddedToNode(inspec
   is(rules[1]._ruleEditor.rule.selectorText, "div:hover",
      "rule view is showing " + PSEUDO + " rule");
 
   info("Show the highlighter on #div-1");
   yield showPickerOn("#div-1", inspector);
 
   info("Check that the infobar selector contains the pseudo-class");
   let value = yield testActor.getHighlighterNodeTextContent(
-    "box-model-nodeinfobar-pseudo-classes");
+    "box-model-infobar-pseudo-classes");
   is(value, PSEUDO, "pseudo-class in infobar selector");
   yield inspector.toolbox.highlighter.hideBoxModel();
 }
 
 function* assertPseudoRemovedFromNode(testActor) {
   info("Make sure the pseudoclass lock is removed from #div-1 and its " +
        "ancestors");
 
@@ -138,12 +138,12 @@ function* assertPseudoRemovedFromView(in
   info("Check that the ruleview no longer contains the pseudo-class rule");
   let rules = ruleview.element.querySelectorAll(
     ".ruleview-rule.theme-separator");
   is(rules.length, 2, "rule view is showing 2 rules after removing lock");
 
   yield showPickerOn("#div-1", inspector);
 
   let value = yield testActor.getHighlighterNodeTextContent(
-    "box-model-nodeinfobar-pseudo-classes");
+    "box-model-infobar-pseudo-classes");
   is(value, "", "pseudo-class removed from infobar selector");
   yield inspector.toolbox.highlighter.hideBoxModel();
 }
--- a/devtools/client/styleeditor/test/sourcemap-css/test-bootstrap-scss.css
+++ b/devtools/client/styleeditor/test/sourcemap-css/test-bootstrap-scss.css
@@ -1849,17 +1849,17 @@ output {
   border: 1px solid #ccc;
   border-radius: 4px;
   box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
   transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; }
   .form-control:focus {
     border-color: #66afe9;
     outline: 0;
     box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); }
-  .form-control::-moz-placeholder {
+  .form-control::placeholder {
     color: #999999;
     opacity: 1; }
   .form-control:-ms-input-placeholder {
     color: #999999; }
   .form-control::-webkit-input-placeholder {
     color: #999999; }
   .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control {
     cursor: not-allowed;
@@ -4505,9 +4505,9 @@ body {
   .header, .marketing, .footer {
     padding-left: 0;
     padding-right: 0; }
   .header {
     margin-bottom: 30px; }
   .jumbotron {
     border-bottom: 300; } }
 
-/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5jc3MiLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJtYWluLnNjc3MiXSwic291cmNlc0NvbnRlbnQiOlsiJGljb24tZm9udC1wYXRoOiBcIi4uL2Jvd2VyX2NvbXBvbmVudHMvYm9vdHN0cmFwLXNhc3Mtb2ZmaWNpYWwvdmVuZG9yL2Fzc2V0cy9mb250cy9ib290c3RyYXAvXCI7XG5cbi8vIGJvd2VyOnNjc3NcbkBpbXBvcnQgXCIuLi9ib3dlcl9jb21wb25lbnRzL2Jvb3RzdHJhcC1zYXNzLW9mZmljaWFsL3ZlbmRvci9hc3NldHMvc3R5bGVzaGVldHMvYm9vdHN0cmFwLnNjc3NcIjtcbi8vIGVuZGJvd2VyXG5cbi5icm93c2VoYXBweSB7XG4gICAgbWFyZ2luOiAwLjJlbSAwO1xuICAgIGJhY2tncm91bmQ6ICNjY2M7XG4gICAgY29sb3I6ICMwMDA7XG4gICAgcGFkZGluZzogMC4yZW0gMDtcbn1cblxuLyogU3BhY2Ugb3V0IGNvbnRlbnQgYSBiaXQgKi9cbmJvZHkge1xuICAgIHBhZGRpbmctdG9wOiAyMHB4O1xuICAgIHBhZGRpbmctYm90dG9tOiAyMHB4O1xufVxuXG4vKiBFdmVyeXRoaW5nIGJ1dCB0aGUganVtYm90cm9uIGdldHMgc2lkZSBzcGFjaW5nIGZvciBtb2JpbGUgZmlyc3Qgdmlld3MgKi9cbi5oZWFkZXIsXG4ubWFya2V0aW5nLFxuLmZvb3RlciB7XG4gICAgcGFkZGluZy1sZWZ0OiAxNXB4O1xuICAgIHBhZGRpbmctcmlnaHQ6IDE1cHg7XG59XG5cbi8qIEN1c3RvbSBwYWdlIGhlYWRlciAqL1xuLmhlYWRlciB7XG4gICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlNWU1ZTU7XG5cbiAgICAvKiBNYWtlIHRoZSBtYXN0aGVhZCBoZWFkaW5nIHRoZSBzYW1lIGhlaWdodCBhcyB0aGUgbmF2aWdhdGlvbiAqL1xuICAgIGgzIHtcbiAgICAgICAgbWFyZ2luLXRvcDogMDtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogMDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDQwcHg7XG4gICAgICAgIHBhZGRpbmctYm90dG9tOiAxOXB4O1xuICAgIH1cbn1cblxuLyogQ3VzdG9tIHBhZ2UgZm9vdGVyICovXG4uZm9vdGVyIHtcbiAgICBwYWRkaW5nLXRvcDogMTlweDtcbiAgICBjb2xvcjogIzc3NztcbiAgICBib3JkZXItdG9wOiAxcHggc29saWQgI2U1ZTVlNTtcbn1cblxuLmNvbnRhaW5lci1uYXJyb3cgPiBociB7XG4gICAgbWFyZ2luOiAzMHB4IDA7XG59XG5cbi8qIE1haW4gbWFya2V0aW5nIG1lc3NhZ2UgYW5kIHNpZ24gdXAgYnV0dG9uICovXG4uanVtYm90cm9uIHtcbiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlNWU1ZTU7XG4gICAgLmJ0biB7XG4gICAgICAgIGZvbnQtc2l6ZTogMjFweDtcbiAgICAgICAgcGFkZGluZzogMTRweCAyNHB4O1xuICAgIH1cbn1cblxuLyogU3VwcG9ydGluZyBtYXJrZXRpbmcgY29udGVudCAqL1xuLm1hcmtldGluZyB7XG4gICAgbWFyZ2luOiA0MHB4IDA7XG4gICAgcCArIGg0IHtcbiAgICAgICAgbWFyZ2luLXRvcDogMjhweDtcbiAgICB9XG59XG5cbi8qIFJlc3BvbnNpdmU6IFBvcnRyYWl0IHRhYmxldHMgYW5kIHVwICovXG5AbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiA3NjhweCkge1xuICAgIC5jb250YWluZXIge1xuICAgICAgICBtYXgtd2lkdGg6IDczMHB4O1xuICAgIH1cblxuICAgIC8qIFJlbW92ZSB0aGUgcGFkZGluZyB3ZSBzZXQgZWFybGllciAqL1xuICAgIC5oZWFkZXIsXG4gICAgLm1hcmtldGluZyxcbiAgICAuZm9vdGVyIHtcbiAgICAgICAgcGFkZGluZy1sZWZ0OiAwO1xuICAgICAgICBwYWRkaW5nLXJpZ2h0OiAwO1xuICAgIH1cblxuICAgIC8qIFNwYWNlIG91dCB0aGUgbWFzdGhlYWQgKi9cbiAgICAuaGVhZGVyIHtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogMzBweDtcbiAgICB9XG5cbiAgICAvKiBSZW1vdmUgdGhlIGJvdHRvbSBib3JkZXIgb24gdGhlIGp1bWJvdHJvbiBmb3IgdmlzdWFsIGVmZmVjdCAqL1xuICAgIC5qdW1ib3Ryb24ge1xuICAgICAgICBib3JkZXItYm90dG9tOiAzMDA7XG4gICAgfVxufVxuXG4vLyB0aGlzIGlzIGEgY29tbWVudC4uLlxuIl0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 */
\ No newline at end of file
+/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5jc3MiLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlcyI6WyJtYWluLnNjc3MiXSwic291cmNlc0NvbnRlbnQiOlsiJGljb24tZm9udC1wYXRoOiBcIi4uL2Jvd2VyX2NvbXBvbmVudHMvYm9vdHN0cmFwLXNhc3Mtb2ZmaWNpYWwvdmVuZG9yL2Fzc2V0cy9mb250cy9ib290c3RyYXAvXCI7XG5cbi8vIGJvd2VyOnNjc3NcbkBpbXBvcnQgXCIuLi9ib3dlcl9jb21wb25lbnRzL2Jvb3RzdHJhcC1zYXNzLW9mZmljaWFsL3ZlbmRvci9hc3NldHMvc3R5bGVzaGVldHMvYm9vdHN0cmFwLnNjc3NcIjtcbi8vIGVuZGJvd2VyXG5cbi5icm93c2VoYXBweSB7XG4gICAgbWFyZ2luOiAwLjJlbSAwO1xuICAgIGJhY2tncm91bmQ6ICNjY2M7XG4gICAgY29sb3I6ICMwMDA7XG4gICAgcGFkZGluZzogMC4yZW0gMDtcbn1cblxuLyogU3BhY2Ugb3V0IGNvbnRlbnQgYSBiaXQgKi9cbmJvZHkge1xuICAgIHBhZGRpbmctdG9wOiAyMHB4O1xuICAgIHBhZGRpbmctYm90dG9tOiAyMHB4O1xufVxuXG4vKiBFdmVyeXRoaW5nIGJ1dCB0aGUganVtYm90cm9uIGdldHMgc2lkZSBzcGFjaW5nIGZvciBtb2JpbGUgZmlyc3Qgdmlld3MgKi9cbi5oZWFkZXIsXG4ubWFya2V0aW5nLFxuLmZvb3RlciB7XG4gICAgcGFkZGluZy1sZWZ0OiAxNXB4O1xuICAgIHBhZGRpbmctcmlnaHQ6IDE1cHg7XG59XG5cbi8qIEN1c3RvbSBwYWdlIGhlYWRlciAqL1xuLmhlYWRlciB7XG4gICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlNWU1ZTU7XG5cbiAgICAvKiBNYWtlIHRoZSBtYXN0aGVhZCBoZWFkaW5nIHRoZSBzYW1lIGhlaWdodCBhcyB0aGUgbmF2aWdhdGlvbiAqL1xuICAgIGgzIHtcbiAgICAgICAgbWFyZ2luLXRvcDogMDtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogMDtcbiAgICAgICAgbGluZS1oZWlnaHQ6IDQwcHg7XG4gICAgICAgIHBhZGRpbmctYm90dG9tOiAxOXB4O1xuICAgIH1cbn1cblxuLyogQ3VzdG9tIHBhZ2UgZm9vdGVyICovXG4uZm9vdGVyIHtcbiAgICBwYWRkaW5nLXRvcDogMTlweDtcbiAgICBjb2xvcjogIzc3NztcbiAgICBib3JkZXItdG9wOiAxcHggc29saWQgI2U1ZTVlNTtcbn1cblxuLmNvbnRhaW5lci1uYXJyb3cgPiBociB7XG4gICAgbWFyZ2luOiAzMHB4IDA7XG59XG5cbi8qIE1haW4gbWFya2V0aW5nIG1lc3NhZ2UgYW5kIHNpZ24gdXAgYnV0dG9uICovXG4uanVtYm90cm9uIHtcbiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gICAgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlNWU1ZTU7XG4gICAgLmJ0biB7XG4gICAgICAgIGZvbnQtc2l6ZTogMjFweDtcbiAgICAgICAgcGFkZGluZzogMTRweCAyNHB4O1xuICAgIH1cbn1cblxuLyogU3VwcG9ydGluZyBtYXJrZXRpbmcgY29udGVudCAqL1xuLm1hcmtldGluZyB7XG4gICAgbWFyZ2luOiA0MHB4IDA7XG4gICAgcCArIGg0IHtcbiAgICAgICAgbWFyZ2luLXRvcDogMjhweDtcbiAgICB9XG59XG5cbi8qIFJlc3BvbnNpdmU6IFBvcnRyYWl0IHRhYmxldHMgYW5kIHVwICovXG5AbWVkaWEgc2NyZWVuIGFuZCAobWluLXdpZHRoOiA3NjhweCkge1xuICAgIC5jb250YWluZXIge1xuICAgICAgICBtYXgtd2lkdGg6IDczMHB4O1xuICAgIH1cblxuICAgIC8qIFJlbW92ZSB0aGUgcGFkZGluZyB3ZSBzZXQgZWFybGllciAqL1xuICAgIC5oZWFkZXIsXG4gICAgLm1hcmtldGluZyxcbiAgICAuZm9vdGVyIHtcbiAgICAgICAgcGFkZGluZy1sZWZ0OiAwO1xuICAgICAgICBwYWRkaW5nLXJpZ2h0OiAwO1xuICAgIH1cblxuICAgIC8qIFNwYWNlIG91dCB0aGUgbWFzdGhlYWQgKi9cbiAgICAuaGVhZGVyIHtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogMzBweDtcbiAgICB9XG5cbiAgICAvKiBSZW1vdmUgdGhlIGJvdHRvbSBib3JkZXIgb24gdGhlIGp1bWJvdHJvbiBmb3IgdmlzdWFsIGVmZmVjdCAqL1xuICAgIC5qdW1ib3Ryb24ge1xuICAgICAgICBib3JkZXItYm90dG9tOiAzMDA7XG4gICAgfVxufVxuXG4vLyB0aGlzIGlzIGEgY29tbWVudC4uLlxuIl0sInNvdXJjZVJvb3QiOiIvc291cmNlLyJ9 */
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -522,16 +522,25 @@ checkbox:-moz-focusring {
   padding-inline-start: 22px;
   padding-inline-end: 4px;
   background-position: 8px center;
   background-size: 11px 11px;
   background-repeat: no-repeat;
   font-size: inherit;
 }
 
+/*
+ * @TODO : has-clear-btn class was added for bug 1296187 and we should remove it
+ *  once we have a standardized search and filter input across the toolboxes.
+ */
+.has-clear-btn > .devtools-searchinput,
+.has-clear-btn > .devtools-filterinput {
+  padding-inline-end: 23px;
+}
+
 .devtools-searchinput {
   background-image: var(--magnifying-glass-image);
 }
 
 .devtools-filterinput {
   background-image: url(images/filter.svg#filterinput);
 }
 
@@ -542,18 +551,18 @@ checkbox:-moz-focusring {
   background-position: calc(100% - 8px) center;
 }
 
 .devtools-searchinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon,
 .devtools-filterinput > .textbox-input-box > .textbox-search-icons > .textbox-search-icon {
   visibility: hidden;
 }
 
-.devtools-searchinput .textbox-input::-moz-placeholder,
-.devtools-filterinput .textbox-input::-moz-placeholder {
+.devtools-searchinput .textbox-input::placeholder,
+.devtools-filterinput .textbox-input::placeholder {
   font-style: normal;
 }
 
 .devtools-plaininput {
   border-color: transparent;
   background-color: transparent;
 }
 
@@ -609,17 +618,16 @@ checkbox:-moz-focusring {
 .devtools-rule-searchbox {
   -moz-box-flex: 1;
   width: 100%;
 }
 
 .devtools-rule-searchbox[filled] {
   background-color: var(--searchbox-background-color);
   border-color: var(--searchbox-border-color);
-  padding-inline-end: 23px;
 }
 
 .devtools-style-searchbox-no-match {
   background-color: var(--searcbox-no-match-background-color) !important;
   border-color: var(--searcbox-no-match-border-color) !important;
 }
 
 .devtools-searchinput-clear {
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -32,82 +32,71 @@
   background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px;
 }
 
 /* Main: contains the box-model regions */
 
 #layout-main {
   position: relative;
   box-sizing: border-box;
-  /* The regions are semi-transparent, so the white background is partly
-     visible */
-  background-color: white;
-  color: var(--theme-selection-color);
+  color: #1A1C22;
   /* Make sure there is some space between the window's edges and the regions */
   margin: 0 14px 4px 14px;
   width: calc(100% - 2 * 14px);
 }
 
-.layout-margin,
-.layout-size {
-  color: var(--theme-highlight-blue);
-}
-
 /* Regions are 3 nested elements with wide borders and outlines */
 
-#layout-content {
-  height: 18px;
-}
-
 #layout-margins,
 #layout-borders,
 #layout-padding {
-  border-color: hsla(210,100%,85%,0.2);
-  border-width: 18px;
   border-style: solid;
-  outline: dotted 1px hsl(210,100%,85%);
-}
-
-#layout-margins {
-  /* This opacity applies to all of the regions, since they are nested */
-  opacity: .8;
+  border-width: 2px;
+  border-radius: 2px;
+  padding: 16px;
 }
 
 /* Regions colors */
 
 #layout-margins {
-  border-color: #edff64;
+  background-color: #9ebce1;
+  border-color: #2f5b8e;
 }
 
 #layout-borders {
-  border-color: #444444;
+  background-color: #b3c8e2;
+  border-color: #3b72b3;
 }
 
 #layout-padding {
-  border-color: #6a5acd;
+  background-color: #7fe3cc;
+  border-color: #00c79a;
 }
 
 #layout-content {
-  background-color: #87ceeb;
+  background-color: #f0f7ff;
+  border: 2px solid #eaf4ff;
+  height: 18px;
 }
 
 .theme-firebug #layout-main,
 .theme-firebug #layout-borders,
 .theme-firebug #layout-content {
   border-style: solid;
 }
 
 .theme-firebug #layout-main,
 .theme-firebug #layout-header {
   font-family: var(--proportional-font-family);
 }
 
 .theme-firebug #layout-main {
   color: var(--theme-body-color);
   font-size: var(--theme-toolbar-font-size);
+  background-color: var(--theme-body-color);
 }
 
 .theme-firebug #layout-header {
   font-size: var(--theme-toolbar-font-size);
 }
 
 /* Editable region sizes are contained in absolutely positioned <p> */
 
@@ -123,112 +112,111 @@
   vertical-align: middle;
   pointer-events: auto;
 }
 
 /* Coordinates for the region sizes */
 
 .layout-top,
 .layout-bottom {
-  width: calc(100% - 2px);
+  width: 100%;
   text-align: center;
 }
 
 .layout-padding.layout-top {
-  top: 37px;
+  top: 38px;
 }
 
 .layout-padding.layout-bottom {
-  bottom: 38px;
+  bottom: 39px;
 }
 
 .layout-border.layout-top {
-  top: 19px;
+  top: 20px;
 }
 
 .layout-border.layout-bottom {
-  bottom: 20px;
+  bottom: 21px;
 }
 
 .layout-margin.layout-top {
-  top: 1px;
+  top: 2px;
 }
 
 .layout-margin.layout-bottom {
-  bottom: 2px;
+  bottom: 3px;
 }
 
 .layout-size,
 .layout-margin.layout-left,
 .layout-margin.layout-right,
 .layout-border.layout-left,
 .layout-border.layout-right,
 .layout-padding.layout-left,
 .layout-padding.layout-right {
-  top: 22px;
+  top: 23px;
   line-height: 80px;
 }
 
 .layout-size {
-  width: calc(100% - 2px);
+  width: 100%;
 }
 
 .layout-margin.layout-right,
 .layout-margin.layout-left,
 .layout-border.layout-left,
 .layout-border.layout-right,
 .layout-padding.layout-right,
 .layout-padding.layout-left {
-  width: 21px;
+  width: 20px;
 }
 
 .layout-padding.layout-left {
-  left: 35px;
+  left: 36px;
 }
 
 .layout-padding.layout-right {
-  right: 35px;
+  right: 36px;
 }
 
 .layout-border.layout-left {
-  left: 16px;
+  left: 18px;
 }
 
 .layout-border.layout-right {
-  right: 17px;
+  right: 18px;
 }
 
 .layout-margin.layout-right {
   right: 0;
 }
 
 .layout-margin.layout-left {
   left: 0;
 }
 
 .layout-rotate.layout-left:not(.layout-editing) {
   transform: rotate(-90deg);
+  left: -2px;
 }
 
 .layout-rotate.layout-right:not(.layout-editing) {
   transform: rotate(90deg);
+  right: -2px;
 }
 
 /* Legend: displayed inside regions */
 
 .layout-legend {
+  color: #1A1C22;
   position: absolute;
-  margin: 2px 6px;
+  margin: 3px 6px;
   z-index: 1;
 }
 
-.layout-legend[data-box="margin"] {
-  color: var(--theme-highlight-blue);
-}
-
 /* Editable fields */
 
 .layout-editable {
   border: 1px dashed transparent;
   -moz-user-select: text;
 }
 
 .layout-editable:hover {
--- a/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js
+++ b/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js
@@ -45,17 +45,17 @@ function test() {
       webconsole: hud,
       messages: [{
         text: "nonExistingMethodCall is not defined",
         category: CATEGORY_JS,
         severity: SEVERITY_ERROR,
         collapsible: true,
         stacktrace: stack,
       }, {
-        text: "An invalid or illegal string was specified",
+        text: "SyntaxError: 'buggy;selector' is not a valid selector",
         category: CATEGORY_JS,
         severity: SEVERITY_ERROR,
         collapsible: true,
         stacktrace: [{
           file: TEST_FILE,
           fn: "domAPI",
           line: 25,
         }, {
--- a/devtools/client/webconsole/test/browser_webconsole_output_04.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_04.js
@@ -53,19 +53,18 @@ var inputTests = [
     printOutput: '"TypeError: window.foobar is not a function"',
     inspectable: true,
     variablesViewLabel: "TypeError",
   },
 
   // 4
   {
     input: "testDOMException()",
-    output: 'DOMException [SyntaxError: "An invalid or illegal string was ' +
-            'specified"',
-    printOutput: '"SyntaxError: An invalid or illegal string was specified"',
+    output: `DOMException [SyntaxError: "'foo;()bar!' is not a valid selector"`,
+    printOutput: `"SyntaxError: 'foo;()bar!' is not a valid selector"`,
     inspectable: true,
     variablesViewLabel: "SyntaxError",
   },
 
   // 5
   {
     input: "testCSSStyleDeclaration()",
     output: 'CSS2Properties { color: "green", font-size: "2em" }',
--- a/devtools/client/webide/modules/app-validator.js
+++ b/devtools/client/webide/modules/app-validator.js
@@ -67,22 +67,22 @@ AppValidator.checkManifest = function (m
   let deferred = promise.defer();
   let error;
 
   let req = new XMLHttpRequest();
   req.overrideMimeType("text/plain");
 
   try {
     req.open("GET", manifestURL, true);
+    req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
   } catch (e) {
     error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
     deferred.reject(error);
     return deferred.promise;
   }
-  req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
 
   req.onload = function () {
     let manifest = null;
     try {
       manifest = JSON.parse(req.responseText);
     } catch (e) {
       error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
       deferred.reject(error);
@@ -223,22 +223,22 @@ AppValidator.prototype.validateLaunchPat
     deferred.resolve();
     return deferred.promise;
   }
 
   let req = new XMLHttpRequest();
   req.overrideMimeType("text/plain");
   try {
     req.open("HEAD", indexURL, true);
+    req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
   } catch (e) {
     this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
     deferred.resolve();
     return deferred.promise;
   }
-  req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
   req.onload = () => {
     if (req.status >= 400)
       this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
     deferred.resolve();
   };
   req.onerror = () => {
     this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
     deferred.resolve();
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -24,17 +24,17 @@
   text-indent: initial;
   letter-spacing: initial;
   word-spacing: initial;
   color: initial;
 }
 
 :-moz-native-anonymous .highlighter-container {
   --highlighter-guide-color: #08c;
-  --highlighter-content-color: #87ceeb;
+  --highlighter-content-color: #f0f7ff;
   --highlighter-bubble-text-color: hsl(216, 33%, 97%);
   --highlighter-bubble-background-color: hsl(214, 13%, 24%);
   --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
 }
 
 :-moz-native-anonymous .highlighter-container {
   position: fixed;
   width: 100%;
@@ -50,42 +50,42 @@
 :-moz-native-anonymous .highlighter-container [hidden] {
   display: none;
 }
 
 :-moz-native-anonymous .highlighter-container [dragging] {
   cursor: grabbing;
 }
 
-/* Box model highlighter */
+/* Box Model Highlighter */
 
 :-moz-native-anonymous .box-model-regions {
   opacity: 0.6;
 }
 
 /* Box model regions can be faded (see the onlyRegionArea option in
    highlighters.js) in order to only display certain regions. */
 :-moz-native-anonymous .box-model-regions [faded] {
   display: none;
 }
 
 :-moz-native-anonymous .box-model-content {
   fill: var(--highlighter-content-color);
 }
 
 :-moz-native-anonymous .box-model-padding {
-  fill: #6a5acd;
+  fill: #7fe3cc;
 }
 
 :-moz-native-anonymous .box-model-border {
-  fill: #444444;
+  fill: #b3c8e2;
 }
 
 :-moz-native-anonymous .box-model-margin {
-  fill: #edff64;
+  fill: #9ebce1;
 }
 
 :-moz-native-anonymous .box-model-content,
 :-moz-native-anonymous .box-model-padding,
 :-moz-native-anonymous .box-model-border,
 :-moz-native-anonymous .box-model-margin {
   stroke: none;
 }
@@ -94,120 +94,143 @@
 :-moz-native-anonymous .box-model-guide-right,
 :-moz-native-anonymous .box-model-guide-bottom,
 :-moz-native-anonymous .box-model-guide-left {
   stroke: var(--highlighter-guide-color);
   stroke-dasharray: 5 3;
   shape-rendering: crispEdges;
 }
 
-/* Highlighter - Node Infobar */
+/* Highlighter - Infobar */
 
-:-moz-native-anonymous .box-model-nodeinfobar-container {
+:-moz-native-anonymous [class$=infobar-container] {
   position: absolute;
   max-width: 95%;
 
   font: message-box;
   font-size: 11px;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar {
+:-moz-native-anonymous [class$=infobar] {
   position: relative;
 
-  /* Centering the nodeinfobar in the container */
+  /* Centering the infobar in the container */
   left: -50%;
 
   padding: 5px;
   min-width: 75px;
 
   border-radius: 3px;
   background: var(--highlighter-bubble-background-color) no-repeat padding-box;
 
   color: var(--highlighter-bubble-text-color);
   text-shadow: none;
 
   border: 1px solid var(--highlighter-bubble-border-color);
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-container[hide-arrow] > .box-model-nodeinfobar {
+:-moz-native-anonymous [class$=infobar-container][hide-arrow] > [class$=infobar] {
   margin: 7px 0;
 }
 
 /* Arrows */
 
-:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:before {
+:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before {
   left: calc(50% - 8px);
   border: 8px solid var(--highlighter-bubble-border-color);
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:after {
+:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after {
   left: calc(50% - 7px);
   border: 7px solid var(--highlighter-bubble-background-color);
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:before,
-:-moz-native-anonymous .box-model-nodeinfobar-container > .box-model-nodeinfobar:after {
+:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:before,
+:-moz-native-anonymous [class$=infobar-container] > [class$=infobar]:after {
   content: "";
   display: none;
   position: absolute;
   height: 0;
   width: 0;
   border-left-color: transparent;
   border-right-color: transparent;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-container[position="top"]:not([hide-arrow]) > .box-model-nodeinfobar:before,
-:-moz-native-anonymous .box-model-nodeinfobar-container[position="top"]:not([hide-arrow]) > .box-model-nodeinfobar:after {
+:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:before,
+:-moz-native-anonymous [class$=infobar-container][position="top"]:not([hide-arrow]) > [class$=infobar]:after {
   border-bottom: 0;
   top: 100%;
   display: block;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-container[position="bottom"]:not([hide-arrow]) > .box-model-nodeinfobar:before,
-:-moz-native-anonymous .box-model-nodeinfobar-container[position="bottom"]:not([hide-arrow]) > .box-model-nodeinfobar:after {
+:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:before,
+:-moz-native-anonymous [class$=infobar-container][position="bottom"]:not([hide-arrow]) > [class$=infobar]:after {
   border-top: 0;
   bottom: 100%;
   display: block;
 }
 
-/* Text container */
+/* Text Container */
 
-:-moz-native-anonymous .box-model-nodeinfobar-text {
+:-moz-native-anonymous [class$=infobar-text] {
   overflow: hidden;
   white-space: nowrap;
   direction: ltr;
   padding-bottom: 1px;
   display: flex;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-tagname {
+:-moz-native-anonymous .box-model-infobar-tagname {
   color: hsl(285,100%, 75%);
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-id {
+:-moz-native-anonymous .box-model-infobar-id {
   color: hsl(103, 46%, 54%);
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-classes,
-:-moz-native-anonymous .box-model-nodeinfobar-pseudo-classes {
+:-moz-native-anonymous .box-model-infobar-classes,
+:-moz-native-anonymous .box-model-infobar-pseudo-classes {
   color: hsl(200, 74%, 57%);
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
-:-moz-native-anonymous .box-model-nodeinfobar-dimensions {
+:-moz-native-anonymous [class$=infobar-dimensions] {
   color: hsl(210, 30%, 85%);
   border-inline-start: 1px solid #5a6169;
   margin-inline-start: 6px;
   padding-inline-start: 6px;
 }
 
-/* Css transform highlighter */
+/* CSS Grid Highlighter */
+
+:-moz-native-anonymous .css-grid-canvas {
+  position: absolute;
+  pointer-events: none;
+  top: 0;
+  left: 0;
+  image-rendering: -moz-crisp-edges;
+}
+
+:-moz-native-anonymous .css-grid-regions {
+  opacity: 0.6;
+}
+
+:-moz-native-anonymous .css-grid-areas {
+  fill: #CEC0ED;
+  stroke: none;
+}
+
+:-moz-native-anonymous .css-grid-infobar-areaname {
+  color: hsl(285,100%, 75%);
+}
+
+/* CSS Transform Highlighter */
 
 :-moz-native-anonymous .css-transform-transformed {
   fill: var(--highlighter-content-color);
   opacity: 0.8;
 }
 
 :-moz-native-anonymous .css-transform-untransformed {
   fill: #66cc52;
@@ -217,25 +240,25 @@
 :-moz-native-anonymous .css-transform-transformed,
 :-moz-native-anonymous .css-transform-untransformed,
 :-moz-native-anonymous .css-transform-line {
   stroke: var(--highlighter-guide-color);
   stroke-dasharray: 5 3;
   stroke-width: 2;
 }
 
-/* Rect highlighter */
+/* Rect Highlighter */
 
 :-moz-native-anonymous .highlighted-rect {
   position: absolute;
   background: var(--highlighter-content-color);
   opacity: 0.8;
 }
 
-/* Element geometry highlighter */
+/* Element Geometry Highlighter */
 
 :-moz-native-anonymous .geometry-editor-root {
   /* The geometry editor can be interacted with, so it needs to react to
      pointer events */
   pointer-events: auto;
   -moz-user-select: none;
 }
 
@@ -295,17 +318,17 @@
 :-moz-native-anonymous .geometry-editor-label-text {
   fill: var(--highlighter-bubble-text-color);
   font: message-box;
   font-size: 10px;
   text-anchor: middle;
   dominant-baseline: middle;
 }
 
-/* Rulers highlighter */
+/* Rulers Highlighter */
 
 :-moz-native-anonymous .rulers-highlighter-elements {
   shape-rendering: crispEdges;
   pointer-events: none;
   position: fixed;
   top: 0;
   left: 0;
 }
@@ -339,17 +362,17 @@
   text-anchor: start;
 }
 
 :-moz-native-anonymous .rulers-highlighter-vertical-labels > text {
   transform: rotate(-90deg);
   text-anchor: end;
 }
 
-/* Measuring Tool highlighter */
+/* Measuring Tool Highlighter */
 
 :-moz-native-anonymous .measuring-tool-highlighter-root {
   position: absolute;
   top: 0;
   left: 0;
   pointer-events: auto;
   cursor: crosshair;
 }
@@ -399,27 +422,17 @@
 :-moz-native-anonymous .measuring-tool-highlighter-guide-right,
 :-moz-native-anonymous .measuring-tool-highlighter-guide-bottom,
 :-moz-native-anonymous .measuring-tool-highlighter-guide-left {
   stroke: var(--highlighter-guide-color);
   stroke-dasharray: 5 3;
   shape-rendering: crispEdges;
 }
 
-/* CSS Grid highlighter */
-
-:-moz-native-anonymous .css-grid-canvas {
-  position: absolute;
-  pointer-events: none;
-  top: 0;
-  left: 0;
-  image-rendering: -moz-crisp-edges;
-}
-
-/* Eye dropper */
+/* Eye Dropper */
 
 :-moz-native-anonymous .eye-dropper-root {
   --magnifier-width: 96px;
   --magnifier-height: 96px;
   /* Width accounts for all color formats (hsl being the longest) */
   --label-width: 160px;
   --label-height: 23px;
   --color: #e0e0e0;
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -2,33 +2,28 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { extend } = require("sdk/core/heritage");
 const { AutoRefreshHighlighter } = require("./auto-refresh");
 const {
-  CanvasFrameAnonymousContentHelper,
+  CanvasFrameAnonymousContentHelper, moveInfobar,
   getBindingElementAndPseudo, hasPseudoClassLock, getComputedStyle,
   createSVGNode, createNode, isNodeValid } = require("./utils/markup");
-const { getCurrentZoom,
-  setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
+const { setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
 const inspector = require("devtools/server/actors/inspector");
 
 // Note that the order of items in this array is important because it is used
 // for drawing the BoxModelHighlighter's path elements correctly.
 const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
 const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
 // Width of boxmodelhighlighter guides
 const GUIDE_STROKE_WIDTH = 1;
-// How high is the nodeinfobar (px).
-const NODE_INFOBAR_HEIGHT = 34;
-// What's the size of the nodeinfobar arrow (px).
-const NODE_INFOBAR_ARROW_SIZE = 9;
 // FIXME: add ":visited" and ":link" after bug 713106 is fixed
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 
 /**
  * The BoxModelHighlighter draws the box model regions on top of a node.
  * If the node is a block box, then each region will be displayed as 1 polygon.
  * If the node is an inline box though, each region may be represented by 1 or
  * more polygons, depending on how many line boxes the inline element has.
@@ -68,27 +63,27 @@ const PSEUDO_CLASSES = [":hover", ":acti
  *         <path class="box-model-padding" points="..." />
  *         <path class="box-model-content" points="..." />
  *       </g>
  *       <line class="box-model-guide-top" x1="..." y1="..." x2="..." y2="..." />
  *       <line class="box-model-guide-right" x1="..." y1="..." x2="..." y2="..." />
  *       <line class="box-model-guide-bottom" x1="..." y1="..." x2="..." y2="..." />
  *       <line class="box-model-guide-left" x1="..." y1="..." x2="..." y2="..." />
  *     </svg>
- *     <div class="box-model-nodeinfobar-container">
- *       <div class="box-model-nodeinfobar-arrow highlighter-nodeinfobar-arrow-top" />
- *       <div class="box-model-nodeinfobar">
- *         <div class="box-model-nodeinfobar-text" align="center">
- *           <span class="box-model-nodeinfobar-tagname">Node name</span>
- *           <span class="box-model-nodeinfobar-id">Node id</span>
- *           <span class="box-model-nodeinfobar-classes">.someClass</span>
- *           <span class="box-model-nodeinfobar-pseudo-classes">:hover</span>
+ *     <div class="box-model-infobar-container">
+ *       <div class="box-model-infobar-arrow highlighter-infobar-arrow-top" />
+ *       <div class="box-model-infobar">
+ *         <div class="box-model-infobar-text" align="center">
+ *           <span class="box-model-infobar-tagname">Node name</span>
+ *           <span class="box-model-infobar-id">Node id</span>
+ *           <span class="box-model-infobar-classes">.someClass</span>
+ *           <span class="box-model-infobar-pseudo-classes">:hover</span>
  *         </div>
  *       </div>
- *       <div class="box-model-nodeinfobar-arrow box-model-nodeinfobar-arrow-bottom"/>
+ *       <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
  *     </div>
  *   </div>
  * </div>
  */
 function BoxModelHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
   this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
@@ -181,81 +176,81 @@ BoxModelHighlighter.prototype = extend(A
       });
     }
 
     // Building the nodeinfo bar markup
 
     let infobarContainer = createNode(this.win, {
       parent: rootWrapper,
       attributes: {
-        "class": "nodeinfobar-container",
-        "id": "nodeinfobar-container",
+        "class": "infobar-container",
+        "id": "infobar-container",
         "position": "top",
         "hidden": "true"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
-    let nodeInfobar = createNode(this.win, {
+    let infobar = createNode(this.win, {
       parent: infobarContainer,
       attributes: {
-        "class": "nodeinfobar"
+        "class": "infobar"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     let texthbox = createNode(this.win, {
-      parent: nodeInfobar,
+      parent: infobar,
       attributes: {
-        "class": "nodeinfobar-text"
+        "class": "infobar-text"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
       parent: texthbox,
       attributes: {
-        "class": "nodeinfobar-tagname",
-        "id": "nodeinfobar-tagname"
+        "class": "infobar-tagname",
+        "id": "infobar-tagname"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
       parent: texthbox,
       attributes: {
-        "class": "nodeinfobar-id",
-        "id": "nodeinfobar-id"
+        "class": "infobar-id",
+        "id": "infobar-id"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
       parent: texthbox,
       attributes: {
-        "class": "nodeinfobar-classes",
-        "id": "nodeinfobar-classes"
+        "class": "infobar-classes",
+        "id": "infobar-classes"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
       parent: texthbox,
       attributes: {
-        "class": "nodeinfobar-pseudo-classes",
-        "id": "nodeinfobar-pseudo-classes"
+        "class": "infobar-pseudo-classes",
+        "id": "infobar-pseudo-classes"
       },
       prefix: this.ID_CLASS_PREFIX
     });
     createNode(this.win, {
       nodeType: "span",
       parent: texthbox,
       attributes: {
-        "class": "nodeinfobar-dimensions",
-        "id": "nodeinfobar-dimensions"
+        "class": "infobar-dimensions",
+        "id": "infobar-dimensions"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     return highlighterContainer;
   },
 
   /**
@@ -345,24 +340,24 @@ BoxModelHighlighter.prototype = extend(A
 
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   /**
    * Hide the infobar
    */
   _hideInfobar: function () {
-    this.getElement("nodeinfobar-container").setAttribute("hidden", "true");
+    this.getElement("infobar-container").setAttribute("hidden", "true");
   },
 
   /**
    * Show the infobar
    */
   _showInfobar: function () {
-    this.getElement("nodeinfobar-container").removeAttribute("hidden");
+    this.getElement("infobar-container").removeAttribute("hidden");
     this._updateInfobar();
   },
 
   /**
    * Hide the box model
    */
   _hideBoxModel: function () {
     this.getElement("elements").setAttribute("hidden", "true");
@@ -374,17 +369,17 @@ BoxModelHighlighter.prototype = extend(A
   _showBoxModel: function () {
     this.getElement("elements").removeAttribute("hidden");
   },
 
   /**
    * Calculate an outer quad based on the quads returned by getAdjustedQuads.
    * The BoxModelHighlighter may highlight more than one boxes, so in this case
    * create a new quad that "contains" all of these quads.
-   * This is useful to position the guides and nodeinfobar.
+   * This is useful to position the guides and infobar.
    * This may happen if the BoxModelHighlighter is used to highlight an inline
    * element that spans line breaks.
    * @param {String} region The box-model region to get the outer quad for.
    * @return {Object} A quad-like object {p1,p2,p3,p4,bounds}
    */
   _getOuterQuad: function (region) {
     let quads = this.currentQuads[region];
     if (!quads.length) {
@@ -671,76 +666,28 @@ BoxModelHighlighter.prototype = extend(A
       pseudos += ":" + pseudo;
     }
 
     let rect = this._getOuterQuad("border").bounds;
     let dim = parseFloat(rect.width.toPrecision(6)) +
               " \u00D7 " +
               parseFloat(rect.height.toPrecision(6));
 
-    this.getElement("nodeinfobar-tagname").setTextContent(displayName);
-    this.getElement("nodeinfobar-id").setTextContent(id);
-    this.getElement("nodeinfobar-classes").setTextContent(classList);
-    this.getElement("nodeinfobar-pseudo-classes").setTextContent(pseudos);
-    this.getElement("nodeinfobar-dimensions").setTextContent(dim);
+    this.getElement("infobar-tagname").setTextContent(displayName);
+    this.getElement("infobar-id").setTextContent(id);
+    this.getElement("infobar-classes").setTextContent(classList);
+    this.getElement("infobar-pseudo-classes").setTextContent(pseudos);
+    this.getElement("infobar-dimensions").setTextContent(dim);
 
     this._moveInfobar();
   },
 
   /**
    * Move the Infobar to the right place in the highlighter.
    */
   _moveInfobar: function () {
     let bounds = this._getOuterBounds();
-    let winHeight = this.win.innerHeight * getCurrentZoom(this.win);
-    let winWidth = this.win.innerWidth * getCurrentZoom(this.win);
-    let winScrollY = this.win.scrollY;
-
-    // Ensure that containerBottom and containerTop are at least zero to avoid
-    // showing tooltips outside the viewport.
-    let containerBottom = Math.max(0, bounds.bottom) + NODE_INFOBAR_ARROW_SIZE;
-    let containerTop = Math.min(winHeight, bounds.top);
-    let container = this.getElement("nodeinfobar-container");
+    let container = this.getElement("infobar-container");
 
-    // Can the bar be above the node?
-    let top;
-    if (containerTop < NODE_INFOBAR_HEIGHT) {
-      // No. Can we move the bar under the node?
-      if (containerBottom + NODE_INFOBAR_HEIGHT > winHeight) {
-        // No. Let's move it inside. Can we show it at the top of the element?
-        if (containerTop < winScrollY) {
-          // No. Window is scrolled past the top of the element.
-          top = 0;
-        } else {
-          // Yes. Show it at the top of the element
-          top = containerTop;
-        }
-        container.setAttribute("position", "overlap");
-      } else {
-        // Yes. Let's move it under the node.
-        top = containerBottom;
-        container.setAttribute("position", "bottom");
-      }
-    } else {
-      // Yes. Let's move it on top of the node.
-      top = containerTop - NODE_INFOBAR_HEIGHT;
-      container.setAttribute("position", "top");
-    }
-
-    // Align the bar with the box's center if possible.
-    let left = bounds.right - bounds.width / 2;
-    // Make sure the while infobar is visible.
-    let buffer = 100;
-    if (left < buffer) {
-      left = buffer;
-      container.setAttribute("hide-arrow", "true");
-    } else if (left > winWidth - buffer) {
-      left = winWidth - buffer;
-      container.setAttribute("hide-arrow", "true");
-    } else {
-      container.removeAttribute("hide-arrow");
-    }
-
-    let style = "top:" + top + "px;left:" + left + "px;";
-    container.setAttribute("style", style);
+    moveInfobar(container, bounds, this.win);
   }
 });
 exports.BoxModelHighlighter = BoxModelHighlighter;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -1,17 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { extend } = require("sdk/core/heritage");
 const { AutoRefreshHighlighter } = require("./auto-refresh");
-const { CanvasFrameAnonymousContentHelper, createNode } = require("./utils/markup");
+const {
+  CanvasFrameAnonymousContentHelper,
+  createNode,
+  createSVGNode,
+  moveInfobar,
+} = require("./utils/markup");
 const {
   getCurrentZoom,
   setIgnoreLayoutChanges
 } = require("devtools/shared/layout/utils");
 const Services = require("Services");
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
 const ROWS = "rows";
@@ -37,20 +42,45 @@ const GRID_LINES_PROPERTIES = {
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
  * Available Options:
- * - showGridLineNumbers {Boolean}
- *   Displays the grid line numbers
- * - showInfiniteLines {Boolean}
- *   Displays an infinite line to represent the grid lines
+ * - showGridArea(areaName)
+ *     @param  {String} areaName
+ *     Shows the grid area highlight for the given area name.
+ * - showAllGridAreas
+ *     Shows all the grid area highlights for the current grid.
+ * - showGridLineNumbers(isShown)
+ *     @param  {Boolean}
+ *     Displays the grid line numbers on the grid lines if isShown is true.
+ * - showInfiniteLines(isShown)
+ *     @param  {Boolean} isShown
+ *     Displays an infinite line to represent the grid lines if isShown is true.
+ *
+ * Structure:
+ * <div class="highlighter-container">
+ *   <canvas id="css-grid-canvas" class="css-grid-canvas">
+ *   <svg class="css-grid-elements" hidden="true">
+ *     <g class="css-grid-regions">
+ *       <path class="css-grid-areas" points="..." />
+ *     </g>
+ *   </svg>
+ *   <div class="css-grid-infobar-container">
+ *     <div class="css-grid-infobar">
+ *       <div class="css-grid-infobar-text">
+ *         <span class="css-grid-infobar-areaname">Grid Area Name</span>
+ *         <span class="css-grid-infobar-dimensions"Grid Area Dimensions></span>
+ *       </div>
+ *     </div>
+ *   </div>
+ * </div>
  */
 function CssGridHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
   this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
     this._buildMarkup.bind(this));
 }
 
@@ -75,16 +105,94 @@ CssGridHighlighter.prototype = extend(Au
       attributes: {
         "id": "canvas",
         "class": "canvas",
         "hidden": "true"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
+    // Build the SVG element
+    let svg = createSVGNode(this.win, {
+      nodeType: "svg",
+      parent: container,
+      attributes: {
+        "id": "elements",
+        "width": "100%",
+        "height": "100%",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let regions = createSVGNode(this.win, {
+      nodeType: "g",
+      parent: svg,
+      attributes: {
+        "class": "regions"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "path",
+      parent: regions,
+      attributes: {
+        "class": "areas",
+        "id": "areas"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // Building the grid infobar markup
+    let infobarContainer = createNode(this.win, {
+      parent: container,
+      attributes: {
+        "class": "infobar-container",
+        "id": "infobar-container",
+        "position": "top",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let infobar = createNode(this.win, {
+      parent: infobarContainer,
+      attributes: {
+        "class": "infobar"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    let textbox = createNode(this.win, {
+      parent: infobar,
+      attributes: {
+        "class": "infobar-text"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: textbox,
+      attributes: {
+        "class": "infobar-areaname",
+        "id": "infobar-areaname"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "span",
+      parent: textbox,
+      attributes: {
+        "class": "infobar-dimensions",
+        "id": "infobar-dimensions"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
     return container;
   },
 
   destroy() {
     AutoRefreshHighlighter.prototype.destroy.call(this);
     this.markup.destroy();
   },
 
@@ -105,16 +213,43 @@ CssGridHighlighter.prototype = extend(Au
       this.hide();
       return false;
     }
 
     return this._update();
   },
 
   /**
+   * Shows the grid area highlight for the given area name.
+   *
+   * @param  {String} areaName
+   *         Grid area name.
+   */
+  showGridArea(areaName) {
+    this.renderGridArea(areaName);
+    this._showGridArea();
+  },
+
+  /**
+   * Shows all the grid area highlights for the current grid.
+   */
+  showAllGridAreas() {
+    this.renderGridArea();
+    this._showGridArea();
+  },
+
+  /**
+   * Clear the grid area highlights.
+   */
+  clearGridAreas() {
+    let box = this.getElement("areas");
+    box.setAttribute("d", "");
+  },
+
+  /**
    * Checks if the current node has a CSS Grid layout.
    *
    * @return  {Boolean} true if the current node has a CSS grid layout, false otherwise.
    */
   isGrid() {
     return this.currentNode.getGridFragments().length > 0;
   },
 
@@ -132,37 +267,100 @@ CssGridHighlighter.prototype = extend(Au
     let newGridData = stringifyGridFragments(this.gridData);
 
     return hasMoved || oldGridData !== newGridData;
   },
 
   /**
    * Update the highlighter on the current highlighted node (the one that was
    * passed as an argument to show(node)).
-   * Should be called whenever node's geometry or grid changes
+   * Should be called whenever node's geometry or grid changes.
    */
   _update() {
     setIgnoreLayoutChanges(true);
 
-    // Clear the canvas.
+    // Clear the canvas the grid area highlights.
     this.clearCanvas();
+    this.clearGridAreas();
 
-    // And start drawing the fragments.
+    // Start drawing the grid fragments.
     for (let i = 0; i < this.gridData.length; i++) {
       let fragment = this.gridData[i];
       let quad = this.currentQuads.content[i];
       this.renderFragment(fragment, quad);
     }
 
+    // Display the grid area highlights if needed.
+    if (this.options.showAllGridAreas) {
+      this.showAllGridAreas();
+    } else if (this.options.showGridArea) {
+      this.showGridArea(this.options.showGridArea);
+    }
+
     this._showGrid();
 
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
     return true;
   },
 
+  /**
+   * Update the grid information displayed in the grid info bar.
+   *
+   * @param  {GridArea} area
+   *         The grid area object.
+   * @param  {Number} x1
+   *         The first x-coordinate of the grid area rectangle.
+   * @param  {Number} x2
+   *         The second x-coordinate of the grid area rectangle.
+   * @param  {Number} y1
+   *         The first y-coordinate of the grid area rectangle.
+   * @param  {Number} y2
+   *         The second y-coordinate of the grid area rectangle.
+   */
+  _updateInfobar(area, x1, x2, y1, y2) {
+    let width = x2 - x1;
+    let height = y2 - y1;
+    let dim = parseFloat(width.toPrecision(6)) +
+              " \u00D7 " +
+              parseFloat(height.toPrecision(6));
+
+    this.getElement("infobar-areaname").setTextContent(area.name);
+    this.getElement("infobar-dimensions").setTextContent(dim);
+
+    this._moveInfobar(x1, x2, y1, y2);
+  },
+
+  /**
+   * Move the grid infobar to the right place in the highlighter.
+   *
+   * @param  {Number} x1
+   *         The first x-coordinate of the grid area rectangle.
+   * @param  {Number} x2
+   *         The second x-coordinate of the grid area rectangle.
+   * @param  {Number} y1
+   *         The first y-coordinate of the grid area rectangle.
+   * @param  {Number} y2
+   *         The second y-coordinate of the grid area rectangle.
+   */
+  _moveInfobar(x1, x2, y1, y2) {
+    let bounds = {
+      bottom: y2,
+      height: y2 - y1,
+      left: x1,
+      right: x2,
+      top: y1,
+      width: x2 - x1,
+      x: x1,
+      y: y1,
+    };
+    let container = this.getElement("infobar-container");
+
+    moveInfobar(container, bounds, this.win);
+  },
+
   clearCanvas() {
     let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2));
     let width = this.win.innerWidth;
     let height = this.win.innerHeight;
 
     // Resize the canvas taking the dpr into account so as to have crisp lines.
     this.canvas.setAttribute("width", width * ratio);
     this.canvas.setAttribute("height", height * ratio);
@@ -327,29 +525,101 @@ CssGridHighlighter.prototype = extend(Au
     if (dimensionType === COLUMNS) {
       this.ctx.fillText(lineNumber, linePos, startPos);
     } else {
       let textWidth = this.ctx.measureText(lineNumber).width;
       this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
     }
   },
 
+  /**
+   * Render the grid area highlight for the given area name or for all the grid areas.
+   *
+   * @param  {String} areaName
+   *         Name of the grid area to be highlighted. If no area name is provided, all
+   *         the grid areas should be highlighted.
+   */
+  renderGridArea(areaName) {
+    let paths = [];
+    let currentZoom = getCurrentZoom(this.win);
+
+    for (let i = 0; i < this.gridData.length; i++) {
+      let fragment = this.gridData[i];
+      let {bounds} = this.currentQuads.content[i];
+
+      for (let area of fragment.areas) {
+        if (areaName && areaName != area.name) {
+          continue;
+        }
+
+        let rowStart = fragment.rows.lines[area.rowStart - 1];
+        let rowEnd = fragment.rows.lines[area.rowEnd - 1];
+        let columnStart = fragment.cols.lines[area.columnStart - 1];
+        let columnEnd = fragment.cols.lines[area.columnEnd - 1];
+
+        let x1 = columnStart.start + columnStart.breadth +
+          (bounds.left / currentZoom);
+        let x2 = columnEnd.start + (bounds.left / currentZoom);
+        let y1 = rowStart.start + rowStart.breadth +
+          (bounds.top / currentZoom);
+        let y2 = rowEnd.start + (bounds.top / currentZoom);
+
+        let path = "M" + x1 + "," + y1 + " " +
+                   "L" + x2 + "," + y1 + " " +
+                   "L" + x2 + "," + y2 + " " +
+                   "L" + x1 + "," + y2;
+        paths.push(path);
+
+        // Update and show the info bar when only displaying a single grid area.
+        if (areaName) {
+          this._updateInfobar(area, x1, x2, y1, y2);
+          this._showInfoBar();
+        }
+      }
+    }
+
+    let box = this.getElement("areas");
+    box.setAttribute("d", paths.join(" "));
+  },
+
+  /**
+   * Hide the highlighter, the canvas and the infobar.
+   */
   _hide() {
     setIgnoreLayoutChanges(true);
     this._hideGrid();
+    this._hideGridArea();
+    this._hideInfoBar();
     setIgnoreLayoutChanges(false, this.currentNode.ownerDocument.documentElement);
   },
 
   _hideGrid() {
     this.getElement("canvas").setAttribute("hidden", "true");
   },
 
   _showGrid() {
     this.getElement("canvas").removeAttribute("hidden");
-  }
+  },
+
+  _hideGridArea() {
+    this.getElement("elements").setAttribute("hidden", "true");
+  },
+
+  _showGridArea() {
+    this.getElement("elements").removeAttribute("hidden");
+  },
+
+  _hideInfoBar() {
+    this.getElement("infobar-container").setAttribute("hidden", "true");
+  },
+
+  _showInfoBar() {
+    this.getElement("infobar-container").removeAttribute("hidden");
+  },
+
 });
 exports.CssGridHighlighter = CssGridHighlighter;
 
 /**
  * Stringify CSS Grid data as returned by node.getGridFragments.
  * This is useful to compare grid state at each update and redraw the highlighter if
  * needed.
  *
--- a/devtools/server/actors/highlighters/utils/markup.js
+++ b/devtools/server/actors/highlighters/utils/markup.js
@@ -32,16 +32,20 @@ exports.removePseudoClassLock = (...args
 
 exports.getCSSStyleRules = (...args) =>
   lazyContainer.DOMUtils.getCSSStyleRules(...args);
 
 const SVG_NS = "http://www.w3.org/2000/svg";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STYLESHEET_URI = "resource://devtools/server/actors/" +
                        "highlighters.css";
+// How high is the infobar (px).
+const INFOBAR_HEIGHT = 34;
+// What's the size of the infobar arrow (px).
+const INFOBAR_ARROW_SIZE = 9;
 
 const _tokens = Symbol("classList/tokens");
 
 /**
  * Shims the element's `classList` for anonymous content elements; used
  * internally by `CanvasFrameAnonymousContentHelper.getElement()` method.
  */
 function ClassList(className) {
@@ -512,8 +516,79 @@ CanvasFrameAnonymousContentHelper.protot
       value += "transform-origin:top left;transform:scale(" + (1 / zoom) + ");";
       value += "width:" + (100 * zoom) + "%;height:" + (100 * zoom) + "%;";
     }
 
     this.setAttributeForElement(id, "style", value);
   }
 };
 exports.CanvasFrameAnonymousContentHelper = CanvasFrameAnonymousContentHelper;
+
+/**
+ * Move the infobar to the right place in the highlighter. This helper method is utilized
+ * in both css-grid.js and box-model.js to help position the infobar in an appropriate
+ * space over the highlighted node element or grid area. The infobar is used to display
+ * relevant information about the highlighted item (ex, node or grid name and dimensions).
+ *
+ * This method will first try to position the infobar to top or bottom of the container
+ * such that it has enough space for the height of the infobar. Afterwards, it will try
+ * to horizontally center align with the container element if possible.
+ *
+ * @param  {DOMNode} container
+ *         The container element which will be used to position the infobar.
+ * @param  {Object} bounds
+ *         The content bounds of the container element.
+ * @param  {Window} win
+ *         The window object.
+ */
+function moveInfobar(container, bounds, win) {
+  let winHeight = win.innerHeight * getCurrentZoom(win);
+  let winWidth = win.innerWidth * getCurrentZoom(win);
+  let winScrollY = win.scrollY;
+
+  // Ensure that containerBottom and containerTop are at least zero to avoid
+  // showing tooltips outside the viewport.
+  let containerBottom = Math.max(0, bounds.bottom) + INFOBAR_ARROW_SIZE;
+  let containerTop = Math.min(winHeight, bounds.top);
+
+  // Can the bar be above the node?
+  let top;
+  if (containerTop < INFOBAR_HEIGHT) {
+    // No. Can we move the bar under the node?
+    if (containerBottom + INFOBAR_HEIGHT > winHeight) {
+      // No. Let's move it inside. Can we show it at the top of the element?
+      if (containerTop < winScrollY) {
+        // No. Window is scrolled past the top of the element.
+        top = 0;
+      } else {
+        // Yes. Show it at the top of the element
+        top = containerTop;
+      }
+      container.setAttribute("position", "overlap");
+    } else {
+      // Yes. Let's move it under the node.
+      top = containerBottom;
+      container.setAttribute("position", "bottom");
+    }
+  } else {
+    // Yes. Let's move it on top of the node.
+    top = containerTop - INFOBAR_HEIGHT;
+    container.setAttribute("position", "top");
+  }
+
+  // Align the bar with the box's center if possible.
+  let left = bounds.right - bounds.width / 2;
+  // Make sure the while infobar is visible.
+  let buffer = 100;
+  if (left < buffer) {
+    left = buffer;
+    container.setAttribute("hide-arrow", "true");
+  } else if (left > winWidth - buffer) {
+    left = winWidth - buffer;
+    container.setAttribute("hide-arrow", "true");
+  } else {
+    container.removeAttribute("hide-arrow");
+  }
+
+  let style = "top:" + top + "px;left:" + left + "px;";
+  container.setAttribute("style", style);
+}
+exports.moveInfobar = moveInfobar;
deleted file mode 100644
--- a/devtools/server/tests/unit/test_memory_footprint.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var gClient;
-var gMgr;
-var gRefMemory;
-
-function run_test() {
-  gMgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
-  Cu.forceGC();
-  gRefMemory = gMgr.residentUnique;
-
-  add_test(init_server);
-  add_test(add_browser_actors);
-  add_test(connect_client);
-  add_test(list_tabs);
-  add_test(close_client);
-  run_next_test();
-}
-
-function check_footprint(step, max) {
-  var footprint = (gMgr.residentUnique - gRefMemory) / 1024;
-  let msg = "Footprint after " + step + " is " + footprint + " kB (should be less than " + max + " kB).\n" +
-            "!!! The devtools server's memory usage increased either significantly, or slowly over time.\n" +
-            "!!! If your patch doesn't cause a big increase, feel free to raise the thresholds for this test as needed.";
-  ok(footprint < max, msg);
-  run_next_test();
-}
-
-function init_server() {
-  DebuggerServer.init();
-  check_footprint("DebuggerServer.init()", 150);
-}
-
-function add_browser_actors() {
-  DebuggerServer.addBrowserActors();
-  check_footprint("DebuggerServer.addBrowserActors()", 2600);
-}
-
-function connect_client() {
-  gClient = new DebuggerClient(DebuggerServer.connectPipe());
-  gClient.connect().then(function onConnect() {
-    check_footprint("DebuggerClient.connect()", 3200);
-  });
-}
-
-function list_tabs() {
-  gClient.listTabs(function onListTabs(aResponse) {
-    check_footprint("DebuggerClient.listTabs()", 3800);
-  });
-}
-
-function close_client() {
-  gClient.close().then(run_next_test);
-}
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -252,20 +252,16 @@ reason = bug 937197
 [test_client_request.js]
 [test_monitor_actor.js]
 [test_symbols-01.js]
 [test_symbols-02.js]
 [test_get-executable-lines.js]
 [test_get-executable-lines-source-map.js]
 [test_xpcshell_debugging.js]
 support-files = xpcshell_debugging_script.js
-[test_memory_footprint.js]
-run-sequentially = measure memory, has to be run solo
-skip-if = os != 'linux' || debug || asan
-reason = bug 1014071
 [test_setBreakpoint-on-column.js]
 [test_setBreakpoint-on-column-in-gcd-script.js]
 [test_setBreakpoint-on-column-with-no-offsets-at-end-of-line.js]
 [test_setBreakpoint-on-line.js]
 [test_setBreakpoint-on-line-in-gcd-script.js]
 [test_setBreakpoint-on-line-with-multiple-offsets.js]
 [test_setBreakpoint-on-line-with-multiple-statements.js]
 [test_setBreakpoint-on-line-with-no-offsets.js]
--- a/devtools/shared/css-properties-db.js
+++ b/devtools/shared/css-properties-db.js
@@ -58,17 +58,17 @@ exports.ANGLE_TAKING_FUNCTIONS = ["linea
 
 var domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 var json = JSON.stringify(domUtils.getCSSPseudoElementNames(), null, 0);
 copy(`exports.PSEUDO_ELEMENTS = ${json};\n`);
 console.log("The PSEUDO_ELEMENTS have been copied to your clipboard.")
  */
 
 /*eslint-disable */
-exports.PSEUDO_ELEMENTS = [":after",":before",":backdrop",":first-letter",":first-line",":-moz-selection",":-moz-focus-inner",":-moz-focus-outer",":-moz-list-bullet",":-moz-list-number",":-moz-math-anonymous",":-moz-progress-bar",":-moz-range-track",":-moz-range-progress",":-moz-range-thumb",":-moz-meter-bar",":-moz-placeholder",":-moz-color-swatch"];
+exports.PSEUDO_ELEMENTS = [":after",":before",":backdrop",":first-letter",":first-line",":-moz-selection",":-moz-focus-inner",":-moz-focus-outer",":-moz-list-bullet",":-moz-list-number",":-moz-math-anonymous",":-moz-progress-bar",":-moz-range-track",":-moz-range-progress",":-moz-range-thumb",":-moz-meter-bar",":-moz-placeholder",":placeholder",":-moz-color-swatch"];
 /*eslint-enable */
 
 /**
  * A list of CSS Properties and their various characteristics. This is used on the
  * client-side when the CssPropertiesActor is not found, or when the client and server
  * are the same version. A single property takes the form:
  *
  *  "animation": {
--- a/devtools/shared/gcli/commands/jsb.js
+++ b/devtools/shared/gcli/commands/jsb.js
@@ -101,35 +101,34 @@ exports.items = [
         jslint_happy: args.jslintHappy,
         brace_style: args.braceStyle,
         space_before_conditional: !args.noSpaceBeforeConditional,
         unescape_strings: args.unescapeStrings
       };
 
       let xhr = new XMLHttpRequest();
 
-      try {
-        xhr.open("GET", args.url, true);
-      } catch(e) {
-        return l10n.lookup("jsbInvalidURL");
-      }
-
       let deferred = context.defer();
 
       xhr.onreadystatechange = function() {
         if (xhr.readyState == 4) {
           if (xhr.status == 200 || xhr.status == 0) {
             let result = beautify.js(xhr.responseText, opts);
 
             ScratchpadManager.openScratchpad({text: result});
 
             deferred.resolve();
           } else {
             deferred.reject("Unable to load page to beautify: " + args.url + " " +
                             xhr.status + " " + xhr.statusText);
           }
         };
       }
-      xhr.send(null);
+      try {
+        xhr.open("GET", args.url, true);
+        xhr.send(null);
+      } catch(e) {
+        return l10n.lookup("jsbInvalidURL");
+      }
       return deferred.promise;
     }
   }
 ];
--- a/devtools/shared/webconsole/test/test_jsterm_queryselector.html
+++ b/devtools/shared/webconsole/test/test_jsterm_queryselector.html
@@ -87,22 +87,22 @@ let checkQuerySelectorAllNotExist = Task
   nextTest();
 });
 
 let checkQuerySelectorAllException = Task.async(function*() {
   info ("$$ returns an exception if an invalid selector was provided");
   let response = yield evaluateJS("$$(':foo')");
   checkObject(response, {
     input: "$$(':foo')",
-    exceptionMessage: "SyntaxError: An invalid or illegal string was specified",
+    exceptionMessage: "SyntaxError: ':foo' is not a valid selector",
     exception: {
       preview: {
         kind: "DOMException",
         name: "SyntaxError",
-        message: "An invalid or illegal string was specified"
+        message: "':foo' is not a valid selector"
       }
     }
   });
   nextTest();
 });
 
 function basicResultCheck(response, input, output) {
   checkObject(response, {
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -196,17 +196,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CI_INTERFACE_GETTER(Exception, nsIXPCException)
 
 Exception::Exception(const nsACString& aMessage,
                      nsresult aResult,
                      const nsACString& aName,
                      nsIStackFrame *aLocation,
                      nsISupports *aData)
 : mResult(NS_OK),
-  mLineNumber(0),
   mInitialized(false),
   mHoldingJSVal(false)
 {
   // A little hack... The nsIGenericModule nsIClassInfo scheme relies on there
   // having been at least one instance made via the factory. Otherwise, the
   // shared factory/classinsance object never gets created and our QI getter
   // for our instance's pointer to our nsIClassInfo will always return null.
   // This is bad because it means that wrapped exceptions will never have a
@@ -218,17 +217,16 @@ Exception::Exception(const nsACString& a
     sEverMadeOneFromFactory = true;
   }
 
   Initialize(aMessage, aResult, aName, aLocation, aData);
 }
 
 Exception::Exception()
   : mResult(NS_OK),
-    mLineNumber(-1),
     mInitialized(false),
     mHoldingJSVal(false)
 {
 }
 
 Exception::~Exception()
 {
   if (mHoldingJSVal) {
@@ -311,34 +309,34 @@ NS_IMETHODIMP
 Exception::GetFilename(JSContext* aCx, nsAString& aFilename)
 {
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
 
   if (mLocation) {
     return mLocation->GetFilename(aCx, aFilename);
   }
 
-  aFilename.Assign(mFilename);
+  aFilename.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Exception::GetLineNumber(JSContext* aCx, uint32_t *aLineNumber)
 {
   NS_ENSURE_ARG_POINTER(aLineNumber);
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
 
   if (mLocation) {
     int32_t lineno;
     nsresult rv = mLocation->GetLineNumber(aCx, &lineno);
     *aLineNumber = lineno;
     return rv;
   }
 
-  *aLineNumber = mLineNumber;
+  *aLineNumber = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Exception::GetColumnNumber(uint32_t* aColumnNumber)
 {
   NS_ENSURE_ARG_POINTER(aColumnNumber);
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
@@ -479,17 +477,17 @@ Exception::LineNumber(JSContext* aCx) co
   if (mLocation) {
     int32_t lineno;
     if (NS_SUCCEEDED(mLocation->GetLineNumber(aCx, &lineno))) {
       return lineno;
     }
     return 0;
   }
 
-  return mLineNumber;
+  return 0;
 }
 
 uint32_t
 Exception::ColumnNumber() const
 {
   return 0;
 }
 
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -131,18 +131,16 @@ protected:
     }
   }
 
   nsCString       mMessage;
   nsresult        mResult;
   nsCString       mName;
   nsCOMPtr<nsIStackFrame> mLocation;
   nsCOMPtr<nsISupports> mData;
-  nsString        mFilename;
-  int             mLineNumber;
   bool            mInitialized;
 
   bool mHoldingJSVal;
   JS::Heap<JS::Value> mThrownJSVal;
 
 private:
   static bool sEverMadeOneFromFactory;
 };
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -10,17 +10,16 @@
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/net/WebSocketChannel.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
-#include "mozilla/dom/nsCSPService.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "nsAutoPtr.h"
 #include "nsGlobalWindow.h"
@@ -1563,37 +1562,38 @@ WebSocketImpl::Init(JSContext* aCx,
       // We crash here because we are sure that mURI is a valid URI, so either we
       // are OOM'ing or something else bad is happening.
       if (NS_WARN_IF(NS_FAILED(rv))) {
         MOZ_CRASH();
       }
     }
 
     // The 'real' nsHttpChannel of the websocket gets opened in the parent.
-    // Since we don't serialize the CSP within child and parent we have to
-    // perform the CSP check here instead of AsyncOpen2().
+    // Since we don't serialize the CSP within child and parent and also not
+    // the context, we have to perform content policy checks here instead of
+    // AsyncOpen2().
     // Please note that websockets can't follow redirects, hence there is no
     // need to perform a CSP check after redirects.
-    nsCOMPtr<nsIContentPolicy> cspService = do_GetService(CSPSERVICE_CONTRACTID);
-    int16_t shouldLoad = nsIContentPolicy::REJECT_REQUEST;
-    aRv = cspService->ShouldLoad(nsIContentPolicy::TYPE_WEBSOCKET,
-                                 uri,
-                                 nullptr, // aRequestOrigin not used within CSP
-                                 originDoc,
-                                 EmptyCString(), // aMimeTypeGuess
-                                 nullptr, // aExtra
-                                 aPrincipal,
-                                 &shouldLoad);
+    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+    aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
+                                    uri,
+                                    aPrincipal,
+                                    originDoc,
+                                    EmptyCString(),
+                                    nullptr,
+                                    &shouldLoad,
+                                    nsContentUtils::GetContentPolicy(),
+                                    nsContentUtils::GetSecurityManager());
 
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
     if (NS_CP_REJECTED(shouldLoad)) {
-      // Disallowed by CSP
+      // Disallowed by content policy
       aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
       return;
     }
   }
 
   // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
   // In such a case we have to upgrade ws: to wss: and also update mSecure
   // to reflect that upgrade. Please note that we can not upgrade from ws:
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2701,33 +2701,43 @@ nsINode::ParseSelectorList(const nsAStri
 {
   nsIDocument* doc = OwnerDoc();
   nsIDocument::SelectorCache& cache = doc->GetSelectorCache();
   nsCSSSelectorList* selectorList = nullptr;
   bool haveCachedList = cache.GetList(aSelectorString, &selectorList);
   if (haveCachedList) {
     if (!selectorList) {
       // Invalid selector.
-      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+      aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
+        NS_LITERAL_CSTRING("'") + NS_ConvertUTF16toUTF8(aSelectorString) +
+        NS_LITERAL_CSTRING("' is not a valid selector")
+      );
     }
     return selectorList;
   }
 
   nsCSSParser parser(doc->CSSLoader());
 
   aRv = parser.ParseSelectorString(aSelectorString,
                                    doc->GetDocumentURI(),
                                    0, // XXXbz get the line number!
                                    &selectorList);
   if (aRv.Failed()) {
     // We hit this for syntax errors, which are quite common, so don't
     // use NS_ENSURE_SUCCESS.  (For example, jQuery has an extended set
     // of selectors, but it sees if we can parse them first.)
     MOZ_ASSERT(aRv.ErrorCodeIs(NS_ERROR_DOM_SYNTAX_ERR),
                "Unexpected error, so cached version won't return it");
+
+    // Change the error message to match above.
+    aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
+      NS_LITERAL_CSTRING("'") + NS_ConvertUTF16toUTF8(aSelectorString) +
+      NS_LITERAL_CSTRING("' is not a valid selector")
+    );
+
     cache.CacheList(aSelectorString, nullptr);
     return nullptr;
   }
 
   // Filter out pseudo-element selectors from selectorList
   nsCSSSelectorList** slot = &selectorList;
   do {
     nsCSSSelectorList* cur = *slot;
--- a/dom/base/test/test_blobURL_expiring.html
+++ b/dom/base/test/test_blobURL_expiring.html
@@ -19,19 +19,19 @@ onmessage = function(e) {
     xhr.onload = function() {
       is(xhr.response, "123", "Response matches!");
       resolve();
     }
   })).then(function() {
     document.body.removeChild(iframe);
   }).then(function() {
     var xhr = new XMLHttpRequest();
-
+    xhr.open("GET", blobURL);
     try {
-      xhr.open("GET", blobURL);
+      xhr.send();
       ok(false, "The URL should be done!");
     } catch(e) {
       ok(true, "The URL should be done!");
     }
 
     SimpleTest.finish();
   });
 }
--- a/dom/base/test/test_blob_fragment_and_query.html
+++ b/dom/base/test/test_blob_fragment_and_query.html
@@ -29,30 +29,30 @@ function runTest() {
   var url = URL.createObjectURL(blob);
   ok(url, "We have a URI");
 
   var test = tests.shift();
 
   URL.revokeObjectURL(url + test.part);
 
   var xhr = new XMLHttpRequest();
-  try {
   xhr.open('GET', url + test.part);
-  } catch(e) {
-    ok(test.revoke, "This should fail!");
-    runTest();
-    return;
-  }
 
   xhr.onload = function() {
     is(xhr.responseText, 'hello world', 'URL: ' + url + test.part);
     runTest();
   }
 
-  xhr.send();
+  try {
+    xhr.send();
+  } catch(e) {
+    ok(test.revoke, "This should fail!");
+    runTest();
+    return;
+  }
   ok(!test.revoke, "This should succeed!");
 }
 
 SimpleTest.waitForExplicitFinish();
 runTest();
 
   </script>
 </body>
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -152,12 +152,14 @@ parent:
 
   async Profile(nsCString aProfile);
 
   // Answers to request about site data
   async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
 
   async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
 
+  intr GetKeyState(int32_t aVirtKey)
+    returns (int16_t aState);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -85,16 +85,19 @@ static CreateFileAPtr sCreateFileAStub =
 
 // Used with fix for flash fullscreen window loosing focus.
 static bool gDelayFlashFocusReplyUntilEval = false;
 // Used to fix GetWindowInfo problems with internal flash settings dialogs
 static WindowsDllInterceptor sUser32Intercept;
 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
 static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
 static HWND sBrowserHwnd = nullptr;
+// sandbox process doesn't get current key states.  So we need get it on chrome.
+typedef SHORT (WINAPI *GetKeyStatePtr)(int);
+static GetKeyStatePtr sGetKeyStatePtrStub = nullptr;
 #endif
 
 /* static */
 PluginModuleChild*
 PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
                                            base::ProcessId aOtherPid)
 {
     PluginModuleChild* child = new PluginModuleChild(false);
@@ -2061,16 +2064,30 @@ PMCGetWindowInfoHook(HWND hWnd, PWINDOWI
   // it's internal settings window. Post removing sub widgets for tabs, touch
   // this up so they get the rect they expect.
   // XXX potentially tie this to a specific major version?
   BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
   if (sBrowserHwnd && sBrowserHwnd == hWnd)
       pwi->rcWindow = pwi->rcClient;
   return result;
 }
+
+// static
+SHORT WINAPI
+PMCGetKeyState(int aVirtKey)
+{
+    PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+    if (chromeInstance) {
+        int16_t ret = 0;
+        if (chromeInstance->CallGetKeyState(aVirtKey, &ret)) {
+          return ret;
+        }
+    }
+    return sGetKeyStatePtrStub(aVirtKey);
+}
 #endif
 
 PPluginInstanceChild*
 PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
                                              const uint16_t& aMode,
                                              const InfallibleTArray<nsCString>& aNames,
                                              const InfallibleTArray<nsCString>& aValues)
 {
@@ -2081,22 +2098,28 @@ PluginModuleChild::AllocPPluginInstanceC
     // allocates an instance on its own. Make sure it gets the latest copy
     // of quirks once we have them. Also note, with process-per-tab, we may
     // have multiple PluginModuleChilds in the same plugin process, so only
     // initialize this once in gChromeInstance, which is a singleton.
     GetChrome()->InitQuirksModes(aMimeType);
     mQuirks = GetChrome()->mQuirks;
 
 #ifdef XP_WIN
+    sUser32Intercept.Init("user32.dll");
     if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
         !sGetWindowInfoPtrStub) {
-        sUser32Intercept.Init("user32.dll");
         sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(PMCGetWindowInfoHook),
                                  (void**) &sGetWindowInfoPtrStub);
     }
+
+    if ((mQuirks & QUIRK_FLASH_HOOK_GETKEYSTATE) &&
+        !sGetKeyStatePtrStub) {
+        sUser32Intercept.AddHook("GetKeyState", reinterpret_cast<intptr_t>(PMCGetKeyState),
+                                 (void**) &sGetKeyStatePtrStub);
+    }
 #endif
 
     return new PluginInstanceChild(&mFunctions, aMimeType, aMode, aNames,
                                    aValues);
 }
 
 void
 PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3360,9 +3360,25 @@ PluginModuleChromeParent::RecvProfile(co
     }
 
     mProfile = aProfile;
     mGatherer->GatheredOOPProfile();
 #endif
     return true;
 }
 
-
+bool
+PluginModuleParent::AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet)
+{
+    return false;
+}
+
+bool
+PluginModuleChromeParent::AnswerGetKeyState(const int32_t& aVirtKey,
+                                            int16_t* aRet)
+{
+#if defined(XP_WIN)
+    *aRet = ::GetKeyState(aVirtKey);
+    return true;
+#else
+    return PluginModuleParent::AnswerGetKeyState(aVirtKey, aRet);
+#endif
+}
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -203,16 +203,18 @@ protected:
     static void TimeoutChanged(const char* aPref, void* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual bool RecvNotifyContentModuleDestroyed() override { return true; }
 
     virtual bool RecvProfile(const nsCString& aProfile) override { return true; }
 
+    virtual bool AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+
     virtual bool RecvReturnClearSiteData(const NPError& aRv,
                                          const uint64_t& aCallbackId) override;
 
     virtual bool RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
                                          const uint64_t& aCallbackId) override;
 
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
 
@@ -493,16 +495,19 @@ class PluginModuleChromeParent
     void GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent);
     void StartProfiler(nsIProfilerStartParams* aParams);
     void StopProfiler();
 #endif
 
     virtual bool
     RecvProfile(const nsCString& aProfile) override;
 
+    virtual bool
+    AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+
 private:
     virtual void
     EnteredCxxStack() override;
 
     void
     ExitedCxxStack() override;
 
     mozilla::ipc::IProtocol* GetInvokingProtocol();
--- a/dom/plugins/ipc/PluginQuirks.cpp
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -30,16 +30,19 @@ int GetQuirksFromMimeTypeAndFilename(con
         quirks |= QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN;
 #ifdef OS_WIN
         quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
         quirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
         quirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
         quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
         quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE;
         quirks |= QUIRK_WINLESS_HOOK_IME;
+#if defined(_M_X64) || defined(__x86_64__)
+        quirks |= QUIRK_FLASH_HOOK_GETKEYSTATE;
+#endif
 #endif
     }
 
 #ifdef OS_WIN
     // QuickTime plugin usually loaded with audio/mpeg mimetype
     NS_NAMED_LITERAL_CSTRING(quicktime, "npqtplugin");
     if (FindInReadable(quicktime, aPluginFilename)) {
         quirks |= QUIRK_QUICKTIME_AVOID_SETWINDOW;
--- a/dom/plugins/ipc/PluginQuirks.h
+++ b/dom/plugins/ipc/PluginQuirks.h
@@ -50,16 +50,18 @@ enum PluginQuirks {
   // Work around a Flash bug where it fails to check the error code of a
   // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference
   // its char* output.
   QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN        = 1 << 10,
   // Win: Addresses a Unity bug with mouse capture.
   QUIRK_UNITY_FIXUP_MOUSE_CAPTURE                 = 1 << 11,
   // Win: Hook IMM32 API to handle IME event on windowless plugin
   QUIRK_WINLESS_HOOK_IME                          = 1 << 12,
+  // Win: Hook GetKeyState to get keyboard state on sandbox process
+  QUIRK_FLASH_HOOK_GETKEYSTATE                    = 1 << 13,
 };
 
 int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
                                      const nsCString& aPluginFilename);
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -492,25 +492,25 @@ WifiGeoPositionProvider.prototype = {
 
     let xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                         .createInstance(Ci.nsIXMLHttpRequest);
 
     this.notifyListener("locationUpdatePending");
 
     try {
       xhr.open("POST", url, true);
+      xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
     } catch (e) {
       this.notifyListener("notifyError",
                           [POSITION_UNAVAILABLE]);
       return;
     }
     xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
     xhr.responseType = "json";
     xhr.mozBackgroundRequest = true;
-    xhr.channel.loadFlags = Ci.nsIChannel.LOAD_ANONYMOUS;
     xhr.timeout = Services.prefs.getIntPref("geo.wifi.xhr.timeout");
     xhr.ontimeout = (function() {
       LOG("Location request XHR timed out.")
       this.notifyListener("notifyError",
                           [POSITION_UNAVAILABLE]);
     }).bind(this);
     xhr.onerror = (function() {
       this.notifyListener("notifyError",
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1521,21 +1521,21 @@ XMLHttpRequestMainThread::OpenInternal(c
   mAuthorRequestHeaders.Clear();
   ResetResponse();
 
   // Gecko-specific
   mFlagHadUploadListenersOnSend = false;
   mFlagAborted = false;
   mFlagTimedOut = false;
 
-  // The channel should really be created on send(), but we have a chrome-only
-  // XHR.channel API which necessitates creating the channel now, while doing
-  // the rest of the channel-setup later at send-time.
-  rv = CreateChannel();
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Per spec we should only create the channel on send(), but we have internal
+  // code that relies on the channel being created now, and that code is not
+  // always IsSystemXHR(). However, we're not supposed to throw channel-creation
+  // errors during open(), so we silently ignore those here.
+  CreateChannel();
 
   // Step 12
   if (mState != State::opened) {
     mState = State::opened;
     FireReadystatechangeEvent();
   }
 
   return NS_OK;
@@ -2627,17 +2627,17 @@ XMLHttpRequestMainThread::InitiateFetch(
     // ref to us to be extra safe.
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
 
     mErrorLoad = true;
 
     // Per spec, we throw on sync errors, but not async.
     if (mFlagSynchronous) {
-      return rv;
+      return NS_ERROR_DOM_NETWORK_ERR;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
@@ -2719,29 +2719,35 @@ XMLHttpRequestMainThread::Send(nsIVarian
   return SendInternal(&body);
 }
 
 nsresult
 XMLHttpRequestMainThread::SendInternal(const RequestBodyBase* aBody)
 {
   NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
 
-  PopulateNetworkInterfaceId();
+  // Steps 1 and 2
+  if (mState != State::opened || mFlagSend) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
   nsresult rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
-  if (mState != State::opened || // Step 1
-      mFlagSend || // Step 2
-      !mChannel) { // Gecko-specific
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  // If open() failed to create the channel, then throw a network error
+  // as per spec. We really should create the channel here in send(), but
+  // we have internal code relying on the channel being created in open().
+  if (!mChannel) {
+    return NS_ERROR_DOM_NETWORK_ERR;
   }
 
+  PopulateNetworkInterfaceId();
+
   // XXX We should probably send a warning to the JS console
   //     if there are no event listeners set and we are doing
   //     an asynchronous call.
 
   mUploadTransferred = 0;
   mUploadTotal = 0;
   // By default we don't have any upload, so mark upload complete.
   mUploadComplete = true;
@@ -2888,17 +2894,17 @@ XMLHttpRequestMainThread::SendInternal(c
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     // Per spec, silently fail on async request failures; throw for sync.
     if (mFlagSynchronous) {
-      return NS_ERROR_FAILURE;
+      return NS_ERROR_DOM_NETWORK_ERR;
     } else {
       // Defer the actual sending of async events just in case listeners
       // are attached after the send() method is called.
       NS_DispatchToCurrentThread(
         NewRunnableMethod<ProgressEventType>(this,
           &XMLHttpRequestMainThread::CloseRequestWithError,
           ProgressEventType::error));
       return NS_OK;
--- a/editor/reftests/xul/placeholder-reset.css
+++ b/editor/reftests/xul/placeholder-reset.css
@@ -1,8 +1,8 @@
 @namespace url('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul');
 @namespace html url('http://www.w3.org/1999/xhtml');
 
 /* We need to have a non-transparent placeholder so we can test it. */
-html|input::-moz-placeholder {
+html|input::placeholder {
   opacity: 1.0;
   color: graytext;
-}
\ No newline at end of file
+}
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -1106,18 +1106,19 @@ nsWindowWatcher::OpenWindowInternal(mozI
   nsCOMPtr<nsIPrincipal> subjectPrincipal =
     nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal() :
                                             nullptr;
 
   if (windowIsNew) {
     auto* docShell = static_cast<nsDocShell*>(newDocShell.get());
 
     // If this is not a chrome docShell, we apply originAttributes from the
-    // subjectPrincipal.
+    // subjectPrincipal unless if it's an expanded principal.
     if (subjectPrincipal &&
+        !subjectPrincipal->GetIsExpandedPrincipal() &&
         docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
       DocShellOriginAttributes attrs;
       attrs.InheritFromDocToChildDocShell(BasePrincipal::Cast(subjectPrincipal)->OriginAttributesRef());
 
       docShell->SetOriginAttributes(attrs);
     }
 
     // Now set the opener principal on the new window.  Note that we need to do
--- a/gfx/2d/BezierUtils.cpp
+++ b/gfx/2d/BezierUtils.cpp
@@ -324,13 +324,16 @@ CalculateDistanceToEllipticArc(const Poi
   Float B = a * b + c * d;
   Float C = a * a + c * c - 1;
 
   Float S = sqrt(B * B - A * C);
 
   Float n1 = (- B + S) / A;
   Float n2 = (- B - S) / A;
 
+  MOZ_ASSERT(n1 >= 0);
+  MOZ_ASSERT(n2 >= 0);
+
   return n1 < n2 ? n1 : n2;
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -157,16 +157,21 @@ struct LayerPropertiesBase : public Laye
   {
     MOZ_COUNT_CTOR(LayerPropertiesBase);
   }
   ~LayerPropertiesBase()
   {
     MOZ_COUNT_DTOR(LayerPropertiesBase);
   }
 
+protected:
+  LayerPropertiesBase(const LayerPropertiesBase& a) = delete;
+  LayerPropertiesBase& operator=(const LayerPropertiesBase& a) = delete;
+
+public:
   virtual nsIntRegion ComputeDifferences(Layer* aRoot,
                                          NotifySubDocInvalidationFunc aCallback,
                                          bool* aGeometryChanged);
 
   virtual void MoveBy(const IntPoint& aOffset);
 
   nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
                             bool& aGeometryChanged)
@@ -277,16 +282,21 @@ struct ContainerLayerProperties : public
     , mPreYScale(aLayer->GetPreYScale())
   {
     for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
       child->CheckCanary();
       mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));
     }
   }
 
+protected:
+  ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
+  ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
+
+public:
   nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                     bool& aGeometryChanged) override
   {
     // Make sure we got our virtual call right
     mSubtypeCanary.Check();
     ContainerLayer* container = mLayer->AsContainerLayer();
     nsIntRegion invalidOfLayer; // Invalid regions of this layer.
     nsIntRegion result;         // Invliad regions for children only.
@@ -434,16 +444,21 @@ struct ContainerLayerProperties : public
 struct ColorLayerProperties : public LayerPropertiesBase
 {
   explicit ColorLayerProperties(ColorLayer *aLayer)
     : LayerPropertiesBase(aLayer)
     , mColor(aLayer->GetColor())
     , mBounds(aLayer->GetBounds())
   { }
 
+protected:
+  ColorLayerProperties(const ColorLayerProperties& a) = delete;
+  ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
+
+public:
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
 
     if (mColor != color->GetColor()) {
       aGeometryChanged = true;
       return NewTransformedBounds();
--- a/gfx/layers/LayerTreeInvalidation.h
+++ b/gfx/layers/LayerTreeInvalidation.h
@@ -27,16 +27,23 @@ typedef void (*NotifySubDocInvalidationF
                                              const nsIntRegion& aRegion);
 
 /**
  * A set of cached layer properties (including those of child layers),
  * used for comparing differences in layer trees.
  */
 struct LayerProperties
 {
+protected:
+  LayerProperties() {}
+
+  LayerProperties(const LayerProperties& a) = delete;
+  LayerProperties& operator=(const LayerProperties& a) = delete;
+
+public:
   virtual ~LayerProperties() {}
 
   /**
    * Copies the current layer tree properties into
    * a new LayerProperties object.
    *
    * @param Layer tree to copy, or nullptr if we have no 
    * initial layer tree.
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -185,17 +185,18 @@ nsPNGDecoder::PostHasTransparencyIfNeede
 // CreateFrame() is used for both simple and animated images.
 nsresult
 nsPNGDecoder::CreateFrame(const FrameInfo& aFrameInfo)
 {
   MOZ_ASSERT(HasSize());
   MOZ_ASSERT(!IsMetadataDecode());
 
   // Check if we have transparency, and send notifications if needed.
-  auto transparency = GetTransparencyType(aFrameInfo.mFormat, aFrameInfo.mFrameRect);
+  auto transparency = GetTransparencyType(aFrameInfo.mFormat,
+                                          aFrameInfo.mFrameRect);
   PostHasTransparencyIfNeeded(transparency);
   SurfaceFormat format = transparency == TransparencyType::eNone
                        ? SurfaceFormat::B8G8R8X8
                        : SurfaceFormat::B8G8R8A8;
 
   // Make sure there's no animation or padding if we're downscaling.
   MOZ_ASSERT_IF(Size() != OutputSize(), mNumFrames == 0);
   MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation());
@@ -681,23 +682,25 @@ nsPNGDecoder::info_callback(png_structp 
       MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
                              "for an animated image?");
       png_error(decoder->mPNG, "Invalid downscale attempt"); // Abort decode.
     }
   }
 #endif
 
   if (decoder->IsMetadataDecode()) {
-    // If we are animated then the first frame rect is either: 1) the whole image
-    // if the IDAT chunk is part of the animation 2) the frame rect of the first
-    // fDAT chunk otherwise. If we are not animated then we want to make sure to
-    // call PostHasTransparency in the metadata decode if we need to. So it's okay
-    // to pass IntRect(0, 0, width, height) here for animated images; they will
-    // call with the proper first frame rect in the full decode.
-    auto transparency = decoder->GetTransparencyType(decoder->format, frameRect);
+    // If we are animated then the first frame rect is either:
+    // 1) the whole image if the IDAT chunk is part of the animation
+    // 2) the frame rect of the first fDAT chunk otherwise.
+    // If we are not animated then we want to make sure to call
+    // PostHasTransparency in the metadata decode if we need to. So it's
+    // okay to pass IntRect(0, 0, width, height) here for animated images;
+    // they will call with the proper first frame rect in the full decode.
+    auto transparency = decoder->GetTransparencyType(decoder->format,
+                                                     frameRect);
     decoder->PostHasTransparencyIfNeeded(transparency);
 
     // We have the metadata we're looking for, so stop here, before we allocate
     // buffers below.
     return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
   }
 
 #ifdef PNG_APNG_SUPPORTED
@@ -752,17 +755,18 @@ nsPNGDecoder::PostInvalidationIfNeeded()
   PostInvalidation(invalidRect->mInputSpaceRect,
                    Some(invalidRect->mOutputSpaceRect));
 }
 
 static NextPixel<uint32_t>
 PackRGBPixelAndAdvance(uint8_t*& aRawPixelInOut)
 {
   const uint32_t pixel =
-    gfxPackedPixel(0xFF, aRawPixelInOut[0], aRawPixelInOut[1], aRawPixelInOut[2]);
+    gfxPackedPixel(0xFF, aRawPixelInOut[0], aRawPixelInOut[1],
+                   aRawPixelInOut[2]);
   aRawPixelInOut += 3;
   return AsVariant(pixel);
 }
 
 static NextPixel<uint32_t>
 PackRGBAPixelAndAdvance(uint8_t*& aRawPixelInOut)
 {
   const uint32_t pixel =
@@ -826,17 +830,18 @@ nsPNGDecoder::row_callback(png_structp p
     // Advance to the next pass. We may have to do this multiple times because
     // libpng will skip passes if the image is so small that no pixels have
     // changed on a given pass, but ADAM7InterpolatingFilter needs to be reset
     // once for every pass to perform interpolation properly.
     decoder->mPipe.ResetToFirstRow();
     decoder->mPass++;
   }
 
-  const png_uint_32 height = static_cast<png_uint_32>(decoder->mFrameRect.height);
+  const png_uint_32 height =
+    static_cast<png_uint_32>(decoder->mFrameRect.height);
 
   if (row_num >= height) {
     // Bail if we receive extra rows. This is especially important because if we
     // didn't, we might overflow the deinterlacing buffer.
     MOZ_ASSERT_UNREACHABLE("libpng producing extra rows?");
     return;
   }
 
@@ -931,17 +936,18 @@ nsPNGDecoder::DoTerminate(png_structp aP
 
 void
 nsPNGDecoder::DoYield(png_structp aPNGStruct)
 {
   // Pause data processing. png_process_data_pause() returns how many bytes of
   // the data that was passed to png_process_data() have not been consumed yet.
   // We use this information to tell StreamingLexer where to place us in the
   // input stream when we come back from the yield.
-  png_size_t pendingBytes = png_process_data_pause(aPNGStruct, /* save = */ false);
+  png_size_t pendingBytes = png_process_data_pause(aPNGStruct,
+                                                   /* save = */ false);
 
   MOZ_ASSERT(pendingBytes < mLastChunkLength);
   size_t consumedBytes = mLastChunkLength - min(pendingBytes, mLastChunkLength);
 
   mNextTransition =
     Transition::ContinueUnbufferedAfterYield(State::PNG_DATA, consumedBytes);
 }
 
@@ -973,20 +979,22 @@ nsPNGDecoder::frame_info_callback(png_st
   const IntRect frameRect(png_get_next_frame_x_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_y_offset(png_ptr, decoder->mInfo),
                           png_get_next_frame_width(png_ptr, decoder->mInfo),
                           png_get_next_frame_height(png_ptr, decoder->mInfo));
   const bool isInterlaced = bool(decoder->interlacebuf);
 
 #ifndef MOZ_EMBEDDED_LIBPNG
   // if using system library, check frame_width and height against 0
-  if (frameRect.width == 0)
+  if (frameRect.width == 0) {
     png_error(png_ptr, "Frame width must not be 0");
-  if (frameRect.height == 0)
+  }
+  if (frameRect.height == 0) {
     png_error(png_ptr, "Frame height must not be 0");
+  }
 #endif
 
   const FrameInfo info { decoder->format, frameRect, isInterlaced };
 
   // If the previous frame was hidden, skip the yield (which will mislead the
   // caller, who will think the previous frame was real) and just allocate the
   // new frame here.
   if (previousFrameWasHidden) {
@@ -1063,17 +1071,17 @@ nsPNGDecoder::SpeedHistogram() const
 bool
 nsPNGDecoder::IsValidICO() const
 {
   // Only 32-bit RGBA PNGs are valid ICO resources; see here:
   //   http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
 
   // If there are errors in the call to png_get_IHDR, the error_callback in
   // nsPNGDecoder.cpp is called.  In this error callback we do a longjmp, so
-  // we need to save the jump buffer here. Oterwise we'll end up without a
+  // we need to save the jump buffer here. Otherwise we'll end up without a
   // proper callstack.
   if (setjmp(png_jmpbuf(mPNG))) {
     // We got here from a longjmp call indirectly from png_get_IHDR
     return false;
   }
 
   png_uint_32
       png_width,  // Unused
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -61,16 +61,32 @@ using mozilla::Move;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::PodZero;
 using mozilla::PositiveInfinity;
 using JS::AsmJSOption;
 using JS::GenericNaN;
 
 /*****************************************************************************/
+
+// The asm.js valid heap lengths are precisely the WASM valid heap lengths for ARM
+// greater or equal to MinHeapLength
+static const size_t MinHeapLength = PageSize;
+
+static uint32_t
+RoundUpToNextValidAsmJSHeapLength(uint32_t length)
+{
+    if (length <= MinHeapLength)
+        return MinHeapLength;
+
+    return wasm::RoundUpToNextValidARMImmediate(length);
+}
+
+
+/*****************************************************************************/
 // asm.js module object
 
 // The asm.js spec recognizes this set of builtin Math functions.
 enum AsmJSMathBuiltinFunction
 {
     AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
     AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
     AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
@@ -265,16 +281,17 @@ enum class CacheResult
 // AsmJSMetadata is built incrementally by ModuleValidator and then shared
 // immutably between AsmJSModules.
 
 struct AsmJSMetadataCacheablePod
 {
     uint32_t                numFFIs;
     uint32_t                srcLength;
     uint32_t                srcLengthWithRightBrace;
+    bool                    usesSimd;
 
     AsmJSMetadataCacheablePod() { PodZero(this); }
 };
 
 struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod
 {
     AsmJSGlobalVector       asmJSGlobals;
     AsmJSImportVector       asmJSImports;
@@ -1585,16 +1602,17 @@ class MOZ_STACK_CLASS ModuleValidator
     LifoAlloc             validationLifo_;
     FuncVector            functions_;
     FuncPtrTableVector    funcPtrTables_;
     GlobalMap             globalMap_;
     SigMap                sigMap_;
     ImportMap             importMap_;
     ArrayViewVector       arrayViews_;
     bool                  atomicsPresent_;
+    bool                  simdPresent_;
 
     // State used to build the AsmJSModule in finish():
     ModuleGenerator       mg_;
     MutableAsmJSMetadata  asmJSMetadata_;
 
     // Error reporting:
     UniqueChars           errorString_;
     uint32_t              errorOffset_;
@@ -1664,16 +1682,17 @@ class MOZ_STACK_CLASS ModuleValidator
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         functions_(cx),
         funcPtrTables_(cx),
         globalMap_(cx),
         sigMap_(cx),
         importMap_(cx),
         arrayViews_(cx),
         atomicsPresent_(false),
+        simdPresent_(false),
         mg_(ImportVector()),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false)
     {}
 
     ~ModuleValidator() {
         if (errorString_) {
@@ -1985,32 +2004,36 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::AtomicsBuiltinFunction, Move(fieldChars));
         g.pod.u.atomicsBuiltinFunc_ = func;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addSimdCtor(PropertyName* var, SimdType type, PropertyName* field) {
+        simdPresent_ = true;
+
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
         Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
         if (!global)
             return false;
         global->u.simdCtorType_ = type;
         if (!globalMap_.putNew(var, global))
             return false;
 
         AsmJSGlobal g(AsmJSGlobal::SimdCtor, Move(fieldChars));
         g.pod.u.simdCtorType_ = type;
         return asmJSMetadata_->asmJSGlobals.append(Move(g));
     }
     bool addSimdOperation(PropertyName* var, SimdType type, SimdOperation op, PropertyName* field) {
+        simdPresent_ = true;
+
         UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
         if (!fieldChars)
             return false;
 
         Global* global = validationLifo_.new_<Global>(Global::SimdOp);
         if (!global)
             return false;
         global->u.simdOp.type_ = type;
@@ -2279,16 +2302,18 @@ class MOZ_STACK_CLASS ModuleValidator
     }
     bool finishFunctionBodies() {
         return mg_.finishFuncDefs();
     }
     SharedModule finish() {
         if (!arrayViews_.empty())
             mg_.initMemoryUsage(atomicsPresent_ ? MemoryUsage::Shared : MemoryUsage::Unshared);
 
+        asmJSMetadata_->usesSimd = simdPresent_;
+
         MOZ_ASSERT(asmJSMetadata_->asmJSFuncNames.empty());
         for (const Func* func : functions_) {
             CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func->name());
             if (!funcName || !asmJSMetadata_->asmJSFuncNames.emplaceBack(Move(funcName)))
                 return nullptr;
         }
 
         uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
@@ -5559,35 +5584,37 @@ CheckSimdOperationCall(FunctionValidator
       case SimdOperation::Fn_swizzle:
         return CheckSimdSwizzle(f, call, opType, type);
       case SimdOperation::Fn_shuffle:
         return CheckSimdShuffle(f, call, opType, type);
 
       case SimdOperation::Fn_load:
       case SimdOperation::Fn_load1:
       case SimdOperation::Fn_load2:
-      case SimdOperation::Fn_load3:
         return CheckSimdLoad(f, call, opType, op, type);
       case SimdOperation::Fn_store:
       case SimdOperation::Fn_store1:
       case SimdOperation::Fn_store2:
-      case SimdOperation::Fn_store3:
         return CheckSimdStore(f, call, opType, op, type);
 
       case SimdOperation::Fn_select:
         return CheckSimdSelect(f, call, opType, type);
 
       case SimdOperation::Fn_splat:
         return CheckSimdSplat(f, call, opType, type);
 
       case SimdOperation::Fn_allTrue:
         return CheckSimdAllTrue(f, call, opType, type);
       case SimdOperation::Fn_anyTrue:
         return CheckSimdAnyTrue(f, call, opType, type);
 
+      case SimdOperation::Fn_load3:
+      case SimdOperation::Fn_store3:
+        return f.fail(call, "asm.js does not support 3-element SIMD loads or stores");
+
       case SimdOperation::Constructor:
         MOZ_CRASH("constructors are handled in CheckSimdCtorCall");
       case SimdOperation::Fn_fromFloat64x2Bits:
         MOZ_CRASH("NYI");
     }
     MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
 }
 
@@ -7805,19 +7832,31 @@ CheckBuffer(JSContext* cx, const AsmJSMe
                         memoryLength,
                         metadata.minMemoryLength));
         if (!msg)
             return false;
         return LinkFail(cx, msg.get());
     }
 
     if (buffer->is<ArrayBufferObject>()) {
-        Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
-        if (!ArrayBufferObject::prepareForAsmJS(cx, abheap))
+        // On 64-bit, bounds checks are statically removed so the huge guard
+        // region is always necessary. On 32-bit, allocating a guard page
+        // requires reallocating the incoming ArrayBuffer which could trigger
+        // OOM. Thus, only ask for a guard page when SIMD is used since SIMD
+        // allows unaligned memory access (see MaxMemoryAccessSize comment);
+#ifdef WASM_HUGE_MEMORY
+        bool needGuard = true;
+#else
+        bool needGuard = metadata.usesSimd;
+#endif
+        Rooted<ArrayBufferObject*> arrayBuffer(cx, &buffer->as<ArrayBufferObject>());
+        if (!ArrayBufferObject::prepareForAsmJS(cx, arrayBuffer, needGuard))
             return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
+
+        MOZ_ASSERT(arrayBuffer->isAsmJSMalloced() || arrayBuffer->isWasmMapped());
     } else {
         if (!buffer->as<SharedArrayBufferObject>().isPreparedForAsmJS())
             return LinkFail(cx, "SharedArrayBuffer must be created with wasm test mode enabled");
     }
 
     return true;
 }
 
@@ -8822,29 +8861,16 @@ js::AsmJSFunctionToString(JSContext* cx,
             return nullptr;
         if (!out.append(src))
             return nullptr;
     }
 
     return out.finishString();
 }
 
-// The asm.js valid heap lengths are precisely the WASM valid heap lengths for ARM
-// greater or equal to MinHeapLength
-static const size_t MinHeapLength = PageSize;
-
 bool
 js::IsValidAsmJSHeapLength(uint32_t length)
 {
     if (length < MinHeapLength)
         return false;
 
-    return wasm::IsValidARMLengthImmediate(length);
-}
-
-uint32_t
-js::RoundUpToNextValidAsmJSHeapLength(uint32_t length)
-{
-    if (length <= MinHeapLength)
-        return MinHeapLength;
-
-    return wasm::RoundUpToNextValidARMLengthImmediate(length);
-}
+    return wasm::IsValidARMImmediate(length);
+}
--- a/js/src/asmjs/AsmJS.h
+++ b/js/src/asmjs/AsmJS.h
@@ -79,14 +79,11 @@ AsmJSFunctionToString(JSContext* cx, Han
 extern JSString*
 AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
 
 // asm.js heap:
 
 extern bool
 IsValidAsmJSHeapLength(uint32_t length);
 
-extern uint32_t
-RoundUpToNextValidAsmJSHeapLength(uint32_t length);
-
 } // namespace js
 
 #endif // asmjs_asmjs_h
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -2899,134 +2899,37 @@ class BaseCompiler
         MOZ_CRASH("BaseCompiler platform hook: storeGlobalVarF64");
 #endif
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Heap access.
 
-    // TODO / CLEANUP - cloned from MIRGraph.cpp, should share.
-
-    bool needsBoundsCheckBranch(const MWasmMemoryAccess& access) const {
-        // A heap access needs a bounds-check branch if we're not relying on signal
-        // handlers to catch errors, and if it's not proven to be within bounds.
-        // We use signal-handlers on x64, but on x86 there isn't enough address
-        // space for a guard region.  Also, on x64 the atomic loads and stores
-        // can't (yet) use the signal handlers.
-
-#ifdef WASM_HUGE_MEMORY
-        return false;
-#else
-        return access.needsBoundsCheck();
-#endif
-    }
-
-    bool throwOnOutOfBounds(const MWasmMemoryAccess& access) {
-        return !isCompilingAsmJS();
-    }
-
-    // For asm.js code only: If we have a non-zero offset, it's possible that
-    // |ptr| itself is out of bounds, while adding the offset computes an
-    // in-bounds address. To catch this case, we need a second branch, which we
-    // emit out of line since it's unlikely to be needed in normal programs.
-    // For this, we'll generate an OffsetBoundsCheck OOL stub.
-
-    bool needsOffsetBoundsCheck(const MWasmMemoryAccess& access) const {
-        return isCompilingAsmJS() && access.offset() != 0;
-    }
-
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-    class OffsetBoundsCheck : public OutOfLineCode
-    {
-        Label* maybeOutOfBounds;
-        Register ptrReg;
-        int32_t offset;
-
-      public:
-        OffsetBoundsCheck(Label* maybeOutOfBounds, Register ptrReg, int32_t offset)
-          : maybeOutOfBounds(maybeOutOfBounds),
-            ptrReg(ptrReg),
-            offset(offset)
-        {}
-
-        void generate(MacroAssembler& masm) {
-            // asm.js code only:
-            //
-            // The access is heap[ptr + offset]. The inline code checks that
-            // ptr < heap.length - offset. We get here when that fails. We need to check
-            // for the case where ptr + offset >= 0, in which case the access is still
-            // in bounds.
-
-            MOZ_ASSERT(offset != 0,
-                       "An access without a constant offset doesn't need a separate "
-                       "OffsetBoundsCheck");
-            masm.cmp32(ptrReg, Imm32(-uint32_t(offset)));
-            if (maybeOutOfBounds)
-                masm.j(Assembler::Below, maybeOutOfBounds);
-            else
-                masm.j(Assembler::Below, wasm::JumpTarget::OutOfBounds);
-
-# ifdef JS_CODEGEN_X64
-            // In order to get the offset to wrap properly, we must sign-extend the
-            // pointer to 32-bits. We'll zero out the sign extension immediately
-            // after the access to restore asm.js invariants.
-            masm.movslq(ptrReg, ptrReg);
-# endif
-
-            masm.jmp(rejoin());
-        }
-    };
-
-    // CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch()
-
-    MOZ_MUST_USE
-    bool emitBoundsCheckBranch(const MWasmMemoryAccess& access, RegI32 ptr, Label* maybeFail) {
-        Label* pass = nullptr;
-
-        if (needsOffsetBoundsCheck(access)) {
-            auto* oolCheck = new(alloc_) OffsetBoundsCheck(maybeFail, ptr.reg, access.offset());
-            maybeFail = oolCheck->entry();
-            pass = oolCheck->rejoin();
-            if (!addOutOfLineCode(oolCheck))
-                return false;
-        }
-
-        // The bounds check is a comparison with an immediate value. The asm.js
-        // module linking process will add the length of the heap to the immediate
-        // field, so -access->endOffset() will turn into
-        // (heapLength - access->endOffset()), allowing us to test whether the end
-        // of the access is beyond the end of the heap.
-        MOZ_ASSERT(access.endOffset() >= 1,
-                   "need to subtract 1 to use JAE, see also AssemblerX86Shared::UpdateBoundsCheck");
-
-        uint32_t cmpOffset = masm.cmp32WithPatch(ptr.reg, Imm32(1 - access.endOffset())).offset();
-        if (maybeFail)
-            masm.j(Assembler::AboveOrEqual, maybeFail);
-        else
-            masm.j(Assembler::AboveOrEqual, wasm::JumpTarget::OutOfBounds);
-
-        if (pass)
-            masm.bind(pass);
-
-        masm.append(wasm::BoundsCheck(cmpOffset));
-        return true;
-    }
-
-    class OutOfLineLoadTypedArrayOOB : public OutOfLineCode
+    // Return true only for real asm.js (HEAP[i>>2]|0) accesses which have the
+    // peculiar property of not throwing on out-of-bounds. Everything else
+    // (wasm, SIMD.js, Atomics) throws on out-of-bounds.
+    bool isAsmJSAccess(const MWasmMemoryAccess& access) {
+        return isCompilingAsmJS() && !access.isSimdAccess() && !access.isAtomicAccess();
+    }
+
+#ifndef WASM_HUGE_MEMORY
+    class AsmJSLoadOOB : public OutOfLineCode
     {
         Scalar::Type viewType;
         AnyRegister dest;
+
       public:
-        OutOfLineLoadTypedArrayOOB(Scalar::Type viewType, AnyRegister dest)
+        AsmJSLoadOOB(Scalar::Type viewType, AnyRegister dest)
           : viewType(viewType),
             dest(dest)
         {}
 
         void generate(MacroAssembler& masm) {
+#if defined(JS_CODEGEN_X86)
             switch (viewType) {
               case Scalar::Float32x4:
               case Scalar::Int32x4:
               case Scalar::Int8x16:
               case Scalar::Int16x8:
               case Scalar::MaxTypedArrayViewType:
                 MOZ_CRASH("unexpected array type");
               case Scalar::Float32:
@@ -3043,252 +2946,126 @@ class BaseCompiler
               case Scalar::Uint32:
               case Scalar::Uint8Clamped:
                 masm.movePtr(ImmWord(0), dest.gpr());
                 break;
               case Scalar::Int64:
                 MOZ_CRASH("unexpected array type");
             }
             masm.jump(rejoin());
+#else
+            Unused << viewType;
+            Unused << dest;
+            MOZ_CRASH("Compiler bug: Unexpected platform.");
+#endif
         }
     };
-
-    MOZ_MUST_USE
-    bool maybeEmitLoadBoundsCheck(const MWasmMemoryAccess& access, RegI32 ptr, AnyRegister dest,
-                                  OutOfLineCode** ool)
-    {
-        *ool = nullptr;
-        if (!needsBoundsCheckBranch(access))
-            return true;
-
-        if (throwOnOutOfBounds(access))
-            return emitBoundsCheckBranch(access, ptr, nullptr);
-
-        // TODO / MEMORY: We'll allocate *a lot* of these OOL objects,
-        // thus risking OOM on a platform that is already
-        // memory-constrained.  We could opt to allocate this path
-        // in-line instead.
-        *ool = new (alloc_) OutOfLineLoadTypedArrayOOB(access.accessType(), dest);
-        if (!addOutOfLineCode(*ool))
-            return false;
-
-        return emitBoundsCheckBranch(access, ptr, (*ool)->entry());
-    }
-
+#endif
+
+  private:
+    void checkOffset(MWasmMemoryAccess* access, RegI32 ptr) {
+        if (access->offset() >= OffsetGuardLimit) {
+            masm.branchAdd32(Assembler::CarrySet,
+                             Imm32(access->offset()), ptr.reg,
+                             JumpTarget::OutOfBounds);
+            access->clearOffset();
+        }
+    }
+
+  public:
     MOZ_MUST_USE
-    bool maybeEmitStoreBoundsCheck(const MWasmMemoryAccess& access, RegI32 ptr, Label** rejoin) {
-        *rejoin = nullptr;
-        if (!needsBoundsCheckBranch(access))
-            return true;
-
-        if (throwOnOutOfBounds(access))
-            return emitBoundsCheckBranch(access, ptr, nullptr);
-
-        *rejoin = newLabel();
-        if (!*rejoin)
-            return false;
-
-        return emitBoundsCheckBranch(access, ptr, *rejoin);
-    }
-
-    void cleanupAfterBoundsCheck(const MWasmMemoryAccess& access, RegI32 ptr) {
-# ifdef JS_CODEGEN_X64
-        if (needsOffsetBoundsCheck(access)) {
-            // Zero out the high 32 bits, in case the OffsetBoundsCheck code had to
-            // sign-extend (movslq) the pointer value to get wraparound to work.
-            masm.movl(ptr.reg, ptr.reg);
-        }
-# endif
-    }
-
-    MOZ_MUST_USE
-    bool loadHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg dest) {
-        if (access.offset() > INT32_MAX) {
-            masm.jump(wasm::JumpTarget::OutOfBounds);
-            return true;
-        }
+    bool load(MWasmMemoryAccess access, RegI32 ptr, AnyReg dest) {
+        checkOffset(&access, ptr);
 
         OutOfLineCode* ool = nullptr;
-        if (!maybeEmitLoadBoundsCheck(access, ptr, dest.any(), &ool))
-            return false;
+#ifndef WASM_HUGE_MEMORY
+        if (isAsmJSAccess(access)) {
+            ool = new (alloc_) AsmJSLoadOOB(access.accessType(), dest.any());
+            if (!addOutOfLineCode(ool))
+                return false;
+
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, ool->entry());
+        } else {
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, JumpTarget::OutOfBounds);
+        }
+#endif
 
 # if defined(JS_CODEGEN_X64)
         Operand srcAddr(HeapReg, ptr.reg, TimesOne, access.offset());
 
         uint32_t before = masm.size();
-        if (dest.tag == AnyReg::I64) {
-            Register out = dest.i64().reg.reg;
-            switch (access.accessType()) {
-              case Scalar::Int8:      masm.movsbq(srcAddr, out); break;
-              case Scalar::Uint8:     masm.movzbq(srcAddr, out); break;
-              case Scalar::Int16:     masm.movswq(srcAddr, out); break;
-              case Scalar::Uint16:    masm.movzwq(srcAddr, out); break;
-              case Scalar::Int32:     masm.movslq(srcAddr, out); break;
-              // Int32 to int64 moves zero-extend by default.
-              case Scalar::Uint32:    masm.movl(srcAddr, out); break;
-              case Scalar::Int64:     masm.movq(srcAddr, out); break;
-              default:
-                MOZ_CRASH("Compiler bug: Unexpected array type in int64 load");
-            }
-        } else {
-            switch (access.accessType()) {
-              case Scalar::Int8:      masm.movsbl(srcAddr, dest.i32().reg); break;
-              case Scalar::Uint8:     masm.movzbl(srcAddr, dest.i32().reg); break;
-              case Scalar::Int16:     masm.movswl(srcAddr, dest.i32().reg); break;
-              case Scalar::Uint16:    masm.movzwl(srcAddr, dest.i32().reg); break;
-              case Scalar::Int32:
-              case Scalar::Uint32:    masm.movl(srcAddr, dest.i32().reg); break;
-              case Scalar::Float32:   masm.loadFloat32(srcAddr, dest.f32().reg); break;
-              case Scalar::Float64:   masm.loadDouble(srcAddr, dest.f64().reg); break;
-              default:
-                MOZ_CRASH("Compiler bug: Unexpected array type");
-            }
-        }
-
-        if (isCompilingAsmJS())
-            masm.append(MemoryAccess(before, MemoryAccess::CarryOn, MemoryAccess::WrapOffset));
-        // TODO: call verifyHeapAccessDisassembly somehow
+        if (dest.tag == AnyReg::I64)
+            masm.wasmLoadI64(access.accessType(), srcAddr, dest.i64().reg);
+        else
+            masm.wasmLoad(access.accessType(), 0, srcAddr, dest.any());
+
+        if (isAsmJSAccess(access))
+            masm.append(MemoryAccess(before));
 # elif defined(JS_CODEGEN_X86)
         Operand srcAddr(ptr.reg, access.offset());
 
-        if (dest.tag == AnyReg::I64)
-            MOZ_CRASH("Not implemented: I64 support");
-
-        bool mustMove = access.byteSize() == 1 && !singleByteRegs_.has(dest.i32().reg);
-        switch (access.accessType()) {
-          case Scalar::Int8:
-          case Scalar::Uint8: {
-            Register rd = mustMove ? ScratchRegX86 : dest.i32().reg;
-            if (access.accessType() == Scalar::Int8)
-                masm.movsblWithPatch(srcAddr, rd);
-            else
-                masm.movzblWithPatch(srcAddr, rd);
-            break;
-          }
-          case Scalar::Int16:     masm.movswlWithPatch(srcAddr, dest.i32().reg); break;
-          case Scalar::Uint16:    masm.movzwlWithPatch(srcAddr, dest.i32().reg); break;
-          case Scalar::Int32:
-          case Scalar::Uint32:    masm.movlWithPatch(srcAddr, dest.i32().reg); break;
-          case Scalar::Float32:   masm.vmovssWithPatch(srcAddr, dest.f32().reg); break;
-          case Scalar::Float64:   masm.vmovsdWithPatch(srcAddr, dest.f64().reg); break;
-          default:
-            MOZ_CRASH("Compiler bug: Unexpected array type");
-        }
-        uint32_t after = masm.size();
-        if (mustMove)
+        bool byteRegConflict = access.byteSize() == 1 && !singleByteRegs_.has(dest.i32().reg);
+        AnyRegister out = byteRegConflict ? AnyRegister(ScratchRegX86) : dest.any();
+
+        masm.wasmLoad(access.accessType(), 0, srcAddr, out);
+
+        if (byteRegConflict)
             masm.mov(ScratchRegX86, dest.i32().reg);
-
-        masm.append(wasm::MemoryAccess(after));
-        // TODO: call verifyHeapAccessDisassembly somehow
 # else
         MOZ_CRASH("Compiler bug: Unexpected platform.");
 # endif
 
-        if (ool) {
-            cleanupAfterBoundsCheck(access, ptr);
+        if (ool)
             masm.bind(ool->rejoin());
-        }
         return true;
     }
 
     MOZ_MUST_USE
-    bool storeHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg src) {
-        if (access.offset() > INT32_MAX) {
-            masm.jump(wasm::JumpTarget::OutOfBounds);
-            return true;
-        }
-
-        Label* rejoin = nullptr;
-        if (!maybeEmitStoreBoundsCheck(access, ptr, &rejoin))
-            return false;
-
+    bool store(MWasmMemoryAccess access, RegI32 ptr, AnyReg src) {
+        checkOffset(&access, ptr);
+
+        Label rejoin;
+#ifndef WASM_HUGE_MEMORY
+        if (isAsmJSAccess(access))
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, &rejoin);
+        else
+            masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr.reg, JumpTarget::OutOfBounds);
+#endif
+
+        // Emit the store
 # if defined(JS_CODEGEN_X64)
         Operand dstAddr(HeapReg, ptr.reg, TimesOne, access.offset());
 
-        Register intReg = Register::Invalid();
-        if (src.tag == AnyReg::I32)
-            intReg = src.i32().reg;
-        else if (src.tag == AnyReg::I64)
-            intReg = src.i64().reg.reg;
-
         uint32_t before = masm.size();
-        switch (access.accessType()) {
-          case Scalar::Int8:
-          case Scalar::Uint8:        masm.movb(intReg, dstAddr); break;
-          case Scalar::Int16:
-          case Scalar::Uint16:       masm.movw(intReg, dstAddr); break;
-          case Scalar::Int32:
-          case Scalar::Uint32:       masm.movl(intReg, dstAddr); break;
-          case Scalar::Int64:        masm.movq(intReg, dstAddr); break;
-          case Scalar::Float32:      masm.storeFloat32(src.f32().reg, dstAddr); break;
-          case Scalar::Float64:      masm.storeDouble(src.f64().reg, dstAddr); break;
-          default:
-            MOZ_CRASH("Compiler bug: Unexpected array type");
-        }
+        masm.wasmStore(access.accessType(), 0, src.any(), dstAddr);
 
         if (isCompilingAsmJS())
-            masm.append(MemoryAccess(before, MemoryAccess::CarryOn, MemoryAccess::WrapOffset));
-        // TODO: call verifyHeapAccessDisassembly somehow
+            masm.append(MemoryAccess(before));
 # elif defined(JS_CODEGEN_X86)
         Operand dstAddr(ptr.reg, access.offset());
 
-        if (src.tag == AnyReg::I64)
-            MOZ_CRASH("Not implemented: I64 support");
-
-        bool didMove = false;
+        AnyRegister value;
         if (access.byteSize() == 1 && !singleByteRegs_.has(src.i32().reg)) {
-            didMove = true;
             masm.mov(src.i32().reg, ScratchRegX86);
+            value = AnyRegister(ScratchRegX86);
+        } else {
+            value = src.any();
         }
-        switch (access.accessType()) {
-          case Scalar::Int8:
-          case Scalar::Uint8: {
-            Register rs = src.i32().reg;
-            Register rt = didMove ? ScratchRegX86 : rs;
-            masm.movbWithPatch(rt, dstAddr);
-            break;
-          }
-          case Scalar::Int16:
-          case Scalar::Uint16:       masm.movwWithPatch(src.i32().reg, dstAddr); break;
-          case Scalar::Int32:
-          case Scalar::Uint32:       masm.movlWithPatch(src.i32().reg, dstAddr); break;
-          case Scalar::Float32:      masm.vmovssWithPatch(src.f32().reg, dstAddr); break;
-          case Scalar::Float64:      masm.vmovsdWithPatch(src.f64().reg, dstAddr); break;
-          default:
-              MOZ_CRASH("Compiler bug: Unexpected array type");
-        }
-        uint32_t after = masm.size();
-
-        masm.append(wasm::MemoryAccess(after));
-        // TODO: call verifyHeapAccessDisassembly somehow
+
+        masm.wasmStore(access.accessType(), 0, value, dstAddr);
 # else
         MOZ_CRASH("Compiler bug: unexpected platform");
 # endif
 
-        if (rejoin) {
-            cleanupAfterBoundsCheck(access, ptr);
-            masm.bind(rejoin);
-        }
+        if (rejoin.used())
+            masm.bind(&rejoin);
+
         return true;
     }
 
-#else
-
-    MOZ_MUST_USE
-    bool loadHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg dest) {
-        MOZ_CRASH("BaseCompiler platform hook: loadHeap");
-    }
-
-    MOZ_MUST_USE
-    bool storeHeap(const MWasmMemoryAccess& access, RegI32 ptr, AnyReg src) {
-        MOZ_CRASH("BaseCompiler platform hook: storeHeap");
-    }
-
-#endif
-
     ////////////////////////////////////////////////////////////
 
     // Generally speaking, ABOVE this point there should be no value
     // stack manipulation (calls to popI32 etc).
 
     // Generally speaking, BELOW this point there should be no
     // platform dependencies.  We make an exception for x86 register
     // targeting, which is not too hard to keep clean.
@@ -5727,50 +5504,50 @@ BaseCompiler::emitLoad(ValType type, Sca
     // TODO / OPTIMIZE: Disable bounds checking on constant accesses
     // below the minimum heap length.
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
 
     switch (type) {
       case ValType::I32: {
         RegI32 rp = popI32();
-        if (!loadHeap(access, rp, AnyReg(rp)))
+        if (!load(access, rp, AnyReg(rp)))
             return false;
         pushI32(rp);
         break;
       }
       case ValType::I64: {
         RegI32 rp = popI32();
         RegI64 rv = needI64();
-        if (!loadHeap(access, rp, AnyReg(rv)))
+        if (!load(access, rp, AnyReg(rv)))
             return false;
         pushI64(rv);
         freeI32(rp);
         break;
       }
       case ValType::F32: {
         RegI32 rp = popI32();
         RegF32 rv = needF32();
-        if (!loadHeap(access, rp, AnyReg(rv)))
+        if (!load(access, rp, AnyReg(rv)))
             return false;
         pushF32(rv);
         freeI32(rp);
         break;
       }
       case ValType::F64: {
         RegI32 rp = popI32();
         RegF64 rv = needF64();
-        if (!loadHeap(access, rp, AnyReg(rv)))
+        if (!load(access, rp, AnyReg(rv)))
             return false;
         pushF64(rv);
         freeI32(rp);
         break;
       }
       default:
-        MOZ_CRASH("loadHeap type");
+        MOZ_CRASH("load type");
         break;
     }
     return true;
 }
 
 bool
 BaseCompiler::emitStore(ValType resultType, Scalar::Type viewType)
 {
@@ -5786,51 +5563,51 @@ BaseCompiler::emitStore(ValType resultTy
     // below the minimum heap length.
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
 
     switch (resultType) {
       case ValType::I32: {
         RegI32 rp, rv;
         pop2xI32(&rp, &rv);
-        if (!storeHeap(access, rp, AnyReg(rv)))
+        if (!store(access, rp, AnyReg(rv)))
             return false;
         freeI32(rp);
         pushI32(rv);
         break;
       }
       case ValType::I64: {
         RegI64 rv = popI64();
         RegI32 rp = popI32();
-        if (!storeHeap(access, rp, AnyReg(rv)))
+        if (!store(access, rp, AnyReg(rv)))
             return false;
         freeI32(rp);
         pushI64(rv);
         break;
       }
       case ValType::F32: {
         RegF32 rv = popF32();
         RegI32 rp = popI32();
-        if (!storeHeap(access, rp, AnyReg(rv)))
+        if (!store(access, rp, AnyReg(rv)))
             return false;
         freeI32(rp);
         pushF32(rv);
         break;
       }
       case ValType::F64: {
         RegF64 rv = popF64();
         RegI32 rp = popI32();
-        if (!storeHeap(access, rp, AnyReg(rv)))
+        if (!store(access, rp, AnyReg(rv)))
             return false;
         freeI32(rp);
         pushF64(rv);
         break;
       }
       default:
-        MOZ_CRASH("storeHeap type");
+        MOZ_CRASH("store type");
         break;
     }
     return true;
 }
 
 bool
 BaseCompiler::emitSelect()
 {
@@ -6075,28 +5852,28 @@ BaseCompiler::emitStoreWithCoercion(ValT
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
 
     if (resultType == ValType::F32 && viewType == Scalar::Float64) {
         RegF32 rv = popF32();
         RegF64 rw = needF64();
         masm.convertFloat32ToDouble(rv.reg, rw.reg);
         RegI32 rp = popI32();
-        if (!storeHeap(access, rp, AnyReg(rw)))
+        if (!store(access, rp, AnyReg(rw)))
             return false;
         pushF32(rv);
         freeI32(rp);
         freeF64(rw);
     }
     else if (resultType == ValType::F64 && viewType == Scalar::Float32) {
         RegF64 rv = popF64();
         RegF32 rw = needF32();
         masm.convertDoubleToFloat32(rv.reg, rw.reg);
         RegI32 rp = popI32();
-        if (!storeHeap(access, rp, AnyReg(rw)))
+        if (!store(access, rp, AnyReg(rw)))
             return false;
         pushF64(rv);
         freeI32(rp);
         freeF32(rw);
     }
     else
         MOZ_CRASH("unexpected coerced store");
 
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -101,35 +101,44 @@ StaticallyLink(CodeSegment& cs, const Li
 
     // These constants are logically part of the code:
 
     *(double*)(cs.globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(cs.globalData() + NaN32GlobalDataOffset) = GenericNaN();
 }
 
 static void
-SpecializeToMemory(CodeSegment& cs, const Metadata& metadata, HandleWasmMemoryObject memory)
+SpecializeToMemory(uint8_t* prevMemoryBase, CodeSegment& cs, const Metadata& metadata,
+                   ArrayBufferObjectMaybeShared& buffer)
 {
-    if (!metadata.boundsChecks.empty()) {
-        uint32_t length = memory->buffer().wasmBoundsCheckLimit();
-        MOZ_RELEASE_ASSERT(length == LegalizeMapLength(length));
-        MOZ_RELEASE_ASSERT(length >= memory->buffer().wasmActualByteLength());
+#ifdef WASM_HUGE_MEMORY
+    MOZ_RELEASE_ASSERT(metadata.boundsChecks.empty());
+    MOZ_RELEASE_ASSERT(metadata.isAsmJS() || metadata.memoryAccesses.empty());
+#else
+    uint32_t limit = buffer.wasmBoundsCheckLimit();
+    MOZ_RELEASE_ASSERT(IsValidBoundsCheckImmediate(limit));
 
-        for (const BoundsCheck& check : metadata.boundsChecks)
-            Assembler::UpdateBoundsCheck(check.patchAt(cs.base()), length);
-    }
+    for (const BoundsCheck& check : metadata.boundsChecks)
+        MacroAssembler::wasmPatchBoundsCheck(check.patchAt(cs.base()), limit);
+#endif
 
 #if defined(JS_CODEGEN_X86)
-    uint8_t* base = memory->buffer().dataPointerEither().unwrap();
-    for (const MemoryAccess& access : metadata.memoryAccesses) {
-        // Patch memory pointer immediate.
-        void* addr = access.patchMemoryPtrImmAt(cs.base());
-        uint32_t disp = reinterpret_cast<uint32_t>(X86Encoding::GetPointer(addr));
-        MOZ_ASSERT(disp <= INT32_MAX);
-        X86Encoding::SetPointer(addr, (void*)(base + disp));
+    uint8_t* memoryBase = buffer.dataPointerEither().unwrap(/* code patching */);
+    if (prevMemoryBase != memoryBase) {
+        for (const MemoryAccess& access : metadata.memoryAccesses) {
+            void* patchAt = access.patchMemoryPtrImmAt(cs.base());
+
+            uint8_t* prevImm = (uint8_t*)X86Encoding::GetPointer(patchAt);
+            MOZ_ASSERT(prevImm >= prevMemoryBase);
+
+            uint32_t offset = prevImm - prevMemoryBase;
+            MOZ_ASSERT(offset <= INT32_MAX);
+
+            X86Encoding::SetPointer(patchAt, memoryBase + offset);
+        }
     }
 #endif
 }
 
 static bool
 SendCodeRangesToProfiler(JSContext* cx, CodeSegment& cs, const Bytes& bytecode,
                          const Metadata& metadata)
 {
@@ -227,17 +236,17 @@ CodeSegment::create(JSContext* cx,
     {
         JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
         AutoFlushICache afc("CodeSegment::create");
         AutoFlushICache::setRange(uintptr_t(codeBase), cs->codeLength());
 
         memcpy(codeBase, bytecode.begin(), bytecode.length());
         StaticallyLink(*cs, linkData, cx);
         if (memory)
-            SpecializeToMemory(*cs, metadata, memory);
+            SpecializeToMemory(nullptr, *cs, metadata, memory->buffer());
     }
 
     if (!ExecutableAllocator::makeExecutable(codeBase, cs->codeLength())) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     if (!SendCodeRangesToProfiler(cx, *cs, bytecode, metadata))
@@ -253,16 +262,26 @@ CodeSegment::~CodeSegment()
 
     MOZ_ASSERT(wasmCodeAllocations > 0);
     wasmCodeAllocations--;
 
     MOZ_ASSERT(totalLength() > 0);
     DeallocateExecutableMemory(bytes_, totalLength(), gc::SystemPageSize());
 }
 
+void
+CodeSegment::onMovingGrow(uint8_t* prevMemoryBase, const Metadata& metadata, ArrayBufferObject& buffer)
+{
+    AutoWritableJitCode awjc(base(), codeLength());
+    AutoFlushICache afc("CodeSegment::onMovingGrow");
+    AutoFlushICache::setRange(uintptr_t(base()), codeLength());
+
+    SpecializeToMemory(prevMemoryBase, *this, metadata, buffer);
+}
+
 size_t
 FuncDefExport::serializedSize() const
 {
     return sig_.serializedSize() +
            sizeof(pod);
 }
 
 uint8_t*
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -95,16 +95,21 @@ class CodeSegment
     // enter/exit.
 
     bool containsFunctionPC(const void* pc) const {
         return pc >= base() && pc < (base() + functionCodeLength_);
     }
     bool containsCodePC(const void* pc) const {
         return pc >= base() && pc < (base() + codeLength_);
     }
+
+    // onMovingGrow must be called if the memory passed to 'create' performs a
+    // moving grow operation.
+
+    void onMovingGrow(uint8_t* prevMemoryBase, const Metadata& metadata, ArrayBufferObject& buffer);
 };
 
 // ShareableBytes is a ref-counted vector of bytes which are incrementally built
 // during compilation and then immutably shared.
 
 struct ShareableBytes : ShareableBase<ShareableBytes>
 {
     // Vector is 'final', so instead make Vector a member and add boilerplate.
@@ -512,16 +517,17 @@ class Code
     CacheableCharsVector     funcLabels_;
     bool                     profilingEnabled_;
 
   public:
     Code(UniqueCodeSegment segment,
          const Metadata& metadata,
          const ShareableBytes* maybeBytecode);
 
+    CodeSegment& segment() { return *segment_; }
     const CodeSegment& segment() const { return *segment_; }
     const Metadata& metadata() const { return *metadata_; }
 
     // Frame iterator support:
 
     const CallSite* lookupCallSite(void* returnAddress) const;
     const CodeRange* lookupRange(void* pc) const;
 #ifdef WASM_HUGE_MEMORY
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -287,52 +287,16 @@ Instance::growMemory_i32(Instance* insta
 }
 
 /* static */ uint32_t
 Instance::currentMemory_i32(Instance* instance)
 {
     return instance->currentMemory();
 }
 
-uint32_t
-Instance::growMemory(uint32_t delta)
-{
-    MOZ_RELEASE_ASSERT(memory_);
-
-    // Using uint64_t to avoid worrying about overflows in safety comp.
-    uint64_t curNumPages = currentMemory();
-    uint64_t newNumPages = curNumPages + (uint64_t) delta;
-
-    if (metadata().maxMemoryLength) {
-        ArrayBufferObject &buf = memory_->buffer().as<ArrayBufferObject>();
-        // Guaranteed by instantiateMemory
-        MOZ_RELEASE_ASSERT(buf.wasmMaxSize() && buf.wasmMaxSize() <= metadata().maxMemoryLength);
-
-        if (newNumPages * wasm::PageSize > buf.wasmMaxSize().value())
-            return (uint32_t) -1;
-
-        // Try to grow the memory
-        if (!buf.growForWasm(delta))
-            return (uint32_t) -1;
-    } else {
-        return -1; // TODO: implement grow_memory w/o max when we add realloc
-    }
-
-    return curNumPages;
-}
-
-uint32_t
-Instance::currentMemory()
-{
-    MOZ_RELEASE_ASSERT(memory_);
-    uint32_t curMemByteLen = memory_->buffer().wasmActualByteLength();
-    MOZ_ASSERT(curMemByteLen % wasm::PageSize == 0);
-    return curMemByteLen / wasm::PageSize;
-}
-
 Instance::Instance(JSContext* cx,
                    Handle<WasmInstanceObject*> object,
                    UniqueCode code,
                    HandleWasmMemoryObject memory,
                    SharedTableVector&& tables,
                    Handle<FunctionVector> funcImports,
                    const ValVector& globalImports)
   : compartment_(cx->compartment()),
@@ -406,16 +370,19 @@ Instance::Instance(JSContext* cx,
 
     for (size_t i = 0; i < tables_.length(); i++)
         *addressOfTableBase(i) = tables_[i]->base();
 }
 
 bool
 Instance::init(JSContext* cx)
 {
+    if (memory_ && memory_->movingGrowable() && !memory_->addMovingGrowObserver(cx, object_))
+        return false;
+
     if (!metadata().sigIds.empty()) {
         ExclusiveData<SigIdSet>::Guard lockedSigIdSet = sigIdSet.lock();
 
         if (!lockedSigIdSet->ensureInitialized(cx))
             return false;
 
         for (const SigWithId& sig : metadata().sigIds) {
             const void* sigId;
@@ -585,16 +552,19 @@ WasmInstanceObject*
 Instance::object() const
 {
     return object_;
 }
 
 bool
 Instance::callExport(JSContext* cx, uint32_t funcDefIndex, CallArgs args)
 {
+    // If there has been a moving grow, this Instance should have been notified.
+    MOZ_RELEASE_ASSERT(!memory_ || tlsData_.memoryBase == memory_->buffer().dataPointerEither());
+
     if (!cx->compartment()->wasm.ensureProfilingState(cx))
         return false;
 
     const FuncDefExport& func = metadata().lookupFuncDefExport(funcDefIndex);
 
     // The calling convention for an external call into wasm is to pass an
     // array of 16-byte values where each value contains either a coerced int32
     // (in the low word), a double value (in the low dword) or a SIMD vector
@@ -801,16 +771,43 @@ Instance::callExport(JSContext* cx, uint
     }
 
     if (retObj)
         args.rval().set(ObjectValue(*retObj));
 
     return true;
 }
 
+uint32_t
+Instance::currentMemory()
+{
+    MOZ_RELEASE_ASSERT(memory_);
+    uint32_t byteLength = memory_->buffer().wasmActualByteLength();
+    MOZ_ASSERT(byteLength % wasm::PageSize == 0);
+    return byteLength / wasm::PageSize;
+}
+
+uint32_t
+Instance::growMemory(uint32_t delta)
+{
+    MOZ_ASSERT(!isAsmJS());
+    uint32_t ret = memory_->grow(delta);
+    MOZ_RELEASE_ASSERT(tlsData_.memoryBase == memory_->buffer().dataPointerEither());
+    return ret;
+}
+
+void
+Instance::onMovingGrow(uint8_t* prevMemoryBase)
+{
+    MOZ_ASSERT(!isAsmJS());
+    ArrayBufferObject& buffer = memory_->buffer().as<ArrayBufferObject>();
+    tlsData_.memoryBase = buffer.dataPointer();
+    code_->segment().onMovingGrow(prevMemoryBase, metadata(), buffer);
+}
+
 void
 Instance::deoptimizeImportExit(uint32_t funcImportIndex)
 {
     const FuncImport& fi = metadata().funcImports[funcImportIndex];
     FuncImportTls& import = funcImportTls(fi);
     import.code = codeBase() + fi.interpExitCodeOffset();
     import.baselineScript = nullptr;
 }
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -53,26 +53,27 @@ class Instance
     // Import call slow paths which are called directly from wasm code.
     friend void* AddressOf(SymbolicAddress, ExclusiveContext*);
     static int32_t callImport_void(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_i32(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_i64(Instance*, int32_t, int32_t, uint64_t*);
     static int32_t callImport_f64(Instance*, int32_t, int32_t, uint64_t*);
     static uint32_t growMemory_i32(Instance* instance, uint32_t delta);
     static uint32_t currentMemory_i32(Instance* instance);
-
     bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc, const uint64_t* argv,
                     MutableHandleValue rval);
-    uint32_t growMemory(uint32_t delta);
-    uint32_t currentMemory();
 
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
     void tracePrivate(JSTracer* trc);
 
+    // Only WasmMemoryObject can call the private onMovingGrow notification.
+    friend class js::WasmMemoryObject;
+    void onMovingGrow(uint8_t* prevMemoryBase);
+
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              UniqueCode code,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
              const ValVector& globalImports);
@@ -101,23 +102,33 @@ class Instance
 
     WasmInstanceObject* object() const;
 
     // Execute the given export given the JS call arguments, storing the return
     // value in args.rval.
 
     MOZ_MUST_USE bool callExport(JSContext* cx, uint32_t funcDefIndex, CallArgs args);
 
+    // These methods implement their respective wasm operator but may also be
+    // called via the Memory JS API.
+
+    uint32_t currentMemory();
+    uint32_t growMemory(uint32_t delta);
+
     // Initially, calls to imports in wasm code call out through the generic
     // callImport method. If the imported callee gets JIT compiled and the types
     // match up, callImport will patch the code to instead call through a thunk
     // directly into the JIT code. If the JIT code is released, the Instance must
     // be notified so it can go back to the generic callImport.
 
     void deoptimizeImportExit(uint32_t funcImportIndex);
+
+    // Called by simulators to check whether accessing 'numBytes' starting at
+    // 'addr' would trigger a fault and be safely handled by signal handlers.
+
     bool memoryAccessWouldFault(uint8_t* addr, unsigned numBytes);
 
     // See Code::ensureProfilingState comment.
 
     MOZ_MUST_USE bool ensureProfilingState(JSContext* cx, bool enabled);
 
     // about:memory reporting:
 
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -697,127 +697,108 @@ class FunctionCompiler
     void assign(unsigned slot, MDefinition* def)
     {
         if (inDeadCode())
             return;
         curBlock_->setSlot(info().localSlot(slot), def);
     }
 
   private:
-    // False means we're sure to be out-of-bounds after this bounds check.
-    bool maybeAddBoundsCheck(MDefinition* base, const MWasmMemoryAccess& access)
+    void checkOffsetAndBounds(MWasmMemoryAccess* access, MDefinition** base)
     {
-        if (access.offset() > uint32_t(INT32_MAX)) {
-            curBlock_->end(MWasmTrap::New(alloc(), Trap::OutOfBounds));
-            curBlock_ = nullptr;
-            return false;
+        // If the offset is bigger than the guard region, a separate instruction
+        // is necessary to add the offset to the base and check for overflow.
+        if (access->offset() >= OffsetGuardLimit || !JitOptions.wasmFoldOffsets) {
+            auto* ins = MWasmAddOffset::New(alloc(), *base, access->offset());
+            curBlock_->add(ins);
+
+            *base = ins;
+            access->clearOffset();
         }
 
 #ifndef WASM_HUGE_MEMORY
-        curBlock_->add(MWasmBoundsCheck::New(alloc(), base, access));
+        curBlock_->add(MWasmBoundsCheck::New(alloc(), *base));
 #endif
-        return true;
     }
 
-    MDefinition* loadHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access,
-                                 bool isInt64 = false)
+    // Return true only for real asm.js (HEAP[i>>2]|0) accesses which have the
+    // peculiar property of not throwing on out-of-bounds. Everything else
+    // (wasm, SIMD.js, Atomics) throws on out-of-bounds.
+    bool isAsmJSAccess(const MWasmMemoryAccess& access) {
+        return mg().isAsmJS() && !access.isSimdAccess() && !access.isAtomicAccess();
+    }
+
+  public:
+    MDefinition* load(MDefinition* base, MWasmMemoryAccess access, ValType result)
     {
         if (inDeadCode())
             return nullptr;
 
         MInstruction* load = nullptr;
-        if (mg().isAsmJS()) {
-            load = MAsmJSLoadHeap::New(alloc(), base, access);
+        if (isAsmJSAccess(access)) {
+            MOZ_ASSERT(access.offset() == 0);
+            load = MAsmJSLoadHeap::New(alloc(), base, access.accessType());
         } else {
-            if (!maybeAddBoundsCheck(base, access))
-                return nullptr;
-            load = MWasmLoad::New(alloc(), base, access, isInt64);
+            checkOffsetAndBounds(&access, &base);
+            load = MWasmLoad::New(alloc(), base, access, ToMIRType(result));
         }
 
         curBlock_->add(load);
         return load;
     }
 
-    void storeHeapPrivate(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
+    void store(MDefinition* base, MWasmMemoryAccess access, MDefinition* v)
     {
         if (inDeadCode())
             return;
 
         MInstruction* store = nullptr;
-        if (mg().isAsmJS()) {
-            store = MAsmJSStoreHeap::New(alloc(), base, access, v);
+        if (isAsmJSAccess(access)) {
+            MOZ_ASSERT(access.offset() == 0);
+            store = MAsmJSStoreHeap::New(alloc(), base, access.accessType(), v);
         } else {
-            if (!maybeAddBoundsCheck(base, access))
-                return;
+            checkOffsetAndBounds(&access, &base);
             store = MWasmStore::New(alloc(), base, access, v);
         }
 
         curBlock_->add(store);
     }
 
-  public:
-    MDefinition* loadHeap(MDefinition* base, const MWasmMemoryAccess& access, bool isInt64)
-    {
-        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD loads should use loadSimdHeap");
-        return loadHeapPrivate(base, access, isInt64);
-    }
-    MDefinition* loadSimdHeap(MDefinition* base, const MWasmMemoryAccess& access)
-    {
-        MOZ_ASSERT(Scalar::isSimdType(access.accessType()), "non-SIMD loads should use loadHeap");
-        return loadHeapPrivate(base, access);
-    }
-    MDefinition* loadAtomicHeap(MDefinition* base, const MWasmMemoryAccess& access)
-    {
-        return loadHeapPrivate(base, access);
-    }
-
-    void storeHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
-    {
-        MOZ_ASSERT(!Scalar::isSimdType(access.accessType()), "SIMD store should use storeSimdHeap");
-        storeHeapPrivate(base, access, v);
-    }
-    void storeSimdHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
-    {
-        MOZ_ASSERT(Scalar::isSimdType(access.accessType()), "non-SIMD stores should use storeHeap");
-        storeHeapPrivate(base, access, v);
-    }
-    void storeAtomicHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
-    {
-        storeHeapPrivate(base, access, v);
-    }
-
-    MDefinition* atomicCompareExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
+    MDefinition* atomicCompareExchangeHeap(MDefinition* base, MWasmMemoryAccess access,
                                            MDefinition* oldv, MDefinition* newv)
     {
         if (inDeadCode())
             return nullptr;
 
+        checkOffsetAndBounds(&access, &base);
         auto* cas = MAsmJSCompareExchangeHeap::New(alloc(), base, access, oldv, newv, tlsPointer_);
         curBlock_->add(cas);
         return cas;
     }
 
-    MDefinition* atomicExchangeHeap(MDefinition* base, const MWasmMemoryAccess& access,
+    MDefinition* atomicExchangeHeap(MDefinition* base, MWasmMemoryAccess access,
                                     MDefinition* value)
     {
         if (inDeadCode())
             return nullptr;
 
+        checkOffsetAndBounds(&access, &base);
         auto* cas = MAsmJSAtomicExchangeHeap::New(alloc(), base, access, value, tlsPointer_);
         curBlock_->add(cas);
         return cas;
     }
 
     MDefinition* atomicBinopHeap(js::jit::AtomicOp op,
-                                 MDefinition* base, const MWasmMemoryAccess& access,
+                                 MDefinition* base, MWasmMemoryAccess access,
                                  MDefinition* v)
     {
         if (inDeadCode())
             return nullptr;
 
+        checkOffsetAndBounds(&access, &base);
         auto* binop = MAsmJSAtomicBinopHeap::New(alloc(), op, base, access, v, tlsPointer_);
         curBlock_->add(binop);
         return binop;
     }
 
     MDefinition* loadGlobalVar(unsigned globalDataOffset, bool isConst, MIRType type)
     {
         if (inDeadCode())
@@ -1108,17 +1089,18 @@ class FunctionCompiler
         if (inDeadCode()) {
             *def = nullptr;
             return true;
         }
 
         CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Register);
         auto* ins = MWasmCall::NewBuiltinInstanceMethodCall(alloc(), desc, builtin,
                                                             call.instanceArg_, call.regArgs_,
-                                                            ToMIRType(ret), call.spIncrement_);
+                                                            ToMIRType(ret), call.spIncrement_,
+                                                            call.tlsStackOffset_);
         if (!ins)
             return false;
 
         curBlock_->add(ins);
         *def = ins;
         return true;
     }
 
@@ -2346,30 +2328,30 @@ EmitSelect(FunctionCompiler& f)
 static bool
 EmitLoad(FunctionCompiler& f, ValType type, Scalar::Type viewType)
 {
     LinearMemoryAddress<MDefinition*> addr;
     if (!f.iter().readLoad(type, Scalar::byteSize(viewType), &addr))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
-    f.iter().setResult(f.loadHeap(addr.base, access, type == ValType::I64));
+    f.iter().setResult(f.load(addr.base, access, type));
     return true;
 }
 
 static bool
 EmitStore(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
 {
     LinearMemoryAddress<MDefinition*> addr;
     MDefinition* value;
     if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
-    f.storeHeap(addr.base, access, value);
+    f.store(addr.base, access, value);
     return true;
 }
 
 static bool
 EmitStoreWithCoercion(FunctionCompiler& f, ValType resultType, Scalar::Type viewType)
 {
     LinearMemoryAddress<MDefinition*> addr;
     MDefinition* value;
@@ -2379,17 +2361,17 @@ EmitStoreWithCoercion(FunctionCompiler& 
     if (resultType == ValType::F32 && viewType == Scalar::Float64)
         value = f.unary<MToDouble>(value);
     else if (resultType == ValType::F64 && viewType == Scalar::Float32)
         value = f.unary<MToFloat32>(value);
     else
         MOZ_CRASH("unexpected coerced store");
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset);
-    f.storeHeap(addr.base, access, value);
+    f.store(addr.base, access, value);
     return true;
 }
 
 static bool
 EmitUnaryMathBuiltinCall(FunctionCompiler& f, uint32_t callOffset, SymbolicAddress callee,
                          ValType operandType)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(callOffset);
@@ -2453,32 +2435,32 @@ EmitAtomicsLoad(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     if (!f.iter().readAtomicLoad(&addr, &viewType))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset, 0,
                              MembarBeforeLoad, MembarAfterLoad);
-    f.iter().setResult(f.loadAtomicHeap(addr.base, access));
+    f.iter().setResult(f.load(addr.base, access, ValType::I32));
     return true;
 }
 
 static bool
 EmitAtomicsStore(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
     Scalar::Type viewType;
     MDefinition* value;
     if (!f.iter().readAtomicStore(&addr, &viewType, &value))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset, 0,
                              MembarBeforeStore, MembarAfterStore);
-    f.storeAtomicHeap(addr.base, access, value);
+    f.store(addr.base, access, value);
     f.iter().setResult(value);
     return true;
 }
 
 static bool
 EmitAtomicsBinOp(FunctionCompiler& f)
 {
     LinearMemoryAddress<MDefinition*> addr;
@@ -2735,17 +2717,17 @@ EmitSimdLoad(FunctionCompiler& f, ValTyp
     if (!numElems)
         numElems = defaultNumElems;
 
     LinearMemoryAddress<MDefinition*> addr;
     if (!f.iter().readLoad(resultType, Scalar::byteSize(viewType), &addr))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset, numElems);
-    f.iter().setResult(f.loadSimdHeap(addr.base, access));
+    f.iter().setResult(f.load(addr.base, access, resultType));
     return true;
 }
 
 static bool
 EmitSimdStore(FunctionCompiler& f, ValType resultType, unsigned numElems)
 {
     unsigned defaultNumElems;
     Scalar::Type viewType = SimdExprTypeToViewType(resultType, &defaultNumElems);
@@ -2754,17 +2736,17 @@ EmitSimdStore(FunctionCompiler& f, ValTy
         numElems = defaultNumElems;
 
     LinearMemoryAddress<MDefinition*> addr;
     MDefinition* value;
     if (!f.iter().readStore(resultType, Scalar::byteSize(viewType), &addr, &value))
         return false;
 
     MWasmMemoryAccess access(viewType, addr.align, addr.offset, numElems);
-    f.storeSimdHeap(addr.base, access, value);
+    f.store(addr.base, access, value);
     return true;
 }
 
 static bool
 EmitSimdSelect(FunctionCompiler& f, ValType simdType)
 {
     MDefinition* trueValue;
     MDefinition* falseValue;
@@ -2934,26 +2916,22 @@ EmitSimdOp(FunctionCompiler& f, ValType 
       case SimdOperation::Fn_shuffle:
         return EmitSimdShuffle(f, type);
       case SimdOperation::Fn_load:
         return EmitSimdLoad(f, type, 0);
       case SimdOperation::Fn_load1:
         return EmitSimdLoad(f, type, 1);
       case SimdOperation::Fn_load2:
         return EmitSimdLoad(f, type, 2);
-      case SimdOperation::Fn_load3:
-        return EmitSimdLoad(f, type, 3);
       case SimdOperation::Fn_store:
         return EmitSimdStore(f, type, 0);
       case SimdOperation::Fn_store1:
         return EmitSimdStore(f, type, 1);
       case SimdOperation::Fn_store2:
         return EmitSimdStore(f, type, 2);
-      case SimdOperation::Fn_store3:
-        return EmitSimdStore(f, type, 3);
       case SimdOperation::Fn_allTrue:
         return EmitSimdAllTrue(f, type);
       case SimdOperation::Fn_anyTrue:
         return EmitSimdAnyTrue(f, type);
       case SimdOperation::Fn_abs:
       case SimdOperation::Fn_neg:
       case SimdOperation::Fn_not:
       case SimdOperation::Fn_sqrt:
@@ -2997,16 +2975,18 @@ EmitSimdOp(FunctionCompiler& f, ValType 
       case SimdOperation::Fn_fromUint16x8Bits:
       case SimdOperation::Fn_fromInt16x8Bits:
         return EmitSimdBitcast(f, ValType::I16x8, type);
       case SimdOperation::Fn_fromInt32x4Bits:
       case SimdOperation::Fn_fromUint32x4Bits:
         return EmitSimdBitcast(f, ValType::I32x4, type);
       case SimdOperation::Fn_fromFloat32x4Bits:
         return EmitSimdBitcast(f, ValType::F32x4, type);
+      case SimdOperation::Fn_load3:
+      case SimdOperation::Fn_store3:
       case SimdOperation::Fn_fromFloat64x2Bits:
         MOZ_CRASH("NYI");
     }
     MOZ_CRASH("unexpected opcode");
 }
 
 static bool
 EmitGrowMemory(FunctionCompiler& f, uint32_t callOffset)
@@ -3022,17 +3002,20 @@ EmitGrowMemory(FunctionCompiler& f, uint
 
     MDefinition* delta;
     if (!f.iter().readUnary(ValType::I32, &delta))
         return false;
 
     if (!f.passArg(delta, ValType::I32, &args))
         return false;
 
-    f.finishCall(&args, PassTls::False, InterModule::False);
+    // As a short-cut, pretend this is an inter-module call so that any pinned
+    // heap pointer will be reloaded after the call. This hack will go away once
+    // we can stop pinning registers.
+    f.finishCall(&args, PassTls::True, InterModule::True);
 
     MDefinition* ret;
     if (!f.builtinInstanceMethodCall(SymbolicAddress::GrowMemory, args, ValType::I32, &ret))
         return false;
 
     f.iter().setResult(ret);
     return true;
 }
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -13,16 +13,17 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmJS.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/Maybe.h"
 
 #include "asmjs/WasmCompile.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmModule.h"
 #include "asmjs/WasmSignalHandlers.h"
 #include "builtin/Promise.h"
 #include "jit/JitOptions.h"
@@ -30,16 +31,17 @@
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
+using mozilla::CheckedInt;
 using mozilla::Nothing;
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
     if (!cx->jitSupportsFloatingPoint())
         return false;
 
@@ -705,33 +707,56 @@ wasm::ExportedFunctionToDefinitionIndex(
     MOZ_ASSERT(IsExportedFunction(fun));
     const Value& v = fun->getExtendedSlot(FunctionExtended::WASM_FUNC_DEF_INDEX_SLOT);
     return v.toInt32();
 }
 
 // ============================================================================
 // WebAssembly.Memory class and methods
 
+const ClassOps WasmMemoryObject::classOps_ =
+{
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    WasmMemoryObject::finalize
+};
+
 const Class WasmMemoryObject::class_ =
 {
     "WebAssembly.Memory",
     JSCLASS_DELAY_METADATA_BUILDER |
-    JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS)
+    JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
+    JSCLASS_FOREGROUND_FINALIZE,
+    &WasmMemoryObject::classOps_
 };
 
+/* static */ void
+WasmMemoryObject::finalize(FreeOp* fop, JSObject* obj)
+{
+    WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
+    if (memory.hasObservers())
+        fop->delete_(&memory.observers());
+}
+
 /* static */ WasmMemoryObject*
 WasmMemoryObject::create(ExclusiveContext* cx, HandleArrayBufferObjectMaybeShared buffer,
                          HandleObject proto)
 {
     AutoSetNewObjectMetadata metadata(cx);
     auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
     if (!obj)
         return nullptr;
 
     obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
+    MOZ_ASSERT(!obj->hasObservers());
     return obj;
 }
 
 /* static */ bool
 WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -835,16 +860,114 @@ const JSFunctionSpec WasmMemoryObject::m
 { JS_FS_END };
 
 ArrayBufferObjectMaybeShared&
 WasmMemoryObject::buffer() const
 {
     return getReservedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObjectMaybeShared>();
 }
 
+bool
+WasmMemoryObject::hasObservers() const
+{
+    return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
+}
+
+WasmMemoryObject::WeakInstanceSet&
+WasmMemoryObject::observers() const
+{
+    MOZ_ASSERT(hasObservers());
+    return *reinterpret_cast<WeakInstanceSet*>(getReservedSlot(OBSERVERS_SLOT).toPrivate());
+}
+
+WasmMemoryObject::WeakInstanceSet*
+WasmMemoryObject::getOrCreateObservers(JSContext* cx)
+{
+    if (!hasObservers()) {
+        auto observers = MakeUnique<WeakInstanceSet>(cx->zone(), InstanceSet());
+        if (!observers || !observers->init()) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+
+        setReservedSlot(OBSERVERS_SLOT, PrivateValue(observers.release()));
+    }
+
+    return &observers();
+}
+
+bool
+WasmMemoryObject::movingGrowable() const
+{
+#ifdef WASM_HUGE_MEMORY
+    return false;
+#else
+    return !buffer().wasmMaxSize();
+#endif
+}
+
+bool
+WasmMemoryObject::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance)
+{
+    MOZ_ASSERT(movingGrowable());
+
+    WeakInstanceSet* observers = getOrCreateObservers(cx);
+    if (!observers)
+        return false;
+
+    if (!observers->putNew(instance)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return true;
+}
+
+uint32_t
+WasmMemoryObject::grow(uint32_t delta)
+{
+    ArrayBufferObject &buf = buffer().as<ArrayBufferObject>();
+
+    MOZ_ASSERT(buf.wasmActualByteLength() % PageSize == 0);
+    uint32_t oldNumPages = buf.wasmActualByteLength() / PageSize;
+
+    CheckedInt<uint32_t> newSize = oldNumPages;
+    newSize += delta;
+    newSize *= PageSize;
+    if (!newSize.isValid())
+        return -1;
+
+    if (Maybe<uint32_t> maxSize = buf.wasmMaxSize()) {
+        if (newSize.value() > maxSize.value())
+            return -1;
+
+        if (!buf.wasmGrowToSizeInPlace(newSize.value()))
+            return -1;
+    } else {
+#ifdef WASM_HUGE_MEMORY
+        if (!buf.wasmGrowToSizeInPlace(newSize.value()))
+            return -1;
+#else
+        MOZ_ASSERT(movingGrowable());
+
+        uint8_t* prevMemoryBase = buf.dataPointer();
+
+        if (!buf.wasmMovingGrowToSize(newSize.value()))
+            return -1;
+
+        if (hasObservers()) {
+            for (InstanceSet::Range r = observers().all(); !r.empty(); r.popFront())
+                r.front()->instance().onMovingGrow(prevMemoryBase);
+        }
+#endif
+    }
+
+    return oldNumPages;
+}
+
 // ============================================================================
 // WebAssembly.Table class and methods
 
 const ClassOps WasmTableObject::classOps_ =
 {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
--- a/js/src/asmjs/WasmJS.h
+++ b/js/src/asmjs/WasmJS.h
@@ -155,28 +155,43 @@ class WasmInstanceObject : public Native
 };
 
 // The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
 // or SharedArrayBuffer object which owns the actual memory.
 
 class WasmMemoryObject : public NativeObject
 {
     static const unsigned BUFFER_SLOT = 0;
+    static const unsigned OBSERVERS_SLOT = 1;
     static const ClassOps classOps_;
+    static void finalize(FreeOp* fop, JSObject* obj);
+
+    using InstanceSet = GCHashSet<ReadBarrieredWasmInstanceObject,
+                                  MovableCellHasher<ReadBarrieredWasmInstanceObject>,
+                                  SystemAllocPolicy>;
+    using WeakInstanceSet = JS::WeakCache<InstanceSet>;
+    bool hasObservers() const;
+    WeakInstanceSet& observers() const;
+    WeakInstanceSet* getOrCreateObservers(JSContext* cx);
+
   public:
-    static const unsigned RESERVED_SLOTS = 1;
+    static const unsigned RESERVED_SLOTS = 2;
     static const Class class_;
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static bool construct(JSContext*, unsigned, Value*);
 
     static WasmMemoryObject* create(ExclusiveContext* cx,
                                     Handle<ArrayBufferObjectMaybeShared*> buffer,
                                     HandleObject proto);
     ArrayBufferObjectMaybeShared& buffer() const;
+
+    bool movingGrowable() const;
+    bool addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance);
+    uint32_t grow(uint32_t delta);
 };
 
 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
 // wasm::Table, allowing a Table to be shared between multiple Instances
 // (eventually between multiple threads).
 
 class WasmTableObject : public NativeObject
 {
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -571,20 +571,20 @@ Module::instantiateMemory(JSContext* cx,
                            buffer->as<ArrayBufferObject>().isWasm());
 
         uint32_t actualLength = buffer->wasmActualByteLength();
         if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
             return false;
         }
 
-        // For asm.js maxMemoryLength doesn't play a role since we can't grow memory.
-        // For wasm we require that either both memory and module don't specify a max size
-        // OR that the memory's max size is less than the modules.
-        if (!metadata_->isAsmJS()) {
+        if (metadata_->isAsmJS()) {
+            MOZ_ASSERT(IsValidAsmJSHeapLength(actualLength));
+            MOZ_ASSERT(actualLength == buffer->wasmMaxSize().value());
+        } else {
             Maybe<uint32_t> actualMax = buffer->as<ArrayBufferObject>().wasmMaxSize();
             if (declaredMax.isSome() != actualMax.isSome() || declaredMax < actualMax) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
                 return false;
             }
         }
     } else {
         MOZ_ASSERT(!metadata_->isAsmJS());
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -596,21 +596,34 @@ ComputeAccessAddress(EMULATOR_CONTEXT* c
                             AddressOfGPRegisterSlot(context, address.index()));
         MOZ_ASSERT(address.scale() < 32, "address shift overflow");
         result += index * (uintptr_t(1) << address.scale());
     }
 
     return reinterpret_cast<uint8_t*>(result);
 }
 
-MOZ_COLD static uint8_t*
-EmulateHeapAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
-                  const MemoryAccess* memoryAccess, const Instance& instance)
+MOZ_COLD static bool
+HugeMemoryAccess(EMULATOR_CONTEXT* context, uint8_t* pc, uint8_t* faultingAddress,
+                 const Instance& instance, uint8_t** ppc)
 {
     MOZ_RELEASE_ASSERT(instance.codeSegment().containsFunctionPC(pc));
+
+    // On WASM_HUGE_MEMORY platforms, wasm::MemoryAccess is only created for
+    // asm.js loads and stores since they unfortunately do not simply throw on
+    // out-of-bounds. Everything else (WebAssembly and experimental
+    // SIMD/Atomics) throws.
+
+    const MemoryAccess* memoryAccess = instance.code().lookupMemoryAccess(pc);
+    if (!memoryAccess) {
+        *ppc = instance.codeSegment().outOfBoundsCode();
+        return true;
+    }
+
+    MOZ_RELEASE_ASSERT(instance.isAsmJS());
     MOZ_RELEASE_ASSERT(memoryAccess->insnOffset() == (pc - instance.codeBase()));
 
     // Disassemble the instruction which caused the trap so that we can extract
     // information about it and decide what to do.
     Disassembler::HeapAccess access;
     uint8_t* end = Disassembler::DisassembleHeapAccess(pc, &access);
     const Disassembler::ComplexAddress& address = access.address();
     MOZ_RELEASE_ASSERT(end > pc);
@@ -646,50 +659,36 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
                        "Access begins outside the asm.js heap");
     MOZ_RELEASE_ASSERT(accessAddress + access.size() <= instance.memoryBase() +
                        instance.memoryMappedSize(),
                        "Access extends beyond the asm.js heap guard region");
     MOZ_RELEASE_ASSERT(accessAddress + access.size() > instance.memoryBase() +
                        instance.memoryLength(),
                        "Computed access address is not actually out of bounds");
 
-    // Wasm loads/stores don't wrap offsets at all, so hitting the guard page
-    // means we are out of bounds in any cases.
-    if (!memoryAccess->wrapOffset()) {
-        MOZ_ASSERT(memoryAccess->throwOnOOB());
-        return instance.codeSegment().outOfBoundsCode();
-    }
-
     // The basic sandbox model is that all heap accesses are a heap base
     // register plus an index, and the index is always computed with 32-bit
     // operations, so we know it can only be 4 GiB off of the heap base.
     //
     // However, we wish to support the optimization of folding immediates
     // and scaled indices into addresses, and any address arithmetic we fold
     // gets done at full pointer width, so it doesn't get properly wrapped.
-    // We support this by extending MappedSize to the greatest size that could
-    // be reached by such an unwrapped address, and then when we arrive here in
-    // the signal handler for such an access, we compute the fully wrapped
-    // address, and perform the load or store on it.
+    // We support this by extending HugeMappedSize to the greatest size that
+    // could be reached by such an unwrapped address, and then when we arrive
+    // here in the signal handler for such an access, we compute the fully
+    // wrapped address, and perform the load or store on it.
     //
     // Taking a signal is really slow, but in theory programs really shouldn't
     // be hitting this anyway.
     intptr_t unwrappedOffset = accessAddress - instance.memoryBase().unwrap(/* for value */);
     uint32_t wrappedOffset = uint32_t(unwrappedOffset);
     size_t size = access.size();
     MOZ_RELEASE_ASSERT(wrappedOffset + size > wrappedOffset);
     bool inBounds = wrappedOffset + size < instance.memoryLength();
 
-    // If this is storing Z of an XYZ, check whether X is also in bounds, so
-    // that we don't store anything before throwing.
-    MOZ_RELEASE_ASSERT(unwrappedOffset > memoryAccess->offsetWithinWholeSimdVector());
-    uint32_t wrappedBaseOffset = uint32_t(unwrappedOffset - memoryAccess->offsetWithinWholeSimdVector());
-    if (wrappedBaseOffset >= instance.memoryLength())
-        inBounds = false;
-
     if (inBounds) {
         // We now know that this is an access that is actually in bounds when
         // properly wrapped. Complete the load or store with the wrapped
         // address.
         SharedMem<uint8_t*> wrappedAddress = instance.memoryBase() + wrappedOffset;
         MOZ_RELEASE_ASSERT(wrappedAddress >= instance.memoryBase());
         MOZ_RELEASE_ASSERT(wrappedAddress + size > wrappedAddress);
         MOZ_RELEASE_ASSERT(wrappedAddress + size <= instance.memoryBase() + instance.memoryLength());
@@ -706,20 +705,16 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
           case Disassembler::HeapAccess::LoadSext64:
             MOZ_CRASH("no int64 accesses in asm.js");
           case Disassembler::HeapAccess::Unknown:
             MOZ_CRASH("Failed to disassemble instruction");
         }
     } else {
         // We now know that this is an out-of-bounds access made by an asm.js
         // load/store that we should handle.
-
-        if (memoryAccess->throwOnOOB())
-            return instance.codeSegment().outOfBoundsCode();
-
         switch (access.kind()) {
           case Disassembler::HeapAccess::Load:
           case Disassembler::HeapAccess::LoadSext32:
             // Assign the JS-defined result value to the destination register
             // (ToInt32(undefined) or ToNumber(undefined), determined by the
             // type of the destination register). Very conveniently, we can
             // infer the type from the register class, since all SIMD accesses
             // throw on out of bounds (see above), so the only types using FP
@@ -731,17 +726,18 @@ EmulateHeapAccess(EMULATOR_CONTEXT* cont
             break;
           case Disassembler::HeapAccess::LoadSext64:
             MOZ_CRASH("no int64 accesses in asm.js");
           case Disassembler::HeapAccess::Unknown:
             MOZ_CRASH("Failed to disassemble instruction");
         }
     }
 
-    return end;
+    *ppc = end;
+    return true;
 }
 #endif // JS_CODEGEN_X64
 
 MOZ_COLD static bool
 IsHeapAccessAddress(const Instance &instance, uint8_t* faultingAddress)
 {
     size_t accessLimit = instance.memoryMappedSize();
 
@@ -798,25 +794,21 @@ HandleFault(PEXCEPTION_POINTERS exceptio
         // always the logically-faulting pc). Fortunately, we can detect this
         // case and silence the exception ourselves (the exception will
         // retrigger after the interrupt jumps back to resumePC).
         return pc == instance->codeSegment().interruptCode() &&
                instance->codeSegment().containsFunctionPC(activation->resumePC());
     }
 
 #ifdef WASM_HUGE_MEMORY
-    const MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
-    if (!memoryAccess)
-        *ppc = instance->codeSegment().outOfBoundsCode();
-    else
-        *ppc = EmulateHeapAccess(context, pc, faultingAddress, memoryAccess, *instance);
+    return HugeMemoryAccess(context, pc, faultingAddress, *instance, ppc);
 #else
     *ppc = instance->codeSegment().outOfBoundsCode();
+    return true;
 #endif
-    return true;
 }
 
 static LONG WINAPI
 AsmJSFaultHandler(LPEXCEPTION_POINTERS exception)
 {
     if (HandleFault(exception))
         return EXCEPTION_CONTINUE_EXECUTION;
 
@@ -929,21 +921,18 @@ HandleMachException(JSRuntime* rt, const
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
 
     // This check isn't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     if (!IsHeapAccessAddress(*instance, faultingAddress))
         return false;
 
 #ifdef WASM_HUGE_MEMORY
-    const MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
-    if (!memoryAccess)
-        *ppc = instance->codeSegment().outOfBoundsCode();
-    else
-        *ppc = EmulateHeapAccess(&context, pc, faultingAddress, memoryAccess, *instance);
+    if (!HugeMemoryAccess(&context, pc, faultingAddress, *instance, ppc))
+        return false;
 #else
     *ppc = instance->codeSegment().outOfBoundsCode();
 #endif
 
     // Update the thread state with the new pc and register values.
     kret = thread_set_state(rtThread, float_state, (thread_state_t)&context.float_, float_state_count);
     if (kret != KERN_SUCCESS)
         return false;
@@ -1148,34 +1137,28 @@ HandleFault(int signum, siginfo_t* info,
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(info->si_addr);
 
     // This check isn't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     if (!IsHeapAccessAddress(*instance, faultingAddress))
         return false;
 
 #ifdef WASM_HUGE_MEMORY
-    MOZ_RELEASE_ASSERT(signal == Signal::SegFault);
-    const MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc);
-    if (!memoryAccess)
-        *ppc = instance->codeSegment().outOfBoundsCode();
-    else
-        *ppc = EmulateHeapAccess(context, pc, faultingAddress, memoryAccess, *instance);
+    return HugeMemoryAccess(context, pc, faultingAddress, *instance, ppc);
 #elif defined(JS_CODEGEN_ARM)
     MOZ_RELEASE_ASSERT(signal == Signal::BusError || signal == Signal::SegFault);
     if (signal == Signal::BusError)
         *ppc = instance->codeSegment().unalignedAccessCode();
     else
         *ppc = instance->codeSegment().outOfBoundsCode();
+    return true;
 #else
-    MOZ_RELEASE_ASSERT(signal == Signal::SegFault);
     *ppc = instance->codeSegment().outOfBoundsCode();
+    return true;
 #endif
-
-    return true;
 }
 
 static struct sigaction sPrevSEGVHandler;
 static struct sigaction sPrevSIGBUSHandler;
 
 template<Signal signal>
 static void
 AsmJSFaultHandler(int signum, siginfo_t* info, void* context)
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -619,55 +619,67 @@ Assumptions::sizeOfExcludingThis(MallocS
 }
 
 //  Heap length on ARM should fit in an ARM immediate. We approximate the set
 //  of valid ARM immediates with the predicate:
 //    2^n for n in [16, 24)
 //  or
 //    2^24 * n for n >= 1.
 bool
-wasm::IsValidARMLengthImmediate(uint32_t length)
+wasm::IsValidARMImmediate(uint32_t i)
 {
-    bool valid = (IsPowerOfTwo(length) ||
-                  (length & 0x00ffffff) == 0);
+    bool valid = (IsPowerOfTwo(i) ||
+                  (i & 0x00ffffff) == 0);
 
-    MOZ_ASSERT_IF(valid, length % PageSize == 0);
+    MOZ_ASSERT_IF(valid, i % PageSize == 0);
 
     return valid;
 }
 
 uint32_t
-wasm::RoundUpToNextValidARMLengthImmediate(uint32_t length)
+wasm::RoundUpToNextValidARMImmediate(uint32_t i)
 {
-    MOZ_ASSERT(length <= 0xff000000);
+    MOZ_ASSERT(i <= 0xff000000);
+
+    if (i <= 16 * 1024 * 1024)
+        i = i ? mozilla::RoundUpPow2(i) : 0;
+    else
+        i = (i + 0x00ffffff) & ~0x00ffffff;
+
+    MOZ_ASSERT(IsValidARMImmediate(i));
 
-    if (length <= 16 * 1024 * 1024)
-        length = length ? mozilla::RoundUpPow2(length) : 0;
-    else
-        length = (length + 0x00ffffff) & ~0x00ffffff;
+    return i;
+}
+
+#ifndef WASM_HUGE_MEMORY
 
-    MOZ_ASSERT(IsValidARMLengthImmediate(length));
-
-    return length;
+bool
+wasm::IsValidBoundsCheckImmediate(uint32_t i)
+{
+#ifdef JS_CODEGEN_ARM
+    return IsValidARMImmediate(i);
+#else
+    return true;
+#endif
 }
 
 size_t
-wasm::LegalizeMapLength(size_t requestedSize)
+wasm::ComputeMappedSize(uint32_t maxSize)
 {
-#ifdef WASM_HUGE_MEMORY
-    // On 64-bit platforms just give us a 4G guard region
-    return wasm::MappedSize;
-#else
-    uint32_t res = requestedSize;
+    MOZ_ASSERT(maxSize % PageSize == 0);
 
-    // On 32-bit platforms clamp down to 1GB
-    uint32_t MaxMappedSize = (1 << 30);
-    res = Min(res, MaxMappedSize);
+    // It is the bounds-check limit, not the mapped size, that gets baked into
+    // code. Thus round up the maxSize to the next valid immediate value
+    // *before* adding in the guard page.
 
 # ifdef JS_CODEGEN_ARM
-    // On Arm round so that it fits in a single instruction
-    res = RoundUpToNextValidARMLengthImmediate(res);
-    MOZ_RELEASE_ASSERT(res <= MaxMappedSize);
+    uint32_t boundsCheckLimit = RoundUpToNextValidARMImmediate(maxSize);
+# else
+    uint32_t boundsCheckLimit = maxSize;
 # endif
+    MOZ_ASSERT(IsValidBoundsCheckImmediate(boundsCheckLimit));
 
-    return res;
-#endif
+    MOZ_ASSERT(boundsCheckLimit % gc::SystemPageSize() == 0);
+    MOZ_ASSERT(GuardSize % gc::SystemPageSize() == 0);
+    return boundsCheckLimit + GuardSize;
 }
+
+#endif  // WASM_HUGE_MEMORY
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -786,105 +786,16 @@ class CallSiteAndTarget : public CallSit
     static const uint32_t NOT_DEFINITION = UINT32_MAX;
 
     bool isDefinition() const { return funcDefIndex_ != NOT_DEFINITION; }
     uint32_t funcDefIndex() const { MOZ_ASSERT(isDefinition()); return funcDefIndex_; }
 };
 
 typedef Vector<CallSiteAndTarget, 0, SystemAllocPolicy> CallSiteAndTargetVector;
 
-// Metadata for a bounds check that may need patching later.
-
-class BoundsCheck
-{
-  public:
-    BoundsCheck() = default;
-
-    explicit BoundsCheck(uint32_t cmpOffset)
-      : cmpOffset_(cmpOffset)
-    { }
-
-    uint8_t* patchAt(uint8_t* code) const { return code + cmpOffset_; }
-    void offsetBy(uint32_t offset) { cmpOffset_ += offset; }
-
-  private:
-    uint32_t cmpOffset_; // absolute offset of the comparison
-};
-
-// Summarizes a heap access made by wasm code that needs to be patched later
-// and/or looked up by the wasm signal handlers. Different architectures need
-// to know different things (x64: intruction offset, wrapping and failure
-// behavior, ARM: nothing, x86: offset of end of instruction (heap length to
-// patch is last 4 bytes of instruction)).
-#if defined(JS_CODEGEN_X86)
-class MemoryAccess
-{
-    uint32_t nextInsOffset_;
-
-  public:
-    MemoryAccess() = default;
-
-    explicit MemoryAccess(uint32_t nextInsOffset)
-      : nextInsOffset_(nextInsOffset)
-    { }
-
-    void* patchMemoryPtrImmAt(uint8_t* code) const { return code + nextInsOffset_; }
-    void offsetBy(uint32_t offset) { nextInsOffset_ += offset; }
-};
-#elif defined(JS_CODEGEN_X64)
-class MemoryAccess
-{
-    uint32_t insnOffset_;
-    uint8_t offsetWithinWholeSimdVector_; // if is this e.g. the Z of an XYZ
-    bool throwOnOOB_;                     // should we throw on OOB?
-    bool wrapOffset_;                     // should we wrap the offset on OOB?
-
-  public:
-    enum OutOfBoundsBehavior {
-        Throw,
-        CarryOn,
-    };
-    enum WrappingBehavior {
-        WrapOffset,
-        DontWrapOffset,
-    };
-
-    MemoryAccess() = default;
-
-    MemoryAccess(uint32_t insnOffset, OutOfBoundsBehavior onOOB, WrappingBehavior onWrap,
-                 uint32_t offsetWithinWholeSimdVector = 0)
-      : insnOffset_(insnOffset),
-        offsetWithinWholeSimdVector_(offsetWithinWholeSimdVector),
-        throwOnOOB_(onOOB == OutOfBoundsBehavior::Throw),
-        wrapOffset_(onWrap == WrappingBehavior::WrapOffset)
-    {
-        MOZ_ASSERT(offsetWithinWholeSimdVector_ == offsetWithinWholeSimdVector, "fits in uint8");
-    }
-
-    uint32_t insnOffset() const { return insnOffset_; }
-    uint32_t offsetWithinWholeSimdVector() const { return offsetWithinWholeSimdVector_; }
-    bool throwOnOOB() const { return throwOnOOB_; }
-    bool wrapOffset() const { return wrapOffset_; }
-
-    void offsetBy(uint32_t offset) { insnOffset_ += offset; }
-};
-#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
-      defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \
-      defined(JS_CODEGEN_NONE)
-// Nothing! We don't patch or emulate memory accesses on these platforms.
-class MemoryAccess {
-  public:
-    void offsetBy(uint32_t) { MOZ_CRASH(); }
-    uint32_t insnOffset() const { MOZ_CRASH(); }
-};
-#endif
-
-WASM_DECLARE_POD_VECTOR(MemoryAccess, MemoryAccessVector)
-WASM_DECLARE_POD_VECTOR(BoundsCheck, BoundsCheckVector)
-
 // A wasm::SymbolicAddress represents a pointer to a well-known function or
 // object that is embedded in wasm code. Since wasm code is serialized and
 // later deserialized into a different address space, symbolic addresses must be
 // used for *all* pointers into the address space. The MacroAssembler records a
 // list of all SymbolicAddresses and the offsets of their use in the code for
 // later patching during static linking.
 
 enum class SymbolicAddress
@@ -1254,31 +1165,163 @@ struct ExternalTableElem
     //    WasmTableCallSigReg holds the signature id.
     void* code;
 
     // The pointer to the callee's instance's TlsData. This must be loaded into
     // WasmTlsReg before calling 'code'.
     TlsData* tls;
 };
 
-// Constants:
+// Because ARM has a fixed-width instruction encoding, ARM can only express a
+// limited subset of immediates (in a single instruction).
+
+extern bool
+IsValidARMImmediate(uint32_t i);
+
+extern uint32_t
+RoundUpToNextValidARMImmediate(uint32_t i);
 
 // The WebAssembly spec hard-codes the virtual page size to be 64KiB and
-// requires linear memory to always be a multiple of 64KiB.
+// requires the size of linear memory to always be a multiple of 64KiB.
+
 static const unsigned PageSize = 64 * 1024;
 
+// Bounds checks always compare the base of the memory access with the bounds
+// check limit. If the memory access is unaligned, this means that, even if the
+// bounds check succeeds, a few bytes of the access can extend past the end of
+// memory. To guard against this, extra space is included in the guard region to
+// catch the overflow. MaxMemoryAccessSize is a conservative approximation of
+// the maximum guard space needed to catch all unaligned overflows.
+
+static const unsigned MaxMemoryAccessSize = sizeof(Val);
+
 #ifdef JS_CODEGEN_X64
-#define WASM_HUGE_MEMORY
-static const uint64_t Uint32Range = uint64_t(UINT32_MAX) + 1;
-static const uint64_t MappedSize = 2 * Uint32Range + PageSize;
+
+// All other code should use WASM_HUGE_MEMORY instead of JS_CODEGEN_X64 so that
+// it is easy to use the huge-mapping optimization for other 64-bit platforms in
+// the future.
+# define WASM_HUGE_MEMORY
+
+// On WASM_HUGE_MEMORY platforms, every asm.js or WebAssembly memory
+// unconditionally allocates a huge region of virtual memory of size
+// wasm::HugeMappedSize. This allows all memory resizing to work without
+// reallocation and provides enough guard space for all offsets to be folded
+// into memory accesses.
+
+static const uint64_t IndexRange = uint64_t(UINT32_MAX) + 1;
+static const uint64_t OffsetGuardLimit = uint64_t(INT32_MAX) + 1;
+static const uint64_t UnalignedGuardPage = PageSize;
+static const uint64_t HugeMappedSize = IndexRange + OffsetGuardLimit + UnalignedGuardPage;
+
+static_assert(MaxMemoryAccessSize <= UnalignedGuardPage, "rounded up to static page size");
+
+#else // !WASM_HUGE_MEMORY
+
+// On !WASM_HUGE_MEMORY platforms:
+//  - To avoid OOM in ArrayBuffer::prepareForAsmJS, asm.js continues to use the
+//    original ArrayBuffer allocation which has no guard region at all.
+//  - For WebAssembly memories, an additional GuardSize is mapped after the
+//    accessible region of the memory to catch folded (base+offset) accesses
+//    where `offset < OffsetGuardLimit` as well as the overflow from unaligned
+//    accesses, as described above for MaxMemoryAccessSize.
+
+static const size_t OffsetGuardLimit = PageSize - MaxMemoryAccessSize;
+static const size_t GuardSize = PageSize;
+
+// Return whether the given immediate satisfies the constraints of the platform
+// (viz. that, on ARM, IsValidARMImmediate).
+
+extern bool
+IsValidBoundsCheckImmediate(uint32_t i);
+
+// For a given WebAssembly/asm.js max size, return the number of bytes to
+// map which will necessarily be a multiple of the system page size and greater
+// than maxSize. For a returned mappedSize:
+//   boundsCheckLimit = mappedSize - GuardSize
+//   IsValidBoundsCheckImmediate(boundsCheckLimit)
+
+extern size_t
+ComputeMappedSize(uint32_t maxSize);
+
+#endif // WASM_HUGE_MEMORY
+
+// Metadata for bounds check instructions that are patched at runtime with the
+// appropriate bounds check limit. On WASM_HUGE_MEMORY platforms for wasm (and
+// SIMD/Atomic) bounds checks, no BoundsCheck is created: the signal handler
+// catches everything. On !WASM_HUGE_MEMORY, a BoundsCheck is created for each
+// memory access (except when statically eliminated by optimizations) so that
+// the length can be patched in as an immediate. This requires that the bounds
+// check limit IsValidBoundsCheckImmediate.
+
+class BoundsCheck
+{
+  public:
+    BoundsCheck() = default;
+
+    explicit BoundsCheck(uint32_t cmpOffset)
+      : cmpOffset_(cmpOffset)
+    { }
+
+    uint8_t* patchAt(uint8_t* code) const { return code + cmpOffset_; }
+    void offsetBy(uint32_t offset) { cmpOffset_ += offset; }
+
+  private:
+    uint32_t cmpOffset_;
+};
+
+// Metadata for memory accesses. On WASM_HUGE_MEMORY platforms, only
+// (non-SIMD/Atomic) asm.js loads and stores create a MemoryAccess so that the
+// signal handler can implement the semantically-correct wraparound logic; the
+// rest simply redirect to the out-of-bounds stub in the signal handler. On x86,
+// the base address of memory is baked into each memory access instruction so
+// the MemoryAccess records the location of each for patching. On all other
+// platforms, no MemoryAccess is created.
+
+#ifdef WASM_HUGE_MEMORY
+class MemoryAccess
+{
+    uint32_t insnOffset_;
+
+  public:
+    MemoryAccess() = default;
+    explicit MemoryAccess(uint32_t insnOffset)
+      : insnOffset_(insnOffset)
+    {}
+
+    uint32_t insnOffset() const { return insnOffset_; }
+
+    void offsetBy(uint32_t offset) { insnOffset_ += offset; }
+};
+#elif defined(JS_CODEGEN_X86)
+class MemoryAccess
+{
+    uint32_t nextInsOffset_;
+
+  public:
+    MemoryAccess() = default;
+    explicit MemoryAccess(uint32_t nextInsOffset)
+      : nextInsOffset_(nextInsOffset)
+    { }
+
+    void* patchMemoryPtrImmAt(uint8_t* code) const { return code + nextInsOffset_; }
+    void offsetBy(uint32_t offset) { nextInsOffset_ += offset; }
+};
+#else
+class MemoryAccess {
+  public:
+    MemoryAccess() { MOZ_CRASH(); }
+    void offsetBy(uint32_t) { MOZ_CRASH(); }
+    uint32_t insnOffset() const { MOZ_CRASH(); }
+};
 #endif
 
-bool IsValidARMLengthImmediate(uint32_t length);
-uint32_t RoundUpToNextValidARMLengthImmediate(uint32_t length);
-size_t LegalizeMapLength(size_t requestedSize);
+WASM_DECLARE_POD_VECTOR(MemoryAccess, MemoryAccessVector)
+WASM_DECLARE_POD_VECTOR(BoundsCheck, BoundsCheckVector)
+
+// Constants:
 
 static const unsigned NaN64GlobalDataOffset       = 0;
 static const unsigned NaN32GlobalDataOffset       = NaN64GlobalDataOffset + sizeof(double);
 static const unsigned InitialGlobalDataBytes      = NaN32GlobalDataOffset + sizeof(float);
 
 static const unsigned MaxSigs                     =        4 * 1024;
 static const unsigned MaxFuncs                    =      512 * 1024;
 static const unsigned MaxGlobals                  =        4 * 1024;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -240,24 +240,20 @@ GetBuildConfiguration(JSContext* cx, uns
 #ifdef EXPOSE_INTL_API
     value = BooleanValue(true);
 #else
     value = BooleanValue(false);
 #endif
     if (!JS_SetProperty(cx, info, "intl-api", value))
         return false;
 
-#if defined(XP_WIN)
-    value = BooleanValue(false);
-#elif defined(SOLARIS)
+#if defined(SOLARIS)
     value = BooleanValue(false);
-#elif defined(XP_UNIX)
+#else
     value = BooleanValue(true);
-#else
-    value = BooleanValue(false);
 #endif
     if (!JS_SetProperty(cx, info, "mapped-array-buffer", value))
         return false;
 
 #ifdef MOZ_MEMORY
     value = BooleanValue(true);
 #else
     value = BooleanValue(false);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1033,17 +1033,54 @@ Parser<ParseHandler>::tryDeclareVar(Hand
     //   { { let x; } var x; }
 
     for (ParseContext::Scope* scope = pc->innermostScope();
          scope != pc->varScope().enclosing();
          scope = scope->enclosing())
     {
         if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) {
             DeclarationKind declaredKind = p->value()->kind();
-            if (!DeclarationKindIsVar(declaredKind) && !DeclarationKindIsParameter(declaredKind)) {
+            if (DeclarationKindIsVar(declaredKind)) {
+                // Any vars that are redeclared as body-level functions must
+                // be recorded as body-level functions.
+                //
+                // In the case of global and eval scripts, GlobalDeclaration-
+                // Instantiation [1] and EvalDeclarationInstantiation [2]
+                // check for the declarability of global var and function
+                // bindings via CanDeclareVar [3] and CanDeclareGlobal-
+                // Function [4]. CanDeclareGlobalFunction is strictly more
+                // restrictive than CanDeclareGlobalVar, so record the more
+                // restrictive kind. These semantics are implemented in
+                // CheckCanDeclareGlobalBinding.
+                //
+                // For a var previously declared as ForOfVar, this previous
+                // DeclarationKind is used only to check for if the
+                // 'arguments' binding should be declared. Since body-level
+                // functions shadow 'arguments' [5], it is correct to alter
+                // the kind to BodyLevelFunction. See
+                // declareFunctionArgumentsObject.
+                //
+                // For a var previously declared as
+                // VarForAnnexBLexicalFunction, this previous DeclarationKind
+                // is used so that vars synthesized solely for Annex B.3.3 may
+                // be removed if an early error would occur. If a synthesized
+                // Annex B.3.3 var has the same name as a body-level function,
+                // this is not a redeclaration, and indeed, because the
+                // body-level function binds the name, this name should not be
+                // removed should a redeclaration occur in the future. Thus it
+                // is also correct to alter the kind to BodyLevelFunction.
+                //
+                // [1] ES 15.1.11
+                // [2] ES 18.2.1.3
+                // [3] ES 8.1.1.4.15
+                // [4] ES 8.1.1.4.16
+                // [5] ES 9.2.12
+                if (kind == DeclarationKind::BodyLevelFunction)
+                    p->value()->alterKind(kind);
+            } else if (!DeclarationKindIsParameter(declaredKind)) {
                 // Annex B.3.5 allows redeclaring simple (non-destructured)
                 // catch parameters with var declarations, except when it
                 // appears in a for-of.
                 bool annexB35Allowance = declaredKind == DeclarationKind::SimpleCatchParameter &&
                                          kind != DeclarationKind::ForOfVar;
 
                 // Annex B.3.3 allows redeclaring functions in the same block.
                 bool annexB33Allowance = declaredKind == DeclarationKind::LexicalFunction &&
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -275,67 +275,71 @@ size_t
 GetPageFaultCount()
 {
     PROCESS_MEMORY_COUNTERS pmc;
     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
         return 0;
     return pmc.PageFaultCount;
 }
 
-// On Windows the minimum size for a mapping is the allocation granularity
-// (64KiB in practice), so mapping very small buffers is potentially wasteful.
 void*
 AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
 {
-    // The allocation granularity must be a whole multiple of the alignment and
-    // the caller must request an aligned offset to satisfy Windows' and the
-    // caller's alignment requirements.
+    MOZ_ASSERT(length && alignment);
+
+    // The allocation granularity and the requested offset
+    // must both be divisible by the requested alignment.
     if (allocGranularity % alignment != 0 || offset % alignment != 0)
         return nullptr;
 
-    // Make sure file exists and do sanity check for offset and size.
     HANDLE hFile = reinterpret_cast<HANDLE>(intptr_t(fd));
-    MOZ_ASSERT(hFile != INVALID_HANDLE_VALUE);
 
-    uint32_t fSizeHgh;
-    uint32_t fSizeLow = GetFileSize(hFile, LPDWORD(&fSizeHgh));
-    if (fSizeLow == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
-        return nullptr;
-
-    uint64_t fSize = (uint64_t(fSizeHgh) << 32) + fSizeLow;
-    if (offset >= size_t(fSize) || length == 0 || length > fSize - offset)
-        return nullptr;
-
-    uint64_t mapSize = length + offset;
-    HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, mapSize >> 32, mapSize, nullptr);
+    // This call will fail if the file does not exist, which is what we want.
+    HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
     if (!hMap)
         return nullptr;
 
-    // MapViewOfFile requires the offset to be a whole multiple of the
-    // allocation granularity.
-    size_t alignOffset = offset - (offset % allocGranularity);
-    size_t alignLength = length + (offset % allocGranularity);
-    void* map = MapViewOfFile(hMap, FILE_MAP_COPY, 0, alignOffset, alignLength);
+    size_t alignedOffset = offset - (offset % allocGranularity);
+    size_t alignedLength = length + (offset % allocGranularity);
+
+    DWORD offsetH = uint32_t(uint64_t(alignedOffset) >> 32);
+    DWORD offsetL = uint32_t(alignedOffset);
+
+    // If the offset or length are out of bounds, this call will fail.
+    uint8_t* map = static_cast<uint8_t*>(MapViewOfFile(hMap, FILE_MAP_COPY, offsetH,
+                                                       offsetL, alignedLength));
+
+    // This just decreases the file mapping object's internal reference count;
+    // it won't actually be destroyed until we unmap the associated view.
     CloseHandle(hMap);
+
     if (!map)
         return nullptr;
 
-    return reinterpret_cast<void*>(uintptr_t(map) + (offset - alignOffset));
+#ifdef DEBUG
+    // Zero out data before and after the desired mapping to catch errors early.
+    if (offset != alignedOffset)
+        memset(map, 0, offset - alignedOffset);
+    if (alignedLength % pageSize)
+        memset(map + alignedLength, 0, pageSize - (alignedLength % pageSize));
+#endif
+
+    return map + (offset - alignedOffset);
 }
 
 void
 DeallocateMappedContent(void* p, size_t /*length*/)
 {
     if (!p)
         return;
 
     // Calculate the address originally returned by MapViewOfFile.
-    // This is required because AllocateMappedContent returns a pointer that
-    // might be offset into the view, necessitated by the requirement that the
-    // beginning of a view must be aligned with the allocation granularity.
+    // This is needed because AllocateMappedContent returns a pointer
+    // that might be offset from the view, as the beginning of a
+    // view must be aligned with the allocation granularity.
     uintptr_t map = uintptr_t(p) - (uintptr_t(p) % allocGranularity);
     MOZ_ALWAYS_TRUE(UnmapViewOfFile(reinterpret_cast<void*>(map)));
 }
 
 #  else // Various APIs are unavailable.
 
 void*
 MapAlignedPages(size_t size, size_t alignment)
@@ -771,71 +775,60 @@ GetPageFaultCount()
     if (err)
         return 0;
     return usage.ru_majflt;
 }
 
 void*
 AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
 {
-#define NEED_PAGE_ALIGNED 0
-    size_t pa_start; // Page aligned starting
-    size_t pa_end; // Page aligned ending
-    size_t pa_size; // Total page aligned size
-    struct stat st;
-    uint8_t* buf;
+    MOZ_ASSERT(length && alignment);
 
-    // Make sure file exists and do sanity check for offset and size.
-    if (fstat(fd, &st) < 0 || offset >= (size_t) st.st_size ||
-        length == 0 || length > (size_t) st.st_size - offset)
+    // The allocation granularity and the requested offset
+    // must both be divisible by the requested alignment.
+    if (allocGranularity % alignment != 0 || offset % alignment != 0)
         return nullptr;
 
-    // Check for minimal alignment requirement.
-#if NEED_PAGE_ALIGNED
-    alignment = std::max(alignment, pageSize);
-#endif
-    if (offset & (alignment - 1))
+    // Sanity check the offset and size, as mmap does not do this for us.
+    struct stat st;
+    if (fstat(fd, &st) || offset >= uint64_t(st.st_size) || length > uint64_t(st.st_size) - offset)
         return nullptr;
 
-    // Page aligned starting of the offset.
-    pa_start = offset & ~(pageSize - 1);
-    // Calculate page aligned ending by adding one page to the page aligned
-    // starting of data end position(offset + length - 1).
-    pa_end = ((offset + length - 1) & ~(pageSize - 1)) + pageSize;
-    pa_size = pa_end - pa_start;
+    size_t alignedOffset = offset - (offset % allocGranularity);
+    size_t alignedLength = length + (offset % allocGranularity);
 
-    // Ask for a continuous memory location.
-    buf = (uint8_t*) MapMemory(pa_size);
-    if (!buf)
+    uint8_t* map = static_cast<uint8_t*>(MapMemory(alignedLength, PROT_READ | PROT_WRITE,
+                                                   MAP_PRIVATE, fd, alignedOffset));
+    if (!map)
         return nullptr;
 
-    buf = (uint8_t*) MapMemoryAt(buf, pa_size, PROT_READ | PROT_WRITE,
-                                  MAP_PRIVATE | MAP_FIXED, fd, pa_start);
-    if (!buf)
-        return nullptr;
+#ifdef DEBUG
+    // Zero out data before and after the desired mapping to catch errors early.
+    if (offset != alignedOffset)
+        memset(map, 0, offset - alignedOffset);
+    if (alignedLength % pageSize)
+        memset(map + alignedLength, 0, pageSize - (alignedLength % pageSize));
+#endif
 
-    // Reset the data before target file, which we don't need to see.
-    memset(buf, 0, offset - pa_start);
-
-    // Reset the data after target file, which we don't need to see.
-    memset(buf + (offset - pa_start) + length, 0, pa_end - (offset + length));
-
-    return buf + (offset - pa_start);
+    return map + (offset - alignedOffset);
 }
 
 void
 DeallocateMappedContent(void* p, size_t length)
 {
-    void* pa_start; // Page aligned starting
-    size_t total_size; // Total allocated size
+    if (!p)
+        return;
 
-    pa_start = (void*)(uintptr_t(p) & ~(pageSize - 1));
-    total_size = ((uintptr_t(p) + length) & ~(pageSize - 1)) + pageSize - uintptr_t(pa_start);
-    if (munmap(pa_start, total_size))
-        MOZ_ASSERT(errno == ENOMEM);
+    // Calculate the address originally returned by mmap.
+    // This is needed because AllocateMappedContent returns a pointer
+    // that might be offset from the mapping, as the beginning of a
+    // mapping must be aligned with the allocation granularity.
+    uintptr_t map = uintptr_t(p) - (uintptr_t(p) % allocGranularity);
+    size_t alignedLength = length + (uintptr_t(p) % allocGranularity);
+    UnmapPages(reinterpret_cast<void*>(map), alignedLength);
 }
 
 #else
 #error "Memory mapping functions are not defined for your OS."
 #endif
 
 void
 ProtectPages(void* p, size_t size)
--- a/js/src/jit-test/tests/asm.js/bug1219954.js
+++ b/js/src/jit-test/tests/asm.js/bug1219954.js
@@ -1,8 +1,9 @@
+// |jit-test| slow
 "use strict";
 
 if (!('oomTest' in this))
     quit();
 
 let g = (function() {
     "use asm";
     function f() {}
--- a/js/src/jit-test/tests/asm.js/testBug1255954.js
+++ b/js/src/jit-test/tests/asm.js/testBug1255954.js
@@ -1,8 +1,10 @@
+// |jit-test| slow
+
 const USE_ASM = '"use asm";';
 if (!('oomTest' in this))
     quit();
 function asmCompile() {
     var f = Function.apply(null, arguments);
 }
 oomTest(() => {
     try {
--- a/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD-load-store.js
@@ -1,13 +1,16 @@
-// |jit-test| test-also-noasmjs
+// |jit-test|
 load(libdir + "asm.js");
 load(libdir + "simd.js");
 load(libdir + "asserts.js");
 
+// Avoid pathological --ion-eager compile times due to bails in loops
+setJitCompilerOption('ion.warmup.trigger', 1000000);
+
 // Set to true to see more JS debugging spew
 const DEBUG = false;
 
 if (!isSimdAvailable() || typeof SIMD === 'undefined') {
     DEBUG && print("won't run tests as simd extensions aren't activated yet");
     quit(0);
 }
 
@@ -268,53 +271,43 @@ for (var i = 0; i < SIZE; i++)
 function MakeCodeFor(typeName) {
     return `
     "use asm";
     var type = glob.SIMD.${typeName};
     var c = type.check;
 
     var l1 = type.load1;
     var l2 = type.load2;
-    var l3 = type.load3;
 
     var s1 = type.store1;
     var s2 = type.store2;
-    var s3 = type.store3;
 
     var u8 = new glob.Uint8Array(heap);
 
     function load1(i) { i=i|0; return l1(u8, i); }
     function load2(i) { i=i|0; return l2(u8, i); }
-    function load3(i) { i=i|0; return l3(u8, i); }
 
     function loadCst1() { return l1(u8, 41 << 2); }
     function loadCst2() { return l2(u8, 41 << 2); }
-    function loadCst3() { return l3(u8, 41 << 2); }
 
     function store1(i, x) { i=i|0; x=c(x); return s1(u8, i, x); }
     function store2(i, x) { i=i|0; x=c(x); return s2(u8, i, x); }
-    function store3(i, x) { i=i|0; x=c(x); return s3(u8, i, x); }
 
     function storeCst1(x) { x=c(x); return s1(u8, 41 << 2, x); }
     function storeCst2(x) { x=c(x); return s2(u8, 41 << 2, x); }
-    function storeCst3(x) { x=c(x); return s3(u8, 41 << 2, x); }
 
     return {
         load1: load1,
         load2: load2,
-        load3: load3,
         loadCst1: loadCst1,
         loadCst2: loadCst2,
-        loadCst3: loadCst3,
         store1: store1,
         store2: store2,
-        store3: store3,
         storeCst1: storeCst1,
         storeCst2: storeCst2,
-        storeCst3: storeCst3,
     }
 `;
 }
 
 var SIZE = 0x10000;
 
 function TestPartialLoads(m, typedArray, x, y, z, w) {
     // Fill array with predictable values
@@ -324,53 +317,44 @@ function TestPartialLoads(m, typedArray,
         typedArray[i + 2] = z(i);
         typedArray[i + 3] = w(i);
     }
 
     // Test correct loads
     var i = 0, j = 0; // i in elems, j in bytes
     assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
     assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
-    assertEqX4(m.load3(j), [x(i), y(i), z(i), 0]);
 
     j += 4;
     assertEqX4(m.load1(j), [y(i), 0, 0, 0]);
     assertEqX4(m.load2(j), [y(i), z(i), 0, 0]);
-    assertEqX4(m.load3(j), [y(i), z(i), w(i), 0]);
 
     j += 4;
     assertEqX4(m.load1(j), [z(i), 0, 0, 0]);
     assertEqX4(m.load2(j), [z(i), w(i), 0, 0]);
-    assertEqX4(m.load3(j), [z(i), w(i), x(i+4), 0]);
 
     j += 4;
     assertEqX4(m.load1(j), [w(i), 0, 0, 0]);
     assertEqX4(m.load2(j), [w(i), x(i+4), 0, 0]);
-    assertEqX4(m.load3(j), [w(i), x(i+4), y(i+4), 0]);
 
     j += 4;
     i += 4;
     assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
     assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
-    assertEqX4(m.load3(j), [x(i), y(i), z(i), 0]);
 
     // Test loads with constant indexes (41)
     assertEqX4(m.loadCst1(), [y(40), 0, 0, 0]);
     assertEqX4(m.loadCst2(), [y(40), z(40), 0, 0]);
-    assertEqX4(m.loadCst3(), [y(40), z(40), w(40), 0]);
 
     // Test limit and OOB accesses
     assertEqX4(m.load1((SIZE - 1) << 2), [w(SIZE - 4), 0, 0, 0]);
     assertThrowsInstanceOf(() => m.load1(((SIZE - 1) << 2) + 1), RangeError);
 
     assertEqX4(m.load2((SIZE - 2) << 2), [z(SIZE - 4), w(SIZE - 4), 0, 0]);
     assertThrowsInstanceOf(() => m.load2(((SIZE - 2) << 2) + 1), RangeError);
-
-    assertEqX4(m.load3((SIZE - 3) << 2), [y(SIZE - 4), z(SIZE - 4), w(SIZE - 4), 0]);
-    assertThrowsInstanceOf(() => m.load3(((SIZE - 3) << 2) + 1), RangeError);
 }
 
 // Partial stores
 function TestPartialStores(m, typedArray, typeName, x, y, z, w) {
     var val = SIMD[typeName](x, y, z, w);
 
     function Reset() {
         for (var i = 0; i < SIZE; i++)
@@ -394,28 +378,16 @@ function TestPartialStores(m, typedArray
         CheckNotModified(0, i >> 2);
         assertEq(typedArray[i >> 2], x);
         assertEq(typedArray[(i >> 2) + 1], y);
         CheckNotModified((i >> 2) + 2, SIZE);
         typedArray[i >> 2] = (i >> 2) + 1;
         typedArray[(i >> 2) + 1] = (i >> 2) + 2;
     }
 
-    function TestStore3(i) {
-        m.store3(i, val);
-        CheckNotModified(0, i >> 2);
-        assertEq(typedArray[i >> 2], x);
-        assertEq(typedArray[(i >> 2) + 1], y);
-        assertEq(typedArray[(i >> 2) + 2], z);
-        CheckNotModified((i >> 2) + 3, SIZE);
-        typedArray[i >> 2] = (i >> 2) + 1;
-        typedArray[(i >> 2) + 1] = (i >> 2) + 2;
-        typedArray[(i >> 2) + 2] = (i >> 2) + 3;
-    }
-
     function TestOOBStore(f) {
         assertThrowsInstanceOf(f, RangeError);
         CheckNotModified(0, SIZE);
     }
 
     Reset();
 
     TestStore1(0);
@@ -435,52 +407,30 @@ function TestPartialStores(m, typedArray
     TestStore2(3 << 2);
     TestStore2(1337 << 2);
 
     var i = (SIZE - 2) << 2;
     TestStore2(i);
     TestOOBStore(() => m.store2(i + 1, val));
     TestOOBStore(() => m.store2(-1, val));
 
-    TestStore3(0);
-    TestStore3(1 << 2);
-    TestStore3(2 << 2);
-    TestStore3(3 << 2);
-    TestStore3(1337 << 2);
-
-    var i = (SIZE - 3) << 2;
-    TestStore3(i);
-    TestOOBStore(() => m.store3(i + 1, val));
-    TestOOBStore(() => m.store3(-1, val));
-    TestOOBStore(() => m.store3(-9, val));
-
     // Constant indexes (41)
     m.storeCst1(val);
     CheckNotModified(0, 41);
     assertEq(typedArray[41], x);
     CheckNotModified(42, SIZE);
     typedArray[41] = 42;
 
     m.storeCst2(val);
     CheckNotModified(0, 41);
     assertEq(typedArray[41], x);
     assertEq(typedArray[42], y);
     CheckNotModified(43, SIZE);
     typedArray[41] = 42;
     typedArray[42] = 43;
-
-    m.storeCst3(val);
-    CheckNotModified(0, 41);
-    assertEq(typedArray[41], x);
-    assertEq(typedArray[42], y);
-    assertEq(typedArray[43], z);
-    CheckNotModified(44, SIZE);
-    typedArray[41] = 42;
-    typedArray[42] = 43;
-    typedArray[43] = 44;
 }
 
 var f32 = new Float32Array(SIZE);
 var mFloat32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('Float32x4')), this, null, f32.buffer);
 
 TestPartialLoads(mFloat32x4, f32,
             (i) => i + 1,
             (i) => Math.fround(13.37),
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -1,13 +1,16 @@
 load(libdir + "asm.js");
 load(libdir + "simd.js");
 load(libdir + "asserts.js");
 var heap = new ArrayBuffer(0x10000);
 
+// Avoid pathological --ion-eager compile times due to bails in loops
+setJitCompilerOption('ion.warmup.trigger', 1000000);
+
 // Set to true to see more JS debugging spew
 const DEBUG = false;
 
 if (!isSimdAvailable() || typeof SIMD === 'undefined') {
     DEBUG && print("won't run tests as simd extensions aren't activated yet");
     quit(0);
 }
 
--- a/js/src/jit-test/tests/asm.js/testZOOB.js
+++ b/js/src/jit-test/tests/asm.js/testZOOB.js
@@ -1,15 +1,12 @@
-// |jit-test| test-also-noasmjs
+// |jit-test|
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
 
-setIonCheckGraphCoherency(false);
-setCachingEnabled(false);
-
 var ab = new ArrayBuffer(BUF_MIN);
 
 // Compute a set of interesting indices.
 indices = [0]
 for (var i of [4,1024,BUF_MIN,Math.pow(2,30),Math.pow(2,31),Math.pow(2,32),Math.pow(2,33)]) {
     for (var j of [-2,-1,0,1,2]) {
         for (var k of [1,-1])
             indices.push((i+j)*k);
@@ -99,105 +96,83 @@ function testSimdX4(ctor, shift, scale, 
     var arr = new ctor(ab);
 
     var c = asmCompile('glob', 'imp', 'b',
                        USE_ASM +
                        'var arr=new glob.' + ctor.name + '(b); ' +
                        'var SIMD_' + simdName + ' = glob.SIMD.' + simdName + '; ' +
                        'var SIMD_' + simdName + '_check = SIMD_' + simdName + '.check; ' +
                        'var SIMD_' + simdName + '_load = SIMD_' + simdName + '.load; ' +
-                       'var SIMD_' + simdName + '_load3 = SIMD_' + simdName + '.load3; ' +
                        'var SIMD_' + simdName + '_load2 = SIMD_' + simdName + '.load2; ' +
                        'var SIMD_' + simdName + '_load1 = SIMD_' + simdName + '.load1; ' +
                        'var SIMD_' + simdName + '_store = SIMD_' + simdName + '.store; ' +
-                       'var SIMD_' + simdName + '_store3 = SIMD_' + simdName + '.store3; ' +
                        'var SIMD_' + simdName + '_store2 = SIMD_' + simdName + '.store2; ' +
                        'var SIMD_' + simdName + '_store1 = SIMD_' + simdName + '.store1; ' +
                        'function load(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
-                       'function load3(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load3(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
                        'function load2(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load2(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
                        'function load1(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load1(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
                        'function store(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
-                       'function store3(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store3(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
                        'function store2(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store2(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
                        'function store1(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store1(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
-                       'return { load: load, load3: load3, load2: load2, load1: load1, store: store, store3: store3, store2 : store2, store1 : store1 }');
+                       'return { load: load, load2: load2, load1: load1, store: store, store2 : store2, store1 : store1 }');
     var f = asmLink(c, this, null, ab);
 
     for (var i of indices) {
         var index = ((i<<scale)+disp)>>shift;
 
-        var v, v3, v2, v1;
-        var t = false, t3 = false, t2 = false, t1 = false;
+        var v, v2, v1;
+        var t = false, t2 = false, t1 = false;
         try { v = simdCtor.load(arr, index); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             t = true;
         }
-        try { v3 = simdCtor.load3(arr, index); }
-        catch (e) {
-            assertEq(e instanceof RangeError, true);
-            t3 = true;
-        }
         try { v2 = simdCtor.load2(arr, index); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             t2 = true;
         }
         try { v1 = simdCtor.load1(arr, index); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             t1 = true;
         }
 
         // Loads
-        var l, l3, l2, l1;
-        var r = false, r3 = false, r2 = false, r1 = false;
+        var l, l2, l1;
+        var r = false, r2 = false, r1 = false;
         try { l = f.load(i); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             r = true;
         }
-        try { l3 = f.load3(i); }
-        catch (e) {
-            assertEq(e instanceof RangeError, true);
-            r3 = true;
-        }
         try { l2 = f.load2(i); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             r2 = true;
         }
         try { l1 = f.load1(i); }
         catch (e) {
             assertEq(e instanceof RangeError, true);
             r1 = true;
         }
         assertEq(t, r);
-        assertEq(t3, r3);
         assertEq(t2, r2);
         assertEq(t1, r1);
         if (!t) assertEqX4(v, l);
-        if (!t3) assertEqX4(v3, l3);
         if (!t2) assertEqX4(v2, l2);
         if (!t1) assertEqX4(v1, l1);
 
         // Stores
         if (!t) {
             simdCtor.store(arr, index, simdCtor.neg(v));
             f.store(i, v);
             assertEqX4(simdCtor.load(arr, index), v);
         } else
             assertThrowsInstanceOf(() => f.store(i, simdCtor()), RangeError);
-        if (!t3) {
-            simdCtor.store3(arr, index, simdCtor.neg(v3));
-            f.store3(i, v3);
-            assertEqX4(simdCtor.load3(arr, index), v3);
-        } else
-            assertThrowsInstanceOf(() => f.store3(i, simdCtor()), RangeError);
         if (!t2) {
             simdCtor.store2(arr, index, simdCtor.neg(v2));
             f.store2(i, v2);
             assertEqX4(simdCtor.load2(arr, index), v2);
         } else
             assertThrowsInstanceOf(() => f.store2(i, simdCtor()), RangeError);
         if (!t1) {
             simdCtor.store1(arr, index, simdCtor.neg(v1));
@@ -234,11 +209,19 @@ test(testInt, Int8Array, 0);
 test(testInt, Uint8Array, 0);
 test(testInt, Int16Array, 1);
 test(testInt, Uint16Array, 1);
 test(testInt, Int32Array, 2);
 test(testInt, Uint32Array, 2);
 test(testFloat32, Float32Array, 2);
 test(testFloat64, Float64Array, 3);
 if (typeof SIMD !== 'undefined' && isSimdAvailable()) {
+    // Avoid pathological --ion-eager compile times due to bails in loops
+    setJitCompilerOption('ion.warmup.trigger', 1000000);
+
+    // Use a fresh ArrayBuffer so prepareForAsmJS can allocated a guard page
+    // which SIMD.js needs. Since the original ArrayBuffer was prepared for
+    // asm.js that didn't use SIMD.js, it has no guard page (on 32-bit).
+    ab = new ArrayBuffer(BUF_MIN);
+
     test(testInt32x4, Uint8Array, 0);
     test(testFloat32x4, Uint8Array, 0);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1300517.js
@@ -0,0 +1,12 @@
+// |jit-test| error: ReferenceError
+g = newGlobal();
+g.log *= "";
+Debugger(g).onDebuggerStatement = frame => frame.eval("log += this.Math.toString();");
+let forceException = g.eval(`
+    (class extends class {} {
+        constructor() {
+            debugger;
+        }
+    })
+`);
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1300528.js
@@ -0,0 +1,34 @@
+load(libdir + "asserts.js");
+
+if (helperThreadCount() === 0)
+  quit(0);
+
+function BigInteger(a, b, c) {}
+function montConvert(x) {
+    var r = new BigInteger(null);
+    return r;
+}
+var ba = new Array();
+a = new BigInteger(ba);
+g = montConvert(a);
+var lfGlobal = newGlobal();
+for (lfLocal in this) {
+    if (!(lfLocal in lfGlobal)) {
+        lfGlobal[lfLocal] = this[lfLocal];
+    }
+}
+lfGlobal.offThreadCompileScript(`
+  var dbg = new Debugger(g);
+  dbg.onEnterFrame = function (frame) {
+    var frameThis = frame.this;
+  }
+`);
+lfGlobal.runOffThreadScript();
+assertThrowsInstanceOf(test, ReferenceError);
+function test() {
+    function check(fun, msg, todo) {
+        success = fun();
+    }
+    check(() => Object.getPrototypeOf(view) == Object.getPrototypeOf(simple));
+    typeof this;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/big-resize.js
@@ -0,0 +1,10 @@
+load(libdir + "wasm.js");
+
+assertEq(new WebAssembly.Instance(new WebAssembly.Module(textToBinary(`(module
+    (memory 1 32768)
+    (func $test (result i32)
+        (if (i32.eq (grow_memory (i32.const 16384)) (i32.const -1)) (return (i32.const 42)))
+        (i32.store (i32.const 1073807356) (i32.const 42))
+        (i32.load (i32.const 1073807356)))
+    (export "test" $test)
+)`))).exports.test(), 42);
--- a/js/src/jit-test/tests/wasm/memory.js
+++ b/js/src/jit-test/tests/wasm/memory.js
@@ -143,24 +143,20 @@ for (let [type, ext] of [
     ['i64', '32'],
     ['f32', ''],
     ['f64', ''],
 ])
 {
     assertErrorMessage(() => badStoreModule(type, ext), TypeError, /can't touch memory/);
 }
 
-for (var ind = 0; ind < 1; ind++) {
-    /*
-     * TODO: wasm.explicit-bounds-check option is being deprecated. We will be adding a
-     * new option that treats all offset as "non-foldable". When that is added trigger
-     * it here when ind == 1.
-    if (ind == 1)
-        setJitCompilerOption('wasm.explicit-bounds-checks', 1);
-    */
+assertEq(getJitCompilerOptions()['wasm.fold-offsets'], 1);
+
+for (var foldOffsets = 0; foldOffsets <= 1; foldOffsets++) {
+    setJitCompilerOption('wasm.fold-offsets', foldOffsets);
 
     testLoad('i32', '', 0, 0, 0, 0x03020100);
     testLoad('i32', '', 1, 0, 1, 0x04030201);
     testLoad('i32', '', 0, 4, 0, 0x07060504);
     testLoad('i32', '', 1, 3, 4, 0x07060504);
     testLoad('f32', '', 0, 0, 0, 3.820471434542632e-37);
     testLoad('f32', '', 1, 0, 1, 1.539989614439558e-36);
     testLoad('f32', '', 0, 4, 0, 1.0082513512365273e-34);
@@ -394,8 +390,10 @@ for (var ind = 0; ind < 1; ind++) {
 
             testStore('i64', '', lastValidIndex, offset, align, '0x1234567887654321');
             testStoreOOB('i64', '', lastValidIndex + 1, offset, align, '0x1234567887654321');
         }
 
         setJitCompilerOption('wasm.test-mode', 0);
     }
 }
+
+setJitCompilerOption('wasm.fold-offsets', 1);
--- a/js/src/jit-test/tests/wasm/regress/oom-eval.js
+++ b/js/src/jit-test/tests/wasm/regress/oom-eval.js
@@ -1,9 +1,9 @@
-// |jit-test| allow-oom
+// |jit-test| slow; allow-oom
 
 if (typeof oomTest !== 'function' || !wasmIsSupported()) {
     print('Missing oomTest or wasm support in wasm/regress/oom-eval');
     quit();
 }
 
 function foo() {
   var g = newGlobal();
--- a/js/src/jit-test/tests/wasm/regress/oom-init.js
+++ b/js/src/jit-test/tests/wasm/regress/oom-init.js
@@ -1,9 +1,9 @@
-// |jit-test| allow-oom
+// |jit-test| slow; allow-oom
 
 if (typeof oomTest !== 'function' || !wasmIsSupported())
     quit();
 
 Object.getOwnPropertyNames(this);
 s = newGlobal();
 evalcx("\
     /x/;\
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/resizing.js
@@ -0,0 +1,77 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+const Module = WebAssembly.Module;
+const Instance = WebAssembly.Instance;
+const Table = WebAssembly.Table;
+const Memory = WebAssembly.Memory;
+
+// Test for stale heap pointers after resize
+
+// Grow directly from builtin call:
+assertEq(evalText(`(module
+    (memory 1)
+    (func $test (result i32)
+        (i32.store (i32.const 0) (i32.const 1))
+        (i32.store (i32.const 65532) (i32.const 10))
+        (grow_memory (i32.const 99))
+        (i32.store (i32.const 6553596) (i32.const 100))
+        (i32.add
+            (i32.load (i32.const 0))
+            (i32.add
+                (i32.load (i32.const 65532))
+                (i32.load (i32.const 6553596)))))
+    (export "test" $test)
+)`).exports.test(), 111);
+
+// Grow during call_import:
+var exports = evalText(`(module
+    (import $imp "a" "imp")
+    (memory 1)
+    (func $grow (grow_memory (i32.const 99)))
+    (export "grow" $grow)
+    (func $test (result i32)
+        (i32.store (i32.const 0) (i32.const 1))
+        (i32.store (i32.const 65532) (i32.const 10))
+        (call $imp)
+        (i32.store (i32.const 6553596) (i32.const 100))
+        (i32.add
+            (i32.load (i32.const 0))
+            (i32.add
+                (i32.load (i32.const 65532))
+                (i32.load (i32.const 6553596)))))
+    (export "test" $test)
+)`, {a:{imp() { exports.grow() }}}).exports;
+
+setJitCompilerOption("baseline.warmup.trigger", 2);
+setJitCompilerOption("ion.warmup.trigger", 4);
+for (var i = 0; i < 10; i++)
+    assertEq(exports.test(), 111);
+
+// Grow during call_indirect:
+var mem = new Memory({initial:1});
+var tbl = new Table({initial:1, element:"anyfunc"});
+var exports1 = evalText(`(module
+    (import "a" "mem" (memory 1))
+    (func $grow
+        (i32.store (i32.const 65532) (i32.const 10))
+        (grow_memory (i32.const 99))
+        (i32.store (i32.const 6553596) (i32.const 100)))
+    (export "grow" $grow)
+)`, {a:{mem}}).exports;
+var exports2 = evalText(`(module
+    (import "a" "tbl" (table 1))
+    (import "a" "mem" (memory 1))
+    (type $v2v (func))
+    (func $test (result i32)
+        (i32.store (i32.const 0) (i32.const 1))
+        (call_indirect $v2v (i32.const 0))
+        (i32.add
+            (i32.load (i32.const 0))
+            (i32.add
+                (i32.load (i32.const 65532))
+                (i32.load (i32.const 6553596)))))
+    (export "test" $test)
+)`, {a:{tbl, mem}}).exports;
+tbl.set(0, exports1.grow);
+assertEq(exports2.test(), 111);
--- a/js/src/jit-test/tests/wasm/spec/memory_trap.wast
+++ b/js/src/jit-test/tests/wasm/spec/memory_trap.wast
@@ -11,26 +11,26 @@
     )
 
     (export "load" $load)
     (func $load (param $i i32) (result i32)
       (i32.load (i32.add (call $addr_limit) (get_local $i)))
     )
 
     (export "grow_memory" $grow_memory)
-    (func $grow_memory (param i32)
+    (func $grow_memory (param i32) (result i32)
       (grow_memory (get_local 0))
     )
 )
 
 (assert_return (invoke "store" (i32.const -4) (i32.const 42)) (i32.const 42))
 (assert_return (invoke "load" (i32.const -4)) (i32.const 42))
 (assert_trap (invoke "store" (i32.const -3) (i32.const 13)) "out of bounds memory access")
 (assert_trap (invoke "load" (i32.const -3)) "out of bounds memory access")
 (assert_trap (invoke "store" (i32.const -2) (i32.const 13)) "out of bounds memory access")
 (assert_trap (invoke "load" (i32.const -2)) "out of bounds memory access")
 (assert_trap (invoke "store" (i32.const -1) (i32.const 13)) "out of bounds memory access")
 (assert_trap (invoke "load" (i32.const -1)) "out of bounds memory access")
 (assert_trap (invoke "store" (i32.const 0) (i32.const 13)) "out of bounds memory access")
 (assert_trap (invoke "load" (i32.const 0)) "out of bounds memory access")
 (assert_trap (invoke "store" (i32.const 0x80000000) (i32.const 13)) "out of bounds memory access")
 (assert_trap (invoke "load" (i32.const 0x80000000)) "out of bounds memory access")
-(assert_trap (invoke "grow_memory" (i32.const 0x80000000)) "memory size exceeds implementation limit")
+(assert_return (invoke "grow_memory" (i32.const 0x80000000)) (i32.const -1))
--- a/js/src/jit-test/tests/wasm/spec/memory_trap.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory_trap.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO current_memory opcode + traps on OOB
-quit();
 var importedArgs = ['memory_trap.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit-test/tests/wasm/spec/resizing.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/resizing.wast.js
@@ -1,4 +1,2 @@
 // |jit-test| test-also-wasm-baseline
-// TODO memory resizing (you don't say)
-quit();
 var importedArgs = ['resizing.wast']; load(scriptdir + '../spec.js');
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11610,16 +11610,37 @@ CodeGenerator::visitInterruptCheck(LInte
 
 void
 CodeGenerator::visitWasmTrap(LWasmTrap* lir)
 {
     MOZ_ASSERT(gen->compilingAsmJS());
     masm.jump(wasm::JumpTarget(lir->mir()->trap()));
 }
 
+void
+CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
+{
+    const MWasmBoundsCheck* mir = ins->mir();
+    Register ptr = ToRegister(ins->ptr());
+
+    if (mir->isRedundant()) {
+        // For better test coverage, inject debug assertions that redundant
+        // bounds checks really are redundant.
+#ifdef DEBUG
+        Label ok;
+        masm.wasmBoundsCheck(Assembler::Below, ptr, &ok);
+        masm.assumeUnreachable("Redundant bounds check failed!");
+        masm.bind(&ok);
+#endif
+        return;
+    }
+
+    masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptr, wasm::JumpTarget::OutOfBounds);
+}
+
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile, "Recompile");
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo =
     FunctionInfo<ForcedRecompileFn>(ForcedRecompile, "ForcedRecompile");
 
 void
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -414,16 +414,17 @@ class CodeGenerator final : public CodeG
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
     void visitWasmTrap(LWasmTrap* lir);
+    void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitRecompileCheck(LRecompileCheck* ins);
     void visitRotate(LRotate* ins);
 
     void visitRandom(LRandom* ins);
     void visitSignExtend(LSignExtend* ins);
 
 #ifdef DEBUG
     void emitDebugForceBailing(LInstruction* lir);
--- a/js/src/jit/EffectiveAddressAnalysis.cpp
+++ b/js/src/jit/EffectiveAddressAnalysis.cpp
@@ -95,46 +95,43 @@ AnalyzeLsh(TempAllocator& alloc, MLsh* l
     if (base->isRecoveredOnBailout())
         return;
 
     MEffectiveAddress* eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement);
     last->replaceAllUsesWith(eaddr);
     last->block()->insertAfter(last, eaddr);
 }
 
-template<typename MWasmMemoryAccessType>
+template<typename AsmJSMemoryAccess>
 bool
-EffectiveAddressAnalysis::tryAddDisplacement(MWasmMemoryAccessType* ins, int32_t o)
+EffectiveAddressAnalysis::tryAddDisplacement(AsmJSMemoryAccess* ins, int32_t o)
 {
+#ifdef WASM_HUGE_MEMORY
     // Compute the new offset. Check for overflow.
     uint32_t oldOffset = ins->offset();
     uint32_t newOffset = oldOffset + o;
     if (o < 0 ? (newOffset >= oldOffset) : (newOffset < oldOffset))
         return false;
 
-    // Compute the new offset to the end of the access. Check for overflow
-    // here also.
-    uint32_t newEnd = newOffset + ins->byteSize();
-    if (newEnd < newOffset)
-        return false;
-
-    // Determine the range of valid offsets which can be folded into this
-    // instruction and check whether our computed offset is within that range.
-    size_t range = mir_->foldableOffsetRange(ins);
-    if (size_t(newEnd) > range)
+    // The offset must ultimately be written into the offset immediate of a load
+    // or store instruction so don't allow folding of the offset is bigger.
+    if (newOffset >= wasm::OffsetGuardLimit)
         return false;
 
     // Everything checks out. This is the new offset.
     ins->setOffset(newOffset);
     return true;
+#else
+    return false;
+#endif
 }
 
-template<typename MWasmMemoryAccessType>
+template<typename AsmJSMemoryAccess>
 void
-EffectiveAddressAnalysis::analyzeAsmHeapAccess(MWasmMemoryAccessType* ins)
+EffectiveAddressAnalysis::analyzeAsmJSHeapAccess(AsmJSMemoryAccess* ins)
 {
     MDefinition* base = ins->base();
 
     if (base->isConstant()) {
         // Look for heap[i] where i is a constant offset, and fold the offset.
         // By doing the folding now, we simplify the task of codegen; the offset
         // is always the address mode immediate. This also allows it to avoid
         // a situation where the sum of a constant pointer value and a non-zero
@@ -193,15 +190,15 @@ EffectiveAddressAnalysis::analyze()
 
             // Note that we don't check for MAsmJSCompareExchangeHeap
             // or MAsmJSAtomicBinopHeap, because the backend and the OOB
             // mechanism don't support non-zero offsets for them yet
             // (TODO bug 1254935).
             if (i->isLsh())
                 AnalyzeLsh(graph_.alloc(), i->toLsh());
             else if (i->isAsmJSLoadHeap())
-                analyzeAsmHeapAccess(i->toAsmJSLoadHeap());
+                analyzeAsmJSHeapAccess(i->toAsmJSLoadHeap());
             else if (i->isAsmJSStoreHeap())
-                analyzeAsmHeapAccess(i->toAsmJSStoreHeap());
+                analyzeAsmJSHeapAccess(i->toAsmJSStoreHeap());
         }
     }
     return true;
 }
--- a/js/src/jit/EffectiveAddressAnalysis.h
+++ b/js/src/jit/EffectiveAddressAnalysis.h
@@ -14,21 +14,21 @@ namespace jit {
 
 class MIRGraph;
 
 class EffectiveAddressAnalysis
 {
     MIRGenerator* mir_;
     MIRGraph& graph_;
 
-    template<typename MWasmMemoryAccessType>
-    MOZ_MUST_USE bool tryAddDisplacement(MWasmMemoryAccessType* ins, int32_t o);
+    template <typename AsmJSMemoryAccess>
+    MOZ_MUST_USE bool tryAddDisplacement(AsmJSMemoryAccess* ins, int32_t o);
 
-    template<typename MWasmMemoryAccessType>
-    void analyzeAsmHeapAccess(MWasmMemoryAccessType* ins);
+    template <typename AsmJSMemoryAccess>
+    void analyzeAsmJSHeapAccess(AsmJSMemoryAccess* ins);
 
   public:
     EffectiveAddressAnalysis(MIRGenerator* mir, MIRGraph& graph)
       : mir_(mir), graph_(graph)
     {}
 
     MOZ_MUST_USE bool analyze();
 };
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -219,16 +219,20 @@ DefaultJitOptions::DefaultJitOptions()
     }
 
     // Toggles whether unboxed plain objects can be created by the VM.
     SET_DEFAULT(disableUnboxedObjects, false);
 
     // Test whether wasm int64 / double NaN bits testing is enabled.
     SET_DEFAULT(wasmTestMode, false);
 
+    // Toggles the optimization whereby offsets are folded into loads and not
+    // included in the bounds check.
+    SET_DEFAULT(wasmFoldOffsets, true);
+
     // Determines whether we suppress using signal handlers
     // for interrupting jit-ed code. This is used only for testing.
     SET_DEFAULT(ionInterruptWithoutSignals, false);
 }
 
 bool
 DefaultJitOptions::isSmallFunction(JSScript* script) const
 {
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -65,16 +65,17 @@ struct DefaultJitOptions
     bool disableSharedStubs;
     bool disableSincos;
     bool disableSink;
     bool eagerCompilation;
     bool forceInlineCaches;
     bool limitScriptSize;
     bool osr;
     bool wasmTestMode;
+    bool wasmFoldOffsets;
     bool ionInterruptWithoutSignals;
     uint32_t baselineWarmUpThreshold;
     uint32_t exceptionBailoutThreshold;
     uint32_t frequentBailoutThreshold;
     uint32_t maxStackArgs;
     uint32_t osrPcMismatchesBeforeRecompile;
     uint32_t smallFunctionMaxBytecodeLength_;
     uint32_t jumpThreshold;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4120,16 +4120,39 @@ void
 LIRGenerator::visitHasClass(MHasClass* ins)
 {
     MOZ_ASSERT(ins->object()->type() == MIRType::Object);
     MOZ_ASSERT(ins->type() == MIRType::Boolean);
     define(new(alloc()) LHasClass(useRegister(ins->object())), ins);
 }
 
 void
+LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins)
+{
+    MOZ_ASSERT(ins->base()->type() == MIRType::Int32);
+    MOZ_ASSERT(ins->type() == MIRType::Int32);
+    define(new(alloc()) LWasmAddOffset(useRegisterAtStart(ins->base())), ins);
+}
+
+void
+LIRGenerator::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
+{
+#ifndef DEBUG
+    if (ins->isRedundant())
+        return;
+#endif
+
+    MDefinition* input = ins->input();
+    MOZ_ASSERT(input->type() == MIRType::Int32);
+
+    auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(input));
+    add(lir, ins);
+}
+
+void
 LIRGenerator::visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins)
 {
     if (ins->type() == MIRType::Int64)
         defineInt64(new(alloc()) LWasmLoadGlobalVarI64, ins);
     else
         define(new(alloc()) LWasmLoadGlobalVar, ins);
 }
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -277,16 +277,18 @@ class LIRGenerator : public LIRGenerator
     void visitIn(MIn* ins);
     void visitInArray(MInArray* ins);
     void visitInstanceOf(MInstanceOf* ins);
     void visitCallInstanceOf(MCallInstanceOf* ins);
     void visitIsCallable(MIsCallable* ins);
     void visitIsConstructor(MIsConstructor* ins);
     void visitIsObject(MIsObject* ins);
     void visitHasClass(MHasClass* ins);
+    void visitWasmAddOffset(MWasmAddOffset* ins);
+    void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
     void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins);
     void visitWasmStoreGlobalVar(MWasmStoreGlobalVar* ins);
     void visitAsmJSParameter(MAsmJSParameter* ins);
     void visitAsmJSReturn(MAsmJSReturn* ins);
     void visitAsmJSVoidReturn(MAsmJSVoidReturn* ins);
     void visitAsmJSPassStackArg(MAsmJSPassStackArg* ins);
     void visitWasmCall(MWasmCall* ins);
     void visitSetDOMProperty(MSetDOMProperty* ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/MIR.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include <ctype.h>
 
 #include "jslibmath.h"
@@ -29,16 +30,17 @@
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 using JS::ToInt32;
 
+using mozilla::CheckedInt;
 using mozilla::NumbersAreIdentical;
 using mozilla::IsFloat32Representable;
 using mozilla::IsNaN;
 using mozilla::Maybe;
 using mozilla::DebugOnly;
 
 #ifdef DEBUG
 size_t MUse::index() const
@@ -4939,16 +4941,34 @@ MDefinition*
 MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc)
 {
     if (MDefinition* def = foldsToStore(alloc))
         return def;
 
     return this;
 }
 
+MDefinition*
+MWasmAddOffset::foldsTo(TempAllocator& alloc)
+{
+    MDefinition* baseArg = base();
+    if (!baseArg->isConstant())
+        return this;
+
+    MOZ_ASSERT(baseArg->type() == MIRType::Int32);
+    CheckedInt<uint32_t> ptr = baseArg->toConstant()->toInt32();
+
+    ptr += offset();
+
+    if (!ptr.isValid())
+        return this;
+
+    return MConstant::New(alloc, Int32Value(ptr.value()));
+}
+
 MDefinition::AliasType
 MAsmJSLoadHeap::mightAlias(const MDefinition* def) const
 {
     if (def->isAsmJSStoreHeap()) {
         const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
         if (store->accessType() != accessType())
             return AliasType::MayAlias;
         if (!base()->isConstant() || !store->base()->isConstant())
@@ -5420,25 +5440,26 @@ MWasmCall::New(TempAllocator& alloc, con
 
 MWasmCall*
 MWasmCall::NewBuiltinInstanceMethodCall(TempAllocator& alloc,
                                         const wasm::CallSiteDesc& desc,
                                         const wasm::SymbolicAddress builtin,
                                         const ABIArg& instanceArg,
                                         const Args& args,
                                         MIRType resultType,
-                                        uint32_t spIncrement)
+                                        uint32_t spIncrement,
+                                        uint32_t tlsStackOffset)
 {
     auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
     MWasmCall* call = MWasmCall::New(alloc, desc, callee, args, resultType, spIncrement,
-                                     MWasmCall::DontSaveTls, nullptr);
-
+                                     tlsStackOffset, nullptr);
     if (!call)
         return nullptr;
-    MOZ_ASSERT(instanceArg != ABIArg()); // instanceArg must be initialized.
+
+    MOZ_ASSERT(instanceArg != ABIArg());
     call->instanceArg_ = instanceArg;
     return call;
 }
 
 void
 MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
         if (input()->type() == MIRType::Float32)
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13219,120 +13219,130 @@ class MAsmJSNeg
 
   public:
     INSTRUCTION_HEADER(AsmJSNeg)
     static MAsmJSNeg* NewAsmJS(TempAllocator& alloc, MDefinition* op, MIRType type) {
         return new(alloc) MAsmJSNeg(op, type);
     }
 };
 
+class MWasmBoundsCheck
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    bool redundant_;
+
+    explicit MWasmBoundsCheck(MDefinition* index)
+      : MUnaryInstruction(index),
+        redundant_(false)
+    {
+        setGuard(); // Effectful: throws for OOB.
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmBoundsCheck)
+    TRIVIAL_NEW_WRAPPERS
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    bool isRedundant() const {
+        return redundant_;
+    }
+
+    void setRedundant(bool val) {
+        redundant_ = val;
+    }
+};
+
+class MWasmAddOffset
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
+    uint32_t offset_;
+
+    MWasmAddOffset(MDefinition* base, uint32_t offset)
+      : MUnaryInstruction(base),
+        offset_(offset)
+    {
+        setGuard();
+        setResultType(MIRType::Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(WasmAddOffset)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, base))
+
+    MDefinition* foldsTo(TempAllocator& alloc) override;
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+
+    uint32_t offset() const {
+        return offset_;
+    }
+};
+
 class MWasmMemoryAccess
 {
     uint32_t offset_;
     uint32_t align_;
     Scalar::Type accessType_ : 8;
-    bool needsBoundsCheck_;
     unsigned numSimdElems_;
     MemoryBarrierBits barrierBefore_;
     MemoryBarrierBits barrierAfter_;
 
   public:
     explicit MWasmMemoryAccess(Scalar::Type accessType, uint32_t align, uint32_t offset,
                                unsigned numSimdElems = 0,
                                MemoryBarrierBits barrierBefore = MembarNobits,
                                MemoryBarrierBits barrierAfter = MembarNobits)
       : offset_(offset),
         align_(align),
         accessType_(accessType),
-        needsBoundsCheck_(true),
         numSimdElems_(numSimdElems),
         barrierBefore_(barrierBefore),
         barrierAfter_(barrierAfter)
     {
         MOZ_ASSERT(numSimdElems <= ScalarTypeToLength(accessType));
         MOZ_ASSERT(mozilla::IsPowerOfTwo(align));
     }
 
     uint32_t offset() const { return offset_; }
-    uint32_t endOffset() const { return offset() + byteSize(); }
     uint32_t align() const { return align_; }
     Scalar::Type accessType() const { return accessType_; }
     unsigned byteSize() const {
         return Scalar::isSimdType(accessType())
                ? Scalar::scalarByteSize(accessType()) * numSimdElems()
                : TypedArrayElemSize(accessType());
     }
-    bool needsBoundsCheck() const { return needsBoundsCheck_; }
-    unsigned numSimdElems() const { MOZ_ASSERT(Scalar::isSimdType(accessType_)); return numSimdElems_; }
+    unsigned numSimdElems() const { return numSimdElems_; }
     MemoryBarrierBits barrierBefore() const { return barrierBefore_; }
     MemoryBarrierBits barrierAfter() const { return barrierAfter_; }
     bool isAtomicAccess() const { return (barrierBefore_ | barrierAfter_) != MembarNobits; }
+    bool isSimdAccess() const { return Scalar::isSimdType(accessType_); }
     bool isUnaligned() const { return align() && align() < byteSize(); }
 
-    void removeBoundsCheck() { needsBoundsCheck_ = false; }
-    void setOffset(uint32_t o) { offset_ = o; }
-};
-
-class MWasmBoundsCheck
-  : public MUnaryInstruction,
-    public MWasmMemoryAccess,
-    public NoTypePolicy::Data
-{
-    bool redundant_;
-
-    explicit MWasmBoundsCheck(MDefinition* index, const MWasmMemoryAccess& access)
-      : MUnaryInstruction(index),
-        MWasmMemoryAccess(access),
-        redundant_(false)
-    {
-        setMovable();
-        setGuard(); // Effectful: throws for OOB.
-    }
-
-  public:
-    INSTRUCTION_HEADER(WasmBoundsCheck)
-    TRIVIAL_NEW_WRAPPERS
-
-    bool congruentTo(const MDefinition* ins) const override {
-        if (!congruentIfOperandsEqual(ins))
-            return false;
-        const MWasmBoundsCheck* other = ins->toWasmBoundsCheck();
-        return accessType() == other->accessType() &&
-               offset() == other->offset() &&
-               align() == other->align();
-    }
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
-    }
-
-    bool isRedundant() const {
-        return redundant_;
-    }
-
-    void setRedundant(bool val) {
-        redundant_ = val;
-    }
+    void clearOffset() { offset_ = 0; }
 };
 
 class MWasmLoad
   : public MUnaryInstruction,
     public MWasmMemoryAccess,
     public NoTypePolicy::Data
 {
-    MWasmLoad(MDefinition* base, const MWasmMemoryAccess& access, bool isInt64)
+    MWasmLoad(MDefinition* base, const MWasmMemoryAccess& access, MIRType resultType)
       : MUnaryInstruction(base),
         MWasmMemoryAccess(access)
     {
         setGuard();
-        MOZ_ASSERT(access.accessType() != Scalar::Uint8Clamped, "unexpected load heap in wasm");
-        if (isInt64)
-            setResultType(MIRType::Int64);
-        else
-            setResultType(ScalarTypeToMIRType(access.accessType()));
+        setResultType(resultType);
     }
 
   public:
     INSTRUCTION_HEADER(WasmLoad)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, base))
 
     AliasSet getAliasSet() const override {
@@ -13361,64 +13371,77 @@ class MWasmStore
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, base), (1, value))
 
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::AsmJSHeap);
     }
 };
 
+class MAsmJSMemoryAccess
+{
+    uint32_t offset_;
+    Scalar::Type accessType_;
+    bool needsBoundsCheck_;
+
+  public:
+    explicit MAsmJSMemoryAccess(Scalar::Type accessType)
+      : offset_(0),
+        accessType_(accessType),
+        needsBoundsCheck_(true)
+    {
+        MOZ_ASSERT(accessType != Scalar::Uint8Clamped);
+        MOZ_ASSERT(!Scalar::isSimdType(accessType));
+    }
+
+    uint32_t offset() const { return offset_; }
+    uint32_t endOffset() const { return offset() + byteSize(); }
+    Scalar::Type accessType() const { return accessType_; }
+    unsigned byteSize() const { return TypedArrayElemSize(accessType()); }
+    bool needsBoundsCheck() const { return needsBoundsCheck_; }
+
+    void removeBoundsCheck() { needsBoundsCheck_ = false; }
+    void setOffset(uint32_t o) { offset_ = o; }
+};
+
 class MAsmJSLoadHeap
   : public MUnaryInstruction,
-    public MWasmMemoryAccess,
+    public MAsmJSMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSLoadHeap(MDefinition* base, const MWasmMemoryAccess& access)
+    MAsmJSLoadHeap(MDefinition* base, Scalar::Type accessType)
       : MUnaryInstruction(base),
-        MWasmMemoryAccess(access)
-    {
-        if (access.barrierBefore() | access.barrierAfter())
-            setGuard(); // Not removable
-        else
-            setMovable();
-
-        MOZ_ASSERT(access.accessType() != Scalar::Uint8Clamped, "unexpected load heap in asm.js");
-        setResultType(ScalarTypeToMIRType(access.accessType()));
+        MAsmJSMemoryAccess(accessType)
+    {
+        setResultType(ScalarTypeToMIRType(accessType));
     }
 
   public:
     INSTRUCTION_HEADER(AsmJSLoadHeap)
     TRIVIAL_NEW_WRAPPERS
 
     MDefinition* base() const { return getOperand(0); }
     void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
 
     bool congruentTo(const MDefinition* ins) const override;
     AliasSet getAliasSet() const override {
-        // When a barrier is needed make the instruction effectful by
-        // giving it a "store" effect.
-        if (isAtomicAccess())
-            return AliasSet::Store(AliasSet::AsmJSHeap);
         return AliasSet::Load(AliasSet::AsmJSHeap);
     }
     AliasType mightAlias(const MDefinition* def) const override;
 };
 
 class MAsmJSStoreHeap
   : public MBinaryInstruction,
-    public MWasmMemoryAccess,
+    public MAsmJSMemoryAccess,
     public NoTypePolicy::Data
 {
-    MAsmJSStoreHeap(MDefinition* base, const MWasmMemoryAccess& access, MDefinition* v)
+    MAsmJSStoreHeap(MDefinition* base, Scalar::Type accessType, MDefinition* v)
       : MBinaryInstruction(base, v),
-        MWasmMemoryAccess(access)
-    {
-        if (access.barrierBefore() | access.barrierAfter())
-            setGuard(); // Not removable
-    }
+        MAsmJSMemoryAccess(accessType)
+    {}
 
   public:
     INSTRUCTION_HEADER(AsmJSStoreHeap)
     TRIVIAL_NEW_WRAPPERS
 
     MDefinition* base() const { return getOperand(0); }
     void replaceBase(MDefinition* newBase) { replaceOperand(0, newBase); }
     MDefinition* value() const { return getOperand(1); }
@@ -13660,28 +13683,33 @@ class MWasmCall final
         AnyRegister reg;
         MDefinition* def;
         Arg(AnyRegister reg, MDefinition* def) : reg(reg), def(def) {}
     };
     typedef Vector<Arg, 8, SystemAllocPolicy> Args;
 
     static const uint32_t DontSaveTls = UINT32_MAX;
 
-    static MWasmCall* New(TempAllocator& alloc, const wasm::CallSiteDesc& desc,
-                          const wasm::CalleeDesc& callee, const Args& args, MIRType resultType,
-                          uint32_t spIncrement, uint32_t tlsStackOffset,
+    static MWasmCall* New(TempAllocator& alloc,
+                          const wasm::CallSiteDesc& desc,
+                          const wasm::CalleeDesc& callee,
+                          const Args& args,
+                          MIRType resultType,
+                          uint32_t spIncrement,
+                          uint32_t tlsStackOffset,
                           MDefinition* tableIndex = nullptr);
 
     static MWasmCall* NewBuiltinInstanceMethodCall(TempAllocator& alloc,
                                                    const wasm::CallSiteDesc& desc,
                                                    const wasm::SymbolicAddress builtin,
                                                    const ABIArg& instanceArg,
                                                    const Args& args,
                                                    MIRType resultType,
-                                                   uint32_t spIncrement);
+                                                   uint32_t spIncrement,
+                                                   uint32_t tlsStackOffset);
 
     size_t numArgs() const {
         return argRegs_.length();
     }
     AnyRegister registerForArg(size_t index) const {
         MOZ_ASSERT(index < numArgs());
         return argRegs_[index];
     }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -209,19 +209,16 @@ class MIRGenerator
 
   public:
     AsmJSPerfSpewer& perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 
   public:
     const JitCompileOptions options;
 
-    bool needsBoundsCheckBranch(const MWasmMemoryAccess* access) const;
-    size_t foldableOffsetRange(const MWasmMemoryAccess* access) const;
-
   private:
     GraphSpewer gs_;
 
   public:
     GraphSpewer& graphSpewer() {
         return gs_;
     }
 };
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -100,62 +100,16 @@ MIRGenerator::addAbortedPreliminaryGroup
         if (group == abortedPreliminaryGroups_[i])
             return;
     }
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!abortedPreliminaryGroups_.append(group))
         oomUnsafe.crash("addAbortedPreliminaryGroup");
 }
 
-bool
-MIRGenerator::needsBoundsCheckBranch(const MWasmMemoryAccess* access) const
-{
-    // A heap access needs a bounds-check branch if we're not relying on signal
-    // handlers to catch errors, and if it's not proven to be within bounds.
-    // We use signal-handlers on x64, but on x86 there isn't enough address
-    // space for a guard region.  Also, on x64 the atomic loads and stores
-    // can't (yet) use the signal handlers.
-#ifdef WASM_HUGE_MEMORY
-    return false;
-#else
-    return access->needsBoundsCheck();
-#endif
-}
-
-size_t
-MIRGenerator::foldableOffsetRange(const MWasmMemoryAccess* access) const
-{
-    // This determines whether it's ok to fold up to WasmImmediateRange
-    // offsets, instead of just WasmCheckedImmediateRange.
-
-    static_assert(WasmCheckedImmediateRange <= WasmImmediateRange,
-                  "WasmImmediateRange should be the size of an unconstrained "
-                  "address immediate");
-
-#ifdef WASM_HUGE_MEMORY
-    static_assert(wasm::Uint32Range + WasmImmediateRange + sizeof(wasm::Val) < wasm::MappedSize,
-                  "When using signal handlers for bounds checking, a uint32 is added to the base "
-                  "address followed by an immediate in the range [0, WasmImmediateRange). An "
-                  "unaligned access (whose size is conservatively approximated by wasm::Val) may "
-                  "spill over, so ensure a space at the end.");
-    return WasmImmediateRange;
-#else
-    // On 32-bit platforms, if we've proven the access is in bounds after
-    // 32-bit wrapping, we can fold full offsets because they're added with
-    // 32-bit arithmetic.
-    if (sizeof(intptr_t) == sizeof(int32_t) && !access->needsBoundsCheck())
-        return WasmImmediateRange;
-
-    // Otherwise, only allow the checked size. This is always less than the
-    // minimum heap length, and allows explicit bounds checks to fold in the
-    // offset without overflow.
-    return WasmCheckedImmediateRange;
-#endif
-}
-
 void
 MIRGraph::addBlock(MBasicBlock* block)
 {
     MOZ_ASSERT(block);
     block->setId(blockIdGen_++);
     blocks_.pushBack(block);
     numBlocks_++;
 }
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -266,16 +266,17 @@ namespace jit {
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsConstructor)                                                        \
     _(IsCallable)                                                           \
     _(IsObject)                                                             \
     _(HasClass)                                                             \
     _(CopySign)                                                             \
     _(WasmBoundsCheck)                                                      \
+    _(WasmAddOffset)                                                        \
     _(WasmLoad)                                                             \
     _(WasmStore)                                                            \
     _(WasmTrap)                                                             \
     _(WasmTruncateToInt32)                                                  \
     _(AsmJSNeg)                                                             \
     _(AsmJSUnsignedToDouble)                                                \
     _(AsmJSUnsignedToFloat32)                                               \
     _(AsmJSLoadHeap)                                                        \
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1035,18 +1035,18 @@ class MacroAssembler : public MacroAssem
     inline void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
                              Label* label) PER_SHARED_ARCH;
 
     inline void branchDoubleNotInInt64Range(Address src, Register temp, Label* fail);
     inline void branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail);
     inline void branchFloat32NotInInt64Range(Address src, Register temp, Label* fail);
     inline void branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail);
 
-    template <typename T>
-    inline void branchAdd32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
+    template <typename T, typename L>
+    inline void branchAdd32(Condition cond, T src, Register dest, L label) PER_SHARED_ARCH;
     template <typename T>
     inline void branchSub32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
 
     inline void decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
 
     template <class L>
     inline void branchTest32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
     template <class L>
@@ -1309,16 +1309,37 @@ class MacroAssembler : public MacroAssem
         DEFINED_ON(x86, x64);
     inline void truncateDoubleToInt64(Address src, Address dest, Register temp)
         DEFINED_ON(x86_shared);
     inline void truncateDoubleToUInt64(Address src, Address dest, Register temp,
                                        FloatRegister floatTemp)
         DEFINED_ON(x86, x64);
 
   public:
+    // ========================================================================
+    // wasm support
+
+    // Emit a bounds check against the (dynamically-patched) wasm bounds check
+    // limit, jumping to 'label' if 'cond' holds.
+    template <class L>
+    inline void wasmBoundsCheck(Condition cond, Register index, L label) PER_ARCH;
+
+    // Called after compilation completes to patch the given limit into the
+    // given instruction's immediate.
+    static inline void wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit) PER_ARCH;
+
+    // On x86, each instruction adds its own wasm::MemoryAccess's to the
+    // wasm::MemoryAccessVector (there can be multiple when i64 is involved).
+    // On x64, only some asm.js accesses need a wasm::MemoryAccess so the caller
+    // is responsible for doing this instead.
+    void wasmLoad(Scalar::Type type, unsigned numSimdElems, Operand srcAddr, AnyRegister out) DEFINED_ON(x86, x64);
+    void wasmLoadI64(Scalar::Type type, Operand srcAddr, Register64 out) DEFINED_ON(x86, x64);
+    void wasmStore(Scalar::Type type, unsigned numSimdElems, AnyRegister value, Operand dstAddr) DEFINED_ON(x86, x64);
+    void wasmStoreI64(Register64 value, Operand dstAddr) DEFINED_ON(x86);
+
     // wasm specific methods, used in both the wasm baseline compiler and ion.
     void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64);
     void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared);
     void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, Label* rejoin) DEFINED_ON(x86_shared);
 
     void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64);
     void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared);
     void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, Label* rejoin) DEFINED_ON(x86_shared);
--- a/js/src/jit/WasmBCE.cpp
+++ b/js/src/jit/WasmBCE.cpp
@@ -6,95 +6,83 @@
 #include "jit/WasmBCE.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace mozilla;
 
-struct DefAndOffset {
-    MDefinition* loc;
-    uint32_t endOffset;
-};
-
-typedef js::HashMap<uint32_t, DefAndOffset, DefaultHasher<uint32_t>, SystemAllocPolicy>
+typedef js::HashMap<uint32_t, MDefinition*, DefaultHasher<uint32_t>, SystemAllocPolicy>
     LastSeenMap;
 
 // The Wasm Bounds Check Elimination (BCE) pass looks for bounds checks
 // on SSA values that have already been checked. (in the same block or in a
 // dominating block). These bounds checks are redundant and thus eliminated.
 //
 // Note: This is safe in the presense of dynamic memory sizes as long as they
 // can ONLY GROW. If we allow SHRINKING the heap, this pass should be
 // RECONSIDERED.
 //
 // TODO (dbounov): Are there a lot of cases where there is no single dominating
 // check, but a set of checks that together dominate a redundant check?
 //
 // TODO (dbounov): Generalize to constant additions relative to one base
-bool jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph) {
+bool
+jit::EliminateBoundsChecks(MIRGenerator* mir, MIRGraph& graph)
+{
     // Map for dominating block where a given definition was checked
     LastSeenMap lastSeen;
     if (!lastSeen.init())
         return false;
 
     for (ReversePostorderIterator bIter(graph.rpoBegin()); bIter != graph.rpoEnd(); bIter++) {
         MBasicBlock* block = *bIter;
         for (MDefinitionIterator dIter(block); dIter;) {
             MDefinition* def = *dIter++;
 
             switch (def->op()) {
               case MDefinition::Op_WasmBoundsCheck: {
                 MWasmBoundsCheck* bc = def->toWasmBoundsCheck();
                 MDefinition* addr = def->getOperand(0);
-                LastSeenMap::Ptr checkPtr = lastSeen.lookup(addr->id());
 
-                if (checkPtr &&
-                    checkPtr->value().endOffset >= bc->endOffset() &&
-                    checkPtr->value().loc->block()->dominates(block)) {
-                    // Address already checked. Discard current check
+                LastSeenMap::AddPtr checkPtr = lastSeen.lookupForAdd(addr->id());
+                if (checkPtr && checkPtr->value()->block()->dominates(block)) {
                     bc->setRedundant(true);
                 } else {
-                    DefAndOffset defOff = { def, bc->endOffset() };
-                    // Address not previously checked - remember current check
-                    if (!lastSeen.put(addr->id(), defOff))
+                    if (!lastSeen.add(checkPtr, addr->id(), def))
                         return false;
                 }
                 break;
               }
               case MDefinition::Op_Phi: {
                 MPhi* phi = def->toPhi();
                 bool phiChecked = true;
-                uint32_t off = UINT32_MAX;
 
                 MOZ_ASSERT(phi->numOperands() > 0);
 
                 // If all incoming values to a phi node are safe (i.e. have a
                 // check that dominates this block) then we can consider this
                 // phi node checked.
                 //
                 // Note that any phi that is part of a cycle
                 // will not be "safe" since the value coming on the backedge
                 // cannot be in lastSeen because its block hasn't been traversed yet.
                 for (int i = 0, nOps = phi->numOperands(); i < nOps; i++) {
                     MDefinition* src = phi->getOperand(i);
+
                     LastSeenMap::Ptr checkPtr = lastSeen.lookup(src->id());
-
-                    if (!checkPtr || !checkPtr->value().loc->block()->dominates(block)) {
+                    if (!checkPtr || !checkPtr->value()->block()->dominates(block)) {
                         phiChecked = false;
                         break;
-                    } else {
-                        off = Min(off, checkPtr->value().endOffset);
                     }
                 }
 
                 if (phiChecked) {
-                    DefAndOffset defOff = { def, off };
-                    if (!lastSeen.put(def->id(), defOff))
+                    if (!lastSeen.put(def->id(), def))
                         return false;
                 }
 
                 break;
               }
               default:
                 break;
             }
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -645,18 +645,12 @@ static inline bool UseHardFpABI()
 #endif
 }
 #endif
 
 // In order to handle SoftFp ABI calls, we need to be able to express that we
 // have ABIArg which are represented by pair of general purpose registers.
 #define JS_CODEGEN_REGISTER_PAIR 1
 
-// See MIRGenerator::foldableOffsetRange for more info.
-// TODO: Implement this for ARM. Note that it requires Codegen to respect the
-// offset field of AsmJSHeapAccess.
-static const size_t WasmCheckedImmediateRange = 0;
-static const size_t WasmImmediateRange = 0;
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Architecture_arm_h */
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -3376,37 +3376,16 @@ Assembler::BailoutTableStart(uint8_t* co
 {
     Instruction* inst = (Instruction*)code;
     // Skip a pool with an artificial guard or NOP fill.
     inst = inst->skipPool();
     MOZ_ASSERT(inst->is<InstBLImm>());
     return (uint8_t*) inst;
 }
 
-void
-Assembler::UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength)
-{
-    Instruction* inst = (Instruction*) patchAt;
-    MOZ_ASSERT(inst->is<InstCMP>());
-    InstCMP* cmp = inst->as<InstCMP>();
-
-    Register index;
-    cmp->extractOp1(&index);
-
-    MOZ_ASSERT(cmp->extractOp2().isImm8());
-
-    Imm8 imm8 = Imm8(heapLength);
-    MOZ_ASSERT(!imm8.invalid);
-
-    *inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCC, Always);
-    // NOTE: we don't update the Auto Flush Cache!  this function is currently
-    // only called from within ModuleGenerator::finish, which does that
-    // for us. Don't call this!
-}
-
 InstructionIterator::InstructionIterator(Instruction* i_)
   : i(i_)
 {
     // Work around pools with an artificial pool guard and around nop-fill.
     i = i->skipPool();
 }
 
 uint32_t Assembler::NopFill = 0;
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1963,17 +1963,16 @@ class Assembler : public AssemblerShared
     static void ToggleToJmp(CodeLocationLabel inst_);
     static void ToggleToCmp(CodeLocationLabel inst_);
 
     static uint8_t* BailoutTableStart(uint8_t* code);
 
     static size_t ToggledCallSize(uint8_t* code);
     static void ToggleCall(CodeLocationLabel inst_, bool enabled);
 
-    static void UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength);
     void processCodeLabels(uint8_t* rawCode);
 
     bool bailed() {
         return m_buffer.bail();
     }
 
     void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
                                      const Disassembler::HeapAccess& heapAccess)
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2235,138 +2235,113 @@ CodeGeneratorARM::visitWasmCallI64(LWasm
 {
     emitWasmCall(ins);
 }
 
 void
 CodeGeneratorARM::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
 {
     const MAsmJSLoadHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
+    const LAllocation* ptr = ins->ptr();
+
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:    isSigned = true;  size =  8; break;
       case Scalar::Uint8:   isSigned = false; size =  8; break;
       case Scalar::Int16:   isSigned = true;  size = 16; break;
       case Scalar::Uint16:  isSigned = false; size = 16; break;
       case Scalar::Int32:
       case Scalar::Uint32:  isSigned = true;  size = 32; break;
       case Scalar::Float64: isFloat = true;   size = 64; break;
       case Scalar::Float32: isFloat = true;   size = 32; break;
       default: MOZ_CRASH("unexpected array type");
     }
 
-    memoryBarrier(mir->barrierBefore());
-
-    const LAllocation* ptr = ins->ptr();
-
     if (ptr->isConstant()) {
         MOZ_ASSERT(!mir->needsBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->output()));
             if (size == 32)
                 masm.ma_vldr(Address(HeapReg, ptrImm), vd.singleOverlay(), Assembler::Always);
             else
                 masm.ma_vldr(Address(HeapReg, ptrImm), vd, Assembler::Always);
         }  else {
             masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->output()), Offset, Assembler::Always);
         }
     } else {
         Register ptrReg = ToRegister(ptr);
-        if (isFloat)
-            masm.ma_loadHeapAsmJS(ptrReg, size, mir->needsBoundsCheck(),
-                                  /*faultOnOOB=*/false, ToFloatRegister(ins->output()));
-        else
-            masm.ma_loadHeapAsmJS(ptrReg, size, isSigned, mir->needsBoundsCheck(),
-                                  mir->isAtomicAccess(), ToRegister(ins->output()));
-    }
-
-    memoryBarrier(mir->barrierAfter());
-}
-
-void
-CodeGeneratorARM::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
-{
-    MWasmBoundsCheck* mir = ins->mir();
-
-    MOZ_ASSERT(mir->offset() <= INT32_MAX);
-
-    if (!mir->isRedundant()) {
-        // No guarantee that heapBase + endOffset can be properly encoded in
-        // the cmp immediate in ma_BoundsCheck, so use an explicit add instead.
-        uint32_t endOffset = mir->endOffset();
-
-        Register ptr = ToRegister(ins->ptr());
-
-        ScratchRegisterScope ptrPlusOffset(masm);
-        masm.move32(Imm32(endOffset), ptrPlusOffset);
-        masm.ma_add(ptr, ptrPlusOffset, SetCC);
-
-        // Detect unsigned overflow by checking the carry bit.
-        masm.as_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet);
-
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrPlusOffset).getOffset();
-        masm.append(wasm::BoundsCheck(cmpOffset));
-        masm.as_b(wasm::JumpTarget::OutOfBounds, Assembler::Above);
-    } else {
-#ifdef DEBUG
-        Label ok1, ok2;
-        uint32_t endOffset = mir->endOffset();
-
-        Register ptr = ToRegister(ins->ptr());
-
-        ScratchRegisterScope ptrPlusOffset(masm);
-        masm.move32(Imm32(endOffset), ptrPlusOffset);
-        masm.ma_add(ptr, ptrPlusOffset, SetCC);
-
-        // Detect unsigned overflow by checking the carry bit.
-        masm.as_b(&ok1, Assembler::CarryClear);
-        masm.assumeUnreachable("Redundant bounds check failed!");
-        masm.bind(&ok1);
-
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrPlusOffset).getOffset();
-        masm.append(wasm::BoundsCheck(cmpOffset));
-        masm.as_b(&ok2, Assembler::BelowOrEqual);
-        masm.assumeUnreachable("Redundant bounds check failed!");
-        masm.bind(&ok2);
-#endif
+        if (isFloat) {
+            FloatRegister output = ToFloatRegister(ins->output());
+            if (size == 32)
+                output = output.singleOverlay();
+
+            Assembler::Condition cond = Assembler::Always;
+            if (mir->needsBoundsCheck()) {
+                BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
+                masm.append(wasm::BoundsCheck(cmp.getOffset()));
+
+                size_t nanOffset = size == 32 ? wasm::NaN32GlobalDataOffset : wasm::NaN64GlobalDataOffset;
+                masm.ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output,
+                             Assembler::AboveOrEqual);
+                cond = Assembler::Below;
+            }
+
+            masm.ma_vldr(output, HeapReg, ptrReg, 0, cond);
+        } else {
+            Register output = ToRegister(ins->output());
+
+            Assembler::Condition cond = Assembler::Always;
+            if (mir->needsBoundsCheck()) {
+                uint32_t cmpOffset = masm.as_cmp(ptrReg, Imm8(0)).getOffset();
+                masm.append(wasm::BoundsCheck(cmpOffset));
+
+                masm.ma_mov(Imm32(0), output, Assembler::AboveOrEqual);
+                cond = Assembler::Below;
+            }
+
+            masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset, cond);
+        }
     }
 }
 
 template <typename T>
 void
 CodeGeneratorARM::emitWasmLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
-    MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
     uint32_t offset = mir->offset();
-    MOZ_ASSERT(offset <= INT32_MAX);
+    MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
-        masm.ma_add(Imm32(offset), ptrPlusOffset);
+        if (offset)
+            masm.ma_add(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
     bool isSigned = type == Scalar::Int8 || type == Scalar::Int16 || type == Scalar::Int32 ||
                     type == Scalar::Int64;
     unsigned byteSize = mir->byteSize();
 
+    memoryBarrier(mir->barrierBefore());
+
     if (mir->type() == MIRType::Int64) {
         Register64 output = ToOutRegister64(lir);
         if (type == Scalar::Int64) {
             MOZ_ASSERT(INT64LOW_OFFSET == 0);
             masm.ma_dataTransferN(IsLoad, 32, /* signed = */ false, HeapReg, ptr, output.low);
             masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
             masm.ma_dataTransferN(IsLoad, 32, isSigned, HeapReg, ptr, output.high);
         } else {
@@ -2383,16 +2358,18 @@ CodeGeneratorARM::emitWasmLoad(T* lir)
             MOZ_ASSERT((byteSize == 4) == output.fpu().isSingle());
             ScratchRegisterScope scratch(masm);
             masm.ma_add(HeapReg, ptr, scratch);
             masm.ma_vldr(Address(scratch, 0), output.fpu());
         } else {
             masm.ma_dataTransferN(IsLoad, byteSize * 8, isSigned, HeapReg, ptr, output.gpr());
         }
     }
+
+    memoryBarrier(mir->barrierAfter());
 }
 
 void
 CodeGeneratorARM::visitWasmLoad(LWasmLoad* lir)
 {
     emitWasmLoad(lir);
 }
 
@@ -2403,20 +2380,18 @@ CodeGeneratorARM::visitWasmLoadI64(LWasm
 }
 
 template<typename T>
 void
 CodeGeneratorARM::emitWasmUnalignedLoad(T* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
-    MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
     uint32_t offset = mir->offset();
-    MOZ_ASSERT(offset <= INT32_MAX);
+    MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
     if (offset)
         masm.ma_add(Imm32(offset), ptr);
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
@@ -2435,16 +2410,18 @@ CodeGeneratorARM::emitWasmUnalignedLoad(
     else if (mirType == MIRType::Int64)
         low = ToOutRegister64(lir).low;
     else
         low = ToRegister(lir->output());
 
     MOZ_ASSERT(low != tmp);
     MOZ_ASSERT(low != ptr);
 
+    memoryBarrier(mir->barrierBefore());
+
     masm.emitUnalignedLoad(isSigned, Min(byteSize, 4u), ptr, tmp, low);
 
     if (IsFloatingPointType(mirType)) {
         FloatRegister output = ToFloatRegister(lir->output());
         if (byteSize == 4) {
             MOZ_ASSERT(output.isSingle());
             masm.ma_vxfer(low, output);
         } else {
@@ -2463,54 +2440,68 @@ CodeGeneratorARM::emitWasmUnalignedLoad(
             MOZ_ASSERT(byteSize <= 4);
             // Propagate sign.
             if (isSigned)
                 masm.ma_asr(Imm32(31), output.low, output.high);
             else
                 masm.ma_mov(Imm32(0), output.high);
         }
     }
+
+    memoryBarrier(mir->barrierAfter());
 }
 
 void
 CodeGeneratorARM::visitWasmUnalignedLoad(LWasmUnalignedLoad* lir)
 {
     emitWasmUnalignedLoad(lir);
 }
 
 void
 CodeGeneratorARM::visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* lir)
 {
     emitWasmUnalignedLoad(lir);
 }
 
+void
+CodeGeneratorARM::visitWasmAddOffset(LWasmAddOffset* lir)
+{
+    MWasmAddOffset* mir = lir->mir();
+    Register base = ToRegister(lir->base());
+    Register out = ToRegister(lir->output());
+
+    masm.ma_add(base, Imm32(mir->offset()), out, SetCC);
+    masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::CarrySet);
+}
+
 template <typename T>
 void
 CodeGeneratorARM::emitWasmStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
-    MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
     uint32_t offset = mir->offset();
-    MOZ_ASSERT(offset <= INT32_MAX);
+    MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptr());
     unsigned byteSize = mir->byteSize();
     Scalar::Type type = mir->accessType();
 
     // Maybe add the offset.
     if (offset || type == Scalar::Int64) {
         Register ptrPlusOffset = ToRegister(lir->ptrCopy());
-        masm.ma_add(Imm32(offset), ptrPlusOffset);
+        if (offset)
+            masm.ma_add(Imm32(offset), ptrPlusOffset);
         ptr = ptrPlusOffset;
     } else {
         MOZ_ASSERT(lir->ptrCopy()->isBogusTemp());
     }
 
+    memoryBarrier(mir->barrierBefore());
+
     if (type == Scalar::Int64) {
         MOZ_ASSERT(INT64LOW_OFFSET == 0);
 
         Register64 value = ToRegister64(lir->getInt64Operand(lir->ValueIndex));
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ false, HeapReg, ptr, value.low);
         masm.ma_add(Imm32(INT64HIGH_OFFSET), ptr);
         masm.ma_dataTransferN(IsStore, 32 /* bits */, /* signed */ true, HeapReg, ptr, value.high);
     } else {
@@ -2522,16 +2513,18 @@ CodeGeneratorARM::emitWasmStore(T* lir)
             masm.ma_add(HeapReg, ptr, scratch);
             masm.ma_vstr(val, Address(scratch, 0));
         } else {
             bool isSigned = type == Scalar::Uint32 || type == Scalar::Int32; // see AsmJSStoreHeap;
             Register val = value.gpr();
             masm.ma_dataTransferN(IsStore, 8 * byteSize /* bits */, isSigned, HeapReg, ptr, val);
         }
     }
+
+    memoryBarrier(mir->barrierAfter());
 }
 
 void
 CodeGeneratorARM::visitWasmStore(LWasmStore* lir)
 {
     emitWasmStore(lir);
 }
 
@@ -2542,30 +2535,30 @@ CodeGeneratorARM::visitWasmStoreI64(LWas
 }
 
 template<typename T>
 void
 CodeGeneratorARM::emitWasmUnalignedStore(T* lir)
 {
     const MWasmStore* mir = lir->mir();
 
-    MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
-
     uint32_t offset = mir->offset();
-    MOZ_ASSERT(offset <= INT32_MAX);
+    MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
 
     Register ptr = ToRegister(lir->ptrCopy());
     if (offset)
         masm.ma_add(Imm32(offset), ptr);
 
     // Add HeapReg to ptr, so we can use base+index addressing in the byte loads.
     masm.ma_add(HeapReg, ptr);
 
     MIRType mirType = mir->value()->type();
 
+    memoryBarrier(mir->barrierAfter());
+
     Register val = ToRegister(lir->valueHelper());
     if (IsFloatingPointType(mirType)) {
         masm.ma_vxfer(ToFloatRegister(lir->getOperand(LWasmUnalignedStore::ValueIndex)), val);
     } else if (mirType == MIRType::Int64) {
         Register64 input = ToRegister64(lir->getInt64Operand(LWasmUnalignedStoreI64::ValueIndex));
         if (input.low != val)
             masm.ma_mov(input.low, val);
     }
@@ -2581,17 +2574,19 @@ CodeGeneratorARM::emitWasmUnalignedStore
             MOZ_ASSERT(fp.isDouble());
             ScratchRegisterScope scratch(masm);
             masm.ma_vxfer(fp, scratch, val);
         } else {
             MOZ_ASSERT(mirType == MIRType::Int64);
             masm.ma_mov(ToRegister64(lir->getInt64Operand(LWasmUnalignedStoreI64::ValueIndex)).high, val);
         }
         masm.emitUnalignedStore(4, ptr, val, /* offset */ 4);
-     }
+    }
+
+    memoryBarrier(mir->barrierBefore());
 }
 
 void
 CodeGeneratorARM::visitWasmUnalignedStore(LWasmUnalignedStore* lir)
 {
     emitWasmUnalignedStore(lir);
 }
 
@@ -2600,35 +2595,35 @@ CodeGeneratorARM::visitWasmUnalignedStor
 {
     emitWasmUnalignedStore(lir);
 }
 
 void
 CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
 {
     const MAsmJSStoreHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
+    const LAllocation* ptr = ins->ptr();
+
     bool isSigned;
     int size;
     bool isFloat = false;
     switch (mir->accessType()) {
       case Scalar::Int8:
       case Scalar::Uint8:   isSigned = false; size = 8; break;
       case Scalar::Int16:
       case Scalar::Uint16:  isSigned = false; size = 16; break;
       case Scalar::Int32:
       case Scalar::Uint32:  isSigned = true;  size = 32; break;
       case Scalar::Float64: isFloat  = true;  size = 64; break;
       case Scalar::Float32: isFloat = true;   size = 32; break;
       default: MOZ_CRASH("unexpected array type");
     }
 
-    memoryBarrier(mir->barrierBefore());
-
-    const LAllocation* ptr = ins->ptr();
-
     if (ptr->isConstant()) {
         MOZ_ASSERT(!mir->needsBoundsCheck());
         int32_t ptrImm = ptr->toConstant()->toInt32();
         MOZ_ASSERT(ptrImm >= 0);
         if (isFloat) {
             VFPRegister vd(ToFloatRegister(ins->value()));
             Address addr(HeapReg, ptrImm);
             if (size == 32)
@@ -2636,54 +2631,64 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
             else
                 masm.storeDouble(vd, addr);
         } else {
             masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, Imm32(ptrImm),
                                   ToRegister(ins->value()), Offset, Assembler::Always);
         }
     } else {
         Register ptrReg = ToRegister(ptr);
-        if (isFloat)
-            masm.ma_storeHeapAsmJS(ptrReg, size, mir->needsBoundsCheck(), /*faultOnOOB=*/false,
-                                   ToFloatRegister(ins->value()));
-        else
-            masm.ma_storeHeapAsmJS(ptrReg, size, isSigned, mir->needsBoundsCheck(),
-                                   mir->isAtomicAccess(), ToRegister(ins->value()));
+
+        Assembler::Condition cond = Assembler::Always;
+        if (mir->needsBoundsCheck()) {
+            BufferOffset cmp = masm.as_cmp(ptrReg, Imm8(0));
+            masm.append(wasm::BoundsCheck(cmp.getOffset()));
+
+            cond = Assembler::Below;
+        }
+
+        if (isFloat) {
+            FloatRegister value = ToFloatRegister(ins->value());
+            if (size == 32)
+                value = value.singleOverlay();
+
+            masm.ma_vstr(value, HeapReg, ptrReg, 0, 0, Assembler::Below);
+        } else {
+            Register value = ToRegister(ins->value());
+            masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset, cond);
+        }
     }
-
-    memoryBarrier(mir->barrierAfter());
 }
 
 void
 CodeGeneratorARM::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
     Scalar::Type vt = mir->accessType();
     const LAllocation* ptr = ins->ptr();
     Register ptrReg = ToRegister(ptr);
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
 
-    if (mir->needsBoundsCheck()) {
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrReg).getOffset();
-        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-        masm.append(wasm::BoundsCheck(cmpOffset));
-    }
     masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                         srcAddr, oldval, newval, InvalidReg,
                                         ToAnyRegister(ins->output()));
 }
 
 void
 CodeGeneratorARM::visitAsmJSCompareExchangeCallout(LAsmJSCompareExchangeCallout* ins)
 {
     const MAsmJSCompareExchangeHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
     Register ptr = ToRegister(ins->ptr());
     Register oldval = ToRegister(ins->oldval());
     Register newval = ToRegister(ins->newval());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
 
     MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg);
@@ -2699,36 +2704,34 @@ CodeGeneratorARM::visitAsmJSCompareExcha
     masm.passABIArg(newval);
     masm.callWithABI(wasm::SymbolicAddress::AtomicCmpXchg);
 }
 
 void
 CodeGeneratorARM::visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins)
 {
     MAsmJSAtomicExchangeHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
     Scalar::Type vt = mir->accessType();
     Register ptrReg = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
-    if (mir->needsBoundsCheck()) {
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrReg).getOffset();
-        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-        masm.append(wasm::BoundsCheck(cmpOffset));
-    }
-
     masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                        srcAddr, value, InvalidReg, ToAnyRegister(ins->output()));
 }
 
 void
 CodeGeneratorARM::visitAsmJSAtomicExchangeCallout(LAsmJSAtomicExchangeCallout* ins)
 {
     const MAsmJSAtomicExchangeHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
 
     MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg);
 
@@ -2741,75 +2744,68 @@ CodeGeneratorARM::visitAsmJSAtomicExchan
     masm.passABIArg(ptr);
     masm.passABIArg(value);
     masm.callWithABI(wasm::SymbolicAddress::AtomicXchg);
 }
 
 void
 CodeGeneratorARM::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins)
 {
-    MOZ_ASSERT(ins->mir()->hasUses());
-    MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
-
     MAsmJSAtomicBinopHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+    MOZ_ASSERT(mir->hasUses());
+
     Scalar::Type vt = mir->accessType();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
+    MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
 
-    if (mir->needsBoundsCheck()) {
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrReg).getOffset();
-        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-        masm.append(wasm::BoundsCheck(cmpOffset));
-    }
-
-    if (value->isConstant())
+    if (value->isConstant()) {
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    Imm32(ToInt32(value)), srcAddr, flagTemp, InvalidReg,
                                    ToAnyRegister(ins->output()));
-    else
+    } else {
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    ToRegister(value), srcAddr, flagTemp, InvalidReg,
                                    ToAnyRegister(ins->output()));
+    }
 }
 
 void
 CodeGeneratorARM::visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins)
 {
-    MOZ_ASSERT(!ins->mir()->hasUses());
-    MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
-
     MAsmJSAtomicBinopHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+    MOZ_ASSERT(!mir->hasUses());
+
     Scalar::Type vt = mir->accessType();
     Register ptrReg = ToRegister(ins->ptr());
     Register flagTemp = ToRegister(ins->flagTemp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
+    MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
 
-    if (mir->needsBoundsCheck()) {
-        uint32_t cmpOffset = masm.ma_BoundsCheck(ptrReg).getOffset();
-        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-        masm.append(wasm::BoundsCheck(cmpOffset));
-    }
-
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, vt, Imm32(ToInt32(value)), srcAddr, flagTemp);
     else
         atomicBinopToTypedIntArray(op, vt, ToRegister(value), srcAddr, flagTemp);
 }
 
 void
 CodeGeneratorARM::visitAsmJSAtomicBinopCallout(LAsmJSAtomicBinopCallout* ins)
 {
     const MAsmJSAtomicBinopHeap* mir = ins->mir();
+    MOZ_ASSERT(mir->offset() == 0);
+
     Register ptr = ToRegister(ins->ptr());
     Register value = ToRegister(ins->value());
     Register tls = ToRegister(ins->tls());
     Register instance = ToRegister(ins->getTemp(0));
     Register viewType = ToRegister(ins->getTemp(1));
 
     masm.loadPtr(Address(tls, offsetof(wasm::TlsData, instance)), instance);
     masm.move32(Imm32(mir->accessType()), viewType);
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -235,21 +235,21 @@ class CodeGeneratorARM : public CodeGene
     void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
     void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
     void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
     void visitAsmSelect(LAsmSelect* ins);
     void visitAsmReinterpret(LAsmReinterpret* ins);
     void emitWasmCall(LWasmCallBase* ins);
     void visitWasmCall(LWasmCall* ins);
     void visitWasmCallI64(LWasmCallI64* ins);
-    void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmLoadI64(LWasmLoadI64* ins);
     void visitWasmUnalignedLoad(LWasmUnalignedLoad* ins);
     void visitWasmUnalignedLoadI64(LWasmUnalignedLoadI64* ins);
+    void visitWasmAddOffset(LWasmAddOffset* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitWasmStoreI64(LWasmStoreI64* ins);
     void visitWasmUnalignedStore(LWasmUnalignedStore* ins);
     void visitWasmUnalignedStoreI64(LWasmUnalignedStoreI64* ins);
     void visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins);
     void visitWasmLoadGlobalVarI64(LWasmLoadGlobalVarI64* ins);
     void visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins);
     void visitWasmStoreGlobalVarI64(LWasmStoreGlobalVarI64* ins);
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -604,27 +604,16 @@ void
 LIRGeneratorARM::visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins)
 {
     MOZ_ASSERT(ins->input()->type() == MIRType::Int32);
     LAsmJSUInt32ToFloat32* lir = new(alloc()) LAsmJSUInt32ToFloat32(useRegisterAtStart(ins->input()));
     define(lir, ins);
 }
 
 void
-LIRGeneratorARM::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
-{
-    MDefinition* input = ins->input();
-    MOZ_ASSERT(input->type() == MIRType::Int32);
-
-    LAllocation baseAlloc = useRegisterAtStart(input);
-    auto* lir = new(alloc()) LWasmBoundsCheck(baseAlloc);
-    add(lir, ins);
-}
-
-void
 LIRGeneratorARM::visitWasmLoad(MWasmLoad* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
     LAllocation ptr = useRegisterAtStart(base);
 
     if (ins->isUnaligned()) {
@@ -718,19 +707,19 @@ LIRGeneratorARM::visitWasmStore(MWasmSto
 
 void
 LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
 {
     MOZ_ASSERT(ins->offset() == 0);
 
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
-    LAllocation baseAlloc;
 
     // For the ARM it is best to keep the 'base' in a register if a bounds check is needed.
+    LAllocation baseAlloc;
     if (base->isConstant() && !ins->needsBoundsCheck()) {
         // A bounds check is only skipped for a positive index.
         MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
         baseAlloc = LAllocation(base->toConstant());
     } else {
         baseAlloc = useRegisterAtStart(base);
     }
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -100,17 +100,16 @@ class LIRGeneratorARM : public LIRGenera
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
     void lowerPhi(MPhi* phi);
     void visitGuardShape(MGuardShape* ins);
     void visitGuardObjectGroup(MGuardObjectGroup* ins);
     void visitAsmSelect(MAsmSelect* ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
-    void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -1445,22 +1445,22 @@ MacroAssembler::branchTruncateDoubleToIn
 
     ma_vcvt_F64_I32(src, scratchSIntReg);
     ma_vxfer(scratchSIntReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
-template <typename T>
+template <typename T, typename L>
 void
-MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L label)
 {
     add32(src, dest);
-    j(cond, label);
+    as_b(label, cond);
 }
 
 template <typename T>
 void
 MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
 {
     ma_sub(src, dest, SetCC);
     j(cond, label);
@@ -1966,16 +1966,48 @@ MacroAssembler::clampIntToUint8(Register
     // Look at (reg >> 8) if it is 0, then reg shouldn't be clamped if it is
     // <0, then we want to clamp to 0, otherwise, we wish to clamp to 255
     ScratchRegisterScope scratch(*this);
     as_mov(scratch, asr(reg, 8), SetCC);
     ma_mov(Imm32(0xff), reg, NotEqual);
     ma_mov(Imm32(0), reg, Signed);
 }
 
+// ========================================================================
+// wasm support
+
+template <class L>
+void
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+{
+    BufferOffset bo = as_cmp(index, Imm8(0));
+    append(wasm::BoundsCheck(bo.getOffset()));
+
+    as_b(label, cond);
+}
+
+void
+MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+{
+    Instruction* inst = (Instruction*) patchAt;
+    MOZ_ASSERT(inst->is<InstCMP>());
+    InstCMP* cmp = inst->as<InstCMP>();
+
+    Register index;
+    cmp->extractOp1(&index);
+
+    MOZ_ASSERT(cmp->extractOp2().isImm8());
+
+    Imm8 imm8 = Imm8(limit);
+    MOZ_RELEASE_ASSERT(!imm8.invalid);
+
+    *inst = InstALU(InvalidReg, index, imm8, OpCmp, SetCC, Always);
+    // Don't call Auto Flush Cache; the wasm caller has done this for us.
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerARMCompat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), ToPayload(addr));
 }
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2126,65 +2126,16 @@ MacroAssemblerARMCompat::loadFloat32(con
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
     int32_t offset = src.offset;
 
     ScratchRegisterScope scratch(asMasm());
     as_add(scratch, base, lsl(index, scale));
     ma_vldr(Address(scratch, offset), VFPRegister(dest).singleOverlay());
 }
 
-
-void
-MacroAssemblerARMCompat::ma_loadHeapAsmJS(Register ptrReg, int size, bool needsBoundsCheck,
-                                          bool faultOnOOB, FloatRegister output)
-{
-    if (size == 32)
-        output = output.singleOverlay();
-
-    if (!needsBoundsCheck) {
-        ma_vldr(output, HeapReg, ptrReg, 0, Assembler::Always);
-    } else {
-        uint32_t cmpOffset = ma_BoundsCheck(ptrReg).getOffset();
-        append(wasm::BoundsCheck(cmpOffset));
-
-        if (faultOnOOB) {
-            ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-        }
-        else {
-            size_t nanOffset =
-                size == 32 ? wasm::NaN32GlobalDataOffset : wasm::NaN64GlobalDataOffset;
-            ma_vldr(Address(GlobalReg, nanOffset - AsmJSGlobalRegBias), output,
-                    Assembler::AboveOrEqual);
-        }
-        ma_vldr(output, HeapReg, ptrReg, 0, Assembler::Below);
-    }
-}
-
-void
-MacroAssemblerARMCompat::ma_loadHeapAsmJS(Register ptrReg, int size, bool isSigned,
-                                          bool needsBoundsCheck, bool faultOnOOB,
-                                          Register output)
-{
-    if (!needsBoundsCheck) {
-        ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset,
-                         Assembler::Always);
-        return;
-    }
-
-    uint32_t cmpOffset = ma_BoundsCheck(ptrReg).getOffset();
-    append(wasm::BoundsCheck(cmpOffset));
-
-    if (faultOnOOB)
-        ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-    else
-        ma_mov(Imm32(0), output, Assembler::AboveOrEqual);
-
-    ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, output, Offset, Assembler::Below);
-}
-
 void
 MacroAssemblerARMCompat::store8(Imm32 imm, const Address& address)
 {
     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
     ma_mov(imm, scratch2);
     store8(scratch2, address);
 }
 
@@ -2351,61 +2302,16 @@ MacroAssemblerARMCompat::storePtr(Regist
 void
 MacroAssemblerARMCompat::storePtr(Register src, AbsoluteAddress dest)
 {
     ScratchRegisterScope scratch(asMasm());
     movePtr(ImmWord(uintptr_t(dest.addr)), scratch);
     storePtr(src, Address(scratch, 0));
 }
 
-void
-MacroAssemblerARMCompat::ma_storeHeapAsmJS(Register ptrReg, int size, bool needsBoundsCheck,
-                                           bool faultOnOOB, FloatRegister value)
-{
-    if (!needsBoundsCheck) {
-        BaseIndex addr(HeapReg, ptrReg, TimesOne, 0);
-        if (size == 32)
-            asMasm().storeFloat32(value, addr);
-        else
-            asMasm().storeDouble(value, addr);
-    } else {
-        uint32_t cmpOffset = ma_BoundsCheck(ptrReg).getOffset();
-        append(wasm::BoundsCheck(cmpOffset));
-
-        if (faultOnOOB)
-            ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-
-        if (size == 32)
-            value = value.singleOverlay();
-
-        ma_vstr(value, HeapReg, ptrReg, 0, 0, Assembler::Below);
-    }
-}
-
-void
-MacroAssemblerARMCompat::ma_storeHeapAsmJS(Register ptrReg, int size, bool isSigned,
-                                           bool needsBoundsCheck, bool faultOnOOB,
-                                           Register value)
-{
-    if (!needsBoundsCheck) {
-        ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset,
-                         Assembler::Always);
-        return;
-    }
-
-    uint32_t cmpOffset = ma_BoundsCheck(ptrReg).getOffset();
-    append(wasm::BoundsCheck(cmpOffset));
-
-    if (faultOnOOB)
-        ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
-
-    ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg, value, Offset,
-                     Assembler::Below);
-}
-
 // Note: this function clobbers the input register.
 void
 MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output)
 {
     if (HasVFPv3()) {
         Label notSplit;
         {
             ScratchDoubleScope scratchDouble(*this);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -976,21 +976,16 @@ class MacroAssemblerARMCompat : public M
 
     // Load a float value into a register, then expand it to a double.
     void loadFloatAsDouble(const Address& addr, FloatRegister dest);
     void loadFloatAsDouble(const BaseIndex& src, FloatRegister dest);
 
     void loadFloat32(const Address& addr, FloatRegister dest);
     void loadFloat32(const BaseIndex& src, FloatRegister dest);
 
-    void ma_loadHeapAsmJS(Register ptrReg, int size, bool needsBoundsCheck, bool faultOnOOB,
-                          FloatRegister output);
-    void ma_loadHeapAsmJS(Register ptrReg, int size, bool isSigned, bool needsBoundsCheck,
-                          bool faultOnOOB, Register output);
-
     void store8(Register src, const Address& address);
     void store8(Imm32 imm, const Address& address);
     void store8(Register src, const BaseIndex& address);
     void store8(Imm32 imm, const BaseIndex& address);
 
     void store16(Register src, const Address& address);
     void store16(Imm32 imm, const Address& address);
     void store16(Register src, const BaseIndex& address);
@@ -1017,21 +1012,16 @@ class MacroAssemblerARMCompat : public M
     template <typename T> void storePtr(ImmGCPtr imm, T address);
     void storePtr(Register src, const Address& address);
     void storePtr(Register src, const BaseIndex& address);
     void storePtr(Register src, AbsoluteAddress dest);
     void moveDouble(FloatRegister src, FloatRegister dest, Condition cc = Always) {
         ma_vmov(src, dest, cc);
     }
 
-    void ma_storeHeapAsmJS(Register ptrReg, int size, bool needsBoundsCheck, bool faultOnOOB,
-                           FloatRegister value);
-    void ma_storeHeapAsmJS(Register ptrReg, int size, bool isSigned, bool needsBoundsCheck,
-                           bool faultOnOOB, Register value);
-
   private:
     template<typename T>
     Register computePointer(const T& src, Register r);
 
     template<typename T>
     void compareExchangeARMv6(int nbytes, bool signExtend, const T& mem, Register oldval,
                               Register newval, Register output);
 
@@ -1439,19 +1429,16 @@ class MacroAssemblerARMCompat : public M
     void abiret() {
         as_bx(lr);
     }
 
     void ma_storeImm(Imm32 c, const Address& dest) {
         ma_mov(c, lr);
         ma_str(lr, dest);
     }
-    BufferOffset ma_BoundsCheck(Register bounded) {
-        return as_cmp(bounded, Imm8(0));
-    }
 
     void moveFloat32(FloatRegister src, FloatRegister dest, Condition cc = Always) {
         as_vmov(VFPRegister(dest).singleOverlay(), VFPRegister(src).singleOverlay(), cc);
     }
 
     void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest) {
         loadPtr(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), dest);
     }
--- a/js/src/jit/arm64/Architecture-arm64.h
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -451,15 +451,12 @@ hasUnaliasedDouble()
 // ARM prior to ARMv8 also has doubles that alias multiple floats.
 // Again, ARMv8 is in the clear.
 inline bool
 hasMultiAlias()
 {
     return false;
 }
 
-static const size_t WasmCheckedImmediateRange = 0;
-static const size_t WasmImmediateRange = 0;
-
 } // namespace jit
 } // namespace js
 
 #endif // jit_arm64_Architecture_arm64_h
--- a/js/src/jit/arm64/Assembler-arm64.cpp
+++ b/js/src/jit/arm64/Assembler-arm64.cpp
@@ -632,30 +632,16 @@ Assembler::FixupNurseryObjects(JSContext
 
 void
 Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
 {
     MOZ_CRASH("PatchInstructionImmediate()");
 }
 
 void
-Assembler::UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength)
-{
-    Instruction* inst = (Instruction*) patchAt;
-    int32_t mask = ~(heapLength - 1);
-    unsigned n, imm_s, imm_r;
-    if (!IsImmLogical(mask, 32, &n, &imm_s, &imm_r))
-        MOZ_CRASH("Could not encode immediate!?");
-
-    inst->SetImmR(imm_r);
-    inst->SetImmS(imm_s);
-    inst->SetBitN(n);
-}
-
-void
 Assembler::retarget(Label* label, Label* target)
 {
     if (label->used()) {
         if (target->bound()) {
             bind(label, BufferOffset(target));
         } else if (target->used()) {
             // The target is not bound but used. Prepend label's branch list
             // onto target's.
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -372,18 +372,16 @@ class Assembler : public vixl::Assembler
             return reinterpret_cast<Instruction*>(&ldr);
         }
     };
 
     // Offset of the patchable target for the given entry.
     static const size_t OffsetOfJumpTableEntryPointer = 8;
 
   public:
-    static void UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength);
-
     void writeCodePointer(AbsoluteLabel* absoluteLabel) {
         MOZ_ASSERT(!absoluteLabel->bound());
         uintptr_t x = LabelBase::INVALID_OFFSET;
         BufferOffset off = EmitData(&x, sizeof(uintptr_t));
 
         // The x86/x64 makes general use of AbsoluteLabel and weaves a linked list
         // of uses of an AbsoluteLabel through the assembly. ARM only uses labels
         // for the case statements of switch jump tables. Thus, for simplicity, we
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -334,22 +334,16 @@ LIRGeneratorARM64::visitRandom(MRandom* 
 
 void
 LIRGeneratorARM64::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
 {
     MOZ_CRASH("NY");
 }
 
 void
-LIRGeneratorARM64::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
-{
-    MOZ_CRASH("NY");
-}
-
-void
 LIRGeneratorARM64::visitWasmLoad(MWasmLoad* ins)
 {
     MOZ_CRASH("NY");
 }
 
 void
 LIRGeneratorARM64::visitWasmStore(MWasmStore* ins)
 {
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -112,17 +112,16 @@ class LIRGeneratorARM64 : public LIRGene
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
     void visitRandom(MRandom* ins);
     void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
-    void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
     void visitCopySign(MCopySign* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
 };
 
 typedef LIRGeneratorARM64 LIRGeneratorSpecific;
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -1005,22 +1005,22 @@ MacroAssembler::branchTruncateDoubleMayb
 }
 
 void
 MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
     convertDoubleToInt32(src, dest, fail);
 }
 
-template <typename T>
+template <typename T, typename L>
 void
-MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* label)
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L label)
 {
     adds32(src, dest);
-    branch(cond, label);
+    B(label, cond);
 }
 
 template <typename T>
 void
 MacroAssembler::branchSub32(Condition cond, T src, Register dest, Label* label)
 {
     subs32(src, dest);
     branch(cond, label);
@@ -1540,16 +1540,32 @@ MacroAssembler::clampIntToUint8(Register
     MOZ_ASSERT(!scratch32.Is(reg32));
 
     Cmp(reg32, Operand(reg32, vixl::UXTB));
     Csel(reg32, reg32, vixl::wzr, Assembler::GreaterThanOrEqual);
     Mov(scratch32, Operand(0xff));
     Csel(reg32, reg32, scratch32, Assembler::LessThanOrEqual);
 }
 
+// ========================================================================
+// wasm support
+
+template <class L>
+void
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+{
+    MOZ_CRASH("NYI");
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 template <typename T>
 void
 MacroAssemblerCompat::addToStackPtr(T t)
 {
     asMasm().addPtr(t, getStackPointer());
--- a/js/src/jit/arm64/vixl/Assembler-vixl.h
+++ b/js/src/jit/arm64/vixl/Assembler-vixl.h
@@ -853,17 +853,19 @@ class Assembler : public MozBaseAssemble
 
 #define COPYENUM(v) static const Condition v = vixl::v
 #define COPYENUM_(v) static const Condition v = vixl::v##_
   COPYENUM(Equal);
   COPYENUM(Zero);
   COPYENUM(NotEqual);
   COPYENUM(NonZero);
   COPYENUM(AboveOrEqual);
+  COPYENUM(CarrySet);
   COPYENUM(Below);
+  COPYENUM(CarryClear);
   COPYENUM(Signed);
   COPYENUM(NotSigned);
   COPYENUM(Overflow);
   COPYENUM(NoOverflow);
   COPYENUM(Above);
   COPYENUM(BelowOrEqual);
   COPYENUM_(GreaterThanOrEqual);
   COPYENUM_(LessThan);
--- a/js/src/jit/arm64/vixl/Constants-vixl.h
+++ b/js/src/jit/arm64/vixl/Constants-vixl.h
@@ -229,18 +229,18 @@ enum Condition {
 
   // Aliases.
   hs = cs,  // C set            Unsigned higher or same.
   lo = cc,  // C clear          Unsigned lower.
 
   // Mozilla expanded aliases.
   Equal = 0, Zero = 0,
   NotEqual = 1, NonZero = 1,
-  AboveOrEqual = 2,
-  Below = 3,
+  AboveOrEqual = 2, CarrySet = 2,
+  Below = 3, CarryClear = 3,
   Signed = 4,
   NotSigned = 5,
   Overflow = 6,
   NoOverflow = 7,
   Above = 8,
   BelowOrEqual = 9,
   GreaterThanOrEqual_ = 10,
   LessThan_ = 11,
--- a/js/src/jit/mips-shared/Architecture-mips-shared.h
+++ b/js/src/jit/mips-shared/Architecture-mips-shared.h
@@ -327,18 +327,12 @@ hasUnaliasedDouble() {
 // On MIPS, fn-double aliases both fn-float32 and fn+1-float32, so if you need
 // to convert a float32 to a double as a temporary, you need a temporary
 // double register.
 inline bool
 hasMultiAlias() {
     return true;
 }
 
-// See MIRGenerator::foldableOffsetRange for more info.
-// TODO: Implement this for MIPS. Note that it requires Codegen to respect the
-// offset field of AsmJSHeapAccess.
-static const size_t WasmCheckedImmediateRange = 0;
-static const size_t WasmImmediateRange = 0;
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_Architecture_mips_shared_h */
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -1656,38 +1656,16 @@ CodeGeneratorMIPSShared::visitWasmCall(L
 
 void
 CodeGeneratorMIPSShared::visitWasmCallI64(LWasmCallI64* ins)
 {
     emitWasmCallBase(ins);
 }
 
 void
-CodeGeneratorMIPSShared::visitWasmBoundsCheck(LWasmBoundsCheck* ins)
-{
-    MWasmBoundsCheck* mir = ins->mir();
-
-    uint32_t offset = mir->offset();
-    MOZ_ASSERT(offset <= INT32_MAX);
-
-    uint32_t endOffset = mir->endOffset();
-    Register ptr = ToRegister(ins->ptr());
-
-    masm.move32(Imm32(endOffset), SecondScratchReg);
-    masm.addPtr(ptr, SecondScratchReg);
-
-    // Detect unsigned overflow.
-    masm.ma_b(SecondScratchReg, ptr, wasm::JumpTarget::OutOfBounds, Assembler::LessThan);
-
-    BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister);
-    masm.ma_b(SecondScratchReg, ScratchRegister, wasm::JumpTarget::OutOfBounds, Assembler::Above);
-    masm.append(wasm::BoundsCheck(bo.getOffset()));
-}
-
-void
 CodeGeneratorMIPSShared::visitWasmLoad(LWasmLoad* lir)
 {
     const MWasmLoad* mir = lir->mir();
 
     MOZ_ASSERT(!mir->barrierBefore() && !mir->barrierAfter(), "atomics NYI");
 
     uint32_t offset = mir->offset();
     MOZ_ASSERT(offset <= INT32_MAX);
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h
@@ -191,17 +191,16 @@ class CodeGeneratorMIPSShared : public C
 
     void visitNegI(LNegI* lir);
     void visitNegD(LNegD* lir);
     void visitNegF(LNegF* lir);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitWasmCall(LWasmCall* ins);
     void visitWasmCallI64(LWasmCallI64* ins);
-    void visitWasmBoundsCheck(LWasmBoundsCheck* ins);
     void visitWasmLoad(LWasmLoad* ins);
     void visitWasmStore(LWasmStore* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -289,27 +289,16 @@ LIRGeneratorMIPSShared::visitAsmJSNeg(MA
         define(new(alloc()) LNegF(useRegisterAtStart(ins->input())), ins);
     } else {
         MOZ_ASSERT(ins->type() == MIRType::Double);
         define(new(alloc()) LNegD(useRegisterAtStart(ins->input())), ins);
     }
 }
 
 void
-LIRGeneratorMIPSShared::visitWasmBoundsCheck(MWasmBoundsCheck* ins)
-{
-    if (!gen->needsBoundsCheckBranch(ins))
-        return;
-
-    MDefinition* index = ins->input();
-    auto* lir = new(alloc()) LWasmBoundsCheck(useRegisterAtStart(index));
-    add(lir, ins);
-}
-
-void
 LIRGeneratorMIPSShared::visitWasmLoad(MWasmLoad* ins)
 {
     MDefinition* base = ins->base();
     MOZ_ASSERT(base->type() == MIRType::Int32);
 
 #ifdef JS_CODEGEN_MIPS64
     if (ins->type() == MIRType::Int64) {
         auto* lir = new(alloc()) LWasmLoadI64(useRegisterAtStart(base));
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -71,17 +71,16 @@ class LIRGeneratorMIPSShared : public LI
     void lowerModI(MMod* mod);
     void lowerDivI64(MDiv* div);
     void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
-    void visitWasmBoundsCheck(MWasmBoundsCheck* ins);
     void visitWasmLoad(MWasmLoad* ins);
     void visitWasmStore(MWasmStore* ins);
     void visitAsmSelect(MAsmSelect* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
 
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -618,19 +618,19 @@ MacroAssembler::branchTruncateDoubleMayb
 }
 
 void
 MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
 {
     convertDoubleToInt32(src, dest, fail);
 }
 
-template <typename T>
+template <typename T, typename L>
 void
-MacroAssembler::branchAdd32(Condition cond, T src, Register dest, Label* overflow)
+MacroAssembler::branchAdd32(Condition cond, T src, Register dest, L overflow)
 {
     switch (cond) {
       case Overflow:
         ma_addTestOverflow(dest, dest, src, overflow);
         break;
       default:
         MOZ_CRASH("NYI");
     }
--- a/js/src/jit/mips32/Assembler-mips32.cpp
+++ b/js/src/jit/mips32/Assembler-mips32.cpp
@@ -478,19 +478,8 @@ Assembler::ToggleCall(CodeLocationLabel 
         *i2 = jalr;
     } else {
         InstNOP nop;
         *i2 = nop;
     }
 
     AutoFlushICache::flush(uintptr_t(i2), 4);
 }
-
-void
-Assembler::UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength)
-{
-    Instruction* inst = (Instruction*) patchAt;
-    InstImm* i0 = (InstImm*) inst;
-    InstImm* i1 = (InstImm*) i0->next();
-
-    // Replace with new value
-    Assembler::UpdateLuiOriValue(i0, i1, heapLength);
-}
--- a/js/src/jit/mips32/Assembler-mips32.h
+++ b/js/src/jit/mips32/Assembler-mips32.h
@@ -167,18 +167,16 @@ class Assembler : public AssemblerMIPSSh
                                         ImmPtr expectedValue);
     static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
                                         PatchedImmPtr expectedValue);
 
     static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
     static uint32_t ExtractInstructionImmediate(uint8_t* code);
 
     static void ToggleCall(CodeLocationLabel inst_, bool enabled);
-
-    static void UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength);
 }; // Assembler
 
 static const uint32_t NumIntArgRegs = 4;
 
 static inline bool
 GetIntArgReg(uint32_t usedArgSlots, Register* out)
 {
     if (usedArgSlots < NumIntArgRegs) {
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -447,16 +447,37 @@ MacroAssembler::storeUncanonicalizedFloa
 }
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_ss(src, addr);
 }
 
+// ========================================================================
+// wasm support
+
+template <class L>
+void
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+{
+    Instruction* inst = (Instruction*) patchAt;
+    InstImm* i0 = (InstImm*) inst;
+    InstImm* i1 = (InstImm*) i0->next();
+
+    // Replace with new value
+    Assembler::UpdateLuiOriValue(i0, i1, limit);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPSCompat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), ToPayload(addr));
 }
--- a/js/src/jit/mips64/Assembler-mips64.cpp
+++ b/js/src/jit/mips64/Assembler-mips64.cpp
@@ -523,15 +523,8 @@ Assembler::ToggleCall(CodeLocationLabel 
         *i4 = jalr;
     } else {
         InstNOP nop;
         *i4 = nop;
     }
 
     AutoFlushICache::flush(uintptr_t(i4), sizeof(uint32_t));
 }
-
-void
-Assembler::UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength)
-{
-    // Replace with new value
-    Assembler::UpdateLoad64Value((Instruction*) patchAt, heapLength);
-}
--- a/js/src/jit/mips64/Assembler-mips64.h
+++ b/js/src/jit/mips64/Assembler-mips64.h
@@ -169,18 +169,16 @@ class Assembler : public AssemblerMIPSSh
                                         ImmPtr expectedValue);
     static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
                                         PatchedImmPtr expectedValue);
 
     static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
     static uint64_t ExtractInstructionImmediate(uint8_t* code);
 
     static void ToggleCall(CodeLocationLabel inst_, bool enabled);
-
-    static void UpdateBoundsCheck(uint8_t* patchAt, uint32_t heapLength);
 }; // Assembler
 
 static const uint32_t NumIntArgRegs = 8;
 static const uint32_t NumFloatArgRegs = NumIntArgRegs;
 
 static inline bool
 GetIntArgReg(uint32_t usedArgSlots, Register* out)
 {
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -404,16 +404,33 @@ MacroAssembler::storeUncanonicalizedFloa
 }
 void
 MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& addr)
 {
     MOZ_ASSERT(addr.offset == 0);
     ma_ss(src, addr);
 }
 
+// ========================================================================
+// wasm support
+
+template <class L>
+void
+MacroAssembler::wasmBoundsCheck(Condition cond, Register index, L label)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit)
+{
+    // Replace with new value
+    Assembler::UpdateLoad64Value((Instruction*) patchAt, limit);
+}
+
 //}}} check_macroassembler_style
 // ===============================================================
 
 void
 MacroAssemblerMIPS64Compat::incrementInt32Value(const Address& addr)
 {
     asMasm().add32(Imm32(1), addr);
 }
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -146,15 +146,12 @@ inline bool hasMultiAlias() { MOZ_CRASH(
 static const uint32_t ShadowStackSpace = 0;
 static const uint32_t JumpImmediateRange = INT32_MAX;
 
 #ifdef JS_NUNBOX32
 static const int32_t NUNBOX32_TYPE_OFFSET = 4;
 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0;
 #endif
 
-static const size_t WasmCheckedImmediateRange = 0;
-static const size_t WasmImmediateRange = 0;
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_none_Architecture_none_h */
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -142,18 +142,16 @@ class Assembler : public AssemblerShared
     static void PatchWrite_NearCall(CodeLocationLabel, CodeLocationLabel) { MOZ_CRASH(); }
     static uint32_t PatchWrite_NearCallSize() { MOZ_CRASH(); }
     static void PatchInstructionImmediate(uint8_t*, PatchedImmPtr) { MOZ_CRASH(); }
 
     static void ToggleToJmp(CodeLocationLabel) { MOZ_CRASH(); }
     static void ToggleToCmp(CodeLocationLabel) { MOZ_CRASH(); }
     static void ToggleCall(CodeLocationLabel, bool) { MOZ_CRASH(); }
 
-    static void UpdateBoundsCheck(uint8_t*, uint32_t) { MOZ_CRASH(); }
-
     static uintptr_t GetPointer(uint8_t*) { MOZ_CRASH(); }
 
     void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
                                      const Disassembler::HeapAccess& heapAccess)
     {
         MOZ_CRASH();
     }
 };
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h
+++ b/js/src/jit/shared/CodeGenerator-shared-inl.h
@@ -342,32 +342,27 @@ CodeGeneratorShared::restoreLiveVolatile
     LSafepoint* safepoint = ins->safepoint();
     LiveRegisterSet regs;
     regs.set() = RegisterSet::Intersect(safepoint->liveRegs().set(), RegisterSet::Volatile());
     masm.PopRegsInMask(regs);
 }
 
 void
 CodeGeneratorShared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad,
-                                                 bool isInt64, Scalar::Type type, unsigned numElems,
-                                                 const Operand& mem, LAllocation alloc)
+                                                 Scalar::Type type, Operand mem, LAllocation alloc)
 {
 #ifdef DEBUG
     using namespace Disassembler;
 
     Disassembler::HeapAccess::Kind kind = isLoad ? HeapAccess::Load : HeapAccess::Store;
     switch (type) {
       case Scalar::Int8:
       case Scalar::Int16:
         if (kind == HeapAccess::Load)
-            kind = isInt64 ? HeapAccess::LoadSext64 : HeapAccess::LoadSext32;
-        break;
-      case Scalar::Int32:
-        if (isInt64 && kind == HeapAccess::Load)
-            kind = HeapAccess::LoadSext64;
+            kind = HeapAccess::LoadSext32;
         break;
       default:
         break;
     }
 
     OtherOperand op;
     switch (type) {
       case Scalar::Int8:
@@ -376,17 +371,17 @@ CodeGeneratorShared::verifyHeapAccessDis
       case Scalar::Uint16:
       case Scalar::Int32:
       case Scalar::Uint32:
         if (!alloc.isConstant()) {
             op = OtherOperand(ToRegister(alloc).encoding());
         } else {
             // x86 doesn't allow encoding an imm64 to memory move; the value
             // is wrapped anyways.
-            int32_t i = isInt64 ? int32_t(ToInt64(&alloc)) : ToInt32(&alloc);
+            int32_t i = ToInt32(&alloc);
 
             // Sign-extend the immediate value out to 32 bits. We do this even
             // for unsigned element types so that we match what the disassembly
             // code does, as it doesn't know about signedness of stores.
             unsigned shift = 32 - TypedArrayElemSize(type) * 8;
             i = i << shift >> shift;
             op = OtherOperand(i);
         }
@@ -403,38 +398,33 @@ CodeGeneratorShared::verifyHeapAccessDis
       case Scalar::Int32x4:
         op = OtherOperand(ToFloatRegister(alloc).encoding());
         break;
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
         MOZ_CRASH("Unexpected array type");
     }
 
-    size_t size = Scalar::isSimdType(type)
-                  ? Scalar::scalarByteSize(type) * numElems
-                  : TypedArrayElemSize(type);
-    masm.verifyHeapAccessDisassembly(begin, end,
-                                     HeapAccess(kind, size, ComplexAddress(mem), op));
+    HeapAccess access(kind, TypedArrayElemSize(type), ComplexAddress(mem), op);
+    masm.verifyHeapAccessDisassembly(begin, end, access);
 #endif
 }
 
 void
-CodeGeneratorShared::verifyLoadDisassembly(uint32_t begin, uint32_t end, bool isInt64,
-                                           Scalar::Type type, unsigned numElems, const Operand& mem,
-                                           LAllocation alloc)
+CodeGeneratorShared::verifyLoadDisassembly(uint32_t begin, uint32_t end, Scalar::Type type,
+                                           Operand mem, LAllocation alloc)
 {
-    verifyHeapAccessDisassembly(begin, end, true, isInt64, type, numElems, mem, alloc);
+    verifyHeapAccessDisassembly(begin, end, true, type, mem, alloc);
 }
 
 void
-CodeGeneratorShared::verifyStoreDisassembly(uint32_t begin, uint32_t end, bool isInt64,
-                                            Scalar::Type type, unsigned numElems,
-                                            const Operand& mem, LAllocation alloc)
+CodeGeneratorShared::verifyStoreDisassembly(uint32_t begin, uint32_t end, Scalar::Type type,
+                                            Operand mem, LAllocation alloc)
 {
-    verifyHeapAccessDisassembly(begin, end, false, isInt64, type, numElems, mem, alloc);
+    verifyHeapAccessDisassembly(begin, end, false, type, mem, alloc);
 }