Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Nov 2016 10:36:41 +0100
changeset 322388 f9c01ced5685b4a1e8af1de4d3089b298afc0507
parent 322387 18a3d8e8a789cd5c748d62bc1c145d8131487d0a (current diff)
parent 322374 1196bf3032e1bce1fb07a01fd9082a767426c5fb (diff)
child 322389 2b0c612e87232b099830ae3b918d2080cc73960c
push id30951
push usercbook@mozilla.com
push dateTue, 15 Nov 2016 11:25:40 +0000
treeherdermozilla-central@85a9d908e91a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
security/manager/ssl/tests/compiled/TestCertDB.cpp
security/manager/ssl/tests/compiled/TestIsCertBuiltInRoot.cpp
security/manager/ssl/tests/compiled/TestSTSParser.cpp
security/manager/ssl/tests/compiled/moz.build
toolkit/components/extensions/ext-test.js
xpcom/reflect/xptcall/tests/TestXPTCInvoke.cpp
xpcom/reflect/xptcall/tests/moz.build
xpcom/reflect/xptinfo/tests/TestInterfaceInfo.cpp
xpcom/reflect/xptinfo/tests/moz.build
--- a/addon-sdk/source/lib/sdk/ui/frame/view.html
+++ b/addon-sdk/source/lib/sdk/ui/frame/view.html
@@ -1,18 +1,18 @@
-<script>
-// HACK: This is not an ideal way to deliver chrome messages
-// to a innef frame content but seems only way that would
-// make `event.source` an this (outer frame) window.
-window.onmessage = function(event) {
-  var frame = document.querySelector("iframe");
-  var content = frame.contentWindow;
-  // If message is posted from chrome it has no `event.source`.
-  if (event.source === null)
-    content.postMessage(event.data, "*");
-};
-// Hack: Ideally we would have used srcdoc on iframe, but in
-// that case origin of document is either content which is unable
-// to load add-on resources or a chrome to which add-on resource
-// can not send messages back.
-document.documentElement.style.overflow = "hidden";
-document.documentElement.innerHTML = atob(location.hash.substr(1));
-</script>
+<!DOCTYPE html>
+<html>
+  <head>
+    <script>
+      // HACK: This is not an ideal way to deliver chrome messages
+      // to an inner frame content but seems only way that would
+      // make `event.source` this (outer frame) window.
+      window.onmessage = function(event) {
+        var frame = document.querySelector("iframe");
+        var content = frame.contentWindow;
+        // If message is posted from chrome it has no `event.source`.
+        if (event.source === null)
+          content.postMessage(event.data, "*");
+      };
+    </script>
+  </head>
+  <body style="overflow: hidden"></body>
+</html>
--- a/addon-sdk/source/lib/sdk/ui/frame/view.js
+++ b/addon-sdk/source/lib/sdk/ui/frame/view.js
@@ -58,39 +58,45 @@ const registerFrame = ({id, url}) => {
     id: id,
     type: "custom",
     removable: true,
     onBuild: document => {
       let view = document.createElementNS(XUL_NS, "toolbaritem");
       view.setAttribute("id", id);
       view.setAttribute("flex", 2);
 
-      let innerFrame = document.createElementNS(HTML_NS, "iframe");
-      innerFrame.setAttribute("id", id);
-      innerFrame.setAttribute("src", url);
-      innerFrame.setAttribute("seamless", "seamless");
-      innerFrame.setAttribute("sandbox", "allow-scripts");
-      innerFrame.setAttribute("scrolling", "no");
-      innerFrame.setAttribute("data-is-sdk-inner-frame", true);
-      innerFrame.setAttribute("style", [ "border:none",
-        "position:absolute", "width:100%", "top: 0",
-        "left: 0", "overflow: hidden"].join(";"));
-
       let outerFrame = document.createElementNS(XUL_NS, "iframe");
-      outerFrame.setAttribute("src", OUTER_FRAME_URI + "#" +
-                                     encode(innerFrame.outerHTML));
+      outerFrame.setAttribute("src", OUTER_FRAME_URI);
       outerFrame.setAttribute("id", "outer-" + id);
       outerFrame.setAttribute("data-is-sdk-outer-frame", true);
       outerFrame.setAttribute("type", "content");
       outerFrame.setAttribute("transparent", true);
       outerFrame.setAttribute("flex", 2);
       outerFrame.setAttribute("style", "overflow: hidden;");
       outerFrame.setAttribute("scrolling", "no");
       outerFrame.setAttribute("disablehistory", true);
       outerFrame.setAttribute("seamless", "seamless");
+      outerFrame.addEventListener("load", function onload() {
+        outerFrame.removeEventListener("load", onload, true);
+
+        let doc = outerFrame.contentDocument;
+
+        let innerFrame = doc.createElementNS(HTML_NS, "iframe");
+        innerFrame.setAttribute("id", id);
+        innerFrame.setAttribute("src", url);
+        innerFrame.setAttribute("seamless", "seamless");
+        innerFrame.setAttribute("sandbox", "allow-scripts");
+        innerFrame.setAttribute("scrolling", "no");
+        innerFrame.setAttribute("data-is-sdk-inner-frame", true);
+        innerFrame.setAttribute("style", [ "border:none",
+          "position:absolute", "width:100%", "top: 0",
+          "left: 0", "overflow: hidden"].join(";"));
+
+        doc.body.appendChild(innerFrame);
+      }, true);
 
       view.appendChild(outerFrame);
 
       return view;
     }
   });
 };
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1478872324437" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1479042428179" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i988" id="{b12785f5-d8d0-4530-a3ea-5c4263b85bef}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i398" id="{377e5d4d-77e5-476a-8716-7e70a9272da0}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -43,29 +43,29 @@
     <emItem blockID="i1078" id="/^(jid1-W4CLFIRExukJIFW@jetpack|jid1-W4CLFIRExukJIFW@jetpack_1|jid1-W3CLwrP[a-z]+@jetpack)$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i682" id="f6682b47-e12f-400b-9bc0-43b3ccae69d1@39d6f481-b198-4349-9ebe-9a93a86f9267.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i522" id="/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i1024" id="{458fb825-2370-4973-bf66-9d7142141847}">
       <prefs>
         <pref>app.update.auto</pref>
         <pref>app.update.enabled</pref>
         <pref>app.update.interval</pref>
         <pref>app.update.url</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i522" id="/^({976cd962-e0ca-4337-aea7-d93fae63a79c}|{525ba996-1ce4-4677-91c5-9fc4ead2d245}|{91659dab-9117-42d1-a09f-13ec28037717}|{c1211069-1163-4ba8-b8b3-32fc724766be})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i6" id="{3f963a5b-e555-4543-90e2-c3908898db71}">
       <prefs/>
       <versionRange minVersion=" " maxVersion="8.5"/>
     </emItem>
     <emItem blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -346,18 +346,18 @@
     <emItem blockID="i618" id="toolbar@ask.com">
       <prefs/>
       <versionRange minVersion="3.15.24" maxVersion="3.15.24.*" severity="1"/>
       <versionRange minVersion="3.15.13" maxVersion="3.15.13.*" severity="1"/>
       <versionRange minVersion="3.15.28" maxVersion="3.15.28.*" severity="1"/>
       <versionRange minVersion="3.15.22" maxVersion="3.15.22.*" severity="1"/>
       <versionRange minVersion="3.15.8" maxVersion="3.15.8.*" severity="1"/>
       <versionRange minVersion="3.15.10" maxVersion="3.15.11.*" severity="1"/>
+      <versionRange minVersion="3.15.5" maxVersion="3.15.5.*" severity="1"/>
       <versionRange minVersion="3.15.18" maxVersion="3.15.20.*" severity="1"/>
-      <versionRange minVersion="3.15.5" maxVersion="3.15.5.*" severity="1"/>
       <versionRange minVersion="3.15.31" maxVersion="3.15.31.*" severity="1"/>
       <versionRange minVersion="3.15.26" maxVersion="3.15.26.*" severity="1"/>
     </emItem>
     <emItem blockID="i15" id="personas@christopher.beard">
       <prefs/>
       <versionRange minVersion="1.6" maxVersion="1.6">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="3.6.*" minVersion="3.6"/>
@@ -593,28 +593,28 @@
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i730" id="25p@9eAkaLq.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i400" id="{dd6b651f-dfb9-4142-b0bd-09912ad22674}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="1"/>
-    </emItem>
     <emItem blockID="i1227" id="{A34CAF42-A3E3-11E5-945F-18C31D5D46B0}">
       <prefs>
         <pref>security.csp.enable</pref>
         <pref>security.fileuri.strict_origin_policy</pref>
         <pref>security.mixed_content.block_active_content</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
+    <emItem blockID="i400" id="{dd6b651f-dfb9-4142-b0bd-09912ad22674}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="1"/>
+    </emItem>
     <emItem blockID="i11" id="yslow@yahoo-inc.com">
       <prefs/>
       <versionRange minVersion="2.0.5" maxVersion="2.0.5">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.5.7"/>
         </targetApplication>
       </versionRange>
     </emItem>
@@ -854,28 +854,28 @@
     <emItem blockID="i165" id="{EEF73632-A085-4fd3-A778-ECD82C8CB297}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1022" id="g99hiaoekjoasiijdkoleabsy278djasi@jetpack">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i75" id="firebug@software.joehewitt.com" os="Darwin,Linux">
       <prefs/>
       <versionRange minVersion="1.9.0" maxVersion="1.9.0" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="9.*" minVersion="9.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
-    <emItem blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i718" id="G4Ce4@w.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i21" id="support@update-firefox.com">
       <prefs/>
@@ -1226,28 +1226,28 @@
         <pref>browser.search.defaultenginename</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
-    <emItem blockID="i92" id="play5@vide04flash.com">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*"/>
-    </emItem>
     <emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
       <prefs/>
       <versionRange minVersion="0.1" maxVersion="7.9.20.6" severity="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="8.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
+    <emItem blockID="i92" id="play5@vide04flash.com">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*"/>
+    </emItem>
     <emItem blockID="i220" id="pricepeep@getpricepeep.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="2.1.0.19.99" severity="1"/>
     </emItem>
     <emItem blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
@@ -1387,21 +1387,21 @@
     <emItem blockID="i344" id="lrcsTube@hansanddeta.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i79" id="GifBlock@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i469" id="OKitSpace@OKitSpace.es">
+    <emItem blockID="i780" id="{b6ef1336-69bb-45b6-8cba-e578fc0e4433}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
-    <emItem blockID="i780" id="{b6ef1336-69bb-45b6-8cba-e578fc0e4433}">
+    <emItem blockID="i469" id="OKitSpace@OKitSpace.es">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i483" id="brasilescapefive@facebook.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1058" id="amo-validator-bypass@example.com">
@@ -1587,24 +1587,24 @@
     <emItem blockID="i465" id="trtv3@trtv.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
       <prefs/>
       <versionRange minVersion=" " severity="1"/>
     </emItem>
+    <emItem blockID="i854" id="/^(7tG@zEb\.net|ru@gfK0J\.edu)$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
     <emItem blockID="i73" id="a1g0a9g219d@a1.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*"/>
     </emItem>
-    <emItem blockID="i854" id="/^(7tG@zEb\.net|ru@gfK0J\.edu)$/">
-      <prefs/>
-      <versionRange minVersion="0" maxVersion="*" severity="3"/>
-    </emItem>
     <emItem blockID="i662" id="imbaty@taringamp3.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1126" id="{bbea93c6-64a3-4a5a-854a-9cc61c8d309e}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -133,19 +133,16 @@ pref("app.update.badge", false);
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
 pref("app.update.url", "https://aus5.mozilla.org/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
 // app.update.url.manual is in branding section
 // app.update.url.details is in branding section
 
-// User-settable override to app.update.url for testing purposes.
-//pref("app.update.url.override", "");
-
 // app.update.interval is in branding section
 // app.update.promptWaitTime is in branding section
 
 // Show the Update Checking/Ready UI when the user was idle for x seconds
 pref("app.update.idletime", 60);
 
 // Whether or not to attempt using the service for updates.
 #ifdef MOZ_MAINTENANCE_SERVICE
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
@@ -36,30 +36,33 @@ add_task(function* testDisabled() {
       browser.test.sendMessage("ready");
     },
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
   yield clickBrowserAction(extension);
+  yield new Promise(resolve => setTimeout(resolve, 0));
 
   extension.sendMessage("check-clicked", true);
   yield extension.awaitMessage("next-test");
 
   extension.sendMessage("disable");
   yield extension.awaitMessage("next-test");
 
   yield clickBrowserAction(extension);
+  yield new Promise(resolve => setTimeout(resolve, 0));
 
   extension.sendMessage("check-clicked", false);
   yield extension.awaitMessage("next-test");
 
   extension.sendMessage("enable");
   yield extension.awaitMessage("next-test");
 
   yield clickBrowserAction(extension);
+  yield new Promise(resolve => setTimeout(resolve, 0));
 
   extension.sendMessage("check-clicked", true);
   yield extension.awaitMessage("next-test");
 
   yield extension.unload();
 });
--- a/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js
@@ -91,24 +91,29 @@ add_task(function* testWindowCreate() {
       browser.test.log("Try to create a window with an invalid tabId");
       await browser.test.assertRejects(
         browser.windows.create({tabId: 0}),
         /Invalid tab ID: 0/,
         "Create call failed as expected");
 
 
       browser.test.log("Try to create a window with two URLs");
-      [, , window] = await Promise.all([
+      let readyPromise = Promise.all([
         // tabs.onUpdated can be invoked between the call of windows.create and
         // the invocation of its callback/promise, so set up the listeners
         // before creating the window.
         promiseTabUpdated("http://example.com/"),
         promiseTabUpdated("http://example.org/"),
-        browser.windows.create({url: ["http://example.com/", "http://example.org/"]}),
       ]);
+
+      await new Promise(resolve => setTimeout(resolve, 0));
+
+      window = await browser.windows.create({url: ["http://example.com/", "http://example.org/"]});
+      await readyPromise;
+
       browser.test.assertEq(2, window.tabs.length, "2 tabs were opened in new window");
       browser.test.assertEq("about:blank", window.tabs[0].url, "about:blank, page not loaded yet");
       browser.test.assertEq("about:blank", window.tabs[1].url, "about:blank, page not loaded yet");
 
       window = await browser.windows.get(window.id, {populate: true});
 
       browser.test.assertEq(2, window.tabs.length, "2 tabs were opened in new window");
       browser.test.assertEq("http://example.com/", window.tabs[0].url, "Correct URL was loaded in tab 1");
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -559,17 +559,17 @@ Cookies.prototype = {
       } catch (ex) {
         Components.utils.reportError("Unable to migrate cookie: " + ex);
         success = false;
       } finally {
         aCallback(success);
       }
     };
     fileReader.addEventListener("loadend", onLoadEnd, false);
-    fileReader.readAsText(new File(aFile));
+    fileReader.readAsText(File.createFromNsIFile(aFile));
   },
 
   /**
    * Parses a cookie file buffer and returns an array of the contained cookies.
    *
    * The cookie file format is a newline-separated-values with a "*" used as
    * delimeter between multiple records.
    * Each cookie has the following fields:
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -165,20 +165,10 @@ function optedOut() {
 
 /* If this function returns a non-empty string, it
  * means that this particular user should be temporarily
  * disqualified due to some particular reason.
  * If a user shouldn't be disqualified, then an empty
  * string must be returned.
  */
 function getTemporaryDisqualification() {
-  let applicationLanguage =
-    Cc["@mozilla.org/chrome/chrome-registry;1"]
-      .getService(Ci.nsIXULChromeRegistry)
-      .getSelectedLocale("global")
-      .split("-")[0];
-
-  if (applicationLanguage == "ru") {
-    return "ru";
-  }
-
   return "";
 }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.6.304
+Current extension version is: 1.6.315
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -19,18 +19,18 @@
   } else if (typeof exports !== 'undefined') {
     factory(exports);
   } else {
     factory(root['pdfjsDistBuildPdf'] = {});
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
-  var pdfjsVersion = '1.6.304';
-  var pdfjsBuild = 'b4100ba';
+  var pdfjsVersion = '1.6.315';
+  var pdfjsBuild = 'a139c75';
   var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
   var pdfjsLibs = {};
   (function pdfjsWrapper() {
     (function (root, factory) {
       factory(root.pdfjsSharedUtil = {});
     }(this, function (exports) {
       var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
       var FONT_IDENTITY_MATRIX = [
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -19,18 +19,18 @@
   } else if (typeof exports !== 'undefined') {
     factory(exports);
   } else {
     factory(root['pdfjsDistBuildPdfWorker'] = {});
   }
 }(this, function (exports) {
   // Use strict in our context only - users might not want it
   'use strict';
-  var pdfjsVersion = '1.6.304';
-  var pdfjsBuild = 'b4100ba';
+  var pdfjsVersion = '1.6.315';
+  var pdfjsBuild = 'a139c75';
   var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
   var pdfjsLibs = {};
   (function pdfjsWrapper() {
     (function (root, factory) {
       factory(root.pdfjsCoreArithmeticDecoder = {});
     }(this, function (exports) {
       /* This class implements the QM Coder decoding as defined in
        *   JPEG 2000 Part I Final Committee Draft Version 1.0
@@ -45074,16 +45074,39 @@
               }
               break;
             case 'Named':
               var namedAction = action.get('N');
               if (isName(namedAction)) {
                 resultObj.action = namedAction.name;
               }
               break;
+            case 'JavaScript':
+              var jsAction = action.get('JS'), js;
+              if (isStream(jsAction)) {
+                js = bytesToString(jsAction.getBytes());
+              } else if (isString(jsAction)) {
+                js = jsAction;
+              }
+              if (js) {
+                // Attempt to recover valid URLs from 'JS' entries with certain
+                // white-listed formats, e.g.
+                //  - window.open('http://example.com')
+                //  - app.launchURL('http://example.com', true)
+                var URL_OPEN_METHODS = [
+                  'app.launchURL',
+                  'window.open'
+                ];
+                var regex = new RegExp('^(?:' + URL_OPEN_METHODS.join('|') + ')' + '\\((?:\'|\")(\\S+)(?:\'|\")(?:,|\\))');
+                var jsUrl = regex.exec(stringToPDFString(js), 'i');
+                if (jsUrl && jsUrl[1]) {
+                  url = jsUrl[1];
+                  break;
+                }
+              }
             default:
               warn('Catalog_parseDestDictionary: Unrecognized link type "' + linkType + '".');
               break;
             }
           } else if (destDict.has('Dest')) {
             // Simple destination link.
             dest = destDict.get('Dest');
           }
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -321,42 +321,16 @@ select {
   cursor: none;
 }
 
 .pdfPresentationMode.pdfPresentationModeControls > *,
 .pdfPresentationMode.pdfPresentationModeControls .textLayer > div {
   cursor: default;
 }
 
-/* outer/inner center provides horizontal center */
-.outerCenter {
-  pointer-events: none;
-  position: relative;
-}
-html[dir='ltr'] .outerCenter {
-  float: right;
-  right: 50%;
-}
-html[dir='rtl'] .outerCenter {
-  float: left;
-  left: 50%;
-}
-.innerCenter {
-  pointer-events: auto;
-  position: relative;
-}
-html[dir='ltr'] .innerCenter {
-  float: right;
-  right: -50%;
-}
-html[dir='rtl'] .innerCenter {
-  float: left;
-  left: -50%;
-}
-
 #outerContainer {
   width: 100%;
   height: 100%;
   position: relative;
 }
 
 #sidebarContainer {
   position: absolute;
@@ -660,34 +634,29 @@ html[dir='ltr'] .doorHangerRight:before 
   font-style: italic;
   color: #A6B7D0;
 }
 
 #findInput.notFound {
   background-color: rgb(255, 102, 102);
 }
 
-html[dir='ltr'] #toolbarViewerLeft {
-  margin-left: -1px;
-}
-html[dir='rtl'] #toolbarViewerRight {
-  margin-right: -1px;
+#toolbarViewerMiddle {
+  position: absolute;
+  left: 50%;
+  transform: translateX(-50%);
 }
 
 html[dir='ltr'] #toolbarViewerLeft,
 html[dir='rtl'] #toolbarViewerRight {
-  position: absolute;
-  top: 0;
-  left: 0;
+  float: left;
 }
 html[dir='ltr'] #toolbarViewerRight,
 html[dir='rtl'] #toolbarViewerLeft {
-  position: absolute;
-  top: 0;
-  right: 0;
+  float: right;
 }
 html[dir='ltr'] #toolbarViewerLeft > *,
 html[dir='ltr'] #toolbarViewerMiddle > *,
 html[dir='ltr'] #toolbarViewerRight > *,
 html[dir='ltr'] .findbar > * {
   position: relative;
   float: left;
 }
@@ -1953,48 +1922,57 @@ html[dir='rtl'] #documentPropertiesOverl
 }
 
 .visibleLargeView,
 .visibleMediumView,
 .visibleSmallView {
   display: none;
 }
 
-@media all and (max-width: 960px) {
-  html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter,
-  html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter {
-    float: left;
-    left: 205px;
-  }
-  html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter,
-  html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter {
-    float: right;
-    right: 205px;
+@media all and (max-width: 1040px) {
+  #outerContainer.sidebarMoving #toolbarViewerMiddle,
+  #outerContainer.sidebarOpen #toolbarViewerMiddle {
+    display: table;
+    margin: auto;
+    left: auto;
+    position: inherit;
+    transform: none;
   }
 }
 
-@media all and (max-width: 900px) {
+@media all and (max-width: 980px) {
+  .sidebarMoving .hiddenLargeView,
   .sidebarOpen .hiddenLargeView {
     display: none;
   }
+  .sidebarMoving .visibleLargeView,
   .sidebarOpen .visibleLargeView {
     display: inherit;
   }
 }
 
-@media all and (max-width: 860px) {
+@media all and (max-width: 900px) {
+  #toolbarViewerMiddle {
+    display: table;
+    margin: auto;
+    left: auto;
+    position: inherit;
+    transform: none;
+  }
+  .sidebarMoving .hiddenMediumView,
   .sidebarOpen .hiddenMediumView {
     display: none;
   }
+  .sidebarMoving .visibleMediumView,
   .sidebarOpen .visibleMediumView {
     display: inherit;
   }
 }
 
-@media all and (max-width: 770px) {
+@media all and (max-width: 840px) {
   #sidebarContainer {
     top: 32px;
     z-index: 100;
   }
   .loadingInProgress #sidebarContainer {
     top: 37px;
   }
   #sidebarContent {
@@ -2004,72 +1982,53 @@ html[dir='rtl'] #documentPropertiesOverl
 
   html[dir='ltr'] #outerContainer.sidebarOpen > #mainContainer {
     left: 0px;
   }
   html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer {
     right: 0px;
   }
 
-  html[dir='ltr'] .outerCenter {
-    float: left;
-    left: 205px;
-  }
-  html[dir='rtl'] .outerCenter {
-    float: right;
-    right: 205px;
-  }
-
   #outerContainer .hiddenLargeView,
   #outerContainer .hiddenMediumView {
     display: inherit;
   }
   #outerContainer .visibleLargeView,
   #outerContainer .visibleMediumView {
     display: none;
   }
 }
 
-@media all and (max-width: 700px) {
+@media all and (max-width: 770px) {
   #outerContainer .hiddenLargeView {
     display: none;
   }
   #outerContainer .visibleLargeView {
     display: inherit;
   }
 }
 
-@media all and (max-width: 660px) {
+@media all and (max-width: 700px) {
   #outerContainer .hiddenMediumView {
     display: none;
   }
   #outerContainer .visibleMediumView {
     display: inherit;
   }
 }
 
-@media all and (max-width: 600px) {
+@media all and (max-width: 640px) {
   .hiddenSmallView {
     display: none;
   }
   .visibleSmallView {
     display: inherit;
   }
-  html[dir='ltr'] #outerContainer.sidebarMoving .outerCenter,
-  html[dir='ltr'] #outerContainer.sidebarOpen .outerCenter,
-  html[dir='ltr'] .outerCenter {
-    left: 156px;
-  }
-  html[dir='rtl'] #outerContainer.sidebarMoving .outerCenter,
-  html[dir='rtl'] #outerContainer.sidebarOpen .outerCenter,
-  html[dir='rtl'] .outerCenter {
-    right: 156px;
-  }
   .toolbarButtonSpacer {
     width: 0;
   }
 }
 
-@media all and (max-width: 510px) {
+@media all and (max-width: 535px) {
   #scaleSelectContainer {
     display: none;
   }
 }
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -189,45 +189,43 @@ See https://github.com/adobe-type-tools/
                 </a>
 
                 <div class="verticalToolbarSeparator hiddenSmallView"></div>
 
                 <button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
                   <span data-l10n-id="tools_label">Tools</span>
                 </button>
               </div>
-              <div class="outerCenter">
-                <div class="innerCenter" id="toolbarViewerMiddle">
-                  <div class="splitToolbarButton">
-                    <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
-                      <span data-l10n-id="zoom_out_label">Zoom Out</span>
-                    </button>
-                    <div class="splitToolbarButtonSeparator"></div>
-                    <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
-                      <span data-l10n-id="zoom_in_label">Zoom In</span>
-                     </button>
-                  </div>
-                  <span id="scaleSelectContainer" class="dropdownToolbarButton">
-                    <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
-                      <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
-                      <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
-                      <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
-                      <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
-                      <option id="customScaleOption" title="" value="custom" hidden="true"></option>
-                      <option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
-                      <option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
-                      <option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
-                      <option title="" value="1.25" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 125 }'>125%</option>
-                      <option title="" value="1.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 150 }'>150%</option>
-                      <option title="" value="2" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 200 }'>200%</option>
-                      <option title="" value="3" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 300 }'>300%</option>
-                      <option title="" value="4" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 400 }'>400%</option>
-                    </select>
-                  </span>
+              <div id="toolbarViewerMiddle">
+                <div class="splitToolbarButton">
+                  <button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
+                    <span data-l10n-id="zoom_out_label">Zoom Out</span>
+                  </button>
+                  <div class="splitToolbarButtonSeparator"></div>
+                  <button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
+                    <span data-l10n-id="zoom_in_label">Zoom In</span>
+                   </button>
                 </div>
+                <span id="scaleSelectContainer" class="dropdownToolbarButton">
+                  <select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
+                    <option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
+                    <option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
+                    <option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
+                    <option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
+                    <option id="customScaleOption" title="" value="custom" disabled="disabled" hidden="true"></option>
+                    <option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
+                    <option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
+                    <option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
+                    <option title="" value="1.25" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 125 }'>125%</option>
+                    <option title="" value="1.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 150 }'>150%</option>
+                    <option title="" value="2" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 200 }'>200%</option>
+                    <option title="" value="3" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 300 }'>300%</option>
+                    <option title="" value="4" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 400 }'>400%</option>
+                  </select>
+                </span>
               </div>
             </div>
             <div id="loadingBar">
               <div class="progress">
                 <div class="glimmer">
                 </div>
               </div>
             </div>
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -529,48 +529,42 @@ Inspector.prototype = {
   setupSidebar: function () {
     let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
     this.sidebar = new ToolSidebar(tabbox, this, "inspector", {
       showAllTabsMenu: true
     });
 
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
+    this._setDefaultSidebar = (event, toolId) => {
+      Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
+    };
+
+    this.sidebar.on("select", this._setDefaultSidebar);
+
     if (!Services.prefs.getBoolPref("devtools.fontinspector.enabled") &&
        defaultTab == "fontinspector") {
       defaultTab = "ruleview";
     }
 
     // Append all side panels
     this.sidebar.addExistingTab(
       "ruleview",
       INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
       defaultTab == "ruleview");
 
     this.sidebar.addExistingTab(
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
-    this._setDefaultSidebar = (event, toolId) => {
-      Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
-    };
-
-    this.sidebar.on("select", this._setDefaultSidebar);
-
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.computedview = new ComputedViewTool(this, this.panelWin);
 
     if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
-      this.sidebar.addExistingTab(
-        "layoutview",
-        INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"),
-        defaultTab == "layoutview"
-      );
-
       const {LayoutView} = require("devtools/client/inspector/layout/layout");
       this.layoutview = new LayoutView(this, this.panelWin);
     }
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -186,21 +186,16 @@
             <div id="propertyContainer" class="theme-separator" tabindex="0">
             </div>
 
             <div id="computedview-no-results" hidden="" data-localization="content=inspector.noProperties"></div>
           </div>
         </div>
       </div>
 
-      <div id="sidebar-panel-layoutview" class="devtools-monospace theme-sidebar inspector-tabpanel"
-           data-localization-bundle="devtools/client/locales/inspector.properties">
-        <div id="layoutview-container"></div>
-      </div>
-
       <div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel"
                 data-localization-bundle="devtools/client/locales/font-inspector.properties">
         <div class="devtools-toolbar">
           <div class="devtools-searchbox">
             <input id="font-preview-text-input" class="devtools-textinput" type="search"
                         data-localization="placeholder=fontinspector.previewText"/>
           </div>
           <label id="font-showall" class="theme-link"
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -13,17 +13,17 @@ const Grid = createFactory(require("./Gr
 
 const App = createClass({
 
   displayName: "App",
 
   render() {
     return dom.div(
       {
-        id: "layoutview-container-focusable",
+        id: "layoutview-container",
       },
       Accordion({
         items: [
           { header: getStr("layout.header"),
             component: Grid,
             opened: true }
         ]
       })
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -1,36 +1,52 @@
 /* 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 { createFactory, createElement } =
-  require("devtools/client/shared/vendor/react");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const Services = require("Services");
+const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const App = createFactory(require("./components/app"));
 const Store = require("./store");
 
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const INSPECTOR_L10N =
+      new LocalizationHelper("devtools/client/locales/inspector.properties");
+
 function LayoutView(inspector, window) {
   this.inspector = inspector;
   this.document = window.document;
   this.store = null;
 
   this.init();
 }
 
 LayoutView.prototype = {
 
   init() {
     let store = this.store = Store();
-    let provider = createElement(Provider, { store }, App());
-    ReactDOM.render(provider, this.document.querySelector("#layoutview-container"));
+    let provider = createElement(Provider, {
+      store,
+      id: "layoutview",
+      title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"),
+      key: "layoutview",
+    }, App());
+
+    let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
+
+    this.inspector.addSidebarTab(
+      "layoutview",
+      INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle"),
+      provider,
+      defaultTab == "layoutview"
+    );
   },
 
   destroy() {
     this.inspector = null;
     this.document = null;
     this.store = null;
   },
 };
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -95,22 +95,17 @@ pref("devtools.debugger.remote-timeout",
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.client-source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.auto-black-box", true);
 pref("devtools.debugger.workers", false);
-
-#if defined(NIGHTLY_BUILD)
 pref("devtools.debugger.new-debugger-frontend", true);
-#else
-pref("devtools.debugger.new-debugger-frontend", false);
-#endif
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -1,27 +1,14 @@
 /* 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/. */
 
-#sidebar-panel-layoutview {
-  margin: 0;
-  display: flex;
-  flex-direction: column;
-  width: 100%;
-  height: 100%;
-}
-
 #layoutview-container {
   height: 100%;
-  overflow: auto;
-}
-
-#layoutview-container-focusable {
-  height: 100%;
-  outline: none;
+  width: 100%;
 }
 
 .layoutview-no-grids {
   font-style: italic;
   text-align: center;
   padding: 0.5em;
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1723,17 +1723,19 @@ nsDocShell::MaybeInitTiming()
       mBlankTiming = false;
     }
   }
 
   if (!mTiming) {
     mTiming = new nsDOMNavigationTiming();
   }
 
-  mTiming->NotifyNavigationStart();
+  mTiming->NotifyNavigationStart(
+    mIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+              : nsDOMNavigationTiming::DocShellState::eInactive);
 }
 
 //
 // Bug 13871: Prevent frameset spoofing
 //
 // This routine answers: 'Is origin's document from same domain as
 // target's document?'
 //
@@ -6214,16 +6216,30 @@ nsDocShell::SetIsActive(bool aIsActive)
           ScreenOrientation::UpdateActiveOrientationLock(orientation);
         }
       }
 
       doc->PostVisibilityUpdateEvent();
     }
   }
 
+  // Tell the nsDOMNavigationTiming about it
+  RefPtr<nsDOMNavigationTiming> timing = mTiming;
+  if (!timing && mContentViewer) {
+    nsIDocument* doc = mContentViewer->GetDocument();
+    if (doc) {
+      timing = doc->GetNavigationTiming();
+    }
+  }
+  if (timing) {
+    timing->NotifyDocShellStateChanged(
+      aIsActive ? nsDOMNavigationTiming::DocShellState::eActive
+                : nsDOMNavigationTiming::DocShellState::eInactive);
+  }
+
   // Recursively tell all of our children, but don't tell <iframe mozbrowser>
   // children; they handle their state separately.
   nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
   while (iter.HasMore()) {
     nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
     if (!docshell) {
       continue;
     }
@@ -9855,28 +9871,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
 
       // requestingElement docshell type = current docshell type.
       MOZ_ASSERT(mItemType == elementDocShell->ItemType(),
                 "subframes should have the same docshell type as their parent");
 #endif
     }
 
-    // XXXbz would be nice to know the loading principal here... but we don't
-    nsCOMPtr<nsIPrincipal> requestingPrincipal = aTriggeringPrincipal;
-    if (!requestingPrincipal && aReferrer) {
-      rv =
-        CreatePrincipalFromReferrer(aReferrer, getter_AddRefs(requestingPrincipal));
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     rv = NS_CheckContentLoadPolicy(contentType,
                                    aURI,
-                                   requestingPrincipal,
+                                   aTriggeringPrincipal,
                                    requestingContext,
                                    EmptyCString(),  // mime guess
                                    nullptr,  // extra
                                    &shouldLoad);
 
     if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
       if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
         return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -154,25 +154,33 @@ DOMIntersectionObserver::Observe(Element
   aTarget.RegisterIntersectionObserver(this);
   mObservationTargets.PutEntry(&aTarget);
   Connect();
 }
 
 void
 DOMIntersectionObserver::Unobserve(Element& aTarget)
 {
-  if (!mObservationTargets.Contains(&aTarget)) {
-    return;
+  if (UnlinkTarget(aTarget)) {
+    aTarget.UnregisterIntersectionObserver(this);
   }
-  if (mObservationTargets.Count() == 1) {
-    Disconnect();
-    return;
-  }
-  aTarget.UnregisterIntersectionObserver(this);
-  mObservationTargets.RemoveEntry(&aTarget);
+}
+
+bool
+DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
+{
+    if (!mObservationTargets.Contains(&aTarget)) {
+        return false;
+    }
+    if (mObservationTargets.Count() == 1) {
+        Disconnect();
+        return false;
+    }
+    mObservationTargets.RemoveEntry(&aTarget);
+    return true;
 }
 
 void
 DOMIntersectionObserver::Connect()
 {
   if (mConnected) {
     return;
   }
@@ -187,18 +195,20 @@ DOMIntersectionObserver::Disconnect()
   if (!mConnected) {
     return;
   }
   for (auto iter = mObservationTargets.Iter(); !iter.Done(); iter.Next()) {
     Element* target = iter.Get()->GetKey();
     target->UnregisterIntersectionObserver(this);
   }
   mObservationTargets.Clear();
-  nsIDocument* document = mOwner->GetExtantDoc();
-  document->RemoveIntersectionObserver(this);
+  if (mOwner) {
+    nsIDocument* document = mOwner->GetExtantDoc();
+    document->RemoveIntersectionObserver(this);
+  }
   mConnected = false;
 }
 
 void
 DOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal)
 {
   aRetVal.SwapElements(mQueuedEntries);
   mQueuedEntries.Clear();
--- a/dom/base/DOMIntersectionObserver.h
+++ b/dom/base/DOMIntersectionObserver.h
@@ -137,17 +137,19 @@ public:
     return mRoot;
   }
 
   void GetRootMargin(mozilla::dom::DOMString& aRetVal);
   void GetThresholds(nsTArray<double>& aRetVal);
   void Observe(Element& aTarget);
   void Unobserve(Element& aTarget);
 
+  bool UnlinkTarget(Element& aTarget);
   void Disconnect();
+
   void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
 
   mozilla::dom::IntersectionCallback* IntersectionCallback() { return mCallback; }
 
   bool SetRootMargin(const nsAString& aString);
 
   void Update(nsIDocument* aDocument, DOMHighResTimeStamp time);
   void Notify();
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -559,46 +559,20 @@ File::Constructor(const GlobalObject& aG
     impl->SetLastModified(aBag.mLastModified.Value());
   }
 
   RefPtr<File> file = new File(aGlobal.GetAsSupports(), impl);
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
-File::Constructor(const GlobalObject& aGlobal,
-                  Blob& aData,
-                  const ChromeFilePropertyBag& aBag,
-                  ErrorResult& aRv)
-{
-  if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
-    aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("Argument 1 of File.constructor"));
-    return nullptr;
-  }
-
-  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(EmptyString());
-  impl->InitializeChromeFile(aData, aBag, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-  MOZ_ASSERT(impl->IsFile());
-
-  if (aBag.mLastModified.WasPassed()) {
-    impl->SetLastModified(aBag.mLastModified.Value());
-  }
-
-  RefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
-  return domFile.forget();
-}
-
-/* static */ already_AddRefed<File>
-File::Constructor(const GlobalObject& aGlobal,
-                  nsIFile* aData,
-                  const ChromeFilePropertyBag& aBag,
-                  ErrorResult& aRv)
+File::CreateFromNsIFile(const GlobalObject& aGlobal,
+                        nsIFile* aData,
+                        const ChromeFilePropertyBag& aBag,
+                        ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!nsContentUtils::IsCallerChrome()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
@@ -614,20 +588,20 @@ File::Constructor(const GlobalObject& aG
     impl->SetLastModified(aBag.mLastModified.Value());
   }
 
   RefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 /* static */ already_AddRefed<File>
-File::Constructor(const GlobalObject& aGlobal,
-                  const nsAString& aData,
-                  const ChromeFilePropertyBag& aBag,
-                  ErrorResult& aRv)
+File::CreateFromFileName(const GlobalObject& aGlobal,
+                         const nsAString& aData,
+                         const ChromeFilePropertyBag& aBag,
+                         ErrorResult& aRv)
 {
   if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
     aRv.ThrowTypeError<MSG_MISSING_ARGUMENTS>(NS_LITERAL_STRING("File"));
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
 
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -206,36 +206,29 @@ public:
   // File constructor
   static already_AddRefed<File>
   Constructor(const GlobalObject& aGlobal,
               const Sequence<BlobPart>& aData,
               const nsAString& aName,
               const FilePropertyBag& aBag,
               ErrorResult& aRv);
 
-  // File constructor - ChromeOnly
-  static already_AddRefed<File>
-  Constructor(const GlobalObject& aGlobal,
-              Blob& aData,
-              const ChromeFilePropertyBag& aBag,
-              ErrorResult& aRv);
-
-  // File constructor - ChromeOnly
+  // ChromeOnly
   static already_AddRefed<File>
-  Constructor(const GlobalObject& aGlobal,
-              const nsAString& aData,
-              const ChromeFilePropertyBag& aBag,
-              ErrorResult& aRv);
+  CreateFromFileName(const GlobalObject& aGlobal,
+                     const nsAString& aData,
+                     const ChromeFilePropertyBag& aBag,
+                     ErrorResult& aRv);
 
-  // File constructor - ChromeOnly
+  // ChromeOnly
   static already_AddRefed<File>
-  Constructor(const GlobalObject& aGlobal,
-              nsIFile* aData,
-              const ChromeFilePropertyBag& aBag,
-              ErrorResult& aRv);
+  CreateFromNsIFile(const GlobalObject& aGlobal,
+                    nsIFile* aData,
+                    const ChromeFilePropertyBag& aBag,
+                    ErrorResult& aRv);
 
   void GetName(nsAString& aName) const;
 
   int64_t GetLastModified(ErrorResult& aRv);
 
   Date GetLastModifiedDate(ErrorResult& aRv);
 
   // GetPath and SetPath are currently used only for the webkitRelativePath
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -352,27 +352,27 @@ nsDOMDataChannel::Send(nsIInputStream* a
   if (state == mozilla::DataChannel::CLOSING ||
       state == mozilla::DataChannel::CLOSED) {
     return;
   }
 
   MOZ_ASSERT(state == mozilla::DataChannel::OPEN,
              "Unknown state in nsDOMDataChannel::Send");
 
-  int32_t sent;
+  bool sent;
   if (aMsgStream) {
     sent = mDataChannel->SendBinaryStream(aMsgStream, aMsgLength);
   } else {
     if (aIsBinary) {
       sent = mDataChannel->SendBinaryMsg(aMsgString);
     } else {
       sent = mDataChannel->SendMsg(aMsgString);
     }
   }
-  if (sent < 0) {
+  if (!sent) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
 }
 
 nsresult
 nsDOMDataChannel::DoOnMessageAvailable(const nsACString& aData,
                                        bool aBinary)
 {
--- a/dom/base/nsDOMNavigationTiming.cpp
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -1,20 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMNavigationTiming.h"
+
+#include "GeckoProfiler.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "prtime.h"
 #include "nsIURI.h"
+#include "nsPrintfCString.h"
 #include "mozilla/dom/PerformanceNavigation.h"
 #include "mozilla/TimeStamp.h"
 
 nsDOMNavigationTiming::nsDOMNavigationTiming()
 {
   Clear();
 }
 
@@ -40,16 +43,17 @@ nsDOMNavigationTiming::Clear()
 
   mLoadEventStartSet = false;
   mLoadEventEndSet = false;
   mDOMLoadingSet = false;
   mDOMInteractiveSet = false;
   mDOMContentLoadedEventStartSet = false;
   mDOMContentLoadedEventEndSet = false;
   mDOMCompleteSet = false;
+  mDocShellHasBeenActiveSinceNavigationStart = false;
 }
 
 DOMTimeMilliSec
 nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const
 {
   if (aStamp.IsNull()) {
     return 0;
   }
@@ -58,20 +62,21 @@ nsDOMNavigationTiming::TimeStampToDOM(mo
 }
 
 DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart()
 {
   return TimeStampToDOM(mozilla::TimeStamp::Now());
 }
 
 void
-nsDOMNavigationTiming::NotifyNavigationStart()
+nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState)
 {
   mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
   mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
+  mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive);
 }
 
 void
 nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType)
 {
   mNavigationType = aNavigationType;
   // At the unload event time we don't really know the loading uri.
   // Need it for later check for unload timing access.
@@ -176,16 +181,54 @@ nsDOMNavigationTiming::NotifyDOMContentL
 {
   if (!mDOMContentLoadedEventEndSet) {
     mLoadedURI = aURI;
     mDOMContentLoadedEventEnd = DurationFromStart();
     mDOMContentLoadedEventEndSet = true;
   }
 }
 
+void
+nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mNavigationStartTimeStamp.IsNull());
+
+  if (!mNonBlankPaintTimeStamp.IsNull()) {
+    return;
+  }
+
+  mNonBlankPaintTimeStamp = TimeStamp::Now();
+  TimeDuration elapsed = mNonBlankPaintTimeStamp - mNavigationStartTimeStamp;
+
+  if (profiler_is_active()) {
+    nsAutoCString spec;
+    if (mLoadedURI) {
+      mLoadedURI->GetSpec(spec);
+    }
+    nsPrintfCString marker("Non-blank paint after %dms for URL %s, %s",
+                           int(elapsed.ToMilliseconds()), spec.get(),
+                           mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start and first non-blank paint");
+    PROFILER_MARKER(marker.get());
+  }
+
+  if (mDocShellHasBeenActiveSinceNavigationStart) {
+    Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
+                                   mNavigationStartTimeStamp,
+                                   mNonBlankPaintTimeStamp);
+  }
+}
+
+void
+nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState)
+{
+  mDocShellHasBeenActiveSinceNavigationStart &=
+    (aDocShellState == DocShellState::eActive);
+}
+
 DOMTimeMilliSec
 nsDOMNavigationTiming::GetUnloadEventStart()
 {
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
   if (NS_SUCCEEDED(rv)) {
     return mUnloadStart;
   }
--- a/dom/base/nsDOMNavigationTiming.h
+++ b/dom/base/nsDOMNavigationTiming.h
@@ -76,32 +76,41 @@ public:
   {
     return mLoadEventStart;
   }
   DOMTimeMilliSec GetLoadEventEnd() const
   {
     return mLoadEventEnd;
   }
 
-  void NotifyNavigationStart();
+  enum class DocShellState : uint8_t {
+    eActive,
+    eInactive
+  };
+
+  void NotifyNavigationStart(DocShellState aDocShellState);
   void NotifyFetchStart(nsIURI* aURI, Type aNavigationType);
   void NotifyBeforeUnload();
   void NotifyUnloadAccepted(nsIURI* aOldURI);
   void NotifyUnloadEventStart();
   void NotifyUnloadEventEnd();
   void NotifyLoadEventStart();
   void NotifyLoadEventEnd();
 
   // Document changes state to 'loading' before connecting to timing
   void SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue);
   void NotifyDOMLoading(nsIURI* aURI);
   void NotifyDOMInteractive(nsIURI* aURI);
   void NotifyDOMComplete(nsIURI* aURI);
   void NotifyDOMContentLoadedStart(nsIURI* aURI);
   void NotifyDOMContentLoadedEnd(nsIURI* aURI);
+
+  void NotifyNonBlankPaintForRootContentDocument();
+  void NotifyDocShellStateChanged(DocShellState aDocShellState);
+
   DOMTimeMilliSec TimeStampToDOM(mozilla::TimeStamp aStamp) const;
 
   inline DOMHighResTimeStamp TimeStampToDOMHighRes(mozilla::TimeStamp aStamp)
   {
     mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
     return duration.ToMilliseconds();
   }
 
@@ -112,16 +121,17 @@ private:
   void Clear();
 
   nsCOMPtr<nsIURI> mUnloadedURI;
   nsCOMPtr<nsIURI> mLoadedURI;
 
   Type mNavigationType;
   DOMHighResTimeStamp mNavigationStartHighRes;
   mozilla::TimeStamp mNavigationStartTimeStamp;
+  mozilla::TimeStamp mNonBlankPaintTimeStamp;
   DOMTimeMilliSec DurationFromStart();
 
   DOMTimeMilliSec mBeforeUnloadStart;
   DOMTimeMilliSec mUnloadStart;
   DOMTimeMilliSec mUnloadEnd;
   DOMTimeMilliSec mLoadEventStart;
   DOMTimeMilliSec mLoadEventEnd;
 
@@ -136,11 +146,12 @@ private:
   // once.
   bool mLoadEventStartSet : 1;
   bool mLoadEventEndSet : 1;
   bool mDOMLoadingSet : 1;
   bool mDOMInteractiveSet : 1;
   bool mDOMContentLoadedEventStartSet : 1;
   bool mDOMContentLoadedEventEndSet : 1;
   bool mDOMCompleteSet : 1;
+  bool mDocShellHasBeenActiveSinceNavigationStart : 1;
 };
 
 #endif /* nsDOMNavigationTiming_h___ */
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -292,16 +292,25 @@ nsNodeUtils::LastRelease(nsINode* aNode)
   nsINode::nsSlots* slots = aNode->GetExistingSlots();
   if (slots) {
     if (!slots->mMutationObservers.IsEmpty()) {
       NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
                                          nsIMutationObserver,
                                          NodeWillBeDestroyed, (aNode));
     }
 
+    if (aNode->IsElement()) {
+      Element* elem = aNode->AsElement();
+      FragmentOrElement::nsDOMSlots* domSlots =
+        static_cast<FragmentOrElement::nsDOMSlots*>(slots);
+      for (auto& reg : domSlots->mRegisteredIntersectionObservers) {
+        reg.observer->UnlinkTarget(*elem);
+      }
+    }
+
     delete slots;
     aNode->mSlots = nullptr;
   }
 
   // Kill properties first since that may run external code, so we want to
   // be in as complete state as possible at that time.
   if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // Delete all properties before tearing down the document. Some of the
--- a/dom/base/test/bug403852_fileOpener.js
+++ b/dom/base/test/bug403852_fileOpener.js
@@ -4,14 +4,14 @@ Cu.importGlobalProperties(["File"]);
 var testFile = Cc["@mozilla.org/file/directory_service;1"]
                  .getService(Ci.nsIDirectoryService)
                  .QueryInterface(Ci.nsIProperties)
                  .get("ProfD", Ci.nsIFile);
 testFile.append("prefs.js");
 
 addMessageListener("file.open", function () {
   sendAsyncMessage("file.opened", {
-    file: new File(testFile),
+    file: File.createFromNsIFile(testFile),
     mtime: testFile.lastModifiedTime,
-    fileWithDate: new File(testFile, { lastModified: 123 }),
+    fileWithDate: File.createFromNsIFile(testFile, { lastModified: 123 }),
     fileDate: 123,
   });
 });
--- a/dom/base/test/bug578096LoadChromeScript.js
+++ b/dom/base/test/bug578096LoadChromeScript.js
@@ -2,15 +2,15 @@ var file;
 Components.utils.importGlobalProperties(["File"]);
 
 addMessageListener("file.create", function (message) {
   file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("TmpD", Components.interfaces.nsIFile);
   file.append("foo.txt");
   file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
-  sendAsyncMessage("file.created", new File(file));
+  sendAsyncMessage("file.created", File.createFromNsIFile(file));
 });
 
 addMessageListener("file.remove", function (message) {
   file.remove(false);
   sendAsyncMessage("file.removed", {});
 });
--- a/dom/base/test/chrome/test_bug914381.html
+++ b/dom/base/test/chrome/test_bug914381.html
@@ -30,19 +30,19 @@ function createFileWithData(fileData) {
   outStream.write(fileData, fileData.length);
   outStream.close();
 
   return testFile;
 }
 
 /** Test for Bug 914381. File's created in JS using an nsIFile should allow mozGetFullPathInternal calls to succeed **/
 var file = createFileWithData("Test bug 914381");
-var f = new File(file);
+var f = File.createFromNsIFile(file);
 is(f.mozFullPathInternal, undefined, "mozFullPathInternal is undefined from js");
 is(f.mozFullPath, file.path, "mozFullPath returns path if created with nsIFile");
 
-f = new File(file.path);
+f = File.createFromFileName(file.path);
 is(f.mozFullPathInternal, undefined, "mozFullPathInternal is undefined from js");
 is(f.mozFullPath, "", "mozFullPath returns blank if created with a string");
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/test/chrome/test_fileconstructor.xul
+++ b/dom/base/test/chrome/test_fileconstructor.xul
@@ -37,43 +37,36 @@ var file = Components.classes["@mozilla.
 // man I wish this were simpler ...
 file.append("chrome");
 file.append("dom");
 file.append("base");
 file.append("test");
 file.append("chrome");
 file.append("fileconstructor_file.png");
 
-doTest(new File(file.path));
-doTest(new File(file));
+doTest(File.createFromFileName(file.path));
+doTest(File.createFromNsIFile(file));
 function doTest(domfile) {
   ok(domfile instanceof File, "File() should return a File");
   is(domfile.type, "image/png", "File should be a PNG");
   is(domfile.size, 95, "File has size 95 (and more importantly we can read it)");
 }
 
 try {
-  var boomfile = new File();
-  ok(false, "This should never be reached!");
-} catch (e) {
-  ok(true, "Botched file constructor attempts throw and do not crash.");
-}
-
-try {
-  var nonexistentfile = new File("i/sure/hope/this/does/not/exist/anywhere.txt");
+  var nonexistentfile = File.createFromFileName("i/sure/hope/this/does/not/exist/anywhere.txt");
   ok(false, "This should never be reached!");
 } catch (e) {
   ok(true, "Attempt to construct a non-existent file should fail.");
 }
 
 try {
   var dir = Components.classes["@mozilla.org/file/directory_service;1"]
                       .getService(Components.interfaces.nsIProperties)
                       .get("CurWorkD", Components.interfaces.nsIFile);
-  var dirfile = new File(dir);
+  var dirfile = File.createFromNsIFile(dir);
   ok(false, "This should never be reached!");
 } catch (e) {
   ok(true, "Attempt to construct a file from a directory should fail.");
 }
 ]]>
 </script>
 
 </window>
--- a/dom/base/test/chrome/test_fileconstructor_tempfile.xul
+++ b/dom/base/test/chrome/test_fileconstructor_tempfile.xul
@@ -67,17 +67,17 @@ try {
                     .createInstance(Ci.nsIFileOutputStream);
   outStream.init(tmp, 0x02 | 0x08 | 0x20, // write, create, truncate
                  0666, 0);
   outStream.write(fileData, fileData.length);
   outStream.close();
 
   // Create a scoped DOMFile so the gc will happily get rid of it.
   {
-    let dirfile = new File(tmp, { temporary: true });
+    let dirfile = File.createFromNsIFile(tmp, { temporary: true });
     ok(true, "Temporary File() created");
     let reader = new FileReader();
     reader.readAsArrayBuffer(dirfile);
     reader.onload = function(event) {
       let buffer = event.target.result;
       ok(buffer.byteLength > 0,
          "Blob size should be > 0 : " + buffer.byteLength);
       cleanup(tmp);
--- a/dom/base/test/create_file_objects.js
+++ b/dom/base/test/create_file_objects.js
@@ -1,10 +1,10 @@
 Components.utils.importGlobalProperties(['File']);
 
 addMessageListener("create-file-objects", function(message) {
   let files = []
   for (fileName of message.fileNames) {
-    files.push(new File(fileName));
+    files.push(File.createFromFileName(fileName));
   }
 
   sendAsyncMessage("created-file-objects", files);
 });
--- a/dom/base/test/file_bug1198095.js
+++ b/dom/base/test/file_bug1198095.js
@@ -8,17 +8,17 @@ function createFileWithData(message) {
 
   var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
   outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
                  0o666, 0);
 
   outStream.write(message, message.length);
   outStream.close();
 
-  var domFile = new File(testFile);
+  var domFile = File.createFromNsIFile(testFile);
   return domFile;
 }
 
 addMessageListener("file.open", function (message) {
   sendAsyncMessage("file.opened", createFileWithData(message));
 });
 
 addMessageListener("file.modify", function (message) {
--- a/dom/base/test/fileapi_chromeScript.js
+++ b/dom/base/test/fileapi_chromeScript.js
@@ -12,17 +12,17 @@ function createFileWithData(fileData) {
   var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
   outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
                  0o666, 0);
   if (willDelete) {
     fileData = "some irrelevant test data\n";
   }
   outStream.write(fileData, fileData.length);
   outStream.close();
-  var domFile = new File(testFile);
+  var domFile = File.createFromNsIFile(testFile);
   if (willDelete) {
     testFile.remove(/* recursive: */ false);
   }
   return domFile;
 }
 
 addMessageListener("files.open", function (message) {
   sendAsyncMessage("files.opened", message.map(createFileWithData));
--- a/dom/base/test/script_bug1238440.js
+++ b/dom/base/test/script_bug1238440.js
@@ -6,17 +6,17 @@ var tmpFile;
 function writeFile(text, answer) {
   var stream = Cc["@mozilla.org/network/file-output-stream;1"]
                  .createInstance(Ci.nsIFileOutputStream);
   stream.init(tmpFile, 0x02 | 0x08 | 0x10, 0o600, 0);
   stream.write(text, text.length);
   stream.close();
 
   sendAsyncMessage(answer, {
-    file: new File(tmpFile)
+    file: File.createFromNsIFile(tmpFile)
   });
 }
 
 addMessageListener("file.open", function (e) {
   tmpFile = Cc["@mozilla.org/file/directory_service;1"]
               .getService(Ci.nsIDirectoryService)
               .QueryInterface(Ci.nsIProperties)
               .get('TmpD', Ci.nsIFile)
--- a/dom/base/test/script_postmessages_fileList.js
+++ b/dom/base/test/script_postmessages_fileList.js
@@ -4,17 +4,17 @@ Cu.importGlobalProperties(["File"]);
 addMessageListener("file.open", function () {
   var testFile = Cc["@mozilla.org/file/directory_service;1"]
                    .getService(Ci.nsIDirectoryService)
                    .QueryInterface(Ci.nsIProperties)
                    .get("ProfD", Ci.nsIFile);
   testFile.append("prefs.js");
 
   sendAsyncMessage("file.opened", {
-    file: new File(testFile)
+    file: File.createFromNsIFile(testFile)
   });
 });
 
 addMessageListener("dir.open", function () {
   var testFile = Cc["@mozilla.org/file/directory_service;1"]
                    .getService(Ci.nsIDirectoryService)
                    .QueryInterface(Ci.nsIProperties)
                    .get("ProfD", Ci.nsIFile);
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -518,12 +518,18 @@ public:
   // Allow converting to const sequences as needed
   operator const Sequence<T>&() const {
     return *reinterpret_cast<const Sequence<T>*>(this);
   }
 };
 
 } // namespace binding_detail
 
+// Enum to represent a system or non-system caller type.
+enum class CallerType {
+  System,
+  NonSystem
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_BindingDeclarations_h__
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1592,16 +1592,21 @@ DOMInterfaces = {
         # Keep this in sync with TestInterface
         'headerFile': 'TestExampleInterface-example.h',
         'register': False,
         'binaryNames': { 'methodRenamedFrom': 'methodRenamedTo',
                          'attributeGetterRenamedFrom': 'attributeGetterRenamedTo',
                          'attributeRenamedFrom': 'attributeRenamedTo' }
         },
 
+'TestExampleWorkerInterface' : {
+        'headerFile': 'TestExampleWorkerInterface-example.h',
+        'register': False,
+        },
+
 'TestExampleProxyInterface' : {
         'headerFile': 'TestExampleProxyInterface-example.h',
         'register': False
         },
 
 'TestDeprecatedInterface' : {
         # Keep this in sync with TestExampleInterface
         'headerFile': 'TestBindingHeader.h',
@@ -1629,16 +1634,21 @@ DOMInterfaces = {
         'register': False,
         },
 
 'TestProtoObjectHackedNamespace' : {
         'headerFile': 'TestBindingHeader.h',
         'register': False,
         },
 
+'TestWorkerExposedInterface' : {
+        'headerFile': 'TestBindingHeader.h',
+        'register': False,
+        },
+
 }
 
 # These are temporary, until they've been converted to use new DOM bindings
 def addExternalIface(iface, nativeType=None, headerFile=None,
                      notflattened=False):
     if iface in DOMInterfaces:
         raise Exception('Interface declared both as WebIDL and External interface')
     domInterface = {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6906,25 +6906,28 @@ def needScopeObject(returnType, argument
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable whose name is given by the |object| argument.
 
     needsSubjectPrincipal is a boolean indicating whether the call should
     receive the subject nsIPrincipal as argument.
 
+    needsCallerType is a boolean indicating whether the call should receive
+    a PrincipalType for the caller.
+
     isFallible is a boolean indicating whether the call should be fallible.
 
     resultVar: If the returnType is not void, then the result of the call is
     stored in a C++ variable named by resultVar. The caller is responsible for
     declaring the result variable. If the caller doesn't care about the result
     value, resultVar can be omitted.
     """
-    def __init__(self, isFallible, needsSubjectPrincipal, arguments, argsPre,
-                 returnType, extendedAttributes, descriptor,
+    def __init__(self, isFallible, needsSubjectPrincipal, needsCallerType,
+                 arguments, argsPre, returnType, extendedAttributes, descriptor,
                  nativeMethodName, static, object="self", argsPost=[],
                  resultVar=None):
         CGThing.__init__(self)
 
         result, resultOutParam, resultRooter, resultArgs, resultConversion = \
             getRetvalDeclarationForType(returnType, descriptor)
 
         args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
@@ -6976,16 +6979,19 @@ class CGCallGenerator(CGThing):
                 args.append(CGGeneric(resultVar))
             else:
                 assert resultOutParam == "ptr"
                 args.append(CGGeneric("&" + resultVar))
 
         if needsSubjectPrincipal:
             args.append(CGGeneric("subjectPrincipal"))
 
+        if needsCallerType:
+            args.append(CGGeneric("callerType"))
+
         if isFallible:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
         call = CGGeneric(nativeMethodName)
@@ -7042,16 +7048,34 @@ class CGCallGenerator(CGThing):
                     """
                     $*{getPrincipal}
                     // Initializing a nonnull is pretty darn annoying...
                     NonNull<nsIPrincipal> subjectPrincipal;
                     subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
                     """,
                     getPrincipal=getPrincipal)))
 
+        if needsCallerType:
+            # Note that we do not want to use
+            # IsCallerChrome/ThreadsafeIsCallerChrome directly because those
+            # will pull in the check for UniversalXPConnect, which we ideally
+            # don't want to have in the new thing we're doing here.  If not
+            # NS_IsMainThread(), though, we'll go ahead and call
+            # ThreasafeIsCallerChrome(), since that won't mess with
+            # UnivesalXPConnect and we don't want to worry about the right
+            # worker includes here.
+            callerCheck = CGGeneric("callerType = nsContentUtils::IsSystemPrincipal(nsContentUtils::SubjectPrincipal()) ? CallerType::System : CallerType::NonSystem;\n")
+            if descriptor.interface.isExposedInAnyWorker():
+                callerCheck = CGIfElseWrapper(
+                    "NS_IsMainThread()",
+                    callerCheck,
+                    CGGeneric("callerType = nsContentUtils::ThreadsafeIsCallerChrome() ? CallerType::System : CallerType::NonSystem;\n"));
+            self.cgRoot.prepend(callerCheck)
+            self.cgRoot.prepend(CGGeneric("CallerType callerType;\n"))
+
         if isFallible:
             self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
             self.cgRoot.append(CGGeneric(dedent(
                 """
                 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
                   return false;
                 }
                 """)))
@@ -7214,16 +7238,18 @@ def wrapArgIntoCurrentCompartment(arg, v
     if wrap and isOptional:
         wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
     return wrap
 
 
 def needsContainsHack(m):
     return m.getExtendedAttribute("ReturnValueNeedsContainsHack")
 
+def needsCallerType(m):
+    return m.getExtendedAttribute("NeedsCallerType")
 
 class CGPerSignatureCall(CGThing):
     """
     This class handles the guts of generating code for a particular
     call signature.  A call signature consists of four things:
 
     1) A return type, which can be None to indicate that there is no
        actual return value (e.g. this is an attribute setter) or an
@@ -7513,16 +7539,17 @@ class CGPerSignatureCall(CGThing):
             else:
                 cgThings.append(CGIterableMethodGenerator(descriptor,
                                                           idlNode.maplikeOrSetlikeOrIterable,
                                                           idlNode.identifier.name))
         else:
             cgThings.append(CGCallGenerator(
                 self.isFallible(),
                 idlNode.getExtendedAttribute('NeedsSubjectPrincipal'),
+                needsCallerType(idlNode),
                 self.getArguments(), argsPre, returnType,
                 self.extendedAttributes, descriptor,
                 nativeMethodName,
                 static, argsPost=argsPost, resultVar=resultVar))
 
         if useCounterName:
             # Generate a telemetry call for when [UseCounter] is used.
             code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
@@ -13633,17 +13660,18 @@ class CGBindingRoot(CGThing):
         bindingHeaders["mozilla/Preferences.h"] = any(
             descriptorRequiresPreferences(d) for d in descriptors)
         bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
             d.concrete and d.proxy for d in descriptors)
 
         def descriptorHasChromeOnly(desc):
             ctor = desc.interface.ctor()
 
-            return (any(isChromeOnly(a) or needsContainsHack(a)
+            return (any(isChromeOnly(a) or needsContainsHack(a) or
+                        needsCallerType(a)
                         for a in desc.interface.members) or
                     desc.interface.getExtendedAttribute("ChromeOnly") is not None or
                     # JS-implemented interfaces with an interface object get a
                     # chromeonly _create method.  And interfaces with an
                     # interface object might have a ChromeOnly constructor.
                     (desc.interface.hasInterfaceObject() and
                      (desc.interface.isJSImplemented() or
                       (ctor and isChromeOnly(ctor)))) or
@@ -14076,17 +14104,24 @@ class CGNativeMember(ClassMethod):
                                  "aRetVal"))
         elif returnType.isAny():
             args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
         elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
             args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
 
         # And the nsIPrincipal
         if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
-            args.append(Argument("nsIPrincipal&", "aPrincipal"))
+            # Cheat and assume self.descriptorProvider is a descriptor
+            if self.descriptorProvider.interface.isExposedInAnyWorker():
+                args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal"))
+            else:
+                args.append(Argument("nsIPrincipal&", "aPrincipal"))
+        # And the caller type, if desired.
+        if needsCallerType(self.member):
+            args.append(Argument("CallerType", "aCallerType"))
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
--- a/dom/bindings/docs/index.rst
+++ b/dom/bindings/docs/index.rst
@@ -78,17 +78,18 @@ Parser unit tests
    The current mechanism for this is ``mach webidl-parser-test``.
 
 Mochitests
    There are various mochitests under ``dom/bindings/test``. They should
    be runnable through the standard mechanisms.
 
 Working with test interfaces
    ``TestExampleGenBinding.cpp`` calls into methods from the
-   ``TestExampleInterface`` and ``TestExampleProxyInterface`` interfaces.
+   ``TestExampleInterface``, ``TestExampleProxyInterface``,
+   and ``TestExampleWorkerInterface`` interfaces.
    These interfaces need to be generated as part of the build. These
    interfaces should not be exported or packaged.
 
    There is a ``compiletests`` make target in ``dom/bindings`` that
    isn't part of the build that facilitates turnkey code generation
    and test file compilation.
 
 Minimal rebuilds
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -4221,16 +4221,17 @@ class IDLAttribute(IDLInterfaceMember):
               identifier == "GetterThrows" or
               identifier == "ChromeOnly" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "Frozen" or
               identifier == "NewObject" or
               identifier == "UnsafeInPrerendering" or
               identifier == "NeedsSubjectPrincipal" or
+              identifier == "NeedsCallerType" or
               identifier == "ReturnValueNeedsContainsHack" or
               identifier == "BinaryName"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
@@ -4939,16 +4940,17 @@ class IDLMethod(IDLInterfaceMember, IDLS
               identifier == "ChromeOnly" or
               identifier == "UnsafeInPrerendering" or
               identifier == "Pref" or
               identifier == "Deprecated" or
               identifier == "Func" or
               identifier == "SecureContext" or
               identifier == "BinaryName" or
               identifier == "NeedsSubjectPrincipal" or
+              identifier == "NeedsCallerType" or
               identifier == "StaticClassOverride"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -931,16 +931,19 @@ public:
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
   void NeedsSubjectPrincipalMethod(nsIPrincipal&);
   bool NeedsSubjectPrincipalAttr(nsIPrincipal&);
   void SetNeedsSubjectPrincipalAttr(bool, nsIPrincipal&);
+  void NeedsCallerTypeMethod(CallerType);
+  bool NeedsCallerTypeAttr(CallerType);
+  void SetNeedsCallerTypeAttr(bool, CallerType);
   int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
                             TestInterface*, const Dict&, double,
                             const Optional<float>&);
 
   void SetDashed_attribute(int8_t);
   int8_t Dashed_attribute();
   void Dashed_method();
@@ -1400,12 +1403,29 @@ public:
 };
 
 class TestRenamedNamespace {
 };
 
 class TestProtoObjectHackedNamespace {
 };
 
+class TestWorkerExposedInterface : public nsISupports,
+                                   public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // We need a GetParentObject to make binding codegen happy
+  nsISupports* GetParentObject();
+
+  void NeedsSubjectPrincipalMethod(Maybe<nsIPrincipal*>);
+  bool NeedsSubjectPrincipalAttr(Maybe<nsIPrincipal*>);
+  void SetNeedsSubjectPrincipalAttr(bool, Maybe<nsIPrincipal*>);
+  void NeedsCallerTypeMethod(CallerType);
+  bool NeedsCallerTypeAttr(CallerType);
+  void SetNeedsCallerTypeAttr(bool, CallerType);
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* TestBindingHeader_h */
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -940,16 +940,18 @@ interface TestInterface {
   [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
   [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+  [NeedsCallerType] void needsCallerTypeMethod();
+  [NeedsCallerType] attribute boolean needsCallerTypeAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
 
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
@@ -1247,8 +1249,16 @@ namespace TestRenamedNamespace {
 [ProtoObjectHack]
 namespace TestProtoObjectHackedNamespace {
 };
 
 [SecureContext]
 interface TestSecureContextInterface {
   static void alsoSecureContext();
 };
+
+[Exposed=(Window,Worker)]
+interface TestWorkerExposedInterface {
+  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+  [NeedsCallerType] void needsCallerTypeMethod();
+  [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+};
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -770,16 +770,18 @@ interface TestExampleInterface {
   [PutForwards=writableByte, LenientThis] readonly attribute TestExampleInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestExampleInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
   [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
   [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+  [NeedsCallerType] void needsCallerTypeMethod();
+  [NeedsCallerType] attribute boolean needsCallerTypeAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
@@ -794,8 +796,16 @@ interface TestExampleInterface {
 interface TestExampleProxyInterface {
   getter long longIndexedGetter(unsigned long ix);
   setter creator void longIndexedSetter(unsigned long y, long z);
   stringifier DOMString myStringifier();
   getter short shortNameGetter(DOMString nom);
   deleter void (DOMString nomnom);
   setter creator void shortNamedSetter(DOMString me, short value);
 };
+
+[Exposed=(Window,Worker)]
+interface TestExampleWorkerInterface {
+  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
+  [NeedsCallerType] void needsCallerTypeMethod();
+  [NeedsCallerType] attribute boolean needsCallerTypeAttr;
+};
--- a/dom/bindings/test/moz.build
+++ b/dom/bindings/test/moz.build
@@ -34,16 +34,17 @@ PREPROCESSED_TEST_WEBIDL_FILES += [
     'TestCodeGen.webidl',
     'TestExampleGen.webidl',
     'TestJSImplGen.webidl',
 ]
 
 WEBIDL_EXAMPLE_INTERFACES += [
     'TestExampleInterface',
     'TestExampleProxyInterface',
+    'TestExampleWorkerInterface',
 ]
 
 # Bug 932082 tracks having bindings use namespaced includes.
 LOCAL_INCLUDES += [
     '!/dist/include/mozilla/dom',
 ]
 
 LOCAL_INCLUDES += [
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -59,20 +59,28 @@ WebGL2Context::CopyBufferSubData(GLenum 
     };
 
     if (!fnValidateOffsetSize("read", readOffset, readBuffer) ||
         !fnValidateOffsetSize("write", writeOffset, writeBuffer))
     {
         return;
     }
 
-    if (readBuffer == writeBuffer &&
-        !ValidateDataRanges(readOffset, writeOffset, size, funcName))
-    {
-        return;
+    if (readBuffer == writeBuffer) {
+        MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(readOffset) + size).isValid());
+        MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
+
+        const bool separate = (readOffset + size <= writeOffset ||
+                               writeOffset + size <= readOffset);
+        if (!separate) {
+            ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and"
+                              " [writeOffset, writeOffset + size) overlap",
+                              funcName);
+            return;
+        }
     }
 
     const auto& readType = readBuffer->Content();
     const auto& writeType = writeBuffer->Content();
     MOZ_ASSERT(readType != WebGLBuffer::Kind::Undefined);
     MOZ_ASSERT(writeType != WebGLBuffer::Kind::Undefined);
     if (writeType != readType) {
         ErrorInvalidOperation("%s: Can't copy %s data to %s data.",
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -84,17 +84,17 @@ WebGL2Context::FramebufferTextureLayer(G
         fb = mBoundReadFramebuffer;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (!fb)
-        return ErrorInvalidOperation("%a: Xannot modify framebuffer 0.");
+        return ErrorInvalidOperation("%s: Cannot modify framebuffer 0.", funcName);
 
     fb->FramebufferTextureLayer(funcName, attachment, texture, level, layer);
 }
 
 JS::Value
 WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                  GLenum target,
                                                  GLenum attachment,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1494,18 +1494,16 @@ protected:
     // Validation functions (implemented in WebGLContextValidate.cpp)
     bool InitAndValidateGL(FailureReason* const out_failReason);
 
     bool ValidateBlendEquationEnum(GLenum cap, const char* info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char* info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
                                              const char* info);
-    bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
-    bool ValidateTextureTargetEnum(GLenum target, const char* info);
     bool ValidateComparisonEnum(GLenum target, const char* info);
     bool ValidateStencilOpEnum(GLenum action, const char* info);
     bool ValidateFaceEnum(GLenum face, const char* info);
     bool ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType,
                               WebGLTexImageFunc func, WebGLTexDimensions dims);
     bool ValidateDrawModeEnum(GLenum mode, const char* info);
     bool ValidateAttribIndex(GLuint index, const char* info);
     bool ValidateAttribPointer(bool integerMode, GLuint index, GLint size, GLenum type,
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -117,59 +117,16 @@ WebGLContext::ValidateBlendFuncEnumsComp
         ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in"
                               " the WebGL 1.0 spec", info);
         return false;
     }
 
     return true;
 }
 
-/**
- * Check data ranges [readOffset, readOffset + size] and [writeOffset,
- * writeOffset + size] for overlap.
- *
- * It is assumed that offset and size have already been validated.
- */
-bool
-WebGLContext::ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info)
-{
-    MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(readOffset) + size).isValid());
-    MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
-
-    bool separate = (readOffset + size < writeOffset || writeOffset + size < readOffset);
-    if (!separate) {
-        ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, "
-                          "writeOffset + size) overlap", info);
-    }
-
-    return separate;
-}
-
-bool
-WebGLContext::ValidateTextureTargetEnum(GLenum target, const char* info)
-{
-    switch (target) {
-    case LOCAL_GL_TEXTURE_2D:
-    case LOCAL_GL_TEXTURE_CUBE_MAP:
-        return true;
-
-    case LOCAL_GL_TEXTURE_3D:
-        if (IsWebGL2())
-            return true;
-
-        break;
-
-    default:
-        break;
-    }
-
-    ErrorInvalidEnumInfo(info, target);
-    return false;
-}
-
 bool
 WebGLContext::ValidateComparisonEnum(GLenum target, const char* info)
 {
     switch (target) {
     case LOCAL_GL_NEVER:
     case LOCAL_GL_LESS:
     case LOCAL_GL_LEQUAL:
     case LOCAL_GL_GREATER:
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -515,17 +515,17 @@ WebGLProgram::BindAttribLocation(GLuint 
 
     if (loc >= mContext->MaxVertexAttribs()) {
         mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than"
                                     " MAX_VERTEX_ATTRIBS.");
         return;
     }
 
     if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
-        mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the  location of a"
+        mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the location of a"
                                         " name that starts with 'gl_'.");
         return;
     }
 
     NS_LossyConvertUTF16toASCII asciiName(name);
 
     auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
 
--- a/dom/filesystem/compat/tests/script_entries.js
+++ b/dom/filesystem/compat/tests/script_entries.js
@@ -31,17 +31,17 @@ addMessageListener("entries.open", funct
   file2.append('bar.txt');
   file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
 
   var dir2 = dir1.clone();
   dir2.append('subsubdir');
   dir2.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
 
   sendAsyncMessage("entries.opened", {
-    data: [ new Directory(tmpDir.path), new File(tmpFile) ]
+    data: [ new Directory(tmpDir.path), File.createFromNsIFile(tmpFile) ]
   });
 });
 
 addMessageListener("entries.delete", function(e) {
   tmpFile.remove(true);
   tmpDir.remove(true);
   sendAsyncMessage("entries.deleted");
 });
--- a/dom/filesystem/tests/script_fileList.js
+++ b/dom/filesystem/tests/script_fileList.js
@@ -119,11 +119,11 @@ addMessageListener("dir.open", function 
 addMessageListener("file.open", function (e) {
   var testFile = Cc["@mozilla.org/file/directory_service;1"]
                    .getService(Ci.nsIDirectoryService)
                    .QueryInterface(Ci.nsIProperties)
                    .get("ProfD", Ci.nsIFile);
   testFile.append("prefs.js");
 
   sendAsyncMessage("file.opened", {
-    file: new File(testFile)
+    file: File.createFromNsIFile(testFile)
   });
 });
--- a/dom/html/test/formSubmission_chrome.js
+++ b/dom/html/test/formSubmission_chrome.js
@@ -1,6 +1,6 @@
 var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 Cu.importGlobalProperties(["File"]);
 
 addMessageListener("files.open", function (message) {
-  sendAsyncMessage("files.opened", message.map(path => new File(path)));
+  sendAsyncMessage("files.opened", message.map(path => File.createFromFileName(path)));
 });
--- a/dom/html/test/simpleFileOpener.js
+++ b/dom/html/test/simpleFileOpener.js
@@ -9,17 +9,17 @@ addMessageListener("file.open", function
       file = Cc["@mozilla.org/file/directory_service;1"]
                .getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
       file.append(stem);
       file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
     }
     sendAsyncMessage("file.opened", {
       fullPath: file.path,
       leafName: file.leafName,
-      domFile: new File(file),
+      domFile: File.createFromNsIFile(file),
     });
   } catch(e) {
     sendAsyncMessage("fail", e.toString());
   }
 });
 
 addMessageListener("file.remove", function () {
   try {
--- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
+++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
@@ -632,17 +632,17 @@ var SpecialPowers = {
       }
         let outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
         outStream.init(testFile, 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
                        filePerms, 0);
         if (request.data) {
           outStream.write(request.data, request.data.length);
           outStream.close();
         }
-        filePaths.push(new File(testFile.path, request.options));
+        filePaths.push(File.createFromFileName(testFile.path, request.options));
         createdFiles.push(testFile);
     });
 
     setTimeout(function () {
       callback(filePaths);
     }, 0);
   },
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -195,16 +195,17 @@ private:
     RefPtr<MediaDataDecoder> mDecoder;
     MozPromiseRequestHolder<TokenPromise> mTokenPromise;
     MozPromiseRequestHolder<InitPromise> mInitPromise;
     ~Data()
     {
       mTokenPromise.DisconnectIfExists();
       mInitPromise.DisconnectIfExists();
       if (mDecoder) {
+        mDecoder->Flush();
         mDecoder->Shutdown();
       }
     }
   } mAudio, mVideo;
 
   void RunStage(TrackType aTrack);
   MediaResult DoCreateDecoder(TrackType aTrack);
   void DoInitDecoder(TrackType aTrack);
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -6,16 +6,17 @@
 #include "RemoteVideoDecoder.h"
 #include "VideoDecoderChild.h"
 #include "VideoDecoderManagerChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "base/thread.h"
 #include "MediaInfo.h"
 #include "MediaPrefs.h"
 #include "ImageContainer.h"
+#include "mozilla/layers/SynchronousTask.h"
 
 namespace mozilla {
 namespace dom {
 
 using base::Thread;
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
@@ -91,21 +92,24 @@ RemoteVideoDecoder::Drain()
     self->mActor->Drain();
   }), NS_DISPATCH_NORMAL);
 }
 
 void
 RemoteVideoDecoder::Shutdown()
 {
   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  SynchronousTask task("Shutdown");
   RefPtr<RemoteVideoDecoder> self = this;
-  VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([self]() {
+  VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([&]() {
+    AutoCompleteTask complete(&task);
     MOZ_ASSERT(self->mActor);
     self->mActor->Shutdown();
   }), NS_DISPATCH_NORMAL);
+  task.Wait();
 }
 
 bool
 RemoteVideoDecoder::IsHardwareAccelerated(nsACString& aFailureReason) const
 {
   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
   return mActor->IsHardwareAccelerated(aFailureReason);
 }
@@ -155,18 +159,18 @@ RemoteDecoderModule::CreateVideoDecoder(
   }
 
   MediaDataDecoderCallback* callback = aParams.mCallback;
   MOZ_ASSERT(callback->OnReaderTaskQueue());
   RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder(callback);
 
   VideoInfo info = aParams.VideoConfig();
 
-  RefPtr<layers::KnowsCompositor> knowsCompositor = aParams.mKnowsCompositor;
+  TextureFactoryIdentifier ident = aParams.mKnowsCompositor->GetTextureFactoryIdentifier();
   VideoDecoderManagerChild::GetManagerThread()->Dispatch(NS_NewRunnableFunction([=]() {
-    object->mActor->InitIPDL(callback, info, knowsCompositor);
+    object->mActor->InitIPDL(callback, info, ident);
   }), NS_DISPATCH_NORMAL);
 
   return object.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -47,41 +47,49 @@ VideoDecoderChild::RecvOutput(const Vide
   RefPtr<VideoData> video = VideoData::CreateFromImage(info,
                                                        aData.base().offset(),
                                                        aData.base().time(),
                                                        aData.base().duration(),
                                                        image,
                                                        aData.base().keyframe(),
                                                        aData.base().timecode(),
                                                        IntRect());
-  mCallback->Output(video);
+  if (mCallback) {
+    mCallback->Output(video);
+  }
   return true;
 }
 
 bool
 VideoDecoderChild::RecvInputExhausted()
 {
   AssertOnManagerThread();
-  mCallback->InputExhausted();
+  if (mCallback) {
+    mCallback->InputExhausted();
+  }
   return true;
 }
 
 bool
 VideoDecoderChild::RecvDrainComplete()
 {
   AssertOnManagerThread();
-  mCallback->DrainComplete();
+  if (mCallback) {
+    mCallback->DrainComplete();
+  }
   return true;
 }
 
 bool
 VideoDecoderChild::RecvError(const nsresult& aError)
 {
   AssertOnManagerThread();
-  mCallback->Error(aError);
+  if (mCallback) {
+    mCallback->Error(aError);
+  }
   return true;
 }
 
 bool
 VideoDecoderChild::RecvInitComplete(const bool& aHardware, const nsCString& aHardwareReason)
 {
   AssertOnManagerThread();
   mInitPromise.Resolve(TrackInfo::kVideoTrack, __func__);
@@ -102,45 +110,45 @@ VideoDecoderChild::RecvInitFailed(const 
 void
 VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
     // Defer reporting an error until we've recreated the manager so that
     // it'll be safe for MediaFormatReader to recreate decoders
     RefPtr<VideoDecoderChild> ref = this;
     GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() {
-      if (ref->mInitialized) {
+      if (ref->mInitialized && ref->mCallback) {
         ref->mCallback->Error(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER);
       } else {
         ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER, __func__);
       }
     }));
   }
   mCanSend = false;
 }
 
 void
 VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback,
                             const VideoInfo& aVideoInfo,
-                            layers::KnowsCompositor* aKnowsCompositor)
+                            const layers::TextureFactoryIdentifier& aIdentifier)
 {
   RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton();
   // If the manager isn't available, then don't initialize mIPDLSelfRef and leave
   // us in an error state. We'll then immediately reject the promise when Init()
   // is called and the caller can try again. Hopefully by then the new manager is
   // ready, or we've notified the caller of it being no longer available.
   // If not, then the cycle repeats until we're ready.
   if (!manager || !manager->CanSend()) {
     return;
   }
 
   mIPDLSelfRef = this;
   mCallback = aCallback;
   mVideoInfo = aVideoInfo;
-  mKnowsCompositor = aKnowsCompositor;
+  mIdentifier = aIdentifier;
   if (manager->SendPVideoDecoderConstructor(this)) {
     mCanSend = true;
   }
 }
 
 void
 VideoDecoderChild::DestroyIPDL()
 {
@@ -164,17 +172,17 @@ VideoDecoderChild::Init()
 
   if (!mIPDLSelfRef) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
       NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
   }
   // If we failed to send this, then we'll still resolve the Init promise
   // as ActorDestroy handles it.
   if (mCanSend) {
-    SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier());
+    SendInit(mVideoInfo, mIdentifier);
   }
   return mInitPromise.Ensure(__func__);
 }
 
 void
 VideoDecoderChild::Input(MediaRawData* aSample)
 {
   AssertOnManagerThread();
@@ -220,16 +228,17 @@ VideoDecoderChild::Drain()
     SendDrain();
   }
 }
 
 void
 VideoDecoderChild::Shutdown()
 {
   AssertOnManagerThread();
+  mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   if (mCanSend) {
     SendShutdown();
   }
   mInitialized = false;
 }
 
 bool
 VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
--- a/dom/media/ipc/VideoDecoderChild.h
+++ b/dom/media/ipc/VideoDecoderChild.h
@@ -41,17 +41,17 @@ public:
   void Drain();
   void Shutdown();
   bool IsHardwareAccelerated(nsACString& aFailureReason) const;
   void SetSeekThreshold(const media::TimeUnit& aTime);
 
   MOZ_IS_CLASS_INIT
   void InitIPDL(MediaDataDecoderCallback* aCallback,
                 const VideoInfo& aVideoInfo,
-                layers::KnowsCompositor* aKnowsCompositor);
+                const layers::TextureFactoryIdentifier& aIdentifier);
   void DestroyIPDL();
 
   // Called from IPDL when our actor has been destroyed
   void IPDLActorDestroyed();
 
   VideoDecoderManagerChild* GetManager();
 
 private:
@@ -62,17 +62,17 @@ private:
   RefPtr<VideoDecoderChild> mIPDLSelfRef;
   RefPtr<nsIThread> mThread;
 
   MediaDataDecoderCallback* mCallback;
 
   MozPromiseHolder<MediaDataDecoder::InitPromise> mInitPromise;
 
   VideoInfo mVideoInfo;
-  RefPtr<layers::KnowsCompositor> mKnowsCompositor;
+  layers::TextureFactoryIdentifier mIdentifier;
   nsCString mHardwareAcceleratedReason;
   bool mCanSend;
   bool mInitialized;
   bool mIsHardwareAccelerated;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -12,16 +12,17 @@
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "nsIEventTarget.h"
 #include "nsThreadUtils.h"
 #include "ImageContainer.h"
 #include "mozilla/layers/VideoBridgeChild.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/SyncRunnable.h"
 
 #if XP_WIN
 #include <objbase.h>
 #endif
 
 namespace mozilla {
 namespace dom {
 
@@ -96,24 +97,33 @@ VideoDecoderManagerParent::StartupThread
   observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 }
 
 void
 VideoDecoderManagerParent::ShutdownThreads()
 {
   sManagerTaskQueue->BeginShutdown();
   sManagerTaskQueue->AwaitShutdownAndIdle();
+  sManagerTaskQueue = nullptr;
 
-  sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() {
-    layers::VideoBridgeChild::Shutdown();
-  }), NS_DISPATCH_SYNC);
   sVideoDecoderManagerThread->Shutdown();
   sVideoDecoderManagerThread = nullptr;
 }
 
+void
+VideoDecoderManagerParent::ShutdownVideoBridge()
+{
+  if (sVideoDecoderManagerThread) {
+    RefPtr<Runnable> task = NS_NewRunnableFunction([]() {
+      VideoBridgeChild::Shutdown();
+    });
+    SyncRunnable::DispatchToThread(sVideoDecoderManagerThread, task);
+  }
+}
+
 bool
 VideoDecoderManagerParent::OnManagerThread()
 {
   return NS_GetCurrentThread() == sVideoDecoderManagerThread;
 }
 
 bool
 VideoDecoderManagerParent::CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint)
--- a/dom/media/ipc/VideoDecoderManagerParent.h
+++ b/dom/media/ipc/VideoDecoderManagerParent.h
@@ -19,16 +19,18 @@ public:
   static bool CreateForContent(Endpoint<PVideoDecoderManagerParent>&& aEndpoint);
 
   // Can be called from any thread
   SurfaceDescriptorGPUVideo StoreImage(layers::Image* aImage, layers::TextureClient* aTexture);
 
   static void StartupThreads();
   static void ShutdownThreads();
 
+  static void ShutdownVideoBridge();
+
   bool OnManagerThread();
 
 protected:
   PVideoDecoderParent* AllocPVideoDecoderParent() override;
   bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
 
   bool RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override;
   bool RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override;
--- a/dom/media/ipc/VideoDecoderParent.cpp
+++ b/dom/media/ipc/VideoDecoderParent.cpp
@@ -132,33 +132,37 @@ VideoDecoderParent::RecvInput(const Medi
   mDecoder->Input(data);
   return true;
 }
 
 bool
 VideoDecoderParent::RecvFlush()
 {
   MOZ_ASSERT(!mDestroyed);
-  mDecoder->Flush();
+  if (mDecoder) {
+    mDecoder->Flush();
+  }
   return true;
 }
 
 bool
 VideoDecoderParent::RecvDrain()
 {
   MOZ_ASSERT(!mDestroyed);
   mDecoder->Drain();
   return true;
 }
 
 bool
 VideoDecoderParent::RecvShutdown()
 {
   MOZ_ASSERT(!mDestroyed);
-  mDecoder->Shutdown();
+  if (mDecoder) {
+    mDecoder->Shutdown();
+  }
   mDecoder = nullptr;
   return true;
 }
 
 bool
 VideoDecoderParent::RecvSetSeekThreshold(const int64_t& aTime)
 {
   MOZ_ASSERT(!mDestroyed);
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -25,16 +25,17 @@
 #include "IMFYCbCrImage.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/Telemetry.h"
 #include "nsPrintfCString.h"
 #include "MediaTelemetryConstants.h"
 #include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place.
 #include "MP4Decoder.h"
 #include "VPXDecoder.h"
+#include "mozilla/SyncRunnable.h"
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 using mozilla::layers::Image;
 using mozilla::layers::IMFYCbCrImage;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
 
@@ -366,17 +367,19 @@ WMFVideoMFTManager::InitializeDXVA(bool 
     new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9
                                           : backend,
                                mKnowsCompositor,
                                mDXVAFailureReason);
 
   if (NS_IsMainThread()) {
     event->Run();
   } else {
-    NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+    // This logic needs to run on the main thread
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    mozilla::SyncRunnable::DispatchToThread(mainThread, event);
   }
   mDXVA2Manager = event->mDXVA2Manager;
 
   return mDXVA2Manager != nullptr;
 }
 
 bool
 WMFVideoMFTManager::ValidateVideoInfo()
@@ -602,17 +605,19 @@ WMFVideoMFTManager::CanUseDXVA(IMFMediaT
   // The supports config check must be done on the main thread since we have
   // a crash guard protecting it.
   RefPtr<SupportsConfigEvent> event =
     new SupportsConfigEvent(mDXVA2Manager, aType, framerate);
 
   if (NS_IsMainThread()) {
     event->Run();
   } else {
-    NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
+    // This logic needs to run on the main thread
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    mozilla::SyncRunnable::DispatchToThread(mainThread, event);
   }
 
   return event->mSupportsConfig;
 }
 
 HRESULT
 WMFVideoMFTManager::ConfigureVideoFrameGeometry()
 {
--- a/dom/webidl/File.webidl
+++ b/dom/webidl/File.webidl
@@ -6,22 +6,16 @@
  * The origin of this IDL file is
  * https://w3c.github.io/FileAPI/#file
  */
 
 interface nsIFile;
 
 [Constructor(sequence<BlobPart> fileBits,
              USVString fileName, optional FilePropertyBag options),
-
- // These constructors are just for chrome callers:
- Constructor(Blob fileBits, optional ChromeFilePropertyBag options),
- Constructor(nsIFile fileBits, optional ChromeFilePropertyBag options),
- Constructor(USVString fileBits, optional ChromeFilePropertyBag options),
-
  Exposed=(Window,Worker)]
 interface File : Blob {
   readonly attribute DOMString name;
 
   [GetterThrows]
   readonly attribute long long lastModified;
 };
 
@@ -40,9 +34,17 @@ partial interface File {
   [GetterThrows, Deprecated="FileLastModifiedDate"]
   readonly attribute Date lastModifiedDate;
 
   [BinaryName="path", Func="mozilla::dom::Directory::WebkitBlinkDirectoryPickerEnabled"]
   readonly attribute USVString webkitRelativePath;
 
   [GetterThrows, ChromeOnly]
   readonly attribute DOMString mozFullPath;
+
+  [ChromeOnly, Throws]
+  static File createFromNsIFile(nsIFile file,
+                                optional ChromeFilePropertyBag options);
+
+  [ChromeOnly, Throws]
+  static File createFromFileName(USVString fileName,
+                                 optional ChromeFilePropertyBag options);
 };
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -250,22 +250,16 @@ interface WindowModal {
   [Throws, Func="nsGlobalWindow::IsModalContentWindow", NeedsSubjectPrincipal]
   readonly attribute any dialogArguments;
 
   [Throws, Func="nsGlobalWindow::IsModalContentWindow", NeedsSubjectPrincipal]
   attribute any returnValue;
 };
 Window implements WindowModal;
 
-// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#self-caches
-partial interface Window {
-[Throws, Func="mozilla::dom::cache::CacheStorage::PrefEnabled", SameObject]
-readonly attribute CacheStorage caches;
-};
-
 // Mozilla-specific stuff
 partial interface Window {
   //[NewObject, Throws] CSSStyleDeclaration getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = "");
   [NewObject, Throws] CSSStyleDeclaration? getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = "");
 
   // Mozilla extensions
   /**
    * Method for scrolling this window by a number of lines.
--- a/dom/webidl/WindowOrWorkerGlobalScope.webidl
+++ b/dom/webidl/WindowOrWorkerGlobalScope.webidl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is:
  * https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin
  * https://fetch.spec.whatwg.org/#fetch-method
  * https://w3c.github.io/webappsec-secure-contexts/#monkey-patching-global-object
+ * https://w3c.github.io/ServiceWorker/#self-caches
  */
 
 // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface WindowOrWorkerGlobalScope {
   // XXXbz We don't implement 'origin' yet on either window or worker globals.
   // See bug 1306170.
   // [Replaceable] readonly attribute USVString origin;
@@ -55,16 +56,22 @@ partial interface WindowOrWorkerGlobalSc
 
 // http://w3c.github.io/IndexedDB/#factory-interface
 partial interface WindowOrWorkerGlobalScope {
    // readonly attribute IDBFactory indexedDB;
    [Throws]
    readonly attribute IDBFactory? indexedDB;
 };
 
+// https://w3c.github.io/ServiceWorker/#self-caches
+partial interface WindowOrWorkerGlobalScope {
+  [Throws, Func="mozilla::dom::cache::CacheStorage::PrefEnabled", SameObject]
+  readonly attribute CacheStorage caches;
+};
+
 // Mozilla extensions
 partial interface WindowOrWorkerGlobalScope {
   // Extensions to ImageBitmap bits.
   // Bug 1141979 - [FoxEye] Extend ImageBitmap with interfaces to access its
   // underlying image data
   //
   // Note:
   // Overloaded functions cannot have different "extended attributes",
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -30,22 +30,16 @@ interface WorkerGlobalScope : EventTarge
 
 partial interface WorkerGlobalScope {
   [Throws]
   void importScripts(DOMString... urls);
 
   readonly attribute WorkerNavigator navigator;
 };
 
-// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#self-caches
-partial interface WorkerGlobalScope {
-[Throws, Func="mozilla::dom::cache::CacheStorage::PrefEnabled", SameObject]
-readonly attribute CacheStorage caches;
-};
-
 WorkerGlobalScope implements GlobalCrypto;
 WorkerGlobalScope implements WindowOrWorkerGlobalScope;
 
 // Not implemented yet: bug 1072107.
 // WorkerGlobalScope implements FontFaceSource;
 
 // Mozilla extensions
 partial interface WorkerGlobalScope {
--- a/dom/workers/test/fileapi_chromeScript.js
+++ b/dom/workers/test/fileapi_chromeScript.js
@@ -12,17 +12,17 @@ function createFileWithData(fileData) {
   var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
   outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
                  0o666, 0);
   if (willDelete) {
     fileData = "some irrelevant test data\n";
   }
   outStream.write(fileData, fileData.length);
   outStream.close();
-  var domFile = new File(testFile);
+  var domFile = File.createFromNsIFile(testFile);
   if (willDelete) {
     testFile.remove(/* recursive: */ false);
   }
   return domFile;
 }
 
 addMessageListener("files.open", function (message) {
   sendAsyncMessage("files.opened", message.map(createFileWithData));
--- a/dom/workers/test/script_bug1301094.js
+++ b/dom/workers/test/script_bug1301094.js
@@ -5,11 +5,11 @@ addMessageListener("file.open", function
   var tmpFile = Cc["@mozilla.org/file/directory_service;1"]
                   .getService(Ci.nsIDirectoryService)
                   .QueryInterface(Ci.nsIProperties)
                   .get('TmpD', Ci.nsIFile)
   tmpFile.append('file.txt');
   tmpFile.createUnique(Components.interfaces.nsIFile.FILE_TYPE, 0o600);
 
   sendAsyncMessage("file.opened", {
-    data: new File(tmpFile)
+    data: File.createFromNsIFile(tmpFile)
   });
 });
--- a/editor/libeditor/CompositionTransaction.cpp
+++ b/editor/libeditor/CompositionTransaction.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CompositionTransaction.h"
 
 #include "mozilla/EditorBase.h"         // mEditorBase
+#include "mozilla/SelectionState.h"     // RangeUpdater
 #include "mozilla/dom/Selection.h"      // local var
 #include "mozilla/dom/Text.h"           // mTextNode
 #include "nsAString.h"                  // params
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsError.h"                    // for NS_SUCCEEDED, NS_FAILED, etc
 #include "nsIPresShell.h"               // nsISelectionController constants
 #include "nsRange.h"                    // local var
 #include "nsQueryObject.h"              // for do_QueryObject
@@ -20,25 +21,28 @@ namespace mozilla {
 using namespace dom;
 
 CompositionTransaction::CompositionTransaction(
                           Text& aTextNode,
                           uint32_t aOffset,
                           uint32_t aReplaceLength,
                           TextRangeArray* aTextRangeArray,
                           const nsAString& aStringToInsert,
-                          EditorBase& aEditorBase)
+                          EditorBase& aEditorBase,
+                          RangeUpdater* aRangeUpdater)
   : mTextNode(&aTextNode)
   , mOffset(aOffset)
   , mReplaceLength(aReplaceLength)
   , mRanges(aTextRangeArray)
   , mStringToInsert(aStringToInsert)
   , mEditorBase(aEditorBase)
+  , mRangeUpdater(aRangeUpdater)
   , mFixed(false)
 {
+  MOZ_ASSERT(mTextNode->TextLength() >= mOffset);
 }
 
 CompositionTransaction::~CompositionTransaction()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(CompositionTransaction, EditTransactionBase,
                                    mTextNode)
@@ -62,22 +66,42 @@ CompositionTransaction::DoTransaction()
   NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
 
   // Advance caret: This requires the presentation shell to get the selection.
   if (mReplaceLength == 0) {
     nsresult rv = mTextNode->InsertData(mOffset, mStringToInsert);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
+    mRangeUpdater->SelAdjInsertText(*mTextNode, mOffset, mStringToInsert);
   } else {
+    uint32_t replaceableLength = mTextNode->TextLength() - mOffset;
     nsresult rv =
       mTextNode->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
+    mRangeUpdater->SelAdjDeleteText(mTextNode, mOffset, mReplaceLength);
+    mRangeUpdater->SelAdjInsertText(*mTextNode, mOffset, mStringToInsert);
+
+    // If IME text node is multiple node, ReplaceData doesn't remove all IME
+    // text.  So we need remove remained text into other text node.
+    if (replaceableLength < mReplaceLength) {
+      int32_t remainLength = mReplaceLength - replaceableLength;
+      nsCOMPtr<nsINode> node = mTextNode->GetNextSibling();
+      while (node && node->IsNodeOfType(nsINode::eTEXT) &&
+             remainLength > 0) {
+        Text* text = static_cast<Text*>(node.get());
+        uint32_t textLength = text->TextLength();
+        text->DeleteData(0, remainLength);
+        mRangeUpdater->SelAdjDeleteText(text, 0, remainLength);
+        remainLength -= textLength;
+        node = node->GetNextSibling();
+      }
+    }
   }
 
   nsresult rv = SetSelectionForRanges();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
--- a/editor/libeditor/CompositionTransaction.h
+++ b/editor/libeditor/CompositionTransaction.h
@@ -12,16 +12,17 @@
 
 #define NS_IMETEXTTXN_IID \
   { 0xb391355d, 0x346c, 0x43d1, \
     { 0x85, 0xed, 0x9e, 0x65, 0xbe, 0xe7, 0x7e, 0x48 } }
 
 namespace mozilla {
 
 class EditorBase;
+class RangeUpdater;
 class TextRangeArray;
 
 namespace dom {
 class Text;
 } // namespace dom
 
 /**
  * CompositionTransaction stores all edit for a composition, i.e.,
@@ -30,30 +31,32 @@ class Text;
  * ranges and commit or cancel the composition.
  */
 class CompositionTransaction final : public EditTransactionBase
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMETEXTTXN_IID)
 
   /**
-   * @param aTextNode           The text content node.
+   * @param aTextNode           The start node of text content.
    * @param aOffset             The location in aTextNode to do the insertion.
    * @param aReplaceLength      The length of text to replace. 0 means not
    *                            replacing existing text.
    * @param aTextRangeArray     Clauses and/or caret information. This may be
    *                            null.
    * @param aString             The new text to insert.
    * @param aEditorBase         Used to get and set the selection.
+   * @param aRangeUpdater       The range updater
    */
   CompositionTransaction(dom::Text& aTextNode,
                          uint32_t aOffset, uint32_t aReplaceLength,
                          TextRangeArray* aTextRangeArray,
                          const nsAString& aString,
-                         EditorBase& aEditorBase);
+                         EditorBase& aEditorBase,
+                         RangeUpdater* aRangeUpdater);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CompositionTransaction,
                                            EditTransactionBase)
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_EDITTRANSACTIONBASE
 
@@ -84,16 +87,18 @@ private:
   RefPtr<TextRangeArray> mRanges;
 
   // The text to insert into mTextNode at mOffset.
   nsString mStringToInsert;
 
   // The editor, which is used to get the selection controller.
   EditorBase& mEditorBase;
 
+  RangeUpdater* mRangeUpdater;
+
   bool mFixed;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(CompositionTransaction, NS_IMETEXTTXN_IID)
 
 } // namespace mozilla
 
 #endif // #ifndef CompositionTransaction_h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -922,16 +922,17 @@ EditorBase::BeginPlaceHolderTransaction(
     // time to turn on the batch
     BeginUpdateViewBatch();
     mPlaceHolderTxn = nullptr;
     mPlaceHolderName = aName;
     RefPtr<Selection> selection = GetSelection();
     if (selection) {
       mSelState = new SelectionState();
       mSelState->SaveSelection(selection);
+      mRangeUpdater.RegisterSelectionState(*mSelState);
     }
   }
   mPlaceHolderBatch++;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -973,16 +974,17 @@ EditorBase::EndPlaceHolderTransaction()
     // cached for frame offset are Not available now
     if (selection) {
       selection->SetCanCacheFrameOffset(false);
     }
 
     if (mSelState) {
       // we saved the selection state, but never got to hand it to placeholder
       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
+      mRangeUpdater.DropSelectionState(*mSelState);
       delete mSelState;
       mSelState = nullptr;
     }
     // We might have never made a placeholder if no action took place.
     if (mPlaceHolderTxn) {
       nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
       if (plcTxn) {
         plcTxn->EndPlaceHolderBatch();
@@ -2431,18 +2433,18 @@ EditorBase::InsertTextImpl(const nsAStri
 nsresult
 EditorBase::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
                                        Text& aTextNode,
                                        int32_t aOffset,
                                        bool aSuppressIME)
 {
   RefPtr<EditTransactionBase> transaction;
   bool isIMETransaction = false;
-  int32_t replacedOffset = 0;
-  int32_t replacedLength = 0;
+  RefPtr<Text> insertedTextNode = &aTextNode;
+  int32_t insertedOffset = aOffset;
   // aSuppressIME is used when editor must insert text, yet this text is not
   // part of the current IME operation. Example: adjusting whitespace around an
   // IME insertion.
   if (ShouldHandleIMEComposition() && !aSuppressIME) {
     if (!mIMETextNode) {
       mIMETextNode = &aTextNode;
       mIMETextOffset = aOffset;
     }
@@ -2462,48 +2464,41 @@ EditorBase::InsertTextIntoTextNodeImpl(c
                          textRange.mStartOffset, textRange.Length());
     }
 
     transaction = CreateTxnForComposition(aStringToInsert);
     isIMETransaction = true;
     // All characters of the composition string will be replaced with
     // aStringToInsert.  So, we need to emulate to remove the composition
     // string.
-    replacedOffset = mIMETextOffset;
-    replacedLength = mIMETextLength;
+    insertedTextNode = mIMETextNode;
+    insertedOffset = mIMETextOffset;
     mIMETextLength = aStringToInsert.Length();
   } else {
     transaction = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset);
   }
 
   // Let listeners know what's up
   for (auto& listener : mActionListeners) {
     listener->WillInsertText(
-      static_cast<nsIDOMCharacterData*>(aTextNode.AsDOMNode()), aOffset,
-      aStringToInsert);
+      static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
+      insertedOffset, aStringToInsert);
   }
 
   // XXX We may not need these view batches anymore.  This is handled at a
   // higher level now I believe.
   BeginUpdateViewBatch();
   nsresult rv = DoTransaction(transaction);
   EndUpdateViewBatch();
 
-  if (replacedLength) {
-    mRangeUpdater.SelAdjDeleteText(
-      static_cast<nsIDOMCharacterData*>(aTextNode.AsDOMNode()),
-      replacedOffset, replacedLength);
-  }
-  mRangeUpdater.SelAdjInsertText(aTextNode, aOffset, aStringToInsert);
-
   // let listeners know what happened
   for (auto& listener : mActionListeners) {
     listener->DidInsertText(
-      static_cast<nsIDOMCharacterData*>(aTextNode.AsDOMNode()),
-      aOffset, aStringToInsert, rv);
+      static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
+      insertedOffset, aStringToInsert, rv);
   }
 
   // Added some cruft here for bug 43366.  Layout was crashing because we left
   // an empty text node lying around in the document.  So I delete empty text
   // nodes caused by IME.  I have to mark the IME transaction as "fixed", which
   // means that furure IME txns won't merge with it.  This is because we don't
   // want future IME txns trying to put their text into a node that is no
   // longer in the document.  This does not break undo/redo, because all these
@@ -2610,17 +2605,18 @@ EditorBase::NotifyDocumentListeners(
 }
 
 already_AddRefed<InsertTextTransaction>
 EditorBase::CreateTxnForInsertText(const nsAString& aStringToInsert,
                                    Text& aTextNode,
                                    int32_t aOffset)
 {
   RefPtr<InsertTextTransaction> transaction =
-    new InsertTextTransaction(aTextNode, aOffset, aStringToInsert, *this);
+    new InsertTextTransaction(aTextNode, aOffset, aStringToInsert, *this,
+                              &mRangeUpdater);
   return transaction.forget();
 }
 
 nsresult
 EditorBase::DeleteText(nsGenericDOMDataNode& aCharData,
                        uint32_t aOffset,
                        uint32_t aLength)
 {
@@ -4240,17 +4236,17 @@ EditorBase::CreateTxnForComposition(cons
 {
   MOZ_ASSERT(mIMETextNode);
   // During handling IME composition, mComposition must have been initialized.
   // TODO: We can simplify CompositionTransaction::Init() with TextComposition
   //       class.
   RefPtr<CompositionTransaction> transaction =
     new CompositionTransaction(*mIMETextNode, mIMETextOffset, mIMETextLength,
                                mComposition->GetRanges(), aStringToInsert,
-                               *this);
+                               *this, &mRangeUpdater);
   return transaction.forget();
 }
 
 NS_IMETHODIMP
 EditorBase::CreateTxnForAddStyleSheet(StyleSheet* aSheet,
                                       AddStyleSheetTransaction** aTransaction)
 {
   RefPtr<AddStyleSheetTransaction> transaction = new AddStyleSheetTransaction();
--- a/editor/libeditor/InsertTextTransaction.cpp
+++ b/editor/libeditor/InsertTextTransaction.cpp
@@ -1,35 +1,38 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "InsertTextTransaction.h"
 
 #include "mozilla/EditorBase.h"         // mEditorBase
+#include "mozilla/SelectionState.h"     // RangeUpdater
 #include "mozilla/dom/Selection.h"      // Selection local var
 #include "mozilla/dom/Text.h"           // mTextNode
 #include "nsAString.h"                  // nsAString parameter
 #include "nsDebug.h"                    // for NS_ASSERTION, etc.
 #include "nsError.h"                    // for NS_OK, etc.
 #include "nsQueryObject.h"              // for do_QueryObject
 
 namespace mozilla {
 
 using namespace dom;
 
 InsertTextTransaction::InsertTextTransaction(Text& aTextNode,
                                              uint32_t aOffset,
                                              const nsAString& aStringToInsert,
-                                             EditorBase& aEditorBase)
+                                             EditorBase& aEditorBase,
+                                             RangeUpdater* aRangeUpdater)
   : mTextNode(&aTextNode)
   , mOffset(aOffset)
   , mStringToInsert(aStringToInsert)
   , mEditorBase(aEditorBase)
+  , mRangeUpdater(aRangeUpdater)
 {
 }
 
 InsertTextTransaction::~InsertTextTransaction()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction, EditTransactionBase,
@@ -56,16 +59,17 @@ InsertTextTransaction::DoTransaction()
     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
     DebugOnly<nsresult> rv =
       selection->Collapse(mTextNode, mOffset + mStringToInsert.Length());
     NS_ASSERTION(NS_SUCCEEDED(rv),
                  "Selection could not be collapsed after insert");
   } else {
     // Do nothing - DOM Range gravity will adjust selection
   }
+  mRangeUpdater->SelAdjInsertText(*mTextNode, mOffset, mStringToInsert);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertTextTransaction::UndoTransaction()
 {
   return mTextNode->DeleteData(mOffset, mStringToInsert.Length());
--- a/editor/libeditor/InsertTextTransaction.h
+++ b/editor/libeditor/InsertTextTransaction.h
@@ -17,16 +17,18 @@ class nsITransaction;
 
 #define NS_INSERTTEXTTXN_IID \
 { 0x8c9ad77f, 0x22a7, 0x4d01, \
   { 0xb1, 0x59, 0x8a, 0x0f, 0xdb, 0x1d, 0x08, 0xe9 } }
 
 namespace mozilla {
 
 class EditorBase;
+class RangeUpdater;
+
 namespace dom {
 class Text;
 } // namespace dom
 
 /**
  * A transaction that inserts text into a content node.
  */
 class InsertTextTransaction final : public EditTransactionBase
@@ -34,19 +36,21 @@ class InsertTextTransaction final : publ
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INSERTTEXTTXN_IID)
 
   /**
    * @param aElement        The text content node.
    * @param aOffset         The location in aElement to do the insertion.
    * @param aString         The new text to insert.
    * @param aPresShell      Used to get and set the selection.
+   * @param aRangeUpdater   The range updater
    */
   InsertTextTransaction(dom::Text& aTextNode, uint32_t aOffset,
-                        const nsAString& aString, EditorBase& aEditorBase);
+                        const nsAString& aString, EditorBase& aEditorBase,
+                        RangeUpdater* aRangeUpdater);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InsertTextTransaction,
                                            EditTransactionBase)
 
   NS_DECL_EDITTRANSACTIONBASE
 
   NS_IMETHOD Merge(nsITransaction* aTransaction, bool* aDidMerge) override;
@@ -68,15 +72,17 @@ private:
   // The offset into mTextNode where the insertion is to take place.
   uint32_t mOffset;
 
   // The text to insert into mTextNode at mOffset.
   nsString mStringToInsert;
 
   // The editor, which we'll need to get the selection.
   EditorBase& mEditorBase;
+
+  RangeUpdater* mRangeUpdater;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(InsertTextTransaction, NS_INSERTTEXTTXN_IID)
 
 } // namespace mozilla
 
 #endif // #ifndef InsertTextTransaction_h
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -205,16 +205,18 @@ skip-if = os == 'android'
 subsuite = clipboard
 skip-if = toolkit == 'android'
 [test_bug1248128.html]
 [test_bug1250010.html]
 [test_bug1257363.html]
 [test_bug1248185.html]
 [test_bug1258085.html]
 [test_bug1268736.html]
+[test_bug1310912.html]
+skip-if = toolkit == 'android' # bug 1315898
 [test_bug1315065.html]
 
 [test_CF_HTML_clipboard.html]
 subsuite = clipboard
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_focus.html]
 [test_dom_input_event_on_htmleditor.html]
 skip-if = toolkit == 'android' # bug 1054087
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1310912.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1310912
+-->
+<html>
+<head>
+  <title>Test for Bug 1310912</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1310912">Mozilla Bug 1310912</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<div id="editable1" contenteditable="true">ABC</div>
+<pre id="test">
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  let elm = document.getElementById("editable1");
+
+  elm.focus();
+  let sel = window.getSelection();
+  sel.collapse(elm.childNodes[0], elm.textContent.length);
+
+  synthesizeCompositionChange({
+    composition: {
+      string: "DEF",
+      clauses: [
+        { length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE }
+      ]
+    },
+    caret: { start: 3, length: 0 }
+  });
+  ok(elm.textContent == "ABCDEF", "composing text should be set");
+
+  window.getSelection().getRangeAt(0).insertNode(document.createTextNode(""));
+  synthesizeCompositionChange({
+    composition: {
+      string: "GHI",
+      clauses: [
+        { length: 3, attr: COMPOSITION_ATTR_CONVERTED_CLAUSE }
+      ]
+    },
+    caret: { start: 0, length: 0 }
+  });
+  ok(elm.textContent == "ABCGHI", "composing text should be replaced");
+
+  window.getSelection().getRangeAt(0).insertNode(document.createTextNode(""));
+  synthesizeCompositionChange({
+    composition: {
+      string: "JKL",
+      clauses: [
+        { length: 3, attr: COMPOSITION_ATTR_CONVERTED_CLAUSE }
+      ]
+    },
+    caret: { start: 0, length: 0 }
+  });
+  ok(elm.textContent == "ABCJKL", "composing text should be replaced");
+
+  window.getSelection().getRangeAt(0).insertNode(document.createTextNode(""));
+  synthesizeCompositionChange({
+    composition: {
+      string: "MNO",
+      clauses: [
+        { length: 3, attr: COMPOSITION_ATTR_CONVERTED_CLAUSE }
+      ]
+    },
+    caret: { start: 1, length: 0 }
+  });
+  ok(elm.textContent == "ABCMNO", "composing text should be replaced");
+
+  window.getSelection().getRangeAt(0).insertNode(document.createTextNode(""));
+  synthesizeComposition({ type: "compositioncommitasis" });
+  ok(elm.textContent == "ABCMNO", "composing text should be committed");
+
+  synthesizeKey("Z", { accelKey: true });
+  ok(elm.textContent == "ABC", "text should be undoed");
+
+  synthesizeKey("Z", { accelKey: true, shiftKey: true });
+  ok(elm.textContent == "ABCMNO", "text should be redoed");
+
+  SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/ipc/CrashReporterClient.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "nsDebugImpl.h"
 #include "nsExceptionHandler.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
@@ -363,16 +364,17 @@ GPUParent::ActorDestroy(ActorDestroyReas
   // state.
   ProcessChild::QuickExit();
 #endif
 
   if (mVsyncBridge) {
     mVsyncBridge->Shutdown();
     mVsyncBridge = nullptr;
   }
+  dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
   Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
   DeviceManagerD3D9::Shutdown();
 #endif
   LayerTreeOwnerTracker::Shutdown();
   gfxVars::Shutdown();
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -789,17 +789,17 @@ DXGITextureHostD3D11::LockInternal()
 {
   if (!GetDevice()) {
     NS_WARNING("trying to lock a TextureHost without a D3D device");
     return false;
   }
 
   if (!mTextureSource) {
     if (!mTexture && !OpenSharedHandle()) {
-      gfxWindowsPlatform::GetPlatform()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
+      DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
       return false;
     }
 
     mTextureSource = new DataTextureSourceD3D11(mFormat, mCompositor, mTexture);
   }
 
   mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
 
@@ -1207,17 +1207,17 @@ SyncObjectD3D11::FinalizeFrame()
   if (!mD3D11Texture && mD3D11SyncedTextures.size()) {
     RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetContentDevice();
 
     hr = device->OpenSharedResource(mHandle, __uuidof(ID3D11Texture2D), (void**)(ID3D11Texture2D**)getter_AddRefs(mD3D11Texture));
 
     if (FAILED(hr) || !mD3D11Texture) {
       gfxCriticalError() << "Failed to D3D11 OpenSharedResource for frame finalization: " << hexa(hr);
 
-      if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+      if (DeviceManagerDx::Get()->HasDeviceReset()) {
         return;
       }
 
       gfxDevCrash(LogReason::D3D11FinalizeFrame) << "Without device reset: " << hexa(hr);
     }
 
     // test QI
     RefPtr<IDXGIKeyedMutex> mutex;
@@ -1233,30 +1233,30 @@ SyncObjectD3D11::FinalizeFrame()
 
   if (mD3D11SyncedTextures.size()) {
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
     {
       AutoTextureLock(mutex, hr, 20000);
 
       if (hr == WAIT_TIMEOUT) {
-        if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+        if (DeviceManagerDx::Get()->HasDeviceReset()) {
           gfxWarning() << "AcquireSync timed out because of device reset.";
           return;
         }
         gfxDevCrash(LogReason::D3D11SyncLock) << "Timeout on the D3D11 sync lock";
       }
 
       D3D11_BOX box;
       box.front = box.top = box.left = 0;
       box.back = box.bottom = box.right = 1;
 
       RefPtr<ID3D11Device> dev = DeviceManagerDx::Get()->GetContentDevice();
       if (!dev) {
-        if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+        if (DeviceManagerDx::Get()->HasDeviceReset()) {
           return;
         }
         MOZ_CRASH("GFX: Invalid D3D11 content device");
       }
 
       RefPtr<ID3D11DeviceContext> ctx;
       dev->GetImmediateContext(getter_AddRefs(ctx));
 
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/layers/Effects.h"
 #include "nsWindowsHelpers.h"
 #include "Nv3DVUtils.h"
 #include "gfxFailure.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "gfxPrefs.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxUtils.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/widget/WinCompositorWidget.h"
 #include "D3D9SurfaceImage.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
@@ -674,17 +675,17 @@ CompositorD3D9::Ready()
 
 void
 CompositorD3D9::FailedToResetDevice() {
   mFailedResetAttempts += 1;
   // 10 is a totally arbitrary number that we may want to increase or decrease
   // depending on how things behave in the wild.
   if (mFailedResetAttempts > 10) {
     mFailedResetAttempts = 0;
-    gfxWindowsPlatform::GetPlatform()->D3D9DeviceReset();
+    DeviceManagerDx::Get()->NotifyD3D9DeviceReset();
     gfxCriticalNote << "[D3D9] Unable to get a working D3D9 Compositor";
   }
 }
 
 void
 CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
                            const IntRect *aClipRectIn,
                            const IntRect& aRenderBounds,
--- a/gfx/layers/ipc/VideoBridgeChild.cpp
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -18,30 +18,33 @@ VideoBridgeChild::Startup()
   sVideoBridgeChildSingleton = new VideoBridgeChild();
   RefPtr<VideoBridgeParent> parent = new VideoBridgeParent();
 
   MessageLoop* loop = CompositorThreadHolder::Loop();
 
   sVideoBridgeChildSingleton->Open(parent->GetIPCChannel(),
                                    loop,
                                    ipc::ChildSide);
+  sVideoBridgeChildSingleton->mIPDLSelfRef = sVideoBridgeChildSingleton;
   parent->SetOtherProcessId(base::GetCurrentProcId());
 }
 
 /* static */ void
 VideoBridgeChild::Shutdown()
 {
-  sVideoBridgeChildSingleton = nullptr;
-
+  if (sVideoBridgeChildSingleton) {
+    sVideoBridgeChildSingleton->Close();
+    sVideoBridgeChildSingleton = nullptr;
+  }
 }
 
 VideoBridgeChild::VideoBridgeChild()
   : mMessageLoop(MessageLoop::current())
+  , mCanSend(true)
 {
-  sVideoBridgeChildSingleton = this;
 }
 
 VideoBridgeChild::~VideoBridgeChild()
 {
 }
 
 VideoBridgeChild*
 VideoBridgeChild::GetSingleton()
@@ -83,16 +86,28 @@ VideoBridgeChild::AllocPTextureChild(con
 }
 
 bool
 VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
   return TextureClient::DestroyIPDLActor(actor);
 }
 
+void
+VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mCanSend = false;
+}
+
+void
+VideoBridgeChild::DeallocPVideoBridgeChild()
+{
+  mIPDLSelfRef = nullptr;
+}
+
 PTextureChild*
 VideoBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 LayersBackend aLayersBackend,
                                 TextureFlags aFlags,
                                 uint64_t aSerial)
 {
   MOZ_ASSERT(CanSend());
   return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
--- a/gfx/layers/ipc/VideoBridgeChild.h
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -26,16 +26,20 @@ public:
 
   // PVideoBridgeChild
   PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
                                     const LayersBackend& aLayersBackend,
                                     const TextureFlags& aFlags,
                                     const uint64_t& aSerial) override;
   bool DeallocPTextureChild(PTextureChild* actor) override;
 
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPVideoBridgeChild() override;
+
+
   // ISurfaceAllocator
   bool AllocUnsafeShmem(size_t aSize,
                         mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                         mozilla::ipc::Shmem* aShmem) override;
   bool AllocShmem(size_t aSize,
                   mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                   mozilla::ipc::Shmem* aShmem) override;
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
@@ -49,21 +53,23 @@ public:
   // ClientIPCAllocator
   base::ProcessId GetParentPid() const override { return OtherPid(); }
   MessageLoop * GetMessageLoop() const override { return mMessageLoop; }
   void CancelWaitForRecycle(uint64_t aTextureId) override { MOZ_ASSERT(false, "NO RECYCLING HERE"); }
 
   // ISurfaceAllocator
   bool IsSameProcess() const override;
 
-  bool CanSend() { return true; }
+  bool CanSend() { return mCanSend; }
 
 private:
   VideoBridgeChild();
   ~VideoBridgeChild();
 
+  RefPtr<VideoBridgeChild> mIPDLSelfRef;
   MessageLoop* mMessageLoop;
+  bool mCanSend;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -61,16 +61,21 @@ DriverCrashGuard::InitializeIfNeeded()
 
   mInitialized = true;
   Initialize();
 }
 
 static inline bool
 AreCrashGuardsEnabled()
 {
+  // Crash guard isn't supported in the GPU process since the entire
+  // process is basically a crash guard.
+  if (XRE_IsGPUProcess()) {
+    return false;
+  }
 #ifdef NIGHTLY_BUILD
   // We only use the crash guard on non-nightly channels, since the nightly
   // channel is for development and having graphics features perma-disabled
   // is rather annoying.  Unless the user forces is with an environment
   // variable, which comes in handy for testing.
   return gfxEnv::ForceCrashGuardNightly();
 #else
   // Check to see if all guards have been disabled through the environment.
--- a/gfx/thebes/DeviceManagerDx.cpp
+++ b/gfx/thebes/DeviceManagerDx.cpp
@@ -574,28 +574,31 @@ void
 DeviceManagerDx::ResetDevices()
 {
   MutexAutoLock lock(mDeviceLock);
 
   mAdapter = nullptr;
   mCompositorDevice = nullptr;
   mContentDevice = nullptr;
   mDeviceStatus = Nothing();
+  mDeviceResetReason = Nothing();
   Factory::SetDirect3D11Device(nullptr);
 }
 
 bool
 DeviceManagerDx::MaybeResetAndReacquireDevices()
 {
   DeviceResetReason resetReason;
-  if (!GetAnyDeviceRemovedReason(&resetReason)) {
+  if (!HasDeviceReset(&resetReason)) {
     return false;
   }
 
-  Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
+  if (resetReason != DeviceResetReason::FORCED_RESET) {
+    Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
+  }
 
   bool createCompositorDevice = !!mCompositorDevice;
   bool createContentDevice = !!mContentDevice;
 
   ResetDevices();
 
   if (createCompositorDevice && !CreateCompositorDevices()) {
     // Just stop, don't try anything more
@@ -651,45 +654,94 @@ static DeviceResetReason HResultToResetR
   case E_OUTOFMEMORY:
     return DeviceResetReason::OUT_OF_MEMORY;
   default:
     MOZ_ASSERT(false);
   }
   return DeviceResetReason::UNKNOWN;
 }
 
+bool
+DeviceManagerDx::HasDeviceReset(DeviceResetReason* aOutReason)
+{
+  MutexAutoLock lock(mDeviceLock);
+
+  if (mDeviceResetReason) {
+    *aOutReason = mDeviceResetReason.value();
+    return true;
+  }
+
+  DeviceResetReason reason;
+  if (GetAnyDeviceRemovedReason(&reason)) {
+    mDeviceResetReason = Some(reason);
+    *aOutReason = reason;
+    return true;
+  }
+
+  return false;
+}
+
 static inline bool
-DidDeviceReset(RefPtr<ID3D11Device> aDevice, DeviceResetReason* aOutReason)
+DidDeviceReset(const RefPtr<ID3D11Device>& aDevice, DeviceResetReason* aOutReason)
 {
   if (!aDevice) {
     return false;
   }
   HRESULT hr = aDevice->GetDeviceRemovedReason();
   if (hr == S_OK) {
     return false;
   }
 
   *aOutReason = HResultToResetReason(hr);
   return true;
 }
 
 bool
 DeviceManagerDx::GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason)
 {
-  // Note: this can be called off the main thread, so we need to use
-  // our threadsafe getters.
-  if (DidDeviceReset(GetCompositorDevice(), aOutReason) ||
-      DidDeviceReset(GetContentDevice(), aOutReason))
+  // Caller must own the lock, since we access devices directly, and can be
+  // called from any thread.
+  mDeviceLock.AssertCurrentThreadOwns();
+
+  if (DidDeviceReset(mCompositorDevice, aOutReason) ||
+      DidDeviceReset(mContentDevice, aOutReason))
   {
     return true;
   }
+
+  if (XRE_IsParentProcess() &&
+      NS_IsMainThread() &&
+      gfxPrefs::DeviceResetForTesting())
+  {
+    gfxPrefs::SetDeviceResetForTesting(0);
+    *aOutReason = DeviceResetReason::FORCED_RESET;
+    return true;
+  }
+
   return false;
 }
 
 void
+DeviceManagerDx::ForceDeviceReset(ForcedDeviceResetReason aReason)
+{
+  Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
+  {
+    MutexAutoLock lock(mDeviceLock);
+    mDeviceResetReason = Some(DeviceResetReason::FORCED_RESET);
+  }
+}
+
+void
+DeviceManagerDx::NotifyD3D9DeviceReset()
+{
+  MutexAutoLock lock(mDeviceLock);
+  mDeviceResetReason = Some(DeviceResetReason::D3D9_RESET);
+}
+
+void
 DeviceManagerDx::DisableD3D11AfterCrash()
 {
   gfxConfig::Disable(Feature::D3D11_COMPOSITING,
     FeatureStatus::CrashedInHandler,
     "Crashed while acquiring a Direct3D11 device",
     NS_LITERAL_CSTRING("FEATURE_FAILURE_D3D11_CRASH"));
   ResetDevices();
 }
--- a/gfx/thebes/DeviceManagerDx.h
+++ b/gfx/thebes/DeviceManagerDx.h
@@ -71,29 +71,33 @@ public:
   void CreateContentDevices();
 
   void ImportDeviceInfo(const D3D11DeviceStatus& aDeviceStatus);
   void ExportDeviceInfo(D3D11DeviceStatus* aOut);
 
   void ResetDevices();
   void InitializeDirectDraw();
 
-  // Call GetDeviceRemovedReason on each device until one returns
-  // a failure.
-  bool GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason);
-
   // Reset and reacquire the devices if a reset has happened.
   // Returns whether a reset occurred not whether reacquiring
   // was successful.
   bool MaybeResetAndReacquireDevices();
 
   // Test whether we can acquire a DXGI 1.2-compatible adapter. This should
   // only be called on startup before devices are initialized.
   bool CheckRemotePresentSupport();
 
+  // Device reset helpers.
+  bool HasDeviceReset(DeviceResetReason* aOutReason = nullptr);
+
+  // Note: these set the cached device reset reason, which will be picked up
+  // on the next frame.
+  void ForceDeviceReset(ForcedDeviceResetReason aReason);
+  void NotifyD3D9DeviceReset();
+
 private:
   IDXGIAdapter1 *GetDXGIAdapter();
 
   void DisableD3D11AfterCrash();
 
   void CreateCompositorDevice(mozilla::gfx::FeatureState& d3d11);
   bool CreateCompositorDeviceHelper(
       mozilla::gfx::FeatureState& aD3d11,
@@ -111,16 +115,20 @@ private:
                     HRESULT& aResOut,
                     RefPtr<ID3D11Device>& aOutDevice);
 
   bool ContentAdapterIsParentAdapter(ID3D11Device* device);
 
   bool LoadD3D11();
   void ReleaseD3D11();
 
+  // Call GetDeviceRemovedReason on each device until one returns
+  // a failure.
+  bool GetAnyDeviceRemovedReason(DeviceResetReason* aOutReason);
+
 private:
   static StaticAutoPtr<DeviceManagerDx> sInstance;
 
   // This is assigned during device creation. Afterwards, it is released if
   // devices failed, and "forgotten" if devices succeeded (meaning, we leak
   // the ref and unassign the module).
   nsModuleHandle mD3D11Module;
 
@@ -131,14 +139,16 @@ private:
   RefPtr<ID3D11Device> mContentDevice;
   RefPtr<ID3D11Device> mDecoderDevice;
   bool mCompositorDeviceSupportsVideo;
 
   Maybe<D3D11DeviceStatus> mDeviceStatus;
 
   nsModuleHandle mDirectDrawDLL;
   RefPtr<IDirectDraw7> mDirectDraw;
+
+  Maybe<DeviceResetReason> mDeviceResetReason;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // mozilla_gfx_thebes_DeviceManagerDx_h
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -125,17 +125,18 @@ enum class DeviceResetReason
   OK = 0,
   HUNG,
   REMOVED,
   RESET,
   DRIVER_ERROR,
   INVALID_CALL,
   OUT_OF_MEMORY,
   FORCED_RESET,
-  UNKNOWN
+  UNKNOWN,
+  D3D9_RESET
 };
 
 enum class ForcedDeviceResetReason
 {
   OPENSHAREDHANDLE = 0,
   COMPOSITOR_UPDATED,
 };
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -305,19 +305,16 @@ public:
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS(D3DSharedTexturesReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
   : mRenderMode(RENDER_GDI)
-  , mHasDeviceReset(false)
-  , mHasFakeDeviceReset(false)
-  , mHasD3D9DeviceReset(false)
 {
   mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
   mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
   /*
    * Initialize COM
    */
   CoInitialize(nullptr);
@@ -434,30 +431,23 @@ gfxWindowsPlatform::InitDWriteSupport()
 bool
 gfxWindowsPlatform::HandleDeviceReset()
 {
   DeviceResetReason resetReason = DeviceResetReason::OK;
   if (!DidRenderingDeviceReset(&resetReason)) {
     return false;
   }
 
-  if (!mHasFakeDeviceReset) {
+  if (resetReason != DeviceResetReason::FORCED_RESET) {
     Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
   }
 
   // Remove devices and adapters.
   DeviceManagerDx::Get()->ResetDevices();
 
-  // Reset local state. Note: we leave feature status variables as-is. They
-  // will be recomputed by InitializeDevices().
-  mHasDeviceReset = false;
-  mHasFakeDeviceReset = false;
-  mHasD3D9DeviceReset = false;
-  mDeviceResetReason = DeviceResetReason::OK;
-
   imgLoader::NormalLoader()->ClearCache(true);
   imgLoader::NormalLoader()->ClearCache(false);
   imgLoader::PrivateBrowsingLoader()->ClearCache(true);
   imgLoader::PrivateBrowsingLoader()->ClearCache(false);
   gfxAlphaBoxBlur::ShutdownBlurCache();
 
   if (XRE_IsContentProcess()) {
     // Fetch updated device parameters.
@@ -512,25 +502,16 @@ gfxWindowsPlatform::UpdateRenderMode()
                       << ", D2D1 status:" << FeatureStatusToString(gfxConfig::GetValue(Feature::DIRECT2D))
                       << ", content:" << int(GetDefaultContentBackend())
                       << ", compositor:" << int(GetCompositorBackend());
       MOZ_CRASH("GFX: Failed to update reference draw target after device reset");
     }
   }
 }
 
-void
-gfxWindowsPlatform::ForceDeviceReset(ForcedDeviceResetReason aReason)
-{
-  Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
-
-  mDeviceResetReason = DeviceResetReason::FORCED_RESET;
-  mHasDeviceReset = true;
-}
-
 mozilla::gfx::BackendType
 gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
 {
   mozilla::gfx::BackendType defaultBackend = gfxPlatform::GetDefaultContentBackend();
   if (aLayers == LayersBackend::LAYERS_D3D11) {
     return defaultBackend;
   }
 
@@ -911,67 +892,31 @@ gfxWindowsPlatform::IsFontFormatSupporte
     if (aFormatFlags != 0) {
         return false;
     }
 
     // no format hint set, need to look at data
     return true;
 }
 
-void
-gfxWindowsPlatform::CompositorUpdated()
-{
-  ForceDeviceReset(ForcedDeviceResetReason::COMPOSITOR_UPDATED);
-  UpdateRenderMode();
-}
-
-void
-gfxWindowsPlatform::TestDeviceReset(DeviceResetReason aReason)
-{
-  if (mHasDeviceReset) {
-    return;
-  }
-  mHasDeviceReset = true;
-  mHasFakeDeviceReset = true;
-  mDeviceResetReason = aReason;
-}
-
 bool
 gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason)
 {
-  if (mHasDeviceReset) {
-    if (aResetReason) {
-      *aResetReason = mDeviceResetReason;
-    }
-    return true;
+  DeviceManagerDx* dm = DeviceManagerDx::Get();
+  if (!dm) {
+    return false;
   }
-  if (aResetReason) {
-    *aResetReason = DeviceResetReason::OK;
-  }
+  return dm->HasDeviceReset(aResetReason);
+}
 
-  if (DeviceManagerDx::Get()->GetAnyDeviceRemovedReason(&mDeviceResetReason)) {
-    mHasDeviceReset = true;
-    if (aResetReason) {
-      *aResetReason = mDeviceResetReason;
-    }
-    return true;
-  }
-
-  if (mHasD3D9DeviceReset) {
-    return true;
-  }
-  if (XRE_IsParentProcess() && gfxPrefs::DeviceResetForTesting()) {
-    TestDeviceReset((DeviceResetReason)gfxPrefs::DeviceResetForTesting());
-    if (aResetReason) {
-      *aResetReason = mDeviceResetReason;
-    }
-    gfxPrefs::SetDeviceResetForTesting(0);
-    return true;
-  }
-  return false;
+void
+gfxWindowsPlatform::CompositorUpdated()
+{
+  DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::COMPOSITOR_UPDATED);
+  UpdateRenderMode();
 }
 
 BOOL CALLBACK
 InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg)
 {
     RedrawWindow(aWnd, nullptr, nullptr,
                  RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_FRAME);
     return TRUE;
@@ -1327,21 +1272,16 @@ gfxWindowsPlatform::SetupClearTypeParams
             getter_AddRefs(mRenderingParams[TEXT_RENDERING_NORMAL]));
 
         GetDWriteFactory()->CreateCustomRenderingParams(gamma, contrast, level,
             dwriteGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC,
             getter_AddRefs(mRenderingParams[TEXT_RENDERING_GDI_CLASSIC]));
     }
 }
 
-void
-gfxWindowsPlatform::D3D9DeviceReset() {
-  mHasD3D9DeviceReset = true;
-}
-
 ReadbackManagerD3D11*
 gfxWindowsPlatform::GetReadbackManager()
 {
   if (!mD3D11ReadbackManager) {
     mD3D11ReadbackManager = new ReadbackManagerD3D11();
   }
 
   return mD3D11ReadbackManager;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -146,21 +146,16 @@ public:
 
     /**
      * Updates render mode with relation to the current preferences and
      * available devices.
      */
     void UpdateRenderMode();
 
     /**
-     * Forces all GPU resources to be recreated on the next frame.
-     */
-    void ForceDeviceReset(ForcedDeviceResetReason aReason);
-
-    /**
      * Verifies a D2D device is present and working, will attempt to create one
      * it is non-functional or non-existant.
      *
      * \param aAttemptForce Attempt to force D2D cairo device creation by using
      * cairo device creation routines.
      */
     void VerifyD2DDevice(bool aAttemptForce);
 
@@ -206,36 +201,32 @@ public:
     IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
     inline bool DWriteEnabled() { return !!mDWriteFactory; }
     inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
 
     IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
     { return mRenderingParams[aRenderMode]; }
 
 public:
-    void D3D9DeviceReset();
-
     bool DwmCompositionEnabled();
 
     mozilla::layers::ReadbackManagerD3D11* GetReadbackManager();
 
     static bool IsOptimus();
 
     bool SupportsApzWheelInput() const override {
       return true;
     }
     bool SupportsApzTouchInput() const override;
 
     // Recreate devices as needed for a device reset. Returns true if a device
     // reset occurred.
     bool HandleDeviceReset();
     void UpdateBackendPrefs();
 
-    void TestDeviceReset(DeviceResetReason aReason);
-
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
     static mozilla::Atomic<size_t> sD3D11SharedTextures;
     static mozilla::Atomic<size_t> sD3D9SharedTextures;
 
     bool SupportsPluginDirectBitmapDrawing() override {
       return true;
     }
     bool SupportsPluginDirectDXGIDrawing();
@@ -277,19 +268,14 @@ private:
     void InitializeD3D11Config();
     void InitializeD2DConfig();
     void InitializeDirectDrawConfig();
 
     RefPtr<IDWriteFactory> mDWriteFactory;
     RefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 
-    bool mHasDeviceReset;
-    bool mHasFakeDeviceReset;
-    mozilla::Atomic<bool> mHasD3D9DeviceReset;
-    DeviceResetReason mDeviceResetReason;
-
     RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
 
     nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/image/SVGDocumentWrapper.cpp
+++ b/image/SVGDocumentWrapper.cpp
@@ -357,17 +357,17 @@ SVGDocumentWrapper::SetupViewer(nsIReque
   // document needs this navigation timing object for time computation, such
   // as to calculate current time stamp based on the start time of navigation
   // time object.
   //
   // For a root document, DocShell would do these sort of things
   // automatically. Since there is no DocShell for this wrapped SVG document,
   // we must set it up manually.
   RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming();
-  timing->NotifyNavigationStart();
+  timing->NotifyNavigationStart(nsDOMNavigationTiming::DocShellState::eInactive);
   viewer->SetNavigationTiming(timing);
 
   nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
   NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
 
   // XML-only, because this is for SVG content
   nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();
   NS_ENSURE_TRUE(sink, NS_ERROR_UNEXPECTED);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -512,17 +512,17 @@ IsProxy(JSContext* cx, unsigned argc, Va
     args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
     return true;
 }
 
 static bool
 WasmIsSupported(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setBoolean(wasm::HasCompilerSupport(cx));
+    args.rval().setBoolean(wasm::HasSupport(cx));
     return true;
 }
 
 static bool
 WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1651,22 +1651,16 @@ function ArrayBufferSlice(start, end) {
     // Step 22.
     return new_;
 }
 
 function IsDetachedBufferThis() {
   return IsDetachedBuffer(this);
 }
 
-function ArrayBufferStaticSlice(buf, start, end) {
-    if (arguments.length < 1)
-        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'ArrayBuffer.slice');
-    return callFunction(ArrayBufferSlice, buf, start, end);
-}
-
 // ES 2016 draft Mar 25, 2016 24.1.3.3.
 function ArrayBufferSpecies() {
     // Step 1.
     return this;
 }
 _SetCanonicalName(ArrayBufferSpecies, "get [Symbol.species]");
 
 // Shared memory and atomics proposal (30 Oct 2016)
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -143,17 +143,17 @@ function GetIterator(obj, method) {
     if (arguments.length === 1)
         method = GetMethod(obj, std_iterator);
 
     // Steps 3-4.
     var iterator = callContentFunction(method, obj);
 
     // Step 5.
     if (!IsObject(iterator))
-        ThrowTypeError(JSMSG_NOT_ITERABLE, ToString(iterator));
+        ThrowTypeError(JSMSG_NOT_ITERATOR, ToString(iterator));
 
     // Step 6.
     return iterator;
 }
 
 var _builtinCtorsCache = {__proto__: null};
 
 function GetBuiltinConstructor(builtinName) {
--- a/js/src/ds/PageProtectingVector.h
+++ b/js/src/ds/PageProtectingVector.h
@@ -73,30 +73,28 @@ class PageProtectingVector final
         unprotectedBytes += offsetToPage;
         offsetToPage = (pageSize - (uintptr_t(vector.begin()) & pageMask)) & pageMask;
         unprotectedBytes -= offsetToPage;
         protectionEnabled = vector.capacity() >= protectionLowerBound &&
                             vector.capacity() >= pageSize + offsetToPage;
     }
 
     void protect() {
-        MOZ_ASSERT(!regionUnprotected);
-        if (protectionEnabled && unprotectedBytes >= intptr_t(pageSize)) {
+        if (!regionUnprotected && protectionEnabled && unprotectedBytes >= intptr_t(pageSize)) {
             size_t toProtect = size_t(unprotectedBytes) & ~pageMask;
             uintptr_t addr = uintptr_t(vector.begin()) + offsetToPage + protectedBytes;
             gc::MakePagesReadOnly(reinterpret_cast<void*>(addr), toProtect);
             unprotectedBytes -= toProtect;
             protectedBytes += toProtect;
         }
     }
 
     void unprotect() {
-        MOZ_ASSERT(!regionUnprotected);
         MOZ_ASSERT_IF(!protectionEnabled, !protectedBytes);
-        if (protectedBytes) {
+        if (!regionUnprotected && protectedBytes) {
             uintptr_t addr = uintptr_t(vector.begin()) + offsetToPage;
             gc::UnprotectPages(reinterpret_cast<void*>(addr), protectedBytes);
             unprotectedBytes += protectedBytes;
             protectedBytes = 0;
         }
     }
 
     void protectNewBuffer() {
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5981,16 +5981,18 @@ BytecodeEmitter::emitIterator()
         return false;
     if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
         return false;
     if (!emit1(JSOP_SWAP))                                        // ITERFN OBJ
         return false;
     if (!emitCall(JSOP_CALLITER, 0))                              // ITER
         return false;
     checkTypeSet(JSOP_CALLITER);
+    if (!emitCheckIsObj(CheckIsObjectKind::GetIterator))          // ITER
+        return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitSpread(bool allowSelfHosted)
 {
     LoopControl loopInfo(this, StatementKind::Spread);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/arraybuffer-slice-warn.js
@@ -0,0 +1,13 @@
+// ArrayBuffer.slice should be warned once and only once.
+
+enableLastWarning();
+
+ArrayBuffer.slice(new ArrayBuffer(10), 1);
+var warning = getLastWarning();
+assertEq(warning !== null, true, "warning should be generated");
+assertEq(warning.name, "Warning");
+
+clearLastWarning();
+ArrayBuffer.slice(new ArrayBuffer(10), 1);
+warning = getLastWarning();
+assertEq(warning, null, "warning should not generated for 2nd ocurrence");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/async-lazy.js
@@ -0,0 +1,24 @@
+async function f1(a, b) {
+  let x = await 10;
+  return x;
+};
+var toStringResult = f1.toString();
+
+async function f2(a, b) {
+  // arguments.callee gets wrapped function from unwrapped function.
+  return arguments.callee;
+};
+
+relazifyFunctions();
+
+// toString gets unwrapped function from wrapped function.
+assertEq(f1.toString(), toStringResult);
+
+var ans = 0;
+f1().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, 10);
+
+f2().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, f2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/xdr/async.js
@@ -0,0 +1,32 @@
+load(libdir + 'bytecode-cache.js');
+
+async function f1(a, b) {
+  let x = await 10;
+  return x;
+};
+var toStringResult = f1.toString();
+
+var test = `
+async function f1(a, b) {
+  let x = await 10;
+  return x;
+};
+// toString gets unwrapped function from wrapped function.
+assertEq(f1.toString(), \`${toStringResult}\`);
+
+var ans = 0;
+f1().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, 10);
+
+async function f2(a, b) {
+  // arguments.callee gets wrapped function from unwrapped function.
+  return arguments.callee;
+};
+
+f2().then(x => { ans = x; });
+drainJobQueue();
+assertEq(ans, f2);
+`;
+
+evalWithCache(test, { assertEqBytecode: true, checkFrozen: true});
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1531,16 +1531,20 @@ class MacroAssemblerARMCompat : public M
         ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, memoryBase)), HeapReg, scratch);
         ma_ldr(Address(WasmTlsReg, offsetof(wasm::TlsData, globalData)), GlobalReg, scratch);
         ma_add(Imm32(WasmGlobalRegBias), GlobalReg, scratch);
     }
 
     // Instrumentation for entering and leaving the profiler.
     void profilerEnterFrame(Register framePtr, Register scratch);
     void profilerExitFrame();
+
+    struct AutoPrepareForPatching {
+        explicit AutoPrepareForPatching(MacroAssemblerARMCompat&) {}
+    };
 };
 
 typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_MacroAssembler_arm_h */
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -2311,16 +2311,20 @@ class MacroAssemblerCompat : public vixl
     }
 
     // FIXME: Should be in Assembler?
     // FIXME: Should be const?
     uint32_t currentOffset() const {
         return nextOffset().getOffset();
     }
 
+    struct AutoPrepareForPatching {
+        explicit AutoPrepareForPatching(MacroAssemblerCompat&) {}
+    };
+
   protected:
     bool buildOOLFakeExitFrame(void* fakeReturnAddr) {
         uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS,
                                                   ExitFrameLayout::Size());
         Push(Imm32(descriptor));
         Push(ImmPtr(fakeReturnAddr));
         return true;
     }
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.h
@@ -244,14 +244,19 @@ class MacroAssemblerMIPSShared : public 
                          Register output);
 
     void atomicExchange(int nbytes, bool signExtend, const Address& address, Register value,
                         Register valueTemp, Register offsetTemp, Register maskTemp,
                         Register output);
     void atomicExchange(int nbytes, bool signExtend, const BaseIndex& address, Register value,
                         Register valueTemp, Register offsetTemp, Register maskTemp,
                         Register output);
+
+  public:
+    struct AutoPrepareForPatching {
+        explicit AutoPrepareForPatching(MacroAssemblerMIPSShared&) {}
+    };
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_shared_MacroAssembler_mips_shared_h */
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1052,27 +1052,34 @@ class AssemblerX86Shared : public Assemb
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
     CodeOffset callWithPatch() {
         return CodeOffset(masm.call().offset());
     }
+
+    struct AutoPrepareForPatching : X86Encoding::AutoUnprotectAssemblerBufferRegion {
+        explicit AutoPrepareForPatching(AssemblerX86Shared& masm)
+          : X86Encoding::AutoUnprotectAssemblerBufferRegion(masm.masm, 0, masm.size())
+        {}
+    };
+
     void patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
+        // The caller uses AutoUnprotectBuffer.
         unsigned char* code = masm.data();
-        X86Encoding::AutoUnprotectAssemblerBufferRegion unprotect(masm, callerOffset - 4, 4);
         X86Encoding::SetRel32(code + callerOffset, code + calleeOffset);
     }
     CodeOffset farJumpWithPatch() {
         return CodeOffset(masm.jmp().offset());
     }
     void patchFarJump(CodeOffset farJump, uint32_t targetOffset) {
+        // The caller uses AutoUnprotectBuffer.
         unsigned char* code = masm.data();
-        X86Encoding::AutoUnprotectAssemblerBufferRegion unprotect(masm, farJump.offset() - 4, 4);
         X86Encoding::SetRel32(code + farJump.offset(), code + targetOffset);
     }
     static void repatchFarJump(uint8_t* code, uint32_t farJumpOffset, uint32_t targetOffset) {
         X86Encoding::SetRel32(code + farJumpOffset, code + targetOffset);
     }
 
     CodeOffset twoByteNop() {
         return CodeOffset(masm.twoByteNop().offset());
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -67,16 +67,17 @@ MSG_DEF(JSMSG_REDECLARED_VAR,          2
 MSG_DEF(JSMSG_UNDECLARED_VAR,          1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
 MSG_DEF(JSMSG_GETTER_ONLY,             0, JSEXN_TYPEERR, "setting a property that has only a getter")
 MSG_DEF(JSMSG_OVERWRITING_ACCESSOR,    1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
 MSG_DEF(JSMSG_UNDEFINED_PROP,          1, JSEXN_REFERENCEERR, "reference to undefined property {0}")
 MSG_DEF(JSMSG_INVALID_MAP_ITERABLE,    1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
 MSG_DEF(JSMSG_NESTING_GENERATOR,       0, JSEXN_TYPEERR, "already executing generator")
 MSG_DEF(JSMSG_INCOMPATIBLE_METHOD,     3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
 MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead")
+MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead")
 MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,      1, JSEXN_TYPEERR, "bad surrogate character {0}")
 MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE,     1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
 MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR,     1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
 MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW,     1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
 MSG_DEF(JSMSG_BAD_GENERATOR_YIELD,     1, JSEXN_TYPEERR, "yield from closing generator {0}")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,      0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,         2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,         2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
@@ -87,17 +88,19 @@ MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE,   1
 MSG_DEF(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE, 2, JSEXN_TYPEERR, "can't define property {1}: {0} is not extensible")
 MSG_DEF(JSMSG_CANT_REDEFINE_PROP,      1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}")
 MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length")
 MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
 MSG_DEF(JSMSG_BAD_GET_SET_FIELD,       1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
 MSG_DEF(JSMSG_THROW_TYPE_ERROR,        0, JSEXN_TYPEERR, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
 MSG_DEF(JSMSG_NOT_EXPECTED_TYPE,       3, JSEXN_TYPEERR, "{0}: expected {1}, got {2}")
 MSG_DEF(JSMSG_NOT_ITERABLE,            1, JSEXN_TYPEERR, "{0} is not iterable")
+MSG_DEF(JSMSG_NOT_ITERATOR,            1, JSEXN_TYPEERR, "{0} is not iterator")
 MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA,      2, JSEXN_WARN, "{0} is being assigned a {1}, but already has one")
+MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
 MSG_DEF(JSMSG_NEXT_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "iterator.next() returned a non-object value")
 MSG_DEF(JSMSG_CANT_SET_PROTO,          0, JSEXN_TYPEERR, "can't set prototype of this object")
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL,     1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -65,16 +65,17 @@
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
+#include "vm/AsyncFunction.h"
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
@@ -3522,16 +3523,21 @@ CloneFunctionObject(JSContext* cx, Handl
         return nullptr;
     }
 
     if (IsAsmJSModule(fun)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
         return nullptr;
     }
 
+    if (IsWrappedAsyncFunction(fun)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
+        return nullptr;
+    }
+
     if (CanReuseScriptForClone(cx->compartment(), fun, env)) {
         // If the script is to be reused, either the script can already handle
         // non-syntactic scopes, or there is only the standard global lexical
         // scope.
 #ifdef DEBUG
         // Fail here if we OOM during debug asserting.
         // CloneFunctionReuseScript will delazify the script anyways, so we
         // are not creating an extra failure condition for DEBUG builds.
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -311,16 +311,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         ArgumentsHasVarBinding,
         NeedsArgsObj,
         HasMappedArgsObj,
         FunctionHasThisBinding,
         FunctionHasExtraBodyVarScope,
         IsGeneratorExp,
         IsLegacyGenerator,
         IsStarGenerator,
+        IsAsync,
         OwnSource,
         ExplicitUseStrict,
         SelfHosted,
         HasSingleton,
         TreatAsRunOnce,
         HasLazyScript,
         HasNonSyntacticScope,
         HasInnerFunctions,
@@ -424,16 +425,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
             scriptBits |= (1 << OwnSource);
         if (script->isGeneratorExp())
             scriptBits |= (1 << IsGeneratorExp);
         if (script->isLegacyGenerator())
             scriptBits |= (1 << IsLegacyGenerator);
         if (script->isStarGenerator())
             scriptBits |= (1 << IsStarGenerator);
+        if (script->asyncKind() == AsyncFunction)
+            scriptBits |= (1 << IsAsync);
         if (script->hasSingletons())
             scriptBits |= (1 << HasSingleton);
         if (script->treatAsRunOnce())
             scriptBits |= (1 << TreatAsRunOnce);
         if (script->isRelazifiable())
             scriptBits |= (1 << HasLazyScript);
         if (script->hasNonSyntacticScope())
             scriptBits |= (1 << HasNonSyntacticScope);
@@ -572,16 +575,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << IsDefaultClassConstructor))
             script->isDefaultClassConstructor_ = true;
 
         if (scriptBits & (1 << IsLegacyGenerator)) {
             MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
             script->setGeneratorKind(LegacyGenerator);
         } else if (scriptBits & (1 << IsStarGenerator))
             script->setGeneratorKind(StarGenerator);
+
+        if (scriptBits & (1 << IsAsync))
+            script->setAsyncKind(AsyncFunction);
     }
 
     JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
     JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
 
     if (scriptBits & (1 << OwnSource)) {
         if (!script->scriptSource()->performXDR<mode>(xdr))
             return false;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/iterator-primitive.js
@@ -0,0 +1,36 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+function f([]) {
+}
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        let [] = obj;
+    }, TypeError);
+    assertThrowsInstanceOf(() => {
+        [] = obj;
+    }, TypeError);
+    assertThrowsInstanceOf(() => {
+        f(obj);
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/spread-iterator-primitive.js
@@ -0,0 +1,28 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+function f() {
+}
+
+for (let primitive of primitives) {
+    let arg = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => f(...arg), TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/yield-star-iterator-primitive.js
@@ -0,0 +1,31 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        function* g() {
+            yield* obj;
+        }
+        for (let x of g()) {
+        }
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Map/constructor-iterator-primitive.js
@@ -0,0 +1,34 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let ctors = [
+    Map,
+    Set,
+    WeakMap,
+    WeakSet
+];
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let ctor of ctors) {
+    for (let primitive of primitives) {
+        let arg = {
+            [Symbol.iterator]() {
+                return primitive;
+            }
+        };
+        assertThrowsInstanceOf(() => new ctor(arg), TypeError);
+    }
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Promise/iterator-primitive.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
+
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let primitive of primitives) {
+    let arg = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertEventuallyThrows(Promise.all(arg), TypeError);
+    assertEventuallyThrows(Promise.race(arg), TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Statements/for-of-iterator-primitive.js
@@ -0,0 +1,28 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let primitive of primitives) {
+    let obj = {
+        [Symbol.iterator]() {
+            return primitive;
+        }
+    };
+    assertThrowsInstanceOf(() => {
+        for (let x of obj) {
+        }
+    }, TypeError);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/constructor-iterator-primitive.js
@@ -0,0 +1,29 @@
+var BUGNUMBER = 1021835;
+var summary = "Returning non-object from @@iterator should throw";
+
+print(BUGNUMBER + ": " + summary);
+
+let primitives = [
+    1,
+    true,
+    undefined,
+    null,
+    "foo",
+    Symbol.iterator
+];
+
+for (let ctor of typedArrayConstructors) {
+    for (let primitive of primitives) {
+        let arg = {
+            [Symbol.iterator]() {
+                return primitive;
+            }
+        };
+        assertThrowsInstanceOf(() => {
+            new ctor(arg);
+        }, TypeError);
+    }
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/AsyncFunctions/clone.js
@@ -0,0 +1,7 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs clone
+
+// Async function cannot be cloned.
+assertThrowsInstanceOf(() => clone(async function f() {}), TypeError);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_7/AsyncFunctions/shell.js
+++ b/js/src/tests/ecma_7/AsyncFunctions/shell.js
@@ -1,26 +0,0 @@
-(function(global) {
-  function getPromiseResult(promise) {
-    var result, error, caught = false;
-    promise.then(r => { result = r; },
-                 e => { caught = true; error = e; });
-    drainJobQueue();
-    if (caught)
-      throw error;
-    return result;
-  }
-
-  function assertEventuallyEq(promise, expected) {
-    assertEq(getPromiseResult(promise), expected);
-  }
-  global.assertEventuallyEq = assertEventuallyEq;
-
-  function assertEventuallyThrows(promise, expectedErrorType) {
-    assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
-  };
-  global.assertEventuallyThrows = assertEventuallyThrows;
-
-  function assertEventuallyDeepEq(promise, expected) {
-    assertDeepEq(getPromiseResult(promise), expected);
-  };
-  global.assertEventuallyDeepEq = assertEventuallyDeepEq;
-})(this);
--- a/js/src/tests/shell.js
+++ b/js/src/tests/shell.js
@@ -319,16 +319,42 @@
   function OptLevel(i) {
     i = Number(i);
     var cx = GetContext();
     cx.setOptimizationLevel(i);
   }
   global.OptLevel = OptLevel;
 })(this);
 
+(function(global) {
+  function getPromiseResult(promise) {
+    var result, error, caught = false;
+    promise.then(r => { result = r; },
+                 e => { caught = true; error = e; });
+    drainJobQueue();
+    if (caught)
+      throw error;
+    return result;
+  }
+
+  function assertEventuallyEq(promise, expected) {
+    assertEq(getPromiseResult(promise), expected);
+  }
+  global.assertEventuallyEq = assertEventuallyEq;
+
+  function assertEventuallyThrows(promise, expectedErrorType) {
+    assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
+  };
+  global.assertEventuallyThrows = assertEventuallyThrows;
+
+  function assertEventuallyDeepEq(promise, expected) {
+    assertDeepEq(getPromiseResult(promise), expected);
+  };
+  global.assertEventuallyDeepEq = assertEventuallyDeepEq;
+})(this);
 
 var STATUS = "STATUS: ";
 
 var gDelayTestDriverEnd = false;
 
 var gTestcases = new Array();
 var gTc = gTestcases.length;
 var summary = '';
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -39,16 +39,17 @@
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
+#include "vm/SelfHosting.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTypes.h"
 
 #include "jsatominlines.h"
 
 #include "vm/NativeObject-inl.h"
@@ -82,16 +83,35 @@ js::ToClampedIndex(JSContext* cx, Handle
             result = 0;
     } else if (uint32_t(result) > length) {
         result = length;
     }
     *out = uint32_t(result);
     return true;
 }
 
+static bool
+arraybuffer_static_slice(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() < 1) {
+        ReportMissingArg(cx, args.calleev(), 1);
+        return false;
+    }
+
+    if (!GlobalObject::warnOnceAboutArrayBufferSlice(cx, cx->global()))
+        return false;
+
+    FixedInvokeArgs<2> args2(cx);
+    args2[0].set(args.get(1));
+    args2[1].set(args.get(2));
+    return CallSelfHostedFunction(cx, "ArrayBufferSlice", args[0], args2, args.rval());
+}
+
 /*
  * ArrayBufferObject
  *
  * This class holds the underlying raw buffer that the TypedArrayObject classes
  * access.  It can be created explicitly and passed to a TypedArrayObject, or
  * can be created implicitly by constructing a TypedArrayObject with a size.
  */
 
@@ -135,17 +155,17 @@ static const ClassOps ArrayBufferObjectC
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
 };
 
 static const JSFunctionSpec static_functions[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
-    JS_SELF_HOSTED_FN("slice", "ArrayBufferStaticSlice", 3, 0),
+    JS_FN("slice", arraybuffer_static_slice, 3, 0),
     JS_FS_END
 };
 
 static const JSPropertySpec static_properties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "ArrayBufferSpecies", 0),
     JS_PS_END
 };
 
--- a/js/src/vm/ForOfIterator.cpp
+++ b/js/src/vm/ForOfIterator.cpp
@@ -69,20 +69,20 @@ ForOfIterator::init(HandleValue iterable
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
         return false;
     }
 
     RootedValue res(cx);
     if (!js::Call(cx, callee, iterable, &res))
         return false;
 
-    iterator = ToObject(cx, res);
-    if (!iterator)
-        return false;
+    if (!res.isObject())
+        return ThrowCheckIsObject(cx, CheckIsObjectKind::GetIterator);
 
+    iterator = &res.toObject();
     return true;
 }
 
 inline bool
 ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool* done)
 {
     MOZ_ASSERT(index != NOT_ARRAY);
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -94,17 +94,17 @@ js::GlobalObject::getTypedObjectModule()
     MOZ_ASSERT(v.isObject());
     return v.toObject().as<TypedObjectModuleObject>();
 }
 
 /* static */ bool
 GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
 {
     if (key == JSProto_WebAssembly)
-        return !wasm::HasCompilerSupport(cx);
+        return !wasm::HasSupport(cx);
 
 #ifdef ENABLE_SHARED_ARRAY_BUFFER
     // Return true if the given constructor has been disabled at run-time.
     switch (key) {
       case JSProto_Atomics:
       case JSProto_SharedArrayBuffer:
         return !cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled();
       default:
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -125,16 +125,17 @@ class GlobalObject : public NativeObject
      * we won't expose GlobalObject, so just assert that the two values are
      * synchronized.
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
 
     enum WarnOnceFlag : int32_t {
         WARN_WATCH_DEPRECATED                   = 1 << 0,
+        WARN_ARRAYBUFFER_SLICE_DEPRECATED       = 1 << 1,
     };
 
     // Emit the specified warning if the given slot in |obj|'s global isn't
     // true, then set the slot to true.  Thus calling this method warns once
     // for each global object it's called on, and every other call does
     // nothing.
     static bool
     warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, unsigned errorNumber);
@@ -720,16 +721,22 @@ class GlobalObject : public NativeObject
     // in which |obj| was created, if no prior warning was given.
     static bool warnOnceAboutWatch(JSContext* cx, HandleObject obj) {
         // Temporarily disabled until we've provided a watch/unwatch workaround for
         // debuggers like Firebug (bug 934669).
         //return warnOnceAbout(cx, obj, WARN_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED);
         return true;
     }
 
+    // Warn about use of the deprecated (static) ArrayBuffer.slice method.
+    static bool warnOnceAboutArrayBufferSlice(JSContext* cx, HandleObject obj) {
+        return warnOnceAbout(cx, obj, WARN_ARRAYBUFFER_SLICE_DEPRECATED,
+                             JSMSG_ARRAYBUFFER_SLICE_DEPRECATED);
+    }
+
     static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(const Value& val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -5028,16 +5028,19 @@ js::ReportRuntimeRedeclaration(JSContext
 
 bool
 js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
 {
     switch (kind) {
       case CheckIsObjectKind::IteratorNext:
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEXT_RETURNED_PRIMITIVE);
         break;
+      case CheckIsObjectKind::GetIterator:
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
+        break;
       default:
         MOZ_CRASH("Unknown kind");
     }
     return false;
 }
 
 bool
 js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -556,17 +556,18 @@ ReportRuntimeLexicalError(JSContext* cx,
 
 // The parser only reports redeclarations that occurs within a single
 // script. Due to the extensibility of the global lexical scope, we also check
 // for redeclarations during runtime in JSOP_DEF{VAR,LET,CONST}.
 void
 ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind);
 
 enum class CheckIsObjectKind : uint8_t {
-    IteratorNext
+    IteratorNext,
+    GetIterator
 };
 
 bool
 ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
 
 bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
 
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -2727,116 +2727,116 @@ IsLiteralInt(ModuleValidator& m, ParseNo
     return IsNumericLiteral(m, pn) &&
            IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
 }
 
 /*****************************************************************************/
 
 namespace {
 
-#define CASE(TYPE, OP) case SimdOperation::Fn_##OP: return Expr::TYPE##OP;
+#define CASE(TYPE, OP) case SimdOperation::Fn_##OP: return Op::TYPE##OP;
 #define I8x16CASE(OP) CASE(I8x16, OP)
 #define I16x8CASE(OP) CASE(I16x8, OP)
 #define I32x4CASE(OP) CASE(I32x4, OP)
 #define F32x4CASE(OP) CASE(F32x4, OP)
 #define B8x16CASE(OP) CASE(B8x16, OP)
 #define B16x8CASE(OP) CASE(B16x8, OP)
 #define B32x4CASE(OP) CASE(B32x4, OP)
 #define ENUMERATE(TYPE, FOR_ALL, DO)                                     \
     switch(op) {                                                         \
-        case SimdOperation::Constructor: return Expr::TYPE##Constructor; \
+        case SimdOperation::Constructor: return Op::TYPE##Constructor;   \
         FOR_ALL(DO)                                                      \
         default: break;                                                  \
     }
 
-static inline Expr
-SimdToExpr(SimdType type, SimdOperation op)
+static inline Op
+SimdToOp(SimdType type, SimdOperation op)
 {
     switch (type) {
       case SimdType::Uint8x16:
         // Handle the special unsigned opcodes, then fall through to Int8x16.
         switch (op) {
-          case SimdOperation::Fn_addSaturate:        return Expr::I8x16addSaturateU;
-          case SimdOperation::Fn_subSaturate:        return Expr::I8x16subSaturateU;
-          case SimdOperation::Fn_extractLane:        return Expr::I8x16extractLaneU;
-          case SimdOperation::Fn_shiftRightByScalar: return Expr::I8x16shiftRightByScalarU;
-          case SimdOperation::Fn_lessThan:           return Expr::I8x16lessThanU;
-          case SimdOperation::Fn_lessThanOrEqual:    return Expr::I8x16lessThanOrEqualU;
-          case SimdOperation::Fn_greaterThan:        return Expr::I8x16greaterThanU;
-          case SimdOperation::Fn_greaterThanOrEqual: return Expr::I8x16greaterThanOrEqualU;
-          case SimdOperation::Fn_fromInt8x16Bits:    return Expr::Limit;
+          case SimdOperation::Fn_addSaturate:        return Op::I8x16addSaturateU;
+          case SimdOperation::Fn_subSaturate:        return Op::I8x16subSaturateU;
+          case SimdOperation::Fn_extractLane:        return Op::I8x16extractLaneU;
+          case SimdOperation::Fn_shiftRightByScalar: return Op::I8x16shiftRightByScalarU;
+          case SimdOperation::Fn_lessThan:           return Op::I8x16lessThanU;
+          case SimdOperation::Fn_lessThanOrEqual:    return Op::I8x16lessThanOrEqualU;
+          case SimdOperation::Fn_greaterThan:        return Op::I8x16greaterThanU;
+          case SimdOperation::Fn_greaterThanOrEqual: return Op::I8x16greaterThanOrEqualU;
+          case SimdOperation::Fn_fromInt8x16Bits:    return Op::Limit;
           default: break;
         }
         MOZ_FALLTHROUGH;
       case SimdType::Int8x16:
         // Bitcasts Uint8x16 <--> Int8x16 become noops.
         switch (op) {
-          case SimdOperation::Fn_fromUint8x16Bits: return Expr::Limit;
-          case SimdOperation::Fn_fromUint16x8Bits: return Expr::I8x16fromInt16x8Bits;
-          case SimdOperation::Fn_fromUint32x4Bits: return Expr::I8x16fromInt32x4Bits;
+          case SimdOperation::Fn_fromUint8x16Bits: return Op::Limit;
+          case SimdOperation::Fn_fromUint16x8Bits: return Op::I8x16fromInt16x8Bits;
+          case SimdOperation::Fn_fromUint32x4Bits: return Op::I8x16fromInt32x4Bits;
           default: break;
         }
         ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
         break;
 
       case SimdType::Uint16x8:
         // Handle the special unsigned opcodes, then fall through to Int16x8.
         switch(op) {
-          case SimdOperation::Fn_addSaturate:        return Expr::I16x8addSaturateU;
-          case SimdOperation::Fn_subSaturate:        return Expr::I16x8subSaturateU;
-          case SimdOperation::Fn_extractLane:        return Expr::I16x8extractLaneU;
-          case SimdOperation::Fn_shiftRightByScalar: return Expr::I16x8shiftRightByScalarU;
-          case SimdOperation::Fn_lessThan:           return Expr::I16x8lessThanU;
-          case SimdOperation::Fn_lessThanOrEqual:    return Expr::I16x8lessThanOrEqualU;
-          case SimdOperation::Fn_greaterThan:        return Expr::I16x8greaterThanU;
-          case SimdOperation::Fn_greaterThanOrEqual: return Expr::I16x8greaterThanOrEqualU;
-          case SimdOperation::Fn_fromInt16x8Bits:    return Expr::Limit;
+          case SimdOperation::Fn_addSaturate:        return Op::I16x8addSaturateU;
+          case SimdOperation::Fn_subSaturate:        return Op::I16x8subSaturateU;
+          case SimdOperation::Fn_extractLane:        return Op::I16x8extractLaneU;
+          case SimdOperation::Fn_shiftRightByScalar: return Op::I16x8shiftRightByScalarU;
+          case SimdOperation::Fn_lessThan:           return Op::I16x8lessThanU;
+          case SimdOperation::Fn_lessThanOrEqual:    return Op::I16x8lessThanOrEqualU;
+          case SimdOperation::Fn_greaterThan:        return Op::I16x8greaterThanU;
+          case SimdOperation::Fn_greaterThanOrEqual: return Op::I16x8greaterThanOrEqualU;
+          case SimdOperation::Fn_fromInt16x8Bits:    return Op::Limit;
           default: break;
         }
         MOZ_FALLTHROUGH;
       case SimdType::Int16x8:
         // Bitcasts Uint16x8 <--> Int16x8 become noops.
         switch (op) {
-          case SimdOperation::Fn_fromUint8x16Bits: return Expr::I16x8fromInt8x16Bits;
-          case SimdOperation::Fn_fromUint16x8Bits: return Expr::Limit;
-          case SimdOperation::Fn_fromUint32x4Bits: return Expr::I16x8fromInt32x4Bits;
+          case SimdOperation::Fn_fromUint8x16Bits: return Op::I16x8fromInt8x16Bits;
+          case SimdOperation::Fn_fromUint16x8Bits: return Op::Limit;
+          case SimdOperation::Fn_fromUint32x4Bits: return Op::I16x8fromInt32x4Bits;
           default: break;
         }
         ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
         break;
 
       case SimdType::Uint32x4:
         // Handle the special unsigned opcodes, then fall through to Int32x4.
         switch(op) {
-          case SimdOperation::Fn_shiftRightByScalar: return Expr::I32x4shiftRightByScalarU;
-          case SimdOperation::Fn_lessThan:           return Expr::I32x4lessThanU;
-          case SimdOperation::Fn_lessThanOrEqual:    return Expr::I32x4lessThanOrEqualU;
-          case SimdOperation::Fn_greaterThan:        return Expr::I32x4greaterThanU;
-          case SimdOperation::Fn_greaterThanOrEqual: return Expr::I32x4greaterThanOrEqualU;
-          case SimdOperation::Fn_fromFloat32x4:      return Expr::I32x4fromFloat32x4U;
-          case SimdOperation::Fn_fromInt32x4Bits:    return Expr::Limit;
+          case SimdOperation::Fn_shiftRightByScalar: return Op::I32x4shiftRightByScalarU;
+          case SimdOperation::Fn_lessThan:           return Op::I32x4lessThanU;
+          case SimdOperation::Fn_lessThanOrEqual:    return Op::I32x4lessThanOrEqualU;
+          case SimdOperation::Fn_greaterThan:        return Op::I32x4greaterThanU;
+          case SimdOperation::Fn_greaterThanOrEqual: return Op::I32x4greaterThanOrEqualU;
+          case SimdOperation::Fn_fromFloat32x4:      return Op::I32x4fromFloat32x4U;
+          case SimdOperation::Fn_fromInt32x4Bits:    return Op::Limit;
           default: break;
         }
         MOZ_FALLTHROUGH;
       case SimdType::Int32x4:
         // Bitcasts Uint32x4 <--> Int32x4 become noops.
         switch (op) {
-          case SimdOperation::Fn_fromUint8x16Bits: return Expr::I32x4fromInt8x16Bits;
-          case SimdOperation::Fn_fromUint16x8Bits: return Expr::I32x4fromInt16x8Bits;
-          case SimdOperation::Fn_fromUint32x4Bits: return Expr::Limit;
+          case SimdOperation::Fn_fromUint8x16Bits: return Op::I32x4fromInt8x16Bits;
+          case SimdOperation::Fn_fromUint16x8Bits: return Op::I32x4fromInt16x8Bits;
+          case SimdOperation::Fn_fromUint32x4Bits: return Op::Limit;
           default: break;
         }
         ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
         break;
 
       case SimdType::Float32x4:
         switch (op) {
-          case SimdOperation::Fn_fromUint8x16Bits: return Expr::F32x4fromInt8x16Bits;
-          case SimdOperation::Fn_fromUint16x8Bits: return Expr::F32x4fromInt16x8Bits;
-          case SimdOperation::Fn_fromUint32x4Bits: return Expr::F32x4fromInt32x4Bits;
+          case SimdOperation::Fn_fromUint8x16Bits: return Op::F32x4fromInt8x16Bits;
+          case SimdOperation::Fn_fromUint16x8Bits: return Op::F32x4fromInt16x8Bits;
+          case SimdOperation::Fn_fromUint32x4Bits: return Op::F32x4fromInt32x4Bits;
           default: break;
         }
         ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
         break;
 
       case SimdType::Bool8x16:
         ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
         break;
@@ -2993,116 +2993,116 @@ class MOZ_STACK_CLASS FunctionValidator
 
     void setReturnedType(ExprType ret) {
         ret_ = ret;
         hasAlreadyReturned_ = true;
     }
 
     /**************************************************************** Labels */
   private:
-    bool writeBr(uint32_t absolute, Expr expr = Expr::Br) {
-        MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
+    bool writeBr(uint32_t absolute, Op op = Op::Br) {
+        MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
         MOZ_ASSERT(absolute < blockDepth_);
-        return encoder().writeExpr(expr) &&
+        return encoder().writeOp(op) &&
                encoder().writeVarU32(blockDepth_ - 1 - absolute);
     }
     void removeLabel(PropertyName* label, LabelMap* map) {
         LabelMap::Ptr p = map->lookup(label);
         MOZ_ASSERT(p);
         map->remove(p);
     }
 
   public:
     bool pushBreakableBlock() {
-        return encoder().writeExpr(Expr::Block) &&
+        return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
                breakableStack_.append(blockDepth_++);
     }
     bool popBreakableBlock() {
         JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
-        return encoder().writeExpr(Expr::End);
+        return encoder().writeOp(Op::End);
     }
 
     bool pushUnbreakableBlock(const NameVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels) {
                 if (!breakLabels_.putNew(label, blockDepth_))
                     return false;
             }
         }
         blockDepth_++;
-        return encoder().writeExpr(Expr::Block) &&
+        return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void));
     }
     bool popUnbreakableBlock(const NameVector* labels = nullptr) {
         if (labels) {
             for (PropertyName* label : *labels)
                 removeLabel(label, &breakLabels_);
         }
         --blockDepth_;
-        return encoder().writeExpr(Expr::End);
+        return encoder().writeOp(Op::End);
     }
 
     bool pushContinuableBlock() {
-        return encoder().writeExpr(Expr::Block) &&
+        return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
                continuableStack_.append(blockDepth_++);
     }
     bool popContinuableBlock() {
         JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
-        return encoder().writeExpr(Expr::End);
+        return encoder().writeOp(Op::End);
     }
 
     bool pushLoop() {
-        return encoder().writeExpr(Expr::Block) &&
+        return encoder().writeOp(Op::Block) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
-               encoder().writeExpr(Expr::Loop) &&
+               encoder().writeOp(Op::Loop) &&
                encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
                breakableStack_.append(blockDepth_++) &&
                continuableStack_.append(blockDepth_++);
     }
     bool popLoop() {
         JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
         JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
-        return encoder().writeExpr(Expr::End) &&
-               encoder().writeExpr(Expr::End);
+        return encoder().writeOp(Op::End) &&
+               encoder().writeOp(Op::End);
     }
 
     bool pushIf(size_t* typeAt) {
         ++blockDepth_;
-        return encoder().writeExpr(Expr::If) &&
+        return encoder().writeOp(Op::If) &&
                encoder().writePatchableFixedU7(typeAt);
     }
     bool switchToElse() {
         MOZ_ASSERT(blockDepth_ > 0);
-        return encoder().writeExpr(Expr::Else);
+        return encoder().writeOp(Op::Else);
     }
     void setIfType(size_t typeAt, ExprType type) {
         encoder().patchFixedU7(typeAt, uint8_t(type));
     }
     bool popIf() {
         MOZ_ASSERT(blockDepth_ > 0);
         --blockDepth_;
-        return encoder().writeExpr(Expr::End);
+        return encoder().writeOp(Op::End);
     }
     bool popIf(size_t typeAt, ExprType type) {
         MOZ_ASSERT(blockDepth_ > 0);
         --blockDepth_;
-        if (!encoder().writeExpr(Expr::End))
+        if (!encoder().writeOp(Op::End))
             return false;
 
         setIfType(typeAt, type);
         return true;
     }
 
     bool writeBreakIf() {
-        return writeBr(breakableStack_.back(), Expr::BrIf);
+        return writeBr(breakableStack_.back(), Op::BrIf);
     }
     bool writeContinueIf() {
-        return writeBr(continuableStack_.back(), Expr::BrIf);
+        return writeBr(continuableStack_.back(), Op::BrIf);
     }
     bool writeUnlabeledBreakOrContinue(bool isBreak) {
         return writeBr(isBreak? breakableStack_.back() : continuableStack_.back());
     }
     bool writeContinue() {
         return writeBr(continuableStack_.back());
     }
 
@@ -3146,75 +3146,75 @@ class MOZ_STACK_CLASS FunctionValidator
 
     size_t numLocals() const { return locals_.count(); }
 
     /**************************************************** Encoding interface */
 
     Encoder& encoder() { return *encoder_; }
 
     MOZ_MUST_USE bool writeInt32Lit(int32_t i32) {
-        return encoder().writeExpr(Expr::I32Const) &&
+        return encoder().writeOp(Op::I32Const) &&
                encoder().writeVarS32(i32);
     }
     MOZ_MUST_USE bool writeConstExpr(const NumLit& lit) {
         switch (lit.which()) {
           case NumLit::Fixnum:
           case NumLit::NegativeInt:
           case NumLit::BigUnsigned:
             return writeInt32Lit(lit.toInt32());
           case NumLit::Float:
-            return encoder().writeExpr(Expr::F32Const) &&
+            return encoder().writeOp(Op::F32Const) &&
                    encoder().writeFixedF32(lit.toFloat());
           case NumLit::Double:
-            return encoder().writeExpr(Expr::F64Const) &&
+            return encoder().writeOp(Op::F64Const) &&
                    encoder().writeFixedF64(lit.toDouble());
           case NumLit::Int8x16:
           case NumLit::Uint8x16:
-            return encoder().writeExpr(Expr::I8x16Const) &&
+            return encoder().writeOp(Op::I8x16Const) &&
                    encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
           case NumLit::Int16x8:
           case NumLit::Uint16x8:
-            return encoder().writeExpr(Expr::I16x8Const) &&
+            return encoder().writeOp(Op::I16x8Const) &&
                    encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
           case NumLit::Int32x4:
           case NumLit::Uint32x4:
-            return encoder().writeExpr(Expr::I32x4Const) &&
+            return encoder().writeOp(Op::I32x4Const) &&
                    encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
           case NumLit::Float32x4:
-            return encoder().writeExpr(Expr::F32x4Const) &&
+            return encoder().writeOp(Op::F32x4Const) &&
                    encoder().writeFixedF32x4(lit.simdValue().asFloat32x4());
           case NumLit::Bool8x16:
             // Boolean vectors use the Int8x16 memory representation.
-            return encoder().writeExpr(Expr::B8x16Const) &&
+            return encoder().writeOp(Op::B8x16Const) &&
                    encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
           case NumLit::Bool16x8:
             // Boolean vectors use the Int16x8 memory representation.
-            return encoder().writeExpr(Expr::B16x8Const) &&
+            return encoder().writeOp(Op::B16x8Const) &&
                    encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
           case NumLit::Bool32x4:
             // Boolean vectors use the Int32x4 memory representation.
-            return encoder().writeExpr(Expr::B32x4Const) &&
+            return encoder().writeOp(Op::B32x4Const) &&
                    encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
           case NumLit::OutOfRangeInt:
             break;
         }
         MOZ_CRASH("unexpected literal type");
     }
-    MOZ_MUST_USE bool writeCall(ParseNode* pn, Expr op) {
-        return encoder().writeExpr(op) &&
+    MOZ_MUST_USE bool writeCall(ParseNode* pn, Op op) {
+        return encoder().writeOp(op) &&
                fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_MUST_USE bool prepareCall(ParseNode* pn) {
         return fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
     }
-    MOZ_MUST_USE bool writeSimdOp(SimdType simdType, SimdOperation op) {
-        Expr expr = SimdToExpr(simdType, op);
-        if (expr == Expr::Limit)
+    MOZ_MUST_USE bool writeSimdOp(SimdType simdType, SimdOperation simdOp) {
+        Op op = SimdToOp(simdType, simdOp);
+        if (op == Op::Limit)
             return true;
-        return encoder().writeExpr(expr);
+        return encoder().writeOp(op);
     }
 };
 
 } /* anonymous namespace */
 
 /*****************************************************************************/
 // asm.js type-checking and code-generation algorithm
 
@@ -3873,17 +3873,17 @@ IsLiteralOrConst(FunctionValidator& f, P
 
     *lit = ExtractNumericLiteral(f.m(), pn);
     return true;
 }
 
 static bool
 CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt)
 {
-    if (!f.encoder().writeExpr(Expr::End))
+    if (!f.encoder().writeOp(Op::End))
         return false;
 
     if (!f.hasAlreadyReturned()) {
         f.setReturnedType(ExprType::Void);
         return true;
     }
 
     if (!lastNonEmptyStmt->isKind(PNK_RETURN) && !IsVoid(f.returnedType()))
@@ -3944,28 +3944,28 @@ CheckVariables(FunctionValidator& f, Par
         return false;
 
     for (uint32_t i = 0; i < inits.length(); i++) {
         NumLit lit = inits[i];
         if (lit.isZeroBits())
             continue;
         if (!f.writeConstExpr(lit))
             return false;
-        if (!f.encoder().writeExpr(Expr::SetLocal))
+        if (!f.encoder().writeOp(Op::SetLocal))
             return false;
         if (!f.encoder().writeVarU32(firstVar + i))
             return false;
     }
 
     *stmtIter = stmt;
     return true;
 }
 
 static bool
-CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type);
+CheckExpr(FunctionValidator& f, ParseNode* op, Type* type);
 
 static bool
 CheckNumericLiteral(FunctionValidator& f, ParseNode* num, Type* type)
 {
     NumLit lit = ExtractNumericLiteral(f.m(), num);
     if (!lit.valid())
         return f.fail(num, "numeric literal out of representable integer range");
     *type = Type::lit(lit);
@@ -3973,33 +3973,33 @@ CheckNumericLiteral(FunctionValidator& f
 }
 
 static bool
 CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
 {
     PropertyName* name = varRef->name();
 
     if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
-        if (!f.encoder().writeExpr(Expr::GetLocal))
+        if (!f.encoder().writeOp(Op::GetLocal))
             return false;
         if (!f.encoder().writeVarU32(local->slot))
             return false;
         *type = local->type;
         return true;
     }
 
     if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
         switch (global->which()) {
           case ModuleValidator::Global::ConstantLiteral:
             *type = global->varOrConstType();
             return f.writeConstExpr(global->constLiteralValue());
           case ModuleValidator::Global::ConstantImport:
           case ModuleValidator::Global::Variable: {
             *type = global->varOrConstType();
-            return f.encoder().writeExpr(Expr::GetGlobal) &&
+            return f.encoder().writeOp(Op::GetGlobal) &&
                    f.encoder().writeVarU32(global->varOrConstIndex());
           }
           case ModuleValidator::Global::Function:
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
           case ModuleValidator::Global::FuncPtrTable:
           case ModuleValidator::Global::ArrayView:
@@ -4097,17 +4097,17 @@ CheckArrayAccess(FunctionValidator& f, P
                 return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
         }
     }
 
     // Don't generate the mask op if there is no need for it which could happen for
     // a shift of zero or a SIMD access.
     if (mask != NoMask) {
         return f.writeInt32Lit(mask) &&
-               f.encoder().writeExpr(Expr::I32And);
+               f.encoder().writeOp(Op::I32And);
     }
 
     return true;
 }
 
 static bool
 CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
                            bool isSimd, Scalar::Type* viewType)
@@ -4135,24 +4135,24 @@ static bool
 CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
 {
     Scalar::Type viewType;
 
     if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), NoSimd, &viewType))
         return false;
 
     switch (viewType) {
-      case Scalar::Int8:    if (!f.encoder().writeExpr(Expr::I32Load8S))  return false; break;
-      case Scalar::Uint8:   if (!f.encoder().writeExpr(Expr::I32Load8U))  return false; break;
-      case Scalar::Int16:   if (!f.encoder().writeExpr(Expr::I32Load16S)) return false; break;
-      case Scalar::Uint16:  if (!f.encoder().writeExpr(Expr::I32Load16U)) return false; break;
+      case Scalar::Int8:    if (!f.encoder().writeOp(Op::I32Load8S))  return false; break;
+      case Scalar::Uint8:   if (!f.encoder().writeOp(Op::I32Load8U))  return false; break;
+      case Scalar::Int16:   if (!f.encoder().writeOp(Op::I32Load16S)) return false; break;
+      case Scalar::Uint16:  if (!f.encoder().writeOp(Op::I32Load16U)) return false; break;
       case Scalar::Uint32:
-      case Scalar::Int32:   if (!f.encoder().writeExpr(Expr::I32Load))    return false; break;
-      case Scalar::Float32: if (!f.encoder().writeExpr(Expr::F32Load))    return false; break;
-      case Scalar::Float64: if (!f.encoder().writeExpr(Expr::F64Load))    return false; break;
+      case Scalar::Int32:   if (!f.encoder().writeOp(Op::I32Load))    return false; break;
+      case Scalar::Float32: if (!f.encoder().writeOp(Op::F32Load))    return false; break;
+      case Scalar::Float64: if (!f.encoder().writeOp(Op::F64Load))    return false; break;
       default: MOZ_CRASH("unexpected scalar type");
     }
 
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
       case Scalar::Uint8:
@@ -4206,44 +4206,44 @@ CheckStoreArray(FunctionValidator& f, Pa
         break;
       default:
         MOZ_CRASH("Unexpected view type");
     }
 
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Uint8:
-        if (!f.encoder().writeExpr(Expr::I32TeeStore8))
+        if (!f.encoder().writeOp(Op::I32TeeStore8))
             return false;
         break;
       case Scalar::Int16:
       case Scalar::Uint16:
-        if (!f.encoder().writeExpr(Expr::I32TeeStore16))
+        if (!f.encoder().writeOp(Op::I32TeeStore16))
             return false;
         break;
       case Scalar::Int32:
       case Scalar::Uint32:
-        if (!f.encoder().writeExpr(Expr::I32TeeStore))
+        if (!f.encoder().writeOp(Op::I32TeeStore))
             return false;
         break;
       case Scalar::Float32:
         if (rhsType.isFloatish()) {
-            if (!f.encoder().writeExpr(Expr::F32TeeStore))
+            if (!f.encoder().writeOp(Op::F32TeeStore))
                 return false;
         } else {
-            if (!f.encoder().writeExpr(Expr::F64TeeStoreF32))
+            if (!f.encoder().writeOp(Op::F64TeeStoreF32))
                 return false;
         }
         break;
       case Scalar::Float64:
         if (rhsType.isFloatish()) {
-            if (!f.encoder().writeExpr(Expr::F32TeeStoreF64))
+            if (!f.encoder().writeOp(Op::F32TeeStoreF64))
                 return false;
         } else {
-            if (!f.encoder().writeExpr(Expr::F64TeeStore))
+            if (!f.encoder().writeOp(Op::F64TeeStore))
                 return false;
         }
         break;
       default: MOZ_CRASH("unexpected scalar type");
     }
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
@@ -4257,17 +4257,17 @@ CheckAssignName(FunctionValidator& f, Pa
 {
     RootedPropertyName name(f.cx(), lhs->name());
 
     if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) {
         Type rhsType;
         if (!CheckExpr(f, rhs, &rhsType))
             return false;
 
-        if (!f.encoder().writeExpr(Expr::TeeLocal))
+        if (!f.encoder().writeOp(Op::TeeLocal))
             return false;
         if (!f.encoder().writeVarU32(lhsVar->slot))
             return false;
 
         if (!(rhsType <= lhsVar->type)) {
             return f.failf(lhs, "%s is not a subtype of %s",
                            rhsType.toChars(), lhsVar->type.toChars());
         }
@@ -4281,17 +4281,17 @@ CheckAssignName(FunctionValidator& f, Pa
 
         Type rhsType;
         if (!CheckExpr(f, rhs, &rhsType))
             return false;
 
         Type globType = global->varOrConstType();
         if (!(rhsType <= globType))
             return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(), globType.toChars());
-        if (!f.encoder().writeExpr(Expr::TeeGlobal))
+        if (!f.encoder().writeOp(Op::TeeGlobal))
             return false;
         if (!f.encoder().writeVarU32(global->varOrConstIndex()))
             return false;
 
         *type = rhsType;
         return true;
     }
 
@@ -4333,17 +4333,17 @@ CheckMathIMul(FunctionValidator& f, Pars
         return false;
 
     if (!lhsType.isIntish())
         return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
     if (!rhsType.isIntish())
         return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
 
     *type = Type::Signed;
-    return f.encoder().writeExpr(Expr::I32Mul);
+    return f.encoder().writeOp(Op::I32Mul);
 }
 
 static bool
 CheckMathClz32(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.clz32 must be passed 1 argument");
 
@@ -4352,44 +4352,44 @@ CheckMathClz32(FunctionValidator& f, Par
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (!argType.isIntish())
         return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
 
     *type = Type::Fixnum;
-    return f.encoder().writeExpr(Expr::I32Clz);
+    return f.encoder().writeOp(Op::I32Clz);
 }
 
 static bool
 CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.abs must be passed 1 argument");
 
     ParseNode* arg = CallArgList(call);
 
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (argType.isSigned()) {
         *type = Type::Unsigned;
-        return f.encoder().writeExpr(Expr::I32Abs);
+        return f.encoder().writeOp(Op::I32Abs);
     }
 
     if (argType.isMaybeDouble()) {
         *type = Type::Double;
-        return f.encoder().writeExpr(Expr::F64Abs);
+        return f.encoder().writeOp(Op::F64Abs);
     }
 
     if (argType.isMaybeFloat()) {
         *type = Type::Floatish;
-        return f.encoder().writeExpr(Expr::F32Abs);
+        return f.encoder().writeOp(Op::F32Abs);
     }
 
     return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
 }
 
 static bool
 CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type)
 {
@@ -4399,66 +4399,66 @@ CheckMathSqrt(FunctionValidator& f, Pars
     ParseNode* arg = CallArgList(call);
 
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (argType.isMaybeDouble()) {
         *type = Type::Double;
-        return f.encoder().writeExpr(Expr::F64Sqrt);
+        return f.encoder().writeOp(Op::F64Sqrt);
     }
 
     if (argType.isMaybeFloat()) {
         *type = Type::Floatish;
-        return f.encoder().writeExpr(Expr::F32Sqrt);
+        return f.encoder().writeOp(Op::F32Sqrt);
     }
 
     return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
 }
 
 static bool
 CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* type)
 {
     if (CallArgListLength(callNode) < 2)
         return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
 
     ParseNode* firstArg = CallArgList(callNode);
     Type firstType;
     if (!CheckExpr(f, firstArg, &firstType))
         return false;
 
-    Expr expr;
+    Op op;
     if (firstType.isMaybeDouble()) {
         *type = Type::Double;
         firstType = Type::MaybeDouble;
-        expr = isMax ? Expr::F64Max : Expr::F64Min;
+        op = isMax ? Op::F64Max : Op::F64Min;
     } else if (firstType.isMaybeFloat()) {
         *type = Type::Float;
         firstType = Type::MaybeFloat;
-        expr = isMax ? Expr::F32Max : Expr::F32Min;
+        op = isMax ? Op::F32Max : Op::F32Min;
     } else if (firstType.isSigned()) {
         *type = Type::Signed;
         firstType = Type::Signed;
-        expr = isMax ? Expr::I32Max : Expr::I32Min;
+        op = isMax ? Op::I32Max : Op::I32Min;
     } else {
         return f.failf(firstArg, "%s is not a subtype of double?, float? or signed",
                        firstType.toChars());
     }
 
     unsigned numArgs = CallArgListLength(callNode);
     ParseNode* nextArg = NextNode(firstArg);
     for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) {
         Type nextType;
         if (!CheckExpr(f, nextArg, &nextType))
             return false;
         if (!(nextType <= firstType))
             return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
 
-        if (!f.encoder().writeExpr(expr))
+        if (!f.encoder().writeOp(op))
             return false;
     }
 
     return true;
 }
 
 static bool
 CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
@@ -4485,36 +4485,36 @@ CheckSharedArrayAtomicAccess(FunctionVal
       default:
         return f.failf(viewName, "not an integer array");
     }
 
     return true;
 }
 
 static bool
-WriteAtomicOperator(FunctionValidator& f, Expr opcode, Scalar::Type viewType)
-{
-    return f.encoder().writeExpr(opcode) &&
+WriteAtomicOperator(FunctionValidator& f, Op opcode, Scalar::Type viewType)
+{
+    return f.encoder().writeOp(opcode) &&
            f.encoder().writeFixedU8(viewType);
 }
 
 static bool
 CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 2)
         return f.fail(call, "Atomics.load must be passed 2 arguments");
 
     ParseNode* arrayArg = CallArgList(call);
     ParseNode* indexArg = NextNode(arrayArg);
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, viewType))
+    if (!WriteAtomicOperator(f, Op::I32AtomicsLoad, viewType))
         return false;
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
 
     *type = Type::Int;
     return true;
 }
@@ -4535,17 +4535,17 @@ CheckAtomicsStore(FunctionValidator& f, 
 
     if (!rhsType.isIntish())
         return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    if (!WriteAtomicOperator(f, Expr::I32AtomicsStore, viewType))
+    if (!WriteAtomicOperator(f, Op::I32AtomicsStore, viewType))
         return false;
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
 
     *type = rhsType;
     return true;
 }
@@ -4566,17 +4566,17 @@ CheckAtomicsBinop(FunctionValidator& f, 
 
     if (!valueArgType.isIntish())
         return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, viewType))
+    if (!WriteAtomicOperator(f, Op::I32AtomicsBinOp, viewType))
         return false;
     if (!f.encoder().writeFixedU8(uint8_t(op)))
         return false;
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
 
     *type = Type::Int;
@@ -4623,17 +4623,17 @@ CheckAtomicsCompareExchange(FunctionVali
 
     if (!newValueArgType.isIntish())
         return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    if (!WriteAtomicOperator(f, Expr::I32AtomicsCompareExchange, viewType))
+    if (!WriteAtomicOperator(f, Op::I32AtomicsCompareExchange, viewType))
         return false;
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
 
     *type = Type::Int;
     return true;
 }
@@ -4654,17 +4654,17 @@ CheckAtomicsExchange(FunctionValidator& 
 
     if (!valueArgType.isIntish())
         return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars());
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    if (!WriteAtomicOperator(f, Expr::I32AtomicsExchange, viewType))
+    if (!WriteAtomicOperator(f, Op::I32AtomicsExchange, viewType))
         return false;
 
     if (!WriteArrayAccessFlags(f, viewType))
         return false;
 
     *type = Type::Int;
     return true;
 }
@@ -4787,17 +4787,17 @@ CheckInternalCall(FunctionValidator& f, 
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
     ModuleValidator::Func* callee;
     if (!CheckFunctionSignature(f.m(), callNode, Move(sig), calleeName, &callee))
         return false;
 
-    if (!f.writeCall(callNode, Expr::Call))
+    if (!f.writeCall(callNode, Op::Call))
         return false;
 
     if (!f.encoder().writeVarU32(callee->index()))
         return false;
 
     *type = Type::ret(ret);
     return true;
 }
@@ -4870,17 +4870,17 @@ CheckFuncPtrCall(FunctionValidator& f, P
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
     uint32_t tableIndex;
     if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &tableIndex))
         return false;
 
-    if (!f.writeCall(callNode, Expr::OldCallIndirect))
+    if (!f.writeCall(callNode, Op::OldCallIndirect))
         return false;
 
     // Call signature
     if (!f.encoder().writeVarU32(f.m().funcPtrTable(tableIndex).sigIndex()))
         return false;
 
     *type = Type::ret(ret);
     return true;
@@ -4911,35 +4911,35 @@ CheckFFICall(FunctionValidator& f, Parse
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
     uint32_t funcIndex;
     if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &funcIndex))
         return false;
 
-    if (!f.writeCall(callNode, Expr::Call))
+    if (!f.writeCall(callNode, Op::Call))
         return false;
 
     if (!f.encoder().writeVarU32(funcIndex))
         return false;
 
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType)
 {
     if (inputType.isMaybeDouble())
-        return f.encoder().writeExpr(Expr::F32DemoteF64);
+        return f.encoder().writeOp(Op::F32DemoteF64);
     if (inputType.isSigned())
-        return f.encoder().writeExpr(Expr::F32ConvertSI32);
+        return f.encoder().writeOp(Op::F32ConvertSI32);
     if (inputType.isUnsigned())
-        return f.encoder().writeExpr(Expr::F32ConvertUI32);
+        return f.encoder().writeOp(Op::F32ConvertUI32);
     if (inputType.isFloatish())
         return true;
 
     return f.failf(inputNode, "%s is not a subtype of signed, unsigned, double? or floatish",
                    inputType.toChars());
 }
 
 static bool
@@ -4987,38 +4987,38 @@ CheckMathFRound(FunctionValidator& f, Pa
     return true;
 }
 
 static bool
 CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
                      Type* type)
 {
     unsigned arity = 0;
-    Expr f32;
-    Expr f64;
+    Op f32;
+    Op f64;
     switch (func) {
       case AsmJSMathBuiltin_imul:   return CheckMathIMul(f, callNode, type);
       case AsmJSMathBuiltin_clz32:  return CheckMathClz32(f, callNode, type);
       case AsmJSMathBuiltin_abs:    return CheckMathAbs(f, callNode, type);
       case AsmJSMathBuiltin_sqrt:   return CheckMathSqrt(f, callNode, type);
       case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, type);
       case AsmJSMathBuiltin_min:    return CheckMathMinMax(f, callNode, /* isMax = */ false, type);
       case AsmJSMathBuiltin_max:    return CheckMathMinMax(f, callNode, /* isMax = */ true, type);
-      case AsmJSMathBuiltin_ceil:   arity = 1; f64 = Expr::F64Ceil;  f32 = Expr::F32Ceil;     break;
-      case AsmJSMathBuiltin_floor:  arity = 1; f64 = Expr::F64Floor; f32 = Expr::F32Floor;    break;
-      case AsmJSMathBuiltin_sin:    arity = 1; f64 = Expr::F64Sin;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_cos:    arity = 1; f64 = Expr::F64Cos;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_tan:    arity = 1; f64 = Expr::F64Tan;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_asin:   arity = 1; f64 = Expr::F64Asin;  f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_acos:   arity = 1; f64 = Expr::F64Acos;  f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_atan:   arity = 1; f64 = Expr::F64Atan;  f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_exp:    arity = 1; f64 = Expr::F64Exp;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_log:    arity = 1; f64 = Expr::F64Log;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_pow:    arity = 2; f64 = Expr::F64Pow;   f32 = Expr::Unreachable; break;
-      case AsmJSMathBuiltin_atan2:  arity = 2; f64 = Expr::F64Atan2; f32 = Expr::Unreachable; break;
+      case AsmJSMathBuiltin_ceil:   arity = 1; f64 = Op::F64Ceil;  f32 = Op::F32Ceil;     break;
+      case AsmJSMathBuiltin_floor:  arity = 1; f64 = Op::F64Floor; f32 = Op::F32Floor;    break;
+      case AsmJSMathBuiltin_sin:    arity = 1; f64 = Op::F64Sin;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_cos:    arity = 1; f64 = Op::F64Cos;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_tan:    arity = 1; f64 = Op::F64Tan;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_asin:   arity = 1; f64 = Op::F64Asin;  f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_acos:   arity = 1; f64 = Op::F64Acos;  f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_atan:   arity = 1; f64 = Op::F64Atan;  f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_exp:    arity = 1; f64 = Op::F64Exp;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_log:    arity = 1; f64 = Op::F64Log;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_pow:    arity = 2; f64 = Op::F64Pow;   f32 = Op::Unreachable; break;
+      case AsmJSMathBuiltin_atan2:  arity = 2; f64 = Op::F64Atan2; f32 = Op::Unreachable; break;
       default: MOZ_CRASH("unexpected mathBuiltin function");
     }
 
     unsigned actualArity = CallArgListLength(callNode);
     if (actualArity != arity)
         return f.failf(callNode, "call passed %u arguments, expected %u", actualArity, arity);
 
     if (!f.prepareCall(callNode))
@@ -5028,36 +5028,36 @@ CheckMathBuiltinCall(FunctionValidator& 
     ParseNode* argNode = CallArgList(callNode);
     if (!CheckExpr(f, argNode, &firstType))
         return false;
 
     if (!firstType.isMaybeFloat() && !firstType.isMaybeDouble())
         return f.fail(argNode, "arguments to math call should be a subtype of double? or float?");
 
     bool opIsDouble = firstType.isMaybeDouble();
-    if (!opIsDouble && f32 == Expr::Unreachable)
+    if (!opIsDouble && f32 == Op::Unreachable)
         return f.fail(callNode, "math builtin cannot be used as float");
 
     if (arity == 2) {
         Type secondType;
         argNode = NextNode(argNode);
         if (!CheckExpr(f, argNode, &secondType))
             return false;
 
         if (firstType.isMaybeDouble() && !secondType.isMaybeDouble())
             return f.fail(argNode, "both arguments to math builtin call should be the same type");
         if (firstType.isMaybeFloat() && !secondType.isMaybeFloat())
             return f.fail(argNode, "both arguments to math builtin call should be the same type");
     }
 
     if (opIsDouble) {
-        if (!f.encoder().writeExpr(f64))
+        if (!f.encoder().writeOp(f64))
             return false;
     } else {
-        if (!f.encoder().writeExpr(f32))
+        if (!f.encoder().writeOp(f32))
             return false;
     }
 
     *type = opIsDouble ? Type::Double : Type::Floatish;
     return true;
 }
 
 namespace {
@@ -5142,17 +5142,17 @@ class CheckSimdScalarArgs
             // re-emitting them as float32 constants.
             if (simdType_ != SimdType::Float32x4 || !actualType.isDoubleLit()) {
                 return f.failf(arg, "%s is not a subtype of %s%s",
                                actualType.toChars(), formalType_.toChars(),
                                simdType_ == SimdType::Float32x4 ? " or doublelit" : "");
             }
 
             // We emitted a double literal and actually want a float32.
-            return f.encoder().writeExpr(Expr::F32DemoteF64);
+            return f.encoder().writeOp(Op::F32DemoteF64);
         }
 
         return true;
     }
 };
 
 class CheckSimdSelectArgs
 {
@@ -5329,17 +5329,17 @@ CheckSimdReplaceLane(FunctionValidator& 
     arg = NextNode(arg);
 
     // Third argument is the scalar
     Type scalarType;
     if (!CheckExpr(f, arg, &scalarType))
         return false;
     if (!(scalarType <= SimdToCoercedScalarType(opType))) {
         if (opType == SimdType::Float32x4 && scalarType.isDoubleLit()) {
-            if (!f.encoder().writeExpr(Expr::F32DemoteF64))
+            if (!f.encoder().writeOp(Op::F32DemoteF64))
                 return false;
         } else {
             return f.failf(arg, "%s is not the correct type to replace an element of %s",
                            scalarType.toChars(), vecType.toChars());
         }
     }
 
     if (!f.writeSimdOp(opType, SimdOperation::Fn_replaceLane))
@@ -5722,39 +5722,39 @@ CoerceResult(FunctionValidator& f, Parse
 {
     MOZ_ASSERT(expected.isCanonical());
 
     // At this point, the bytecode resembles this:
     //      | the thing we wanted to coerce | current position |>
     switch (expected.which()) {
       case Type::Void:
         if (!actual.isVoid()) {
-            if (!f.encoder().writeExpr(Expr::Drop))
+            if (!f.encoder().writeOp(Op::Drop))
                 return false;
         }
         break;
       case Type::Int:
         if (!actual.isIntish())
             return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
         break;
       case Type::Float:
         if (!CheckFloatCoercionArg(f, expr, actual))
             return false;
         break;
       case Type::Double:
         if (actual.isMaybeDouble()) {
             // No conversion necessary.
         } else if (actual.isMaybeFloat()) {
-            if (!f.encoder().writeExpr(Expr::F64PromoteF32))
+            if (!f.encoder().writeOp(Op::F64PromoteF32))
                 return false;
         } else if (actual.isSigned()) {
-            if (!f.encoder().writeExpr(Expr::F64ConvertSI32))
+            if (!f.encoder().writeOp(Op::F64ConvertSI32))
                 return false;
         } else if (actual.isUnsigned()) {
-            if (!f.encoder().writeExpr(Expr::F64ConvertUI32))
+            if (!f.encoder().writeOp(Op::F64ConvertUI32))
                 return false;
         } else {
             return f.failf(expr, "%s is not a subtype of double?, float?, signed or unsigned", actual.toChars());
         }
         break;
       default:
         MOZ_ASSERT(expected.isSimd(), "Incomplete switch");
         if (actual != expected)
@@ -5886,42 +5886,42 @@ CheckNot(FunctionValidator& f, ParseNode
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (!operandType.isInt())
         return f.failf(operand, "%s is not a subtype of int", operandType.toChars());
 
     *type = Type::Int;
-    return f.encoder().writeExpr(Expr::I32Eqz);
+    return f.encoder().writeOp(Op::I32Eqz);
 }
 
 static bool
 CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     MOZ_ASSERT(expr->isKind(PNK_NEG));
     ParseNode* operand = UnaryKid(expr);
 
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (operandType.isInt()) {
         *type = Type::Intish;
-        return f.encoder().writeExpr(Expr::I32Neg);
+        return f.encoder().writeOp(Op::I32Neg);
     }
 
     if (operandType.isMaybeDouble()) {
         *type = Type::Double;
-        return f.encoder().writeExpr(Expr::F64Neg);
+        return f.encoder().writeOp(Op::F64Neg);
     }
 
     if (operandType.isMaybeFloat()) {
         *type = Type::Floatish;
-        return f.encoder().writeExpr(Expr::F32Neg);
+        return f.encoder().writeOp(Op::F32Neg);
     }
 
     return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
 }
 
 static bool
 CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type)
 {
@@ -5929,18 +5929,18 @@ CheckCoerceToInt(FunctionValidator& f, P
     ParseNode* operand = UnaryKid(expr);
 
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
         *type = Type::Signed;
-        Expr opcode = operandType.isMaybeDouble() ? Expr::I32TruncSF64 : Expr::I32TruncSF32;
-        return f.encoder().writeExpr(opcode);
+        Op opcode = operandType.isMaybeDouble() ? Op::I32TruncSF64 : Op::I32TruncSF32;
+        return f.encoder().writeOp(opcode);
     }
 
     if (!operandType.isIntish())
         return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
 
     *type = Type::Signed;
     return true;
 }
@@ -5956,17 +5956,17 @@ CheckBitNot(FunctionValidator& f, ParseN
 
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (!operandType.isIntish())
         return f.failf(operand, "%s is not a subtype of intish", operandType.toChars());
 
-    if (!f.encoder().writeExpr(Expr::I32BitNot))
+    if (!f.encoder().writeOp(Op::I32BitNot))
         return false;
 
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckAsExprStatement(FunctionValidator& f, ParseNode* exprStmt);
@@ -5974,17 +5974,17 @@ CheckAsExprStatement(FunctionValidator& 
 static bool
 CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
 {
     MOZ_ASSERT(comma->isKind(PNK_COMMA));
     ParseNode* operands = ListHead(comma);
 
     // The block depth isn't taken into account here, because a comma list can't
     // contain breaks and continues and nested control flow structures.
-    if (!f.encoder().writeExpr(Expr::Block))
+    if (!f.encoder().writeOp(Op::Block))
         return false;
 
     size_t typeAt;
     if (!f.encoder().writePatchableFixedU7(&typeAt))
         return false;
 
     ParseNode* pn = operands;
     for (; NextNode(pn); pn = NextNode(pn)) {
@@ -5992,17 +5992,17 @@ CheckComma(FunctionValidator& f, ParseNo
             return false;
     }
 
     if (!CheckExpr(f, pn, type))
         return false;
 
     f.encoder().patchFixedU7(typeAt, uint8_t(type->toWasmBlockSignatureType()));
 
-    return f.encoder().writeExpr(Expr::End);
+    return f.encoder().writeOp(Op::End);
 }
 
 static bool
 CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
 {
     MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL));
 
     ParseNode* cond = TernaryKid1(ternary);
@@ -6098,27 +6098,27 @@ CheckMultiply(FunctionValidator& f, Pars
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
     if (lhsType.isInt() && rhsType.isInt()) {
         if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
             return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
         *type = Type::Intish;
-        return f.encoder().writeExpr(Expr::I32Mul);
+        return f.encoder().writeOp(Op::I32Mul);
     }
 
     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
         *type = Type::Double;
-        return f.encoder().writeExpr(Expr::F64Mul);
+        return f.encoder().writeOp(Op::F64Mul);
     }
 
     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
         *type = Type::Floatish;
-        return f.encoder().writeExpr(Expr::F32Mul);
+        return f.encoder().writeOp(Op::F32Mul);
     }
 
     return f.fail(star, "multiply operands must be both int, both double? or both float?");
 }
 
 static bool
 CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAddOrSubOut = nullptr)
 {
@@ -6153,25 +6153,25 @@ CheckAddOrSub(FunctionValidator& f, Pars
         rhsNumAddOrSub = 0;
     }
 
     unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
     if (numAddOrSub > (1<<20))
         return f.fail(expr, "too many + or - without intervening coercion");
 
     if (lhsType.isInt() && rhsType.isInt()) {
-        if (!f.encoder().writeExpr(expr->isKind(PNK_ADD) ? Expr::I32Add : Expr::I32Sub))
+        if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::I32Add : Op::I32Sub))
             return false;
         *type = Type::Intish;
     } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
-        if (!f.encoder().writeExpr(expr->isKind(PNK_ADD) ? Expr::F64Add : Expr::F64Sub))
+        if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::F64Add : Op::F64Sub))
             return false;
         *type = Type::Double;
     } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
-        if (!f.encoder().writeExpr(expr->isKind(PNK_ADD) ? Expr::F32Add : Expr::F32Sub))
+        if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::F32Add : Op::F32Sub))
             return false;
         *type = Type::Floatish;
     } else {
         return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
                        lhsType.toChars(), rhsType.toChars());
     }
 
     if (numAddOrSubOut)
@@ -6190,35 +6190,35 @@ CheckDivOrMod(FunctionValidator& f, Pars
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
         *type = Type::Double;
-        return f.encoder().writeExpr(expr->isKind(PNK_DIV) ? Expr::F64Div : Expr::F64Mod);
+        return f.encoder().writeOp(expr->isKind(PNK_DIV) ? Op::F64Div : Op::F64Mod);
     }
 
     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
         *type = Type::Floatish;
         if (expr->isKind(PNK_DIV))
-            return f.encoder().writeExpr(Expr::F32Div);
+            return f.encoder().writeOp(Op::F32Div);
         else
             return f.fail(expr, "modulo cannot receive float arguments");
     }
 
     if (lhsType.isSigned() && rhsType.isSigned()) {
         *type = Type::Intish;
-        return f.encoder().writeExpr(expr->isKind(PNK_DIV) ? Expr::I32DivS : Expr::I32RemS);
+        return f.encoder().writeOp(expr->isKind(PNK_DIV) ? Op::I32DivS : Op::I32RemS);
     }
 
     if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
         *type = Type::Intish;
-        return f.encoder().writeExpr(expr->isKind(PNK_DIV) ? Expr::I32DivU : Expr::I32RemU);
+        return f.encoder().writeOp(expr->isKind(PNK_DIV) ? Op::I32DivU : Op::I32RemU);
     }
 
     return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type)
@@ -6239,63 +6239,63 @@ CheckComparison(FunctionValidator& f, Pa
         !(lhsType.isUnsigned() && rhsType.isUnsigned()) &&
         !(lhsType.isDouble() && rhsType.isDouble()) &&
         !(lhsType.isFloat() && rhsType.isFloat()))
     {
         return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
                        "%s and %s are given", lhsType.toChars(), rhsType.toChars());
     }
 
-    Expr stmt;
+    Op stmt;
     if (lhsType.isSigned() && rhsType.isSigned()) {
         switch (comp->getOp()) {
-          case JSOP_EQ: stmt = Expr::I32Eq;  break;
-          case JSOP_NE: stmt = Expr::I32Ne;  break;
-          case JSOP_LT: stmt = Expr::I32LtS; break;
-          case JSOP_LE: stmt = Expr::I32LeS; break;
-          case JSOP_GT: stmt = Expr::I32GtS; break;
-          case JSOP_GE: stmt = Expr::I32GeS; break;
+          case JSOP_EQ: stmt = Op::I32Eq;  break;
+          case JSOP_NE: stmt = Op::I32Ne;  break;
+          case JSOP_LT: stmt = Op::I32LtS; break;
+          case JSOP_LE: stmt = Op::I32LeS; break;
+          case JSOP_GT: stmt = Op::I32GtS; break;
+          case JSOP_GE: stmt = Op::I32GeS; break;
           default: MOZ_CRASH("unexpected comparison op");
         }
     } else if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
         switch (comp->getOp()) {
-          case JSOP_EQ: stmt = Expr::I32Eq;  break;
-          case JSOP_NE: stmt = Expr::I32Ne;  break;
-          case JSOP_LT: stmt = Expr::I32LtU; break;
-          case JSOP_LE: stmt = Expr::I32LeU; break;
-          case JSOP_GT: stmt = Expr::I32GtU; break;
-          case JSOP_GE: stmt = Expr::I32GeU; break;
+          case JSOP_EQ: stmt = Op::I32Eq;  break;
+          case JSOP_NE: stmt = Op::I32Ne;  break;
+          case JSOP_LT: stmt = Op::I32LtU; break;
+          case JSOP_LE: stmt = Op::I32LeU; break;
+          case JSOP_GT: stmt = Op::I32GtU; break;
+          case JSOP_GE: stmt = Op::I32GeU; break;
           default: MOZ_CRASH("unexpected comparison op");
         }
     } else if (lhsType.isDouble()) {
         switch (comp->getOp()) {
-          case JSOP_EQ: stmt = Expr::F64Eq; break;
-          case JSOP_NE: stmt = Expr::F64Ne; break;
-          case JSOP_LT: stmt = Expr::F64Lt; break;
-          case JSOP_LE: stmt = Expr::F64Le; break;
-          case JSOP_GT: stmt = Expr::F64Gt; break;
-          case JSOP_GE: stmt = Expr::F64Ge; break;
+          case JSOP_EQ: stmt = Op::F64Eq; break;
+          case JSOP_NE: stmt = Op::F64Ne; break;
+          case JSOP_LT: stmt = Op::F64Lt; break;
+          case JSOP_LE: stmt = Op::F64Le; break;
+          case JSOP_GT: stmt = Op::F64Gt; break;
+          case JSOP_GE: stmt = Op::F64Ge; break;
           default: MOZ_CRASH("unexpected comparison op");
         }
     } else if (lhsType.isFloat()) {
         switch (comp->getOp()) {
-          case JSOP_EQ: stmt = Expr::F32Eq; break;
-          case JSOP_NE: stmt = Expr::F32Ne; break;
-          case JSOP_LT: stmt = Expr::F32Lt; break;
-          case JSOP_LE: stmt = Expr::F32Le; break;
-          case JSOP_GT: stmt = Expr::F32Gt; break;
-          case JSOP_GE: stmt = Expr::F32Ge; break;
+          case JSOP_EQ: stmt = Op::F32Eq; break;
+          case JSOP_NE: stmt = Op::F32Ne; break;
+          case JSOP_LT: stmt = Op::F32Lt; break;
+          case JSOP_LE: stmt = Op::F32Le; break;
+          case JSOP_GT: stmt = Op::F32Gt; break;
+          case JSOP_GE: stmt = Op::F32Ge; break;
           default: MOZ_CRASH("unexpected comparison op");
         }
     } else {
         MOZ_CRASH("unexpected type");
     }
 
     *type = Type::Int;
-    return f.encoder().writeExpr(stmt);
+    return f.encoder().writeOp(stmt);
 }
 
 static bool
 CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type)
 {
     ParseNode* lhs = BitwiseLeft(bitwise);
     ParseNode* rhs = BitwiseRight(bitwise);
 
@@ -6342,22 +6342,22 @@ CheckBitwise(FunctionValidator& f, Parse
         return false;
 
     if (!lhsType.isIntish())
         return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
     if (!rhsType.isIntish())
         return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
 
     switch (bitwise->getKind()) {
-      case PNK_BITOR:  if (!f.encoder().writeExpr(Expr::I32Or))   return false; break;
-      case PNK_BITAND: if (!f.encoder().writeExpr(Expr::I32And))  return false; break;
-      case PNK_BITXOR: if (!f.encoder().writeExpr(Expr::I32Xor))  return false; break;
-      case PNK_LSH:    if (!f.encoder().writeExpr(Expr::I32Shl))  return false; break;
-      case PNK_RSH:    if (!f.encoder().writeExpr(Expr::I32ShrS)) return false; break;
-      case PNK_URSH:   if (!f.encoder().writeExpr(Expr::I32ShrU)) return false; break;
+      case PNK_BITOR:  if (!f.encoder().writeOp(Op::I32Or))   return false; break;
+      case PNK_BITAND: if (!f.encoder().writeOp(Op::I32And))  return false; break;
+      case PNK_BITXOR: if (!f.encoder().writeOp(Op::I32Xor))  return false; break;
+      case PNK_LSH:    if (!f.encoder().writeOp(Op::I32Shl))  return false; break;
+      case PNK_RSH:    if (!f.encoder().writeOp(Op::I32ShrS)) return false; break;
+      case PNK_URSH:   if (!f.encoder().writeOp(Op::I32ShrU)) return false; break;
       default: MOZ_CRASH("not a bitwise op");
     }
 
     return true;
 }
 
 static bool
 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
@@ -6421,17 +6421,17 @@ CheckAsExprStatement(FunctionValidator& 
         return CheckCoercedCall(f, expr, Type::Void, &ignored);
     }
 
     Type resultType;
     if (!CheckExpr(f, expr, &resultType))
         return false;
 
     if (!resultType.isVoid()) {
-        if (!f.encoder().writeExpr(Expr::Drop))
+        if (!f.encoder().writeOp(Op::Drop))
             return false;
     }
 
     return true;
 }
 
 static bool
 CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt)
@@ -6455,17 +6455,17 @@ CheckLoopConditionOnEntry(FunctionValida
         return false;
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     // TODO change this to i32.eqz
     // i32.eq 0 $f
     if (!f.writeInt32Lit(0))
         return false;
-    if (!f.encoder().writeExpr(Expr::I32Eq))
+    if (!f.encoder().writeOp(Op::I32Eq))
         return false;
 
     // brIf (i32.eq 0 $f) $out
     if (!f.writeBreakIf())
         return false;
 
     return true;
 }
@@ -6828,17 +6828,17 @@ CheckSwitch(FunctionValidator& f, ParseN
             return f.fail(switchBody, "switch body may not contain lexical declarations");
         switchBody = switchBody->scopeBody();
     }
 
     ParseNode* stmt = ListHead(switchBody);
     if (!stmt) {
         if (!CheckSwitchExpr(f, switchExpr))
             return false;
-        if (!f.encoder().writeExpr(Expr::Drop))
+        if (!f.encoder().writeOp(Op::Drop))
             return false;
         return true;
     }
 
     if (!CheckDefaultAtEnd(f, stmt))
         return false;
 
     int32_t low = 0, high = 0;
@@ -6883,25 +6883,25 @@ CheckSwitch(FunctionValidator& f, ParseN
     uint32_t defaultDepth = numCases;
 
     // Subtract lowest case value, so that all the cases start from 0.
     if (low) {
         if (!CheckSwitchExpr(f, switchExpr))
             return false;
         if (!f.writeInt32Lit(low))
             return false;
-        if (!f.encoder().writeExpr(Expr::I32Sub))
+        if (!f.encoder().writeOp(Op::I32Sub))
             return false;
     } else {
         if (!CheckSwitchExpr(f, switchExpr))
             return false;
     }
 
     // Start the br_table block.
-    if (!f.encoder().writeExpr(Expr::BrTable))
+    if (!f.encoder().writeOp(Op::BrTable))
         return false;
 
     // Write the number of cases (tableLength - 1 + 1 (default)).
     // Write the number of cases (tableLength - 1 + 1 (default)).
     if (!f.encoder().writeVarU32(tableLength))
         return false;
 
     // Each case value describes the relative depth to the actual block. When
@@ -6970,17 +6970,17 @@ CheckReturn(FunctionValidator& f, ParseN
 
         if (!type.isReturnType())
             return f.failf(expr, "%s is not a valid return type", type.toChars());
 
         if (!CheckReturnType(f, expr, Type::canonicalize(type)))
             return false;
     }
 
-    if (!f.encoder().writeExpr(Expr::Return))
+    if (!f.encoder().writeOp(Op::Return))
         return false;
 
     return true;
 }
 
 static bool
 CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* labels /*= nullptr */)
 {
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -369,71 +369,71 @@ class AstTeeLocal : public AstExpr
     }
     AstExpr& value() const {
         return value_;
     }
 };
 
 class AstBlock : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstName name_;
     AstExprVector exprs_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Block;
-    explicit AstBlock(Expr expr, ExprType type, AstName name, AstExprVector&& exprs)
+    explicit AstBlock(Op op, ExprType type, AstName name, AstExprVector&& exprs)
       : AstExpr(Kind, type),
-        expr_(expr),
+        op_(op),
         name_(name),
         exprs_(Move(exprs))
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstName name() const { return name_; }
     const AstExprVector& exprs() const { return exprs_; }
 };
 
 class AstBranch : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstExpr* cond_;
     AstRef target_;
     AstExpr* value_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Branch;
-    explicit AstBranch(Expr expr, ExprType type,
+    explicit AstBranch(Op op, ExprType type,
                        AstExpr* cond, AstRef target, AstExpr* value)
       : AstExpr(Kind, type),
-        expr_(expr),
+        op_(op),
         cond_(cond),
         target_(target),
         value_(value)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstRef& target() { return target_; }
     AstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; }
     AstExpr* maybeValue() const { return value_; }
 };
 
 class AstCall : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstRef func_;
     AstExprVector args_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Call;
-    AstCall(Expr expr, ExprType type, AstRef func, AstExprVector&& args)
-      : AstExpr(Kind, type), expr_(expr), func_(func), args_(Move(args))
+    AstCall(Op op, ExprType type, AstRef func, AstExprVector&& args)
+      : AstExpr(Kind, type), op_(op), func_(func), args_(Move(args))
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstRef& func() { return func_; }
     const AstExprVector& args() const { return args_; }
 };
 
 class AstCallIndirect : public AstExpr
 {
     AstRef sig_;
     AstExprVector args_;
@@ -502,72 +502,71 @@ class AstLoadStoreAddress
 
     AstExpr& base() const { return *base_; }
     int32_t flags() const { return flags_; }
     int32_t offset() const { return offset_; }
 };
 
 class AstLoad : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstLoadStoreAddress address_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Load;
-    explicit AstLoad(Expr expr, const AstLoadStoreAddress &address)
+    explicit AstLoad(Op op, const AstLoadStoreAddress &address)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr),
+        op_(op),
         address_(address)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     const AstLoadStoreAddress& address() const { return address_; }
 };
 
 class AstStore : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstLoadStoreAddress address_;
     AstExpr* value_;
 
   public:
     static const AstExprKind Kind = AstExprKind::Store;
-    explicit AstStore(Expr expr, const AstLoadStoreAddress &address,
-                          AstExpr* value)
+    explicit AstStore(Op op, const AstLoadStoreAddress &address, AstExpr* value)
       : AstExpr(Kind, ExprType::Void),
-        expr_(expr),
+        op_(op),
         address_(address),
         value_(value)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     const AstLoadStoreAddress& address() const { return address_; }
     AstExpr& value() const { return *value_; }
 };
 
 class AstCurrentMemory final : public AstExpr
 {
   public:
     static const AstExprKind Kind = AstExprKind::CurrentMemory;
     explicit AstCurrentMemory()
       : AstExpr(Kind, ExprType::I32)
     {}
 };
 
 class AstGrowMemory final : public AstExpr
 {
-    AstExpr* op_;
+    AstExpr* operand_;
 
   public:
     static const AstExprKind Kind = AstExprKind::GrowMemory;
-    explicit AstGrowMemory(AstExpr* op)
-      : AstExpr(Kind, ExprType::I32), op_(op)
+    explicit AstGrowMemory(AstExpr* operand)
+      : AstExpr(Kind, ExprType::I32), operand_(operand)
     {}
 
-    AstExpr* op() const { return op_; }
+    AstExpr* operand() const { return operand_; }
 };
 
 class AstBranchTable : public AstExpr
 {
     AstExpr& index_;
     AstRef default_;
     AstRefVector table_;
     AstExpr* value_;
@@ -912,100 +911,100 @@ class AstModule : public AstNode
     }
     const AstGlobalVector& globals() const {
         return globals_;
     }
 };
 
 class AstUnaryOperator final : public AstExpr
 {
-    Expr expr_;
-    AstExpr* op_;
+    Op op_;
+    AstExpr* operand_;
 
   public:
     static const AstExprKind Kind = AstExprKind::UnaryOperator;
-    explicit AstUnaryOperator(Expr expr, AstExpr* op)
+    explicit AstUnaryOperator(Op op, AstExpr* operand)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr), op_(op)
+        op_(op), operand_(operand)
     {}
 
-    Expr expr() const { return expr_; }
-    AstExpr* op() const { return op_; }
+    Op op() const { return op_; }
+    AstExpr* operand() const { return operand_; }
 };
 
 class AstBinaryOperator final : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstExpr* lhs_;
     AstExpr* rhs_;
 
   public:
     static const AstExprKind Kind = AstExprKind::BinaryOperator;
-    explicit AstBinaryOperator(Expr expr, AstExpr* lhs, AstExpr* rhs)
+    explicit AstBinaryOperator(Op op, AstExpr* lhs, AstExpr* rhs)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr), lhs_(lhs), rhs_(rhs)
+        op_(op), lhs_(lhs), rhs_(rhs)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstExpr* lhs() const { return lhs_; }
     AstExpr* rhs() const { return rhs_; }
 };
 
 class AstTernaryOperator : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstExpr* op0_;
     AstExpr* op1_;
     AstExpr* op2_;
 
   public:
     static const AstExprKind Kind = AstExprKind::TernaryOperator;
-    AstTernaryOperator(Expr expr, AstExpr* op0, AstExpr* op1, AstExpr* op2)
+    AstTernaryOperator(Op op, AstExpr* op0, AstExpr* op1, AstExpr* op2)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr), op0_(op0), op1_(op1), op2_(op2)
+        op_(op), op0_(op0), op1_(op1), op2_(op2)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstExpr* op0() const { return op0_; }
     AstExpr* op1() const { return op1_; }
     AstExpr* op2() const { return op2_; }
 };
 
 class AstComparisonOperator final : public AstExpr
 {
-    Expr expr_;
+    Op op_;
     AstExpr* lhs_;
     AstExpr* rhs_;
 
   public:
     static const AstExprKind Kind = AstExprKind::ComparisonOperator;
-    explicit AstComparisonOperator(Expr expr, AstExpr* lhs, AstExpr* rhs)
+    explicit AstComparisonOperator(Op op, AstExpr* lhs, AstExpr* rhs)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr), lhs_(lhs), rhs_(rhs)
+        op_(op), lhs_(lhs), rhs_(rhs)
     {}
 
-    Expr expr() const { return expr_; }
+    Op op() const { return op_; }
     AstExpr* lhs() const { return lhs_; }
     AstExpr* rhs() const { return rhs_; }
 };
 
 class AstConversionOperator final : public AstExpr
 {
-    Expr expr_;
-    AstExpr* op_;
+    Op op_;
+    AstExpr* operand_;
 
   public:
     static const AstExprKind Kind = AstExprKind::ConversionOperator;
-    explicit AstConversionOperator(Expr expr, AstExpr* op)
+    explicit AstConversionOperator(Op op, AstExpr* operand)
       : AstExpr(Kind, ExprType::Limit),
-        expr_(expr), op_(op)
+        op_(op), operand_(operand)
     {}
 
-    Expr expr() const { return expr_; }
-    AstExpr* op() const { return op_; }
+    Op op() const { return op_; }
+    AstExpr* operand() const { return operand_; }
 };
 
 // This is an artificial AST node which can fill operand slots in an AST
 // constructed from parsing or decoding stack-machine code that doesn't have
 // an inherent AST structure.
 class AstPop final : public AstExpr
 {
   public:
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -54,17 +54,17 @@
  *   this reduces register pressure and instruction count.
  *
  * - (Bug 1286816) Opportunities for cheaply folding in a constant rhs to
  *   conditionals.
  *
  * - (Bug 1286816) Boolean evaluation for control can be optimized by pushing a
  *   bool-generating operation onto the value stack in the same way that we now
  *   push latent constants and local lookups, or (easier) by remembering the
- *   operation in a side location if the next Expr will consume it.
+ *   operation in a side location if the next Op will consume it.
  *
  * - (Bug 1286816) brIf pessimizes by branching over code that performs stack
  *   cleanup and a branch.  If no cleanup is needed we can just branch
  *   conditionally to the target.
  *
  * - (Bug 1316804) brTable pessimizes by always dispatching to code that pops
  *   the stack and then jumps to the code for the target case.  If no cleanup is
  *   needed we could just branch conditionally to the target; if the same amount
@@ -128,33 +128,33 @@ using mozilla::IsPowerOfTwo;
 using mozilla::SpecificNaN;
 
 namespace js {
 namespace wasm {
 
 using namespace js::jit;
 using JS::GenericNaN;
 
-struct BaseCompilePolicy : ExprIterPolicy
+struct BaseCompilePolicy : OpIterPolicy
 {
     static const bool Output = true;
 
     // The baseline compiler tracks values on a stack of its own -- it
     // needs to scan that stack for spilling -- and thus has no need
     // for the values maintained by the iterator.
     //
     // The baseline compiler tracks control items on a stack of its
     // own as well.
     //
     // TODO / REDUNDANT (Bug 1316814): It would be nice if we could
     // make use of the iterator's ControlItems and not require our own
     // stack for that.
 };
 
-typedef ExprIter<BaseCompilePolicy> BaseExprIter;
+typedef OpIter<BaseCompilePolicy> BaseOpIter;
 
 typedef bool IsUnsigned;
 typedef bool IsSigned;
 typedef bool ZeroOnOverflow;
 typedef bool IsKnownNotZero;
 typedef bool HandleNaNSpecially;
 typedef unsigned ByteSize;
 typedef unsigned BitSize;
@@ -501,17 +501,17 @@ class BaseCompiler
         //
         // All other registers must be explicitly saved and restored
         // by the OOL code before being used.
 
         virtual void generate(MacroAssembler& masm) = 0;
     };
 
     const ModuleGeneratorData&  mg_;
-    BaseExprIter                iter_;
+    BaseOpIter                  iter_;
     const FuncBytes&            func_;
     size_t                      lastReadCallSite_;
     TempAllocator&              alloc_;
     const ValTypeVector&        locals_;         // Types of parameters and locals
     int32_t                     localSize_;      // Size of local area in bytes (stable after beginFunction)
     int32_t                     varLow_;         // Low byte offset of local area for true locals (not parameters)
     int32_t                     varHigh_;        // High byte offset + 1 of local area for true locals
     int32_t                     maxFramePushed_; // Max value of masm.framePushed() observed
@@ -6579,540 +6579,540 @@ BaseCompiler::emitBody()
             CHECK(stk_.reserve(stk_.length() + overhead * 2));
         }
 
         overhead--;
 
         if (done())
             return true;
 
-        Expr expr;
-        CHECK(iter_.readExpr(&expr));
-
-        switch (expr) {
+        uint16_t op;
+        CHECK(iter_.readOp(&op));
+
+        switch (op) {
           // Control opcodes
-          case Expr::Nop:
+          case uint16_t(Op::Nop):
             CHECK(iter_.readNop());
             NEXT();
-          case Expr::Drop:
+          case uint16_t(Op::Drop):
             CHECK_NEXT(emitDrop());
-          case Expr::Block:
+          case uint16_t(Op::Block):
             CHECK_NEXT(emitBlock());
-          case Expr::Loop:
+          case uint16_t(Op::Loop):
             CHECK_NEXT(emitLoop());
-          case Expr::If:
+          case uint16_t(Op::If):
             CHECK_NEXT(emitIf());
-          case Expr::Else:
+          case uint16_t(Op::Else):
             CHECK_NEXT(emitElse());
-          case Expr::End:
+          case uint16_t(Op::End):
             CHECK_NEXT(emitEnd());
-          case Expr::Br:
+          case uint16_t(Op::Br):
             CHECK_NEXT(emitBr());
-          case Expr::BrIf:
+          case uint16_t(Op::BrIf):
             CHECK_NEXT(emitBrIf());
-          case Expr::BrTable:
+          case uint16_t(Op::BrTable):
             CHECK_NEXT(emitBrTable());
-          case Expr::Return:
+          case uint16_t(Op::Return):
             CHECK_NEXT(emitReturn());
-          case Expr::Unreachable:
+          case uint16_t(Op::Unreachable):
             CHECK(iter_.readUnreachable());
             if (!deadCode_) {
                 unreachableTrap();
                 deadCode_ = true;
                 popValueStackTo(ctl_.back().stackSize);
             }
             NEXT();
 
           // Calls
-          case Expr::Call:
+          case uint16_t(Op::Call):
             CHECK_NEXT(emitCall());
-          case Expr::CallIndirect:
+          case uint16_t(Op::CallIndirect):
             CHECK_NEXT(emitCallIndirect(/* oldStyle = */ false));
-          case Expr::OldCallIndirect:
+          case uint16_t(Op::OldCallIndirect):
             CHECK_NEXT(emitCallIndirect(/* oldStyle = */ true));
 
           // Locals and globals
-          case Expr::GetLocal:
+          case uint16_t(Op::GetLocal):
             CHECK_NEXT(emitGetLocal());
-          case Expr::SetLocal:
+          case uint16_t(Op::SetLocal):
             CHECK_NEXT(emitSetLocal());
-          case Expr::TeeLocal:
+          case uint16_t(Op::TeeLocal):
             CHECK_NEXT(emitTeeLocal());
-          case Expr::GetGlobal:
+          case uint16_t(Op::GetGlobal):
             CHECK_NEXT(emitGetGlobal());
-          case Expr::SetGlobal:
+          case uint16_t(Op::SetGlobal):
             CHECK_NEXT(emitSetGlobal());
-          case Expr::TeeGlobal:
+          case uint16_t(Op::TeeGlobal):
             CHECK_NEXT(emitTeeGlobal());
 
           // Select
-          case Expr::Select:
+          case uint16_t(Op::Select):
             CHECK_NEXT(emitSelect());
 
           // I32
-          case Expr::I32Const: {
+          case uint16_t(Op::I32Const): {
             int32_t i32;
             CHECK(iter_.readI32Const(&i32));
             if (!deadCode_)
                 pushI32(i32);
             NEXT();
           }
-          case Expr::I32Add:
+          case uint16_t(Op::I32Add):
             CHECK_NEXT(emitBinary(emitAddI32, ValType::I32));
-          case Expr::I32Sub:
+          case uint16_t(Op::I32Sub):
             CHECK_NEXT(emitBinary(emitSubtractI32, ValType::I32));
-          case Expr::I32Mul:
+          case uint16_t(Op::I32Mul):
             CHECK_NEXT(emitBinary(emitMultiplyI32, ValType::I32));
-          case Expr::I32DivS:
+          case uint16_t(Op::I32DivS):
             CHECK_NEXT(emitBinary(emitQuotientI32, ValType::I32));
-          case Expr::I32DivU:
+          case uint16_t(Op::I32DivU):
             CHECK_NEXT(emitBinary(emitQuotientU32, ValType::I32));
-          case Expr::I32RemS:
+          case uint16_t(Op::I32RemS):
             CHECK_NEXT(emitBinary(emitRemainderI32, ValType::I32));
-          case Expr::I32RemU:
+          case uint16_t(Op::I32RemU):
             CHECK_NEXT(emitBinary(emitRemainderU32, ValType::I32));
-          case Expr::I32Min:
+          case uint16_t(Op::I32Min):
             CHECK_NEXT(emitBinary(emitMinI32, ValType::I32));
-          case Expr::I32Max:
+          case uint16_t(Op::I32Max):
             CHECK_NEXT(emitBinary(emitMaxI32, ValType::I32));
-          case Expr::I32Eqz:
+          case uint16_t(Op::I32Eqz):
             CHECK_NEXT(emitConversion(emitEqzI32, ValType::I32, ValType::I32));
-          case Expr::I32TruncSF32:
+          case uint16_t(Op::I32TruncSF32):
             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<false>, ValType::F32, ValType::I32));
-          case Expr::I32TruncUF32:
+          case uint16_t(Op::I32TruncUF32):
             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<true>, ValType::F32, ValType::I32));
-          case Expr::I32TruncSF64:
+          case uint16_t(Op::I32TruncSF64):
             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<false>, ValType::F64, ValType::I32));
-          case Expr::I32TruncUF64:
+          case uint16_t(Op::I32TruncUF64):
             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<true>, ValType::F64, ValType::I32));
-          case Expr::I32WrapI64:
+          case uint16_t(Op::I32WrapI64):
             CHECK_NEXT(emitConversion(emitWrapI64ToI32, ValType::I64, ValType::I32));
-          case Expr::I32ReinterpretF32:
+          case uint16_t(Op::I32ReinterpretF32):
             CHECK_NEXT(emitConversion(emitReinterpretF32AsI32, ValType::F32, ValType::I32));
-          case Expr::I32Clz:
+          case uint16_t(Op::I32Clz):
             CHECK_NEXT(emitUnary(emitClzI32, ValType::I32));
-          case Expr::I32Ctz:
+          case uint16_t(Op::I32Ctz):
             CHECK_NEXT(emitUnary(emitCtzI32, ValType::I32));
-          case Expr::I32Popcnt:
+          case uint16_t(Op::I32Popcnt):
             CHECK_NEXT(emitUnary(emitPopcntI32, ValType::I32));
-          case Expr::I32Abs:
+          case uint16_t(Op::I32Abs):
             CHECK_NEXT(emitUnary(emitAbsI32, ValType::I32));
-          case Expr::I32Neg:
+          case uint16_t(Op::I32Neg):
             CHECK_NEXT(emitUnary(emitNegateI32, ValType::I32));
-          case Expr::I32Or:
+          case uint16_t(Op::I32Or):
             CHECK_NEXT(emitBinary(emitOrI32, ValType::I32));
-          case Expr::I32And:
+          case uint16_t(Op::I32And):
             CHECK_NEXT(emitBinary(emitAndI32, ValType::I32));
-          case Expr::I32Xor:
+          case uint16_t(Op::I32Xor):
             CHECK_NEXT(emitBinary(emitXorI32, ValType::I32));
-          case Expr::I32Shl:
+          case uint16_t(Op::I32Shl):
             CHECK_NEXT(emitBinary(emitShlI32, ValType::I32));
-          case Expr::I32ShrS:
+          case uint16_t(Op::I32ShrS):
             CHECK_NEXT(emitBinary(emitShrI32, ValType::I32));
-          case Expr::I32ShrU:
+          case uint16_t(Op::I32ShrU):
             CHECK_NEXT(emitBinary(emitShrU32, ValType::I32));
-          case Expr::I32BitNot:
+          case uint16_t(Op::I32BitNot):
             CHECK_NEXT(emitUnary(emitBitNotI32, ValType::I32));
-          case Expr::I32Load8S:
+          case uint16_t(Op::I32Load8S):
             CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int8));
-          case Expr::I32Load8U:
+          case uint16_t(Op::I32Load8U):
             CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint8));
-          case Expr::I32Load16S:
+          case uint16_t(Op::I32Load16S):
             CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int16));
-          case Expr::I32Load16U:
+          case uint16_t(Op::I32Load16U):
             CHECK_NEXT(emitLoad(ValType::I32, Scalar::Uint16));
-          case Expr::I32Load:
+          case uint16_t(Op::I32Load):
             CHECK_NEXT(emitLoad(ValType::I32, Scalar::Int32));
-          case Expr::I32Store8:
+          case uint16_t(Op::I32Store8):
             CHECK_NEXT(emitStore(ValType::I32, Scalar::Int8));
-          case Expr::I32TeeStore8:
+          case uint16_t(Op::I32TeeStore8):
             CHECK_NEXT(emitTeeStore(ValType::I32, Scalar::Int8));
-          case Expr::I32Store16:
+          case uint16_t(Op::I32Store16):
             CHECK_NEXT(emitStore(ValType::I32, Scalar::Int16));
-          case Expr::I32TeeStore16:
+          case uint16_t(Op::I32TeeStore16):
             CHECK_NEXT(emitTeeStore(ValType::I32, Scalar::Int16));
-          case Expr::I32Store:
+          case uint16_t(Op::I32Store):
             CHECK_NEXT(emitStore(ValType::I32, Scalar::Int32));
-          case Expr::I32TeeStore:
+          case uint16_t(Op::I32TeeStore):
             CHECK_NEXT(emitTeeStore(ValType::I32, Scalar::Int32));
-          case Expr::I32Rotr:
+          case uint16_t(Op::I32Rotr):
             CHECK_NEXT(emitBinary(emitRotrI32, ValType::I32));
-          case Expr::I32Rotl:
+          case uint16_t(Op::I32Rotl):
             CHECK_NEXT(emitBinary(emitRotlI32, ValType::I32));
 
           // I64
-          case Expr::I64Const: {
+          case uint16_t(Op::I64Const): {
             int64_t i64;
             CHECK(iter_.readI64Const(&i64));
             if (!deadCode_)
                 pushI64(i64);
             NEXT();
           }
-          case Expr::I64Add:
+          case uint16_t(Op::I64Add):
             CHECK_NEXT(emitBinary(emitAddI64, ValType::I64));
-          case Expr::I64Sub:
+          case uint16_t(Op::I64Sub):
             CHECK_NEXT(emitBinary(emitSubtractI64, ValType::I64));
-          case Expr::I64Mul:
+          case uint16_t(Op::I64Mul):
             CHECK_NEXT(emitBinary(emitMultiplyI64, ValType::I64));
-          case Expr::I64DivS:
+          case uint16_t(Op::I64DivS):
 #ifdef INT_DIV_I64_CALLOUT
             CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::DivI64, ValType::I64));
 #else
             CHECK_NEXT(emitBinary(emitQuotientI64, ValType::I64));
 #endif
-          case Expr::I64DivU:
+          case uint16_t(Op::I64DivU):
 #ifdef INT_DIV_I64_CALLOUT
             CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::UDivI64, ValType::I64));
 #else
             CHECK_NEXT(emitBinary(emitQuotientU64, ValType::I64));
 #endif
-          case Expr::I64RemS:
+          case uint16_t(Op::I64RemS):
 #ifdef INT_DIV_I64_CALLOUT
             CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::ModI64, ValType::I64));
 #else
             CHECK_NEXT(emitBinary(emitRemainderI64, ValType::I64));
 #endif
-          case Expr::I64RemU:
+          case uint16_t(Op::I64RemU):
 #ifdef INT_DIV_I64_CALLOUT
             CHECK_NEXT(emitDivOrModI64BuiltinCall(SymbolicAddress::UModI64, ValType::I64));
 #else
             CHECK_NEXT(emitBinary(emitRemainderU64, ValType::I64));
 #endif
-          case Expr::I64TruncSF32:
+          case uint16_t(Op::I64TruncSF32):
 #ifdef FLOAT_TO_I64_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
                                                 SymbolicAddress::TruncateDoubleToInt64,
                                                 ValType::F32, ValType::I64));
 #else
             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<false>, ValType::F32, ValType::I64));
 #endif
-          case Expr::I64TruncUF32:
+          case uint16_t(Op::I64TruncUF32):
 #ifdef FLOAT_TO_I64_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
                                                 SymbolicAddress::TruncateDoubleToUint64,
                                                 ValType::F32, ValType::I64));
 #else
             CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<true>, ValType::F32, ValType::I64));
 #endif
-          case Expr::I64TruncSF64:
+          case uint16_t(Op::I64TruncSF64):
 #ifdef FLOAT_TO_I64_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
                                                 SymbolicAddress::TruncateDoubleToInt64,
                                                 ValType::F64, ValType::I64));
 #else
             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<false>, ValType::F64, ValType::I64));
 #endif
-          case Expr::I64TruncUF64:
+          case uint16_t(Op::I64TruncUF64):
 #ifdef FLOAT_TO_I64_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
                                                 SymbolicAddress::TruncateDoubleToUint64,
                                                 ValType::F64, ValType::I64));
 #else
             CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<true>, ValType::F64, ValType::I64));
 #endif
-          case Expr::I64ExtendSI32:
+          case uint16_t(Op::I64ExtendSI32):
             CHECK_NEXT(emitConversion(emitExtendI32ToI64, ValType::I32, ValType::I64));
-          case Expr::I64ExtendUI32:
+          case uint16_t(Op::I64ExtendUI32):
             CHECK_NEXT(emitConversion(emitExtendU32ToI64, ValType::I32, ValType::I64));
-          case Expr::I64ReinterpretF64:
+          case uint16_t(Op::I64ReinterpretF64):
             CHECK_NEXT(emitConversion(emitReinterpretF64AsI64, ValType::F64, ValType::I64));
-          case Expr::I64Or:
+          case uint16_t(Op::I64Or):
             CHECK_NEXT(emitBinary(emitOrI64, ValType::I64));
-          case Expr::I64And:
+          case uint16_t(Op::I64And):
             CHECK_NEXT(emitBinary(emitAndI64, ValType::I64));
-          case Expr::I64Xor:
+          case uint16_t(Op::I64Xor):
             CHECK_NEXT(emitBinary(emitXorI64, ValType::I64));
-          case Expr::I64Shl:
+          case uint16_t(Op::I64Shl):
             CHECK_NEXT(emitBinary(emitShlI64, ValType::I64));
-          case Expr::I64ShrS:
+          case uint16_t(Op::I64ShrS):
             CHECK_NEXT(emitBinary(emitShrI64, ValType::I64));
-          case Expr::I64ShrU:
+          case uint16_t(Op::I64ShrU):
             CHECK_NEXT(emitBinary(emitShrU64, ValType::I64));
-          case Expr::I64Rotr:
+          case uint16_t(Op::I64Rotr):
             CHECK_NEXT(emitBinary(emitRotrI64, ValType::I64));
-          case Expr::I64Rotl:
+          case uint16_t(Op::I64Rotl):
             CHECK_NEXT(emitBinary(emitRotlI64, ValType::I64));
-          case Expr::I64Clz:
+          case uint16_t(Op::I64Clz):
             CHECK_NEXT(emitUnary(emitClzI64, ValType::I64));
-          case Expr::I64Ctz:
+          case uint16_t(Op::I64Ctz):
             CHECK_NEXT(emitUnary(emitCtzI64, ValType::I64));
-          case Expr::I64Popcnt:
+          case uint16_t(Op::I64Popcnt):
             CHECK_NEXT(emitUnary(emitPopcntI64, ValType::I64));
-          case Expr::I64Eqz:
+          case uint16_t(Op::I64Eqz):
             CHECK_NEXT(emitConversion(emitEqzI64, ValType::I64, ValType::I32));
-          case Expr::I64Load8S:
+          case uint16_t(Op::I64Load8S):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int8));
-          case Expr::I64Load16S:
+          case uint16_t(Op::I64Load16S):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int16));
-          case Expr::I64Load32S:
+          case uint16_t(Op::I64Load32S):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int32));
-          case Expr::I64Load8U:
+          case uint16_t(Op::I64Load8U):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint8));
-          case Expr::I64Load16U:
+          case uint16_t(Op::I64Load16U):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint16));
-          case Expr::I64Load32U:
+          case uint16_t(Op::I64Load32U):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Uint32));
-          case Expr::I64Load:
+          case uint16_t(Op::I64Load):
             CHECK_NEXT(emitLoad(ValType::I64, Scalar::Int64));
-          case Expr::I64Store8:
+          case uint16_t(Op::I64Store8):
             CHECK_NEXT(emitStore(ValType::I64, Scalar::Int8));
-          case Expr::I64TeeStore8:
+          case uint16_t(Op::I64TeeStore8):
             CHECK_NEXT(emitTeeStore(ValType::I64, Scalar::Int8));
-          case Expr::I64Store16:
+          case uint16_t(Op::I64Store16):
             CHECK_NEXT(emitStore(ValType::I64, Scalar::Int16));
-          case Expr::I64TeeStore16:
+          case uint16_t(Op::I64TeeStore16):
             CHECK_NEXT(emitTeeStore(ValType::I64, Scalar::Int16));
-          case Expr::I64Store32:
+          case uint16_t(Op::I64Store32):
             CHECK_NEXT(emitStore(ValType::I64, Scalar::Int32));
-          case Expr::I64TeeStore32:
+          case uint16_t(Op::I64TeeStore32):
             CHECK_NEXT(emitTeeStore(ValType::I64, Scalar::Int32));
-          case Expr::I64Store:
+          case uint16_t(Op::I64Store):
             CHECK_NEXT(emitStore(ValType::I64, Scalar::Int64));
-          case Expr::I64TeeStore:
+          case uint16_t(Op::I64TeeStore):
             CHECK_NEXT(emitTeeStore(ValType::I64, Scalar::Int64));
 
           // F32
-          case Expr::F32Const: {
+          case uint16_t(Op::F32Const): {
             RawF32 f32;
             CHECK(iter_.readF32Const(&f32));
             if (!deadCode_)
                 pushF32(f32);
             NEXT();
           }
-          case Expr::F32Add:
+          case uint16_t(Op::F32Add):
             CHECK_NEXT(emitBinary(emitAddF32, ValType::F32));
-          case Expr::F32Sub:
+          case uint16_t(Op::F32Sub):
             CHECK_NEXT(emitBinary(emitSubtractF32, ValType::F32));
-          case Expr::F32Mul:
+          case uint16_t(Op::F32Mul):
             CHECK_NEXT(emitBinary(emitMultiplyF32, ValType::F32));
-          case Expr::F32Div:
+          case uint16_t(Op::F32Div):
             CHECK_NEXT(emitBinary(emitDivideF32, ValType::F32));
-          case Expr::F32Min:
+          case uint16_t(Op::F32Min):
             CHECK_NEXT(emitBinary(emitMinF32, ValType::F32));
-          case Expr::F32Max:
+          case uint16_t(Op::F32Max):
             CHECK_NEXT(emitBinary(emitMaxF32, ValType::F32));
-          case Expr::F32Neg:
+          case uint16_t(Op::F32Neg):
             CHECK_NEXT(emitUnary(emitNegateF32, ValType::F32));
-          case Expr::F32Abs:
+          case uint16_t(Op::F32Abs):
             CHECK_NEXT(emitUnary(emitAbsF32, ValType::F32));
-          case Expr::F32Sqrt:
+          case uint16_t(Op::F32Sqrt):
             CHECK_NEXT(emitUnary(emitSqrtF32, ValType::F32));
-          case Expr::F32Ceil:
+          case uint16_t(Op::F32Ceil):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::CeilF, ValType::F32));
-          case Expr::F32Floor:
+          case uint16_t(Op::F32Floor):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::FloorF, ValType::F32));
-          case Expr::F32DemoteF64:
+          case uint16_t(Op::F32DemoteF64):
             CHECK_NEXT(emitConversion(emitConvertF64ToF32, ValType::F64, ValType::F32));
-          case Expr::F32ConvertSI32:
+          case uint16_t(Op::F32ConvertSI32):
             CHECK_NEXT(emitConversion(emitConvertI32ToF32, ValType::I32, ValType::F32));
-          case Expr::F32ConvertUI32:
+          case uint16_t(Op::F32ConvertUI32):
             CHECK_NEXT(emitConversion(emitConvertU32ToF32, ValType::I32, ValType::F32));
-          case Expr::F32ConvertSI64:
+          case uint16_t(Op::F32ConvertSI64):
 #ifdef I64_TO_FLOAT_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
                                                 SymbolicAddress::Int64ToFloatingPoint,
                                                 ValType::I64, ValType::F32));
 #else
             CHECK_NEXT(emitConversion(emitConvertI64ToF32, ValType::I64, ValType::F32));
 #endif
-          case Expr::F32ConvertUI64:
+          case uint16_t(Op::F32ConvertUI64):
 #ifdef I64_TO_FLOAT_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
                                                 SymbolicAddress::Uint64ToFloatingPoint,
                                                 ValType::I64, ValType::F32));
 #else
             CHECK_NEXT(emitConversion(emitConvertU64ToF32, ValType::I64, ValType::F32));
 #endif
-          case Expr::F32ReinterpretI32:
+          case uint16_t(Op::F32ReinterpretI32):
             CHECK_NEXT(emitConversion(emitReinterpretI32AsF32, ValType::I32, ValType::F32));
-          case Expr::F32Load:
+          case uint16_t(Op::F32Load):
             CHECK_NEXT(emitLoad(ValType::F32, Scalar::Float32));
-          case Expr::F32Store:
+          case uint16_t(Op::F32Store):
             CHECK_NEXT(emitStore(ValType::F32, Scalar::Float32));
-          case Expr::F32TeeStore:
+          case uint16_t(Op::F32TeeStore):
             CHECK_NEXT(emitTeeStore(ValType::F32, Scalar::Float32));
-          case Expr::F32TeeStoreF64:
+          case uint16_t(Op::F32TeeStoreF64):
             CHECK_NEXT(emitTeeStoreWithCoercion(ValType::F32, Scalar::Float64));
-          case Expr::F32CopySign:
+          case uint16_t(Op::F32CopySign):
             CHECK_NEXT(emitBinary(emitCopysignF32, ValType::F32));
-          case Expr::F32Nearest:
+          case uint16_t(Op::F32Nearest):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntF, ValType::F32));
-          case Expr::F32Trunc:
+          case uint16_t(Op::F32Trunc):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::TruncF, ValType::F32));
 
           // F64
-          case Expr::F64Const: {
+          case uint16_t(Op::F64Const): {
             RawF64 f64;
             CHECK(iter_.readF64Const(&f64));
             if (!deadCode_)
                 pushF64(f64);
             NEXT();
           }
-          case Expr::F64Add:
+          case uint16_t(Op::F64Add):
             CHECK_NEXT(emitBinary(emitAddF64, ValType::F64));
-          case Expr::F64Sub:
+          case uint16_t(Op::F64Sub):
             CHECK_NEXT(emitBinary(emitSubtractF64, ValType::F64));
-          case Expr::F64Mul:
+          case uint16_t(Op::F64Mul):
             CHECK_NEXT(emitBinary(emitMultiplyF64, ValType::F64));
-          case Expr::F64Div:
+          case uint16_t(Op::F64Div):
             CHECK_NEXT(emitBinary(emitDivideF64, ValType::F64));
-          case Expr::F64Mod:
+          case uint16_t(Op::F64Mod):
             CHECK_NEXT(emitBinaryMathBuiltinCall(SymbolicAddress::ModD, ValType::F64));
-          case Expr::F64Min:
+          case uint16_t(Op::F64Min):
             CHECK_NEXT(emitBinary(emitMinF64, ValType::F64));
-          case Expr::F64Max:
+          case uint16_t(Op::F64Max):
             CHECK_NEXT(emitBinary(emitMaxF64, ValType::F64));
-          case Expr::F64Neg:
+          case uint16_t(Op::F64Neg):
             CHECK_NEXT(emitUnary(emitNegateF64, ValType::F64));
-          case Expr::F64Abs:
+          case uint16_t(Op::F64Abs):
             CHECK_NEXT(emitUnary(emitAbsF64, ValType::F64));
-          case Expr::F64Sqrt:
+          case uint16_t(Op::F64Sqrt):
             CHECK_NEXT(emitUnary(emitSqrtF64, ValType::F64));
-          case Expr::F64Ceil:
+          case uint16_t(Op::F64Ceil):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::CeilD, ValType::F64));
-          case Expr::F64Floor:
+          case uint16_t(Op::F64Floor):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::FloorD, ValType::F64));
-          case Expr::F64Sin:
+          case uint16_t(Op::F64Sin):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::SinD, ValType::F64));
-          case Expr::F64Cos:
+          case uint16_t(Op::F64Cos):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::CosD, ValType::F64));
-          case Expr::F64Tan:
+          case uint16_t(Op::F64Tan):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::TanD, ValType::F64));
-          case Expr::F64Asin:
+          case uint16_t(Op::F64Asin):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::ASinD, ValType::F64));
-          case Expr::F64Acos:
+          case uint16_t(Op::F64Acos):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::ACosD, ValType::F64));
-          case Expr::F64Atan:
+          case uint16_t(Op::F64Atan):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::ATanD, ValType::F64));
-          case Expr::F64Exp:
+          case uint16_t(Op::F64Exp):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::ExpD, ValType::F64));
-          case Expr::F64Log:
+          case uint16_t(Op::F64Log):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::LogD, ValType::F64));
-          case Expr::F64Pow:
+          case uint16_t(Op::F64Pow):
             CHECK_NEXT(emitBinaryMathBuiltinCall(SymbolicAddress::PowD, ValType::F64));
-          case Expr::F64Atan2:
+          case uint16_t(Op::F64Atan2):
             CHECK_NEXT(emitBinaryMathBuiltinCall(SymbolicAddress::ATan2D, ValType::F64));
-          case Expr::F64PromoteF32:
+          case uint16_t(Op::F64PromoteF32):
             CHECK_NEXT(emitConversion(emitConvertF32ToF64, ValType::F32, ValType::F64));
-          case Expr::F64ConvertSI32:
+          case uint16_t(Op::F64ConvertSI32):
             CHECK_NEXT(emitConversion(emitConvertI32ToF64, ValType::I32, ValType::F64));
-          case Expr::F64ConvertUI32:
+          case uint16_t(Op::F64ConvertUI32):
             CHECK_NEXT(emitConversion(emitConvertU32ToF64, ValType::I32, ValType::F64));
-          case Expr::F64ConvertSI64:
+          case uint16_t(Op::F64ConvertSI64):
 #ifdef I64_TO_FLOAT_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
                                                 SymbolicAddress::Int64ToFloatingPoint,
                                                 ValType::I64, ValType::F64));
 #else
             CHECK_NEXT(emitConversion(emitConvertI64ToF64, ValType::I64, ValType::F64));
 #endif
-          case Expr::F64ConvertUI64:
+          case uint16_t(Op::F64ConvertUI64):
 #ifdef I64_TO_FLOAT_CALLOUT
             CHECK_NEXT(emitCalloutConversionOOM(emitConvertInt64ToFloatingCallout,
                                                 SymbolicAddress::Uint64ToFloatingPoint,
                                                 ValType::I64, ValType::F64));
 #else
             CHECK_NEXT(emitConversion(emitConvertU64ToF64, ValType::I64, ValType::F64));
 #endif
-          case Expr::F64Load:
+          case uint16_t(Op::F64Load):
             CHECK_NEXT(emitLoad(ValType::F64, Scalar::Float64));
-          case Expr::F64Store:
+          case uint16_t(Op::F64Store):
             CHECK_NEXT(emitStore(ValType::F64, Scalar::Float64));
-          case Expr::F64TeeStore:
+          case uint16_t(Op::F64TeeStore):
             CHECK_NEXT(emitTeeStore(ValType::F64, Scalar::Float64));
-          case Expr::F64TeeStoreF32:
+          case uint16_t(Op::F64TeeStoreF32):
             CHECK_NEXT(emitTeeStoreWithCoercion(ValType::F64, Scalar::Float32));
-          case Expr::F64ReinterpretI64:
+          case uint16_t(Op::F64ReinterpretI64):
             CHECK_NEXT(emitConversion(emitReinterpretI64AsF64, ValType::I64, ValType::F64));
-          case Expr::F64CopySign:
+          case uint16_t(Op::F64CopySign):
             CHECK_NEXT(emitBinary(emitCopysignF64, ValType::F64));
-          case Expr::F64Nearest:
+          case uint16_t(Op::F64Nearest):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::NearbyIntD, ValType::F64));
-          case Expr::F64Trunc:
+          case uint16_t(Op::F64Trunc):
             CHECK_NEXT(emitUnaryMathBuiltinCall(SymbolicAddress::TruncD, ValType::F64));
 
           // Comparisons
-          case Expr::I32Eq:
+          case uint16_t(Op::I32Eq):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_EQ, MCompare::Compare_Int32));
-          case Expr::I32Ne:
+          case uint16_t(Op::I32Ne):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_NE, MCompare::Compare_Int32));
-          case Expr::I32LtS:
+          case uint16_t(Op::I32LtS):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_LT, MCompare::Compare_Int32));
-          case Expr::I32LeS:
+          case uint16_t(Op::I32LeS):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_LE, MCompare::Compare_Int32));
-          case Expr::I32GtS:
+          case uint16_t(Op::I32GtS):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_GT, MCompare::Compare_Int32));
-          case Expr::I32GeS:
+          case uint16_t(Op::I32GeS):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_GE, MCompare::Compare_Int32));
-          case Expr::I32LtU:
+          case uint16_t(Op::I32LtU):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_LT, MCompare::Compare_UInt32));
-          case Expr::I32LeU:
+          case uint16_t(Op::I32LeU):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_LE, MCompare::Compare_UInt32));
-          case Expr::I32GtU:
+          case uint16_t(Op::I32GtU):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_GT, MCompare::Compare_UInt32));
-          case Expr::I32GeU:
+          case uint16_t(Op::I32GeU):
             CHECK_NEXT(emitComparison(emitCompareI32, ValType::I32, JSOP_GE, MCompare::Compare_UInt32));
-          case Expr::I64Eq:
+          case uint16_t(Op::I64Eq):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_EQ, MCompare::Compare_Int64));
-          case Expr::I64Ne:
+          case uint16_t(Op::I64Ne):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_NE, MCompare::Compare_Int64));
-          case Expr::I64LtS:
+          case uint16_t(Op::I64LtS):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_LT, MCompare::Compare_Int64));
-          case Expr::I64LeS:
+          case uint16_t(Op::I64LeS):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_LE, MCompare::Compare_Int64));
-          case Expr::I64GtS:
+          case uint16_t(Op::I64GtS):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_GT, MCompare::Compare_Int64));
-          case Expr::I64GeS:
+          case uint16_t(Op::I64GeS):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_GE, MCompare::Compare_Int64));
-          case Expr::I64LtU:
+          case uint16_t(Op::I64LtU):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_LT, MCompare::Compare_UInt64));
-          case Expr::I64LeU:
+          case uint16_t(Op::I64LeU):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_LE, MCompare::Compare_UInt64));
-          case Expr::I64GtU:
+          case uint16_t(Op::I64GtU):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_GT, MCompare::Compare_UInt64));
-          case Expr::I64GeU:
+          case uint16_t(Op::I64GeU):
             CHECK_NEXT(emitComparison(emitCompareI64, ValType::I64, JSOP_GE, MCompare::Compare_UInt64));
-          case Expr::F32Eq:
+          case uint16_t(Op::F32Eq):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_EQ, MCompare::Compare_Float32));
-          case Expr::F32Ne:
+          case uint16_t(Op::F32Ne):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_NE, MCompare::Compare_Float32));
-          case Expr::F32Lt:
+          case uint16_t(Op::F32Lt):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_LT, MCompare::Compare_Float32));
-          case Expr::F32Le:
+          case uint16_t(Op::F32Le):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_LE, MCompare::Compare_Float32));
-          case Expr::F32Gt:
+          case uint16_t(Op::F32Gt):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_GT, MCompare::Compare_Float32));
-          case Expr::F32Ge:
+          case uint16_t(Op::F32Ge):
             CHECK_NEXT(emitComparison(emitCompareF32, ValType::F32, JSOP_GE, MCompare::Compare_Float32));
-          case Expr::F64Eq:
+          case uint16_t(Op::F64Eq):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_EQ, MCompare::Compare_Double));
-          case Expr::F64Ne:
+          case uint16_t(Op::F64Ne):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_NE, MCompare::Compare_Double));
-          case Expr::F64Lt:
+          case uint16_t(Op::F64Lt):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_LT, MCompare::Compare_Double));
-          case Expr::F64Le:
+          case uint16_t(Op::F64Le):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_LE, MCompare::Compare_Double));
-          case Expr::F64Gt:
+          case uint16_t(Op::F64Gt):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_GT, MCompare::Compare_Double));
-          case Expr::F64Ge:
+          case uint16_t(Op::F64Ge):
             CHECK_NEXT(emitComparison(emitCompareF64, ValType::F64, JSOP_GE, MCompare::Compare_Double));
 
           // SIMD
 #define CASE(TYPE, OP, SIGN) \
-          case Expr::TYPE##OP: \
+          case uint16_t(Op::TYPE##OP): \
             MOZ_CRASH("Unimplemented SIMD");
 #define I8x16CASE(OP) CASE(I8x16, OP, SimdSign::Signed)
 #define I16x8CASE(OP) CASE(I16x8, OP, SimdSign::Signed)
 #define I32x4CASE(OP) CASE(I32x4, OP, SimdSign::Signed)
 #define F32x4CASE(OP) CASE(F32x4, OP, SimdSign::NotApplicable)
 #define B8x16CASE(OP) CASE(B8x16, OP, SimdSign::NotApplicable)
 #define B16x8CASE(OP) CASE(B16x8, OP, SimdSign::NotApplicable)
 #define B32x4CASE(OP) CASE(B32x4, OP, SimdSign::NotApplicable)
 #define ENUMERATE(TYPE, FORALL, DO) \
-          case Expr::TYPE##Constructor: \
+          case uint16_t(Op::TYPE##Constructor): \
             FORALL(DO)
 
           ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
           ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
           ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
           ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
           ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
           ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
@@ -7123,62 +7123,60 @@ BaseCompiler::emitBody()
 #undef I16x8CASE
 #undef I32x4CASE
 #undef F32x4CASE
 #undef B8x16CASE
 #undef B16x8CASE
 #undef B32x4CASE
 #undef ENUMERATE
 
-          case Expr::I8x16Const:
-          case Expr::I16x8Const:
-          case Expr::I32x4Const:
-          case Expr::F32x4Const:
-          case Expr::B8x16Const:
-          case Expr::B16x8Const:
-          case Expr::B32x4Const:
-          case Expr::I32x4shiftRightByScalarU:
-          case Expr::I8x16addSaturateU:
-          case Expr::I8x16subSaturateU:
-          case Expr::I8x16shiftRightByScalarU:
-          case Expr::I8x16lessThanU:
-          case Expr::I8x16lessThanOrEqualU:
-          case Expr::I8x16greaterThanU:
-          case Expr::I8x16greaterThanOrEqualU:
-          case Expr::I8x16extractLaneU:
-          case Expr::I16x8addSaturateU:
-          case Expr::I16x8subSaturateU:
-          case Expr::I16x8shiftRightByScalarU:
-          case Expr::I16x8lessThanU:
-          case Expr::I16x8lessThanOrEqualU:
-          case Expr::I16x8greaterThanU:
-          case Expr::I16x8greaterThanOrEqualU:
-          case Expr::I16x8extractLaneU:
-          case Expr::I32x4lessThanU:
-          case Expr::I32x4lessThanOrEqualU:
-          case Expr::I32x4greaterThanU:
-          case Expr::I32x4greaterThanOrEqualU:
-          case Expr::I32x4fromFloat32x4U:
+          case uint16_t(Op::I8x16Const):
+          case uint16_t(Op::I16x8Const):
+          case uint16_t(Op::I32x4Const):
+          case uint16_t(Op::F32x4Const):
+          case uint16_t(Op::B8x16Const):
+          case uint16_t(Op::B16x8Const):
+          case uint16_t(Op::B32x4Const):
+          case uint16_t(Op::I32x4shiftRightByScalarU):
+          case uint16_t(Op::I8x16addSaturateU):
+          case uint16_t(Op::I8x16subSaturateU):
+          case uint16_t(Op::I8x16shiftRightByScalarU):
+          case uint16_t(Op::I8x16lessThanU):
+          case uint16_t(Op::I8x16lessThanOrEqualU):
+          case uint16_t(Op::I8x16greaterThanU):
+          case uint16_t(Op::I8x16greaterThanOrEqualU):
+          case uint16_t(Op::I8x16extractLaneU):
+          case uint16_t(Op::I16x8addSaturateU):
+          case uint16_t(Op::I16x8subSaturateU):
+          case uint16_t(Op::I16x8shiftRightByScalarU):
+          case uint16_t(Op::I16x8lessThanU):
+          case uint16_t(Op::I16x8lessThanOrEqualU):
+          case uint16_t(Op::I16x8greaterThanU):
+          case uint16_t(Op::I16x8greaterThanOrEqualU):
+          case uint16_t(Op::I16x8extractLaneU):
+          case uint16_t(Op::I32x4lessThanU):
+          case uint16_t(Op::I32x4lessThanOrEqualU):
+          case uint16_t(Op::I32x4greaterThanU):
+          case uint16_t(Op::I32x4greaterThanOrEqualU):
+          case uint16_t(Op::I32x4fromFloat32x4U):
             MOZ_CRASH("Unimplemented SIMD");
 
           // Atomics
-          case Expr::I32AtomicsLoad:
-          case Expr::I32AtomicsStore:
-          case Expr::I32AtomicsBinOp:
-          case Expr::I32AtomicsCompareExchange:
-          case Expr::I32AtomicsExchange:
+          case uint16_t(Op::I32AtomicsLoad):
+          case uint16_t(Op::I32AtomicsStore):
+          case uint16_t(Op::I32AtomicsBinOp):
+          case uint16_t(Op::I32AtomicsCompareExchange):
+          case uint16_t(Op::I32AtomicsExchange):
             MOZ_CRASH("Unimplemented Atomics");
 
           // Memory Related
-          case Expr::GrowMemory:
+          case uint16_t(Op::GrowMemory):
             CHECK_NEXT(emitGrowMemory());
-          case Expr::CurrentMemory:
+          case uint16_t(Op::CurrentMemory):
             CHECK_NEXT(emitCurrentMemory());
-
-          case Expr::Limit:;
         }
 
         MOZ_CRASH("unexpected wasm opcode");
 
 #undef CHECK
 #undef NEXT
 #undef CHECK_NEXT
 #undef emitBinary
@@ -7451,17 +7449,17 @@ js::wasm::BaselineCompileFunction(IonCom
 
     Decoder d(func.bytes());
 
     // Build the local types vector.
 
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
-    if (!DecodeLocalEntries(d, &locals))
+    if (!DecodeLocalEntries(d, task->mg().kind, &locals))
         return false;
 
     // The MacroAssembler will sometimes access the jitContext.
 
     JitContext jitContext(&results.alloc());
 
     // One-pass baseline compilation.
 
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -64,22 +64,20 @@ enum class TypeCode
     AnyFunc                              = 0x70,  // SLEB128(-0x10)
 
     // Type constructor for function types
     Func                                 = 0x60,  // SLEB128(-0x20)
 
     // Special code representing the block signature ()->()
     BlockVoid                            = 0x40,  // SLEB128(-0x40)
 
-    // Type codes currently always fit in a single byte
-    Max                                  = 0x7f,
     Limit                                = 0x80
 };
 
-enum class ValType : uint32_t // fix type so we can cast from any u8 in decoder
+enum class ValType
 {
     I32                                  = uint8_t(TypeCode::I32),
     I64                                  = uint8_t(TypeCode::I64),
     F32                                  = uint8_t(TypeCode::F32),
     F64                                  = uint8_t(TypeCode::F64),
 
     // ------------------------------------------------------------------------
     // The rest of these types are currently only emitted internally when
@@ -110,17 +108,17 @@ enum class GlobalFlags
     AllowedMask                          = 0x1
 };
 
 enum class MemoryTableFlags
 {
     Default                              = 0x0
 };
 
-enum class Expr : uint32_t // fix type so we can cast from any u16 in decoder
+enum class Op
 {
     // Control flow operators
     Unreachable                          = 0x00,
     Nop                                  = 0x01,
     Block                                = 0x02,
     Loop                                 = 0x03,
     If                                   = 0x04,
     Else                                 = 0x05,
--- a/js/src/wasm/WasmBinaryFormat.cpp
+++ b/js/src/wasm/WasmBinaryFormat.cpp
@@ -38,31 +38,44 @@ wasm::DecodePreamble(Decoder& d)
 
     if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
         return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
                       u32, EncodingVersion);
 
     return true;
 }
 
-bool
-wasm::CheckValType(Decoder& d, ValType type)
+static bool
+DecodeValType(Decoder& d, ModuleKind kind, ValType* type)
 {
-    switch (type) {
-      case ValType::I32:
-      case ValType::F32:
-      case ValType::F64:
-      case ValType::I64:
+    uint8_t unchecked;
+    if (!d.readValType(&unchecked))
+        return false;
+
+    switch (unchecked) {
+      case uint8_t(ValType::I32):
+      case uint8_t(ValType::F32):
+      case uint8_t(ValType::F64):
+      case uint8_t(ValType::I64):
+        *type = ValType(unchecked);
+        return true;
+      case uint8_t(ValType::I8x16):
+      case uint8_t(ValType::I16x8):
+      case uint8_t(ValType::I32x4):
+      case uint8_t(ValType::F32x4):
+      case uint8_t(ValType::B8x16):
+      case uint8_t(ValType::B16x8):
+      case uint8_t(ValType::B32x4):
+        if (kind != ModuleKind::AsmJS)
+            return d.fail("bad type");
+        *type = ValType(unchecked);
         return true;
       default:
-        // Note: it's important not to remove this default since readValType()
-        // can return ValType values for which there is no enumerator.
         break;
     }
-
     return d.fail("bad type");
 }
 
 bool
 wasm::DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(SectionId::Type, &sectionStart, &sectionSize, "type"))
@@ -92,38 +105,32 @@ wasm::DecodeTypeSection(Decoder& d, SigW
         if (numArgs > MaxArgsPerFunc)
             return d.fail("too many arguments in signature");
 
         ValTypeVector args;
         if (!args.resize(numArgs))
             return false;
 
         for (uint32_t i = 0; i < numArgs; i++) {
-            if (!d.readValType(&args[i]))
-                return d.fail("bad value type");
-
-            if (!CheckValType(d, args[i]))
+            if (!DecodeValType(d, ModuleKind::Wasm, &args[i]))
                 return false;
         }
 
         uint32_t numRets;
         if (!d.readVarU32(&numRets))
             return d.fail("bad number of function returns");
 
         if (numRets > 1)
             return d.fail("too many returns in signature");
 
         ExprType result = ExprType::Void;
 
         if (numRets == 1) {
             ValType type;
-            if (!d.readValType(&type))
-                return d.fail("bad expression type");
-
-            if (!CheckValType(d, type))
+            if (!DecodeValType(d, ModuleKind::Wasm, &type))
                 return false;
 
             result = ToExprType(type);
         }
 
         (*sigs)[sigIndex] = Sig(Move(args), result);
     }
 
@@ -362,96 +369,96 @@ wasm::EncodeLocalEntries(Encoder& e, con
         if (!e.writeValType(prev))
             return false;
     }
 
     return true;
 }
 
 bool
-wasm::DecodeLocalEntries(Decoder& d, ValTypeVector* locals)
+wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals)
 {
     uint32_t numLocalEntries;
     if (!d.readVarU32(&numLocalEntries))
-        return false;
+        return d.fail("failed to read number of local entries");
 
     for (uint32_t i = 0; i < numLocalEntries; i++) {
         uint32_t count;
         if (!d.readVarU32(&count))
-            return false;
+            return d.fail("failed to read local entry count");
 
         if (MaxLocals - locals->length() < count)
-            return false;
+            return d.fail("too many locals");
 
         ValType type;
-        if (!d.readValType(&type))
+        if (!DecodeValType(d, kind, &type))
             return false;
 
         if (!locals->appendN(type, count))
             return false;
     }
 
     return true;
 }
 
 bool
 wasm::DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
 {
-    if (!d.readValType(type))
-        return d.fail("bad global type");
+    if (!DecodeValType(d, ModuleKind::Wasm, type))
+        return false;
 
     uint32_t flags;
     if (!d.readVarU32(&flags))
         return d.fail("expected global flags");
 
     if (flags & ~uint32_t(GlobalFlags::AllowedMask))
         return d.fail("unexpected bits set in global flags");
 
     *isMutable = flags & uint32_t(GlobalFlags::IsMutable);
     return true;
 }
 
 bool
 wasm::DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
                                   InitExpr* init)
 {
-    Expr expr;
-    if (!d.readExpr(&expr))
+    uint16_t op;
+    if (!d.readOp(&op))
         return d.fail("failed to read initializer type");
 
-    switch (expr) {
-      case Expr::I32Const: {
+    switch (op) {
+      case uint16_t(Op::I32Const): {
         int32_t i32;
         if (!d.readVarS32(&i32))
             return d.fail("failed to read initializer i32 expression");
         *init = InitExpr(Val(uint32_t(i32)));
         break;
       }
-      case Expr::I64Const: {
+      case uint16_t(Op::I64Const): {
         int64_t i64;
         if (!d.readVarS64(&i64))
             return d.fail("failed to read initializer i64 expression");
         *init = InitExpr(Val(uint64_t(i64)));
         break;
       }
-      case Expr::F32Const: {
+      case uint16_t(Op::F32Const): {
         RawF32 f32;
         if (!d.readFixedF32(&f32))
             return d.fail("failed to read initializer f32 expression");
         *init = InitExpr(Val(f32));
         break;
       }
-      case Expr::F64Const: {
+      case uint16_t(Op::F64Const): {
         RawF64 f64;
         if (!d.readFixedF64(&f64))
             return d.fail("failed to read initializer f64 expression");
         *init = InitExpr(Val(f64));
         break;
       }
-      case Expr::GetGlobal: {
+      case uint16_t(Op::GetGlobal): {
         uint32_t i;
         if (!d.readVarU32(&i))
             return d.fail("failed to read get_global index in initializer expression");
         if (i >= globals.length())
             return d.fail("global index out of range in initializer expression");
         if (!globals[i].isImport() || globals[i].isMutable())
             return d.fail("initializer expression must reference a global immutable import");
         *init = InitExpr(i, globals[i].type());
@@ -460,18 +467,18 @@ wasm::DecodeInitializerExpression(Decode
       default: {
         return d.fail("unexpected initializer expression");
       }
     }
 
     if (expected != init->type())
         return d.fail("type mismatch: initializer type and expected type don't match");
 
-    Expr end;
-    if (!d.readExpr(&end) || end != Expr::End)
+    uint16_t end;
+    if (!d.readOp(&end) || end != uint16_t(Op::End))
         return d.fail("failed to read end of initializer expression");
 
     return true;
 }
 
 bool
 wasm::DecodeLimits(Decoder& d, Limits* limits)
 {
--- a/js/src/wasm/WasmBinaryFormat.h
+++ b/js/src/wasm/WasmBinaryFormat.h
@@ -93,18 +93,16 @@ class Encoder
 
     uint32_t varU32ByteLength(size_t offset) const {
         size_t start = offset;
         while (bytes_[offset] & 0x80)
             offset++;
         return offset - start + 1;
     }
 
-    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
-
   public:
     explicit Encoder(Bytes& bytes)
       : bytes_(bytes)
     {
         MOZ_ASSERT(empty());
     }
 
     size_t currentOffset() const { return bytes_.length(); }
@@ -152,31 +150,32 @@ class Encoder
     }
     MOZ_MUST_USE bool writeVarU64(uint64_t i) {
         return writeVarU<uint64_t>(i);
     }
     MOZ_MUST_USE bool writeVarS64(int64_t i) {
         return writeVarS<int64_t>(i);
     }
     MOZ_MUST_USE bool writeValType(ValType type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
         return writeFixedU8(uint8_t(type));
     }
     MOZ_MUST_USE bool writeBlockType(ExprType type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
         return writeFixedU8(uint8_t(type));
     }
-    MOZ_MUST_USE bool writeExpr(Expr expr) {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
-        if (size_t(expr) < UINT8_MAX)
-            return writeFixedU8(uint8_t(expr));
+    MOZ_MUST_USE bool writeOp(Op op) {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
+        if (size_t(op) < UINT8_MAX)
+            return writeFixedU8(uint8_t(op));
         return writeFixedU8(UINT8_MAX) &&
-               writeFixedU8(size_t(expr) - UINT8_MAX);
+               writeFixedU8(size_t(op) - UINT8_MAX);
     }
 
     // Fixed-length encodings that allow back-patching.
 
     MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
         *offset = bytes_.length();
         return writeFixedU8(UINT8_MAX);
     }
@@ -303,18 +302,16 @@ class Decoder
             return false;
         uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
         if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
             return false;
         *out = s | SInt(byte) << shift;
         return true;
     }
 
-    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
-
   public:
     Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error)
       : beg_(begin),
         end_(end),
         cur_(begin),
         error_(error)
     {
         MOZ_ASSERT(begin <= end);
@@ -397,46 +394,36 @@ class Decoder
         return readVarS<int32_t>(out);
     }
     MOZ_MUST_USE bool readVarU64(uint64_t* out) {
         return readVarU<uint64_t>(out);
     }
     MOZ_MUST_USE bool readVarS64(int64_t* out) {
         return readVarS<int64_t>(out);
     }
-    MOZ_MUST_USE bool readValType(ValType* type) {
-        static_assert(uint8_t(TypeCode::Max) <= INT8_MAX, "fits");
+    MOZ_MUST_USE bool readValType(uint8_t* type) {
+        static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        return readFixedU8(type);
+    }
+    MOZ_MUST_USE bool readBlockType(uint8_t* type) {
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        return readFixedU8(type);
+    }
+    MOZ_MUST_USE bool readOp(uint16_t* op) {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
         uint8_t u8;
         if (!readFixedU8(&u8))
             return false;
-        *type = (ValType)u8;
-        return true;
-    }
-    MOZ_MUST_USE bool readBlockType(ExprType* type) {
-        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        *type = (ExprType)u8;
-        return true;
-    }
-    MOZ_MUST_USE bool readExpr(Expr* expr) {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        if (u8 != UINT8_MAX) {
-            *expr = Expr(u8);
+        if (MOZ_LIKELY(u8 != UINT8_MAX)) {
+            *op = u8;
             return true;
         }
         if (!readFixedU8(&u8))
             return false;
-        if (u8 == UINT8_MAX)
-            return false;
-        *expr = Expr(uint16_t(u8) + UINT8_MAX);
+        *op = uint16_t(u8) + UINT8_MAX;
         return true;
     }
 
     // See writeBytes comment.
 
     MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
         if (bytes)
             *bytes = cur_;
@@ -597,22 +584,22 @@ class Decoder
     int64_t uncheckedReadVarS64() {
         int64_t i64 = 0;
         MOZ_ALWAYS_TRUE(readVarS64(&i64));
         return i64;
     }
     ValType uncheckedReadValType() {
         return (ValType)uncheckedReadFixedU8();
     }
-    Expr uncheckedReadExpr() {
-        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
+    Op uncheckedReadOp() {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
         uint8_t u8 = uncheckedReadFixedU8();
         return u8 != UINT8_MAX
-               ? Expr(u8)
-               : Expr(uncheckedReadFixedU8() + UINT8_MAX);
+               ? Op(u8)
+               : Op(uncheckedReadFixedU8() + UINT8_MAX);
     }
     void uncheckedReadFixedI8x16(I8x16* i8x16) {
         struct T { I8x16 v; };
         T t = uncheckedRead<T>();
         memcpy(i8x16, &t, sizeof(t));
     }
     void uncheckedReadFixedI16x8(I16x8* i16x8) {
         struct T { I16x8 v; };
@@ -632,33 +619,30 @@ class Decoder
 };
 
 // Reusable macro encoding/decoding functions reused by both the two
 // encoders (AsmJS/WasmTextToBinary) and all the decoders
 // (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
 
 // Misc helpers.
 
-MOZ_MUST_USE bool
-CheckValType(Decoder& d, ValType type);
-
 UniqueChars
 DecodeName(Decoder& d);
 
 MOZ_MUST_USE bool
 DecodeTableLimits(Decoder& d, TableDescVector* tables);
 
 MOZ_MUST_USE bool
 GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable);
 
 MOZ_MUST_USE bool
 EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
 
 MOZ_MUST_USE bool
-DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
+DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals);
 
 MOZ_MUST_USE bool
 DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable);
 
 MOZ_MUST_USE bool
 DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
                             InitExpr* init);
 
--- a/js/src/wasm/WasmBinaryIterator.cpp
+++ b/js/src/wasm/WasmBinaryIterator.cpp
@@ -18,481 +18,481 @@
 
 #include "wasm/WasmBinaryIterator.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 #ifdef DEBUG
-ExprKind
-wasm::Classify(Expr expr)
+OpKind
+wasm::Classify(Op op)
 {
-    switch (expr) {
-      case Expr::Block:
-        return ExprKind::Block;
-      case Expr::Loop:
-        return ExprKind::Loop;
-      case Expr::Unreachable:
-        return ExprKind::Unreachable;
-      case Expr::Drop:
-        return ExprKind::Drop;
-      case Expr::I32Const:
-        return ExprKind::I32;
-      case Expr::I64Const:
-        return ExprKind::I64;
-      case Expr::F32Const:
-        return ExprKind::F32;
-      case Expr::F64Const:
-        return ExprKind::F64;
-      case Expr::I8x16Const:
-        return ExprKind::I8x16;
-      case Expr::I16x8Const:
-        return ExprKind::I16x8;
-      case Expr::I32x4Const:
-        return ExprKind::I32x4;
-      case Expr::B8x16Const:
-        return ExprKind::B8x16;
-      case Expr::B16x8Const:
-        return ExprKind::B16x8;
-      case Expr::B32x4Const:
-        return ExprKind::B32x4;
-      case Expr::F32x4Const:
-        return ExprKind::F32x4;
-      case Expr::Br:
-        return ExprKind::Br;
-      case Expr::BrIf:
-        return ExprKind::BrIf;
-      case Expr::BrTable:
-        return ExprKind::BrTable;
-      case Expr::Nop:
-        return ExprKind::Nop;
-      case Expr::I32Clz:
-      case Expr::I32Ctz:
-      case Expr::I32Popcnt:
-      case Expr::I64Clz:
-      case Expr::I64Ctz:
-      case Expr::I64Popcnt:
-      case Expr::F32Abs:
-      case Expr::F32Neg:
-      case Expr::F32Ceil:
-      case Expr::F32Floor:
-      case Expr::F32Trunc:
-      case Expr::F32Nearest:
-      case Expr::F32Sqrt:
-      case Expr::F64Abs:
-      case Expr::F64Neg:
-      case Expr::F64Ceil:
-      case Expr::F64Floor:
-      case Expr::F64Trunc:
-      case Expr::F64Nearest:
-      case Expr::F64Sqrt:
-      case Expr::I32BitNot:
-      case Expr::I32Abs:
-      case Expr::F64Sin:
-      case Expr::F64Cos:
-      case Expr::F64Tan:
-      case Expr::F64Asin:
-      case Expr::F64Acos:
-      case Expr::F64Atan:
-      case Expr::F64Exp:
-      case Expr::F64Log:
-      case Expr::I32Neg:
-      case Expr::I8x16neg:
-      case Expr::I8x16not:
-      case Expr::I16x8neg:
-      case Expr::I16x8not:
-      case Expr::I32x4neg:
-      case Expr::I32x4not:
-      case Expr::F32x4neg:
-      case Expr::F32x4sqrt:
-      case Expr::F32x4abs:
-      case Expr::F32x4reciprocalApproximation:
-      case Expr::F32x4reciprocalSqrtApproximation:
-      case Expr::B8x16not:
-      case Expr::B16x8not:
-      case Expr::B32x4not:
-        return ExprKind::Unary;
-      case Expr::I32Add:
-      case Expr::I32Sub:
-      case Expr::I32Mul:
-      case Expr::I32DivS:
-      case Expr::I32DivU:
-      case Expr::I32RemS:
-      case Expr::I32RemU:
-      case Expr::I32And:
-      case Expr::I32Or:
-      case Expr::I32Xor:
-      case Expr::I32Shl:
-      case Expr::I32ShrS:
-      case Expr::I32ShrU:
-      case Expr::I32Rotl:
-      case Expr::I32Rotr:
-      case Expr::I64Add:
-      case Expr::I64Sub:
-      case Expr::I64Mul:
-      case Expr::I64DivS:
-      case Expr::I64DivU:
-      case Expr::I64RemS:
-      case Expr::I64RemU:
-      case Expr::I64And:
-      case Expr::I64Or:
-      case Expr::I64Xor:
-      case Expr::I64Shl:
-      case Expr::I64ShrS:
-      case Expr::I64ShrU:
-      case Expr::I64Rotl:
-      case Expr::I64Rotr:
-      case Expr::F32Add:
-      case Expr::F32Sub:
-      case Expr::F32Mul:
-      case Expr::F32Div:
-      case Expr::F32Min:
-      case Expr::F32Max:
-      case Expr::F32CopySign:
-      case Expr::F64Add:
-      case Expr::F64Sub:
-      case Expr::F64Mul:
-      case Expr::F64Div:
-      case Expr::F64Min:
-      case Expr::F64Max:
-      case Expr::F64CopySign:
-      case Expr::I32Min:
-      case Expr::I32Max:
-      case Expr::F64Mod:
-      case Expr::F64Pow:
-      case Expr::F64Atan2:
-      case Expr::I8x16add:
-      case Expr::I8x16sub:
-      case Expr::I8x16mul:
-      case Expr::I8x16addSaturate:
-      case Expr::I8x16subSaturate:
-      case Expr::I8x16addSaturateU:
-      case Expr::I8x16subSaturateU:
-      case Expr::I8x16and:
-      case Expr::I8x16or:
-      case Expr::I8x16xor:
-      case Expr::I16x8add:
-      case Expr::I16x8sub:
-      case Expr::I16x8mul:
-      case Expr::I16x8addSaturate:
-      case Expr::I16x8subSaturate:
-      case Expr::I16x8addSaturateU:
-      case Expr::I16x8subSaturateU:
-      case Expr::I16x8and:
-      case Expr::I16x8or:
-      case Expr::I16x8xor:
-      case Expr::I32x4add:
-      case Expr::I32x4sub:
-      case Expr::I32x4mul:
-      case Expr::I32x4and:
-      case Expr::I32x4or:
-      case Expr::I32x4xor:
-      case Expr::F32x4add:
-      case Expr::F32x4sub:
-      case Expr::F32x4mul:
-      case Expr::F32x4div:
-      case Expr::F32x4min:
-      case Expr::F32x4max:
-      case Expr::F32x4minNum:
-      case Expr::F32x4maxNum:
-      case Expr::B8x16and:
-      case Expr::B8x16or:
-      case Expr::B8x16xor:
-      case Expr::B16x8and:
-      case Expr::B16x8or:
-      case Expr::B16x8xor:
-      case Expr::B32x4and:
-      case Expr::B32x4or:
-      case Expr::B32x4xor:
-        return ExprKind::Binary;
-      case Expr::I32Eq:
-      case Expr::I32Ne:
-      case Expr::I32LtS:
-      case Expr::I32LtU:
-      case Expr::I32LeS:
-      case Expr::I32LeU:
-      case Expr::I32GtS:
-      case Expr::I32GtU:
-      case Expr::I32GeS:
-      case Expr::I32GeU:
-      case Expr::I64Eq:
-      case Expr::I64Ne:
-      case Expr::I64LtS:
-      case Expr::I64LtU:
-      case Expr::I64LeS:
-      case Expr::I64LeU:
-      case Expr::I64GtS:
-      case Expr::I64GtU:
-      case Expr::I64GeS:
-      case Expr::I64GeU:
-      case Expr::F32Eq:
-      case Expr::F32Ne:
-      case Expr::F32Lt:
-      case Expr::F32Le:
-      case Expr::F32Gt:
-      case Expr::F32Ge:
-      case Expr::F64Eq:
-      case Expr::F64Ne:
-      case Expr::F64Lt:
-      case Expr::F64Le:
-      case Expr::F64Gt:
-      case Expr::F64Ge:
-        return ExprKind::Comparison;
-      case Expr::I32Eqz:
-      case Expr::I32WrapI64:
-      case Expr::I32TruncSF32:
-      case Expr::I32TruncUF32:
-      case Expr::I32ReinterpretF32:
-      case Expr::I32TruncSF64:
-      case Expr::I32TruncUF64:
-      case Expr::I64ExtendSI32:
-      case Expr::I64ExtendUI32:
-      case Expr::I64TruncSF32:
-      case Expr::I64TruncUF32:
-      case Expr::I64TruncSF64:
-      case Expr::I64TruncUF64:
-      case Expr::I64ReinterpretF64:
-      case Expr::I64Eqz:
-      case Expr::F32ConvertSI32:
-      case Expr::F32ConvertUI32:
-      case Expr::F32ReinterpretI32:
-      case Expr::F32ConvertSI64:
-      case Expr::F32ConvertUI64:
-      case Expr::F32DemoteF64:
-      case Expr::F64ConvertSI32:
-      case Expr::F64ConvertUI32:
-      case Expr::F64ConvertSI64:
-      case Expr::F64ConvertUI64:
-      case Expr::F64ReinterpretI64:
-      case Expr::F64PromoteF32:
-      case Expr::I32x4fromFloat32x4:
-      case Expr::I32x4fromFloat32x4U:
-      case Expr::F32x4fromInt32x4:
-      case Expr::F32x4fromUint32x4:
-      case Expr::I32x4fromFloat32x4Bits:
-      case Expr::I32x4fromInt8x16Bits:
-      case Expr::I32x4fromInt16x8Bits:
-      case Expr::I16x8fromInt8x16Bits:
-      case Expr::I16x8fromInt32x4Bits:
-      case Expr::I16x8fromFloat32x4Bits:
-      case Expr::I8x16fromInt16x8Bits:
-      case Expr::I8x16fromInt32x4Bits:
-      case Expr::I8x16fromFloat32x4Bits:
-      case Expr::F32x4fromInt8x16Bits:
-      case Expr::F32x4fromInt16x8Bits:
-      case Expr::F32x4fromInt32x4Bits:
-        return ExprKind::Conversion;
-      case Expr::I32Load8S:
-      case Expr::I32Load8U:
-      case Expr::I32Load16S:
-      case Expr::I32Load16U:
-      case Expr::I64Load8S:
-      case Expr::I64Load8U:
-      case Expr::I64Load16S:
-      case Expr::I64Load16U:
-      case Expr::I64Load32S:
-      case Expr::I64Load32U:
-      case Expr::I32Load:
-      case Expr::I64Load:
-      case Expr::F32Load:
-      case Expr::F64Load:
-      case Expr::I8x16load:
-      case Expr::I16x8load:
-      case Expr::I32x4load:
-      case Expr::I32x4load1:
-      case Expr::I32x4load2:
-      case Expr::I32x4load3:
-      case Expr::F32x4load:
-      case Expr::F32x4load1:
-      case Expr::F32x4load2:
-      case Expr::F32x4load3:
-        return ExprKind::Load;
-      case Expr::I32Store8:
-      case Expr::I32Store16:
-      case Expr::I64Store8:
-      case Expr::I64Store16:
-      case Expr::I64Store32:
-      case Expr::I32Store:
-      case Expr::I64Store:
-      case Expr::F32Store:
-      case Expr::F64Store:
-        return ExprKind::Store;
-      case Expr::I32TeeStore8:
-      case Expr::I32TeeStore16:
-      case Expr::I64TeeStore8:
-      case Expr::I64TeeStore16:
-      case Expr::I64TeeStore32:
-      case Expr::I32TeeStore:
-      case Expr::I64TeeStore:
-      case Expr::F32TeeStore:
-      case Expr::F64TeeStore:
-      case Expr::F32TeeStoreF64:
-      case Expr::F64TeeStoreF32:
-      case Expr::I8x16store:
-      case Expr::I16x8store:
-      case Expr::I32x4store:
-      case Expr::I32x4store1:
-      case Expr::I32x4store2:
-      case Expr::I32x4store3:
-      case Expr::F32x4store:
-      case Expr::F32x4store1:
-      case Expr::F32x4store2:
-      case Expr::F32x4store3:
-        return ExprKind::TeeStore;
-      case Expr::Select:
-        return ExprKind::Select;
-      case Expr::GetLocal:
-        return ExprKind::GetLocal;
-      case Expr::SetLocal:
-        return ExprKind::SetLocal;
-      case Expr::TeeLocal:
-        return ExprKind::TeeLocal;
-      case Expr::GetGlobal:
-        return ExprKind::GetGlobal;
-      case Expr::SetGlobal:
-        return ExprKind::SetGlobal;
-      case Expr::TeeGlobal:
-        return ExprKind::TeeGlobal;
-      case Expr::Call:
-        return ExprKind::Call;
-      case Expr::CallIndirect:
-        return ExprKind::CallIndirect;
-      case Expr::OldCallIndirect:
-        return ExprKind::OldCallIndirect;
-      case Expr::Return:
-      case Expr::Limit:
+    switch (op) {
+      case Op::Block:
+        return OpKind::Block;
+      case Op::Loop:
+        return OpKind::Loop;
+      case Op::Unreachable:
+        return OpKind::Unreachable;
+      case Op::Drop:
+        return OpKind::Drop;
+      case Op::I32Const:
+        return OpKind::I32;
+      case Op::I64Const:
+        return OpKind::I64;
+      case Op::F32Const:
+        return OpKind::F32;
+      case Op::F64Const:
+        return OpKind::F64;
+      case Op::I8x16Const:
+        return OpKind::I8x16;
+      case Op::I16x8Const:
+        return OpKind::I16x8;
+      case Op::I32x4Const:
+        return OpKind::I32x4;
+      case Op::B8x16Const:
+        return OpKind::B8x16;
+      case Op::B16x8Const:
+        return OpKind::B16x8;
+      case Op::B32x4Const:
+        return OpKind::B32x4;
+      case Op::F32x4Const:
+        return OpKind::F32x4;
+      case Op::Br:
+        return OpKind::Br;
+      case Op::BrIf:
+        return OpKind::BrIf;
+      case Op::BrTable:
+        return OpKind::BrTable;
+      case Op::Nop:
+        return OpKind::Nop;
+      case Op::I32Clz:
+      case Op::I32Ctz:
+      case Op::I32Popcnt:
+      case Op::I64Clz:
+      case Op::I64Ctz:
+      case Op::I64Popcnt:
+      case Op::F32Abs:
+      case Op::F32Neg:
+      case Op::F32Ceil:
+      case Op::F32Floor:
+      case Op::F32Trunc:
+      case Op::F32Nearest:
+      case Op::F32Sqrt:
+      case Op::F64Abs:
+      case Op::F64Neg:
+      case Op::F64Ceil:
+      case Op::F64Floor:
+      case Op::F64Trunc:
+      case Op::F64Nearest:
+      case Op::F64Sqrt:
+      case Op::I32BitNot:
+      case Op::I32Abs:
+      case Op::F64Sin:
+      case Op::F64Cos:
+      case Op::F64Tan:
+      case Op::F64Asin:
+      case Op::F64Acos:
+      case Op::F64Atan:
+      case Op::F64Exp:
+      case Op::F64Log:
+      case Op::I32Neg:
+      case Op::I8x16neg:
+      case Op::I8x16not:
+      case Op::I16x8neg:
+      case Op::I16x8not:
+      case Op::I32x4neg:
+      case Op::I32x4not:
+      case Op::F32x4neg:
+      case Op::F32x4sqrt:
+      case Op::F32x4abs:
+      case Op::F32x4reciprocalApproximation:
+      case Op::F32x4reciprocalSqrtApproximation:
+      case Op::B8x16not:
+      case Op::B16x8not:
+      case Op::B32x4not:
+        return OpKind::Unary;
+      case Op::I32Add:
+      case Op::I32Sub:
+      case Op::I32Mul:
+      case Op::I32DivS:
+      case Op::I32DivU:
+      case Op::I32RemS:
+      case Op::I32RemU:
+      case Op::I32And:
+      case Op::I32Or:
+      case Op::I32Xor:
+      case Op::I32Shl:
+      case Op::I32ShrS:
+      case Op::I32ShrU:
+      case Op::I32Rotl:
+      case Op::I32Rotr:
+      case Op::I64Add:
+      case Op::I64Sub:
+      case Op::I64Mul:
+      case Op::I64DivS:
+      case Op::I64DivU:
+      case Op::I64RemS:
+      case Op::I64RemU:
+      case Op::I64And:
+      case Op::I64Or:
+      case Op::I64Xor:
+      case Op::I64Shl:
+      case Op::I64ShrS:
+      case Op::I64ShrU:
+      case Op::I64Rotl:
+      case Op::I64Rotr:
+      case Op::F32Add:
+      case Op::F32Sub:
+      case Op::F32Mul:
+      case Op::F32Div:
+      case Op::F32Min:
+      case Op::F32Max:
+      case Op::F32CopySign:
+      case Op::F64Add:
+      case Op::F64Sub:
+      case Op::F64Mul:
+      case Op::F64Div:
+      case Op::F64Min:
+      case Op::F64Max:
+      case Op::F64CopySign:
+      case Op::I32Min:
+      case Op::I32Max:
+      case Op::F64Mod:
+      case Op::F64Pow:
+      case Op::F64Atan2:
+      case Op::I8x16add:
+      case Op::I8x16sub:
+      case Op::I8x16mul:
+      case Op::I8x16addSaturate:
+      case Op::I8x16subSaturate:
+      case Op::I8x16addSaturateU:
+      case Op::I8x16subSaturateU:
+      case Op::I8x16and:
+      case Op::I8x16or:
+      case Op::I8x16xor:
+      case Op::I16x8add:
+      case Op::I16x8sub:
+      case Op::I16x8mul:
+      case Op::I16x8addSaturate:
+      case Op::I16x8subSaturate:
+      case Op::I16x8addSaturateU:
+      case Op::I16x8subSaturateU:
+      case Op::I16x8and:
+      case Op::I16x8or:
+      case Op::I16x8xor:
+      case Op::I32x4add:
+      case Op::I32x4sub:
+      case Op::I32x4mul:
+      case Op::I32x4and:
+      case Op::I32x4or:
+      case Op::I32x4xor:
+      case Op::F32x4add:
+      case Op::F32x4sub:
+      case Op::F32x4mul:
+      case Op::F32x4div:
+      case Op::F32x4min:
+      case Op::F32x4max:
+      case Op::F32x4minNum:
+      case Op::F32x4maxNum:
+      case Op::B8x16and:
+      case Op::B8x16or:
+      case Op::B8x16xor:
+      case Op::B16x8and:
+      case Op::B16x8or:
+      case Op::B16x8xor:
+      case Op::B32x4and:
+      case Op::B32x4or:
+      case Op::B32x4xor:
+        return OpKind::Binary;
+      case Op::I32Eq:
+      case Op::I32Ne:
+      case Op::I32LtS:
+      case Op::I32LtU:
+      case Op::I32LeS:
+      case Op::I32LeU:
+      case Op::I32GtS:
+      case Op::I32GtU:
+      case Op::I32GeS:
+      case Op::I32GeU:
+      case Op::I64Eq:
+      case Op::I64Ne:
+      case Op::I64LtS:
+      case Op::I64LtU:
+      case Op::I64LeS:
+      case Op::I64LeU:
+      case Op::I64GtS:
+      case Op::I64GtU:
+      case Op::I64GeS:
+      case Op::I64GeU:
+      case Op::F32Eq:
+      case Op::F32Ne:
+      case Op::F32Lt:
+      case Op::F32Le:
+      case Op::F32Gt:
+      case Op::F32Ge:
+      case Op::F64Eq:
+      case Op::F64Ne:
+      case Op::F64Lt:
+      case Op::F64Le:
+      case Op::F64Gt:
+      case Op::F64Ge:
+        return OpKind::Comparison;
+      case Op::I32Eqz:
+      case Op::I32WrapI64:
+      case Op::I32TruncSF32:
+      case Op::I32TruncUF32:
+      case Op::I32ReinterpretF32:
+      case Op::I32TruncSF64:
+      case Op::I32TruncUF64:
+      case Op::I64ExtendSI32:
+      case Op::I64ExtendUI32:
+      case Op::I64TruncSF32:
+      case Op::I64TruncUF32:
+      case Op::I64TruncSF64:
+      case Op::I64TruncUF64:
+      case Op::I64ReinterpretF64:
+      case Op::I64Eqz:
+      case Op::F32ConvertSI32:
+      case Op::F32ConvertUI32:
+      case Op::F32ReinterpretI32:
+      case Op::F32ConvertSI64:
+      case Op::F32ConvertUI64:
+      case Op::F32DemoteF64:
+      case Op::F64ConvertSI32:
+      case Op::F64ConvertUI32:
+      case Op::F64ConvertSI64:
+      case Op::F64ConvertUI64:
+      case Op::F64ReinterpretI64:
+      case Op::F64PromoteF32:
+      case Op::I32x4fromFloat32x4:
+      case Op::I32x4fromFloat32x4U:
+      case Op::F32x4fromInt32x4:
+      case Op::F32x4fromUint32x4:
+      case Op::I32x4fromFloat32x4Bits:
+      case Op::I32x4fromInt8x16Bits:
+      case Op::I32x4fromInt16x8Bits:
+      case Op::I16x8fromInt8x16Bits:
+      case Op::I16x8fromInt32x4Bits:
+      case Op::I16x8fromFloat32x4Bits:
+      case Op::I8x16fromInt16x8Bits:
+      case Op::I8x16fromInt32x4Bits:
+      case Op::I8x16fromFloat32x4Bits:
+      case Op::F32x4fromInt8x16Bits:
+      case Op::F32x4fromInt16x8Bits:
+      case Op::F32x4fromInt32x4Bits:
+        return OpKind::Conversion;
+      case Op::I32Load8S:
+      case Op::I32Load8U:
+      case Op::I32Load16S:
+      case Op::I32Load16U:
+      case Op::I64Load8S:
+      case Op::I64Load8U:
+      case Op::I64Load16S:
+      case Op::I64Load16U:
+      case Op::I64Load32S:
+      case Op::I64Load32U:
+      case Op::I32Load:
+      case Op::I64Load:
+      case Op::F32Load:
+      case Op::F64Load:
+      case Op::I8x16load:
+      case Op::I16x8load:
+      case Op::I32x4load:
+      case Op::I32x4load1:
+      case Op::I32x4load2:
+      case Op::I32x4load3:
+      case Op::F32x4load:
+      case Op::F32x4load1:
+      case Op::F32x4load2:
+      case Op::F32x4load3:
+        return OpKind::Load;
+      case Op::I32Store8:
+      case Op::I32Store16:
+      case Op::I64Store8:
+      case Op::I64Store16:
+      case Op::I64Store32:
+      case Op::I32Store:
+      case Op::I64Store:
+      case Op::F32Store:
+      case Op::F64Store:
+        return OpKind::Store;
+      case Op::I32TeeStore8:
+      case Op::I32TeeStore16:
+      case Op::I64TeeStore8:
+      case Op::I64TeeStore16:
+      case Op::I64TeeStore32:
+      case Op::I32TeeStore:
+      case Op::I64TeeStore:
+      case Op::F32TeeStore:
+      case Op::F64TeeStore:
+      case Op::F32TeeStoreF64:
+      case Op::F64TeeStoreF32:
+      case Op::I8x16store:
+      case Op::I16x8store:
+      case Op::I32x4store:
+      case Op::I32x4store1:
+      case Op::I32x4store2:
+      case Op::I32x4store3:
+      case Op::F32x4store:
+      case Op::F32x4store1:
+      case Op::F32x4store2:
+      case Op::F32x4store3:
+        return OpKind::TeeStore;
+      case Op::Select:
+        return OpKind::Select;
+      case Op::GetLocal:
+        return OpKind::GetLocal;
+      case Op::SetLocal:
+        return OpKind::SetLocal;
+      case Op::TeeLocal:
+        return OpKind::TeeLocal;
+      case Op::GetGlobal:
+        return OpKind::GetGlobal;
+      case Op::SetGlobal:
+        return OpKind::SetGlobal;
+      case Op::TeeGlobal:
+        return OpKind::TeeGlobal;
+      case Op::Call:
+        return OpKind::Call;
+      case Op::CallIndirect:
+        return OpKind::CallIndirect;
+      case Op::OldCallIndirect:
+        return OpKind::OldCallIndirect;
+      case Op::Return:
+      case Op::Limit:
         // Accept Limit, for use in decoding the end of a function after the body.
-        return ExprKind::Return;
-      case Expr::If:
-        return ExprKind::If;
-      case Expr::Else:
-        return ExprKind::Else;
-      case Expr::End:
-        return ExprKind::End;
-      case Expr::I32AtomicsLoad:
-        return ExprKind::AtomicLoad;
-      case Expr::I32AtomicsStore:
-        return ExprKind::AtomicStore;
-      case Expr::I32AtomicsBinOp:
-        return ExprKind::AtomicBinOp;
-      case Expr::I32AtomicsCompareExchange:
-        return ExprKind::AtomicCompareExchange;
-      case Expr::I32AtomicsExchange:
-        return ExprKind::AtomicExchange;
-      case Expr::I8x16extractLane:
-      case Expr::I8x16extractLaneU:
-      case Expr::I16x8extractLane:
-      case Expr::I16x8extractLaneU:
-      case Expr::I32x4extractLane:
-      case Expr::F32x4extractLane:
-      case Expr::B8x16extractLane:
-      case Expr::B16x8extractLane:
-      case Expr::B32x4extractLane:
-        return ExprKind::ExtractLane;
-      case Expr::I8x16replaceLane:
-      case Expr::I16x8replaceLane:
-      case Expr::I32x4replaceLane:
-      case Expr::F32x4replaceLane:
-      case Expr::B8x16replaceLane:
-      case Expr::B16x8replaceLane:
-      case Expr::B32x4replaceLane:
-        return ExprKind::ReplaceLane;
-      case Expr::I8x16swizzle:
-      case Expr::I16x8swizzle:
-      case Expr::I32x4swizzle:
-      case Expr::F32x4swizzle:
-        return ExprKind::Swizzle;
-      case Expr::I8x16shuffle:
-      case Expr::I16x8shuffle:
-      case Expr::I32x4shuffle:
-      case Expr::F32x4shuffle:
-        return ExprKind::Shuffle;
-      case Expr::I16x8check:
-      case Expr::I16x8splat:
-      case Expr::I32x4check:
-      case Expr::I32x4splat:
-      case Expr::I8x16check:
-      case Expr::I8x16splat:
-      case Expr::F32x4check:
-      case Expr::F32x4splat:
-      case Expr::B16x8check:
-      case Expr::B16x8splat:
-      case Expr::B32x4check:
-      case Expr::B32x4splat:
-      case Expr::B8x16check:
-      case Expr::B8x16splat:
-        return ExprKind::Splat;
-      case Expr::I8x16select:
-      case Expr::I16x8select:
-      case Expr::I32x4select:
-      case Expr::F32x4select:
-        return ExprKind::SimdSelect;
-      case Expr::I8x16Constructor:
-      case Expr::I16x8Constructor:
-      case Expr::I32x4Constructor:
-      case Expr::F32x4Constructor:
-      case Expr::B8x16Constructor:
-      case Expr::B16x8Constructor:
-      case Expr::B32x4Constructor:
-        return ExprKind::SimdCtor;
-      case Expr::B8x16allTrue:
-      case Expr::B8x16anyTrue:
-      case Expr::B16x8allTrue:
-      case Expr::B16x8anyTrue:
-      case Expr::B32x4allTrue:
-      case Expr::B32x4anyTrue:
-        return ExprKind::SimdBooleanReduction;
-      case Expr::I8x16shiftLeftByScalar:
-      case Expr::I8x16shiftRightByScalar:
-      case Expr::I8x16shiftRightByScalarU:
-      case Expr::I16x8shiftLeftByScalar:
-      case Expr::I16x8shiftRightByScalar:
-      case Expr::I16x8shiftRightByScalarU:
-      case Expr::I32x4shiftLeftByScalar:
-      case Expr::I32x4shiftRightByScalar:
-      case Expr::I32x4shiftRightByScalarU:
-        return ExprKind::SimdShiftByScalar;
-      case Expr::I8x16equal:
-      case Expr::I8x16notEqual:
-      case Expr::I8x16greaterThan:
-      case Expr::I8x16greaterThanOrEqual:
-      case Expr::I8x16lessThan:
-      case Expr::I8x16lessThanOrEqual:
-      case Expr::I8x16greaterThanU:
-      case Expr::I8x16greaterThanOrEqualU:
-      case Expr::I8x16lessThanU:
-      case Expr::I8x16lessThanOrEqualU:
-      case Expr::I16x8equal:
-      case Expr::I16x8notEqual:
-      case Expr::I16x8greaterThan:
-      case Expr::I16x8greaterThanOrEqual:
-      case Expr::I16x8lessThan:
-      case Expr::I16x8lessThanOrEqual:
-      case Expr::I16x8greaterThanU:
-      case Expr::I16x8greaterThanOrEqualU:
-      case Expr::I16x8lessThanU:
-      case Expr::I16x8lessThanOrEqualU:
-      case Expr::I32x4equal:
-      case Expr::I32x4notEqual:
-      case Expr::I32x4greaterThan:
-      case Expr::I32x4greaterThanOrEqual:
-      case Expr::I32x4lessThan:
-      case Expr::I32x4lessThanOrEqual:
-      case Expr::I32x4greaterThanU:
-      case Expr::I32x4greaterThanOrEqualU:
-      case Expr::I32x4lessThanU:
-      case Expr::I32x4lessThanOrEqualU:
-      case Expr::F32x4equal:
-      case Expr::F32x4notEqual:
-      case Expr::F32x4greaterThan:
-      case Expr::F32x4greaterThanOrEqual:
-      case Expr::F32x4lessThan:
-      case Expr::F32x4lessThanOrEqual:
-        return ExprKind::SimdComparison;
-      case Expr::CurrentMemory:
-        return ExprKind::CurrentMemory;
-      case Expr::GrowMemory:
-        return ExprKind::GrowMemory;
+        return OpKind::Return;
+      case Op::If:
+        return OpKind::If;
+      case Op::Else:
+        return OpKind::Else;
+      case Op::End:
+        return OpKind::End;
+      case Op::I32AtomicsLoad:
+        return OpKind::AtomicLoad;
+      case Op::I32AtomicsStore:
+        return OpKind::AtomicStore;
+      case Op::I32AtomicsBinOp:
+        return OpKind::AtomicBinOp;
+      case Op::I32AtomicsCompareExchange:
+        return OpKind::AtomicCompareExchange;
+      case Op::I32AtomicsExchange:
+        return OpKind::AtomicExchange;
+      case Op::I8x16extractLane:
+      case Op::I8x16extractLaneU:
+      case Op::I16x8extractLane:
+      case Op::I16x8extractLaneU:
+      case Op::I32x4extractLane:
+      case Op::F32x4extractLane:
+      case Op::B8x16extractLane:
+      case Op::B16x8extractLane:
+      case Op::B32x4extractLane:
+        return OpKind::ExtractLane;
+      case Op::I8x16replaceLane:
+      case Op::I16x8replaceLane:
+      case Op::I32x4replaceLane:
+      case Op::F32x4replaceLane:
+      case Op::B8x16replaceLane:
+      case Op::B16x8replaceLane:
+      case Op::B32x4replaceLane:
+        return OpKind::ReplaceLane;
+      case Op::I8x16swizzle:
+      case Op::I16x8swizzle:
+      case Op::I32x4swizzle:
+      case Op::F32x4swizzle:
+        return OpKind::Swizzle;
+      case Op::I8x16shuffle:
+      case Op::I16x8shuffle:
+      case Op::I32x4shuffle:
+      case Op::F32x4shuffle:
+        return OpKind::Shuffle;
+      case Op::I16x8check:
+      case Op::I16x8splat:
+      case Op::I32x4check:
+      case Op::I32x4splat:
+      case Op::I8x16check:
+      case Op::I8x16splat:
+      case Op::F32x4check:
+      case Op::F32x4splat:
+      case Op::B16x8check:
+      case Op::B16x8splat:
+      case Op::B32x4check:
+      case Op::B32x4splat:
+      case Op::B8x16check:
+      case Op::B8x16splat:
+        return OpKind::Splat;
+      case Op::I8x16select:
+      case Op::I16x8select:
+      case Op::I32x4select:
+      case Op::F32x4select:
+        return OpKind::SimdSelect;
+      case Op::I8x16Constructor:
+      case Op::I16x8Constructor:
+      case Op::I32x4Constructor:
+      case Op::F32x4Constructor:
+      case Op::B8x16Constructor:
+      case Op::B16x8Constructor:
+      case Op::B32x4Constructor:
+        return OpKind::SimdCtor;
+      case Op::B8x16allTrue:
+      case Op::B8x16anyTrue:
+      case Op::B16x8allTrue:
+      case Op::B16x8anyTrue:
+      case Op::B32x4allTrue:
+      case Op::B32x4anyTrue:
+        return OpKind::SimdBooleanReduction;
+      case Op::I8x16shiftLeftByScalar:
+      case Op::I8x16shiftRightByScalar:
+      case Op::I8x16shiftRightByScalarU:
+      case Op::I16x8shiftLeftByScalar:
+      case Op::I16x8shiftRightByScalar:
+      case Op::I16x8shiftRightByScalarU:
+      case Op::I32x4shiftLeftByScalar:
+      case Op::I32x4shiftRightByScalar:
+      case Op::I32x4shiftRightByScalarU:
+        return OpKind::SimdShiftByScalar;
+      case Op::I8x16equal:
+      case Op::I8x16notEqual:
+      case Op::I8x16greaterThan:
+      case Op::I8x16greaterThanOrEqual:
+      case Op::I8x16lessThan:
+      case Op::I8x16lessThanOrEqual:
+      case Op::I8x16greaterThanU:
+      case Op::I8x16greaterThanOrEqualU:
+      case Op::I8x16lessThanU:
+      case Op::I8x16lessThanOrEqualU:
+      case Op::I16x8equal:
+      case Op::I16x8notEqual:
+      case Op::I16x8greaterThan:
+      case Op::I16x8greaterThanOrEqual:
+      case Op::I16x8lessThan:
+      case Op::I16x8lessThanOrEqual:
+      case Op::I16x8greaterThanU:
+      case Op::I16x8greaterThanOrEqualU:
+      case Op::I16x8lessThanU:
+      case Op::I16x8lessThanOrEqualU:
+      case Op::I32x4equal:
+      case Op::I32x4notEqual:
+      case Op::I32x4greaterThan:
+      case Op::I32x4greaterThanOrEqual:
+      case Op::I32x4lessThan:
+      case Op::I32x4lessThanOrEqual:
+      case Op::I32x4greaterThanU:
+      case Op::I32x4greaterThanOrEqualU:
+      case Op::I32x4lessThanU:
+      case Op::I32x4lessThanOrEqualU:
+      case Op::F32x4equal:
+      case Op::F32x4notEqual:
+      case Op::F32x4greaterThan:
+      case Op::F32x4greaterThanOrEqual:
+      case Op::F32x4lessThan:
+      case Op::F32x4lessThanOrEqual:
+        return OpKind::SimdComparison;
+      case Op::CurrentMemory:
+        return OpKind::CurrentMemory;
+      case Op::GrowMemory:
+        return OpKind::GrowMemory;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unimplemented opcode");
 }
 #endif
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -35,17 +35,17 @@ enum class LabelKind : uint8_t {
     Loop,
     Then,
     UnreachableThen, // like Then, but not reachable
     Else
 };
 
 #ifdef DEBUG
 // Families of opcodes that share a signature and validation logic.
-enum class ExprKind {
+enum class OpKind {
     Block,
     Loop,
     Unreachable,
     Drop,
     I32,
     I64,
     F32,
     F64,
@@ -96,20 +96,20 @@ enum class ExprKind {
     Splat,
     SimdSelect,
     SimdCtor,
     SimdBooleanReduction,
     SimdShiftByScalar,
     SimdComparison,
 };
 
-// Return the ExprKind for a given Expr. This is used for sanity-checking that
-// API users use the correct read function for a given Expr.
-ExprKind
-Classify(Expr expr);
+// Return the OpKind for a given Op. This is used for sanity-checking that
+// API users use the correct read function for a given Op.
+OpKind
+Classify(Op op);
 #endif
 
 // Common fields for linear memory access.
 template <typename Value>
 struct LinearMemoryAddress
 {
     Value base;
     uint32_t offset;
@@ -225,19 +225,19 @@ class TypeAndValue<Nothing>
       : type_(type)
     {}
 
     ValType type() const { return type_; }
     Nothing value() const { return Nothing(); }
     void setValue(Nothing value) {}
 };
 
-// A policy class for configuring ExprIter. Clients can use this as a
+// A policy class for configuring OpIter. Clients can use this as a
 // base class, and override the behavior as needed.
-struct ExprIterPolicy
+struct OpIterPolicy
 {
     // Should the iterator perform validation, such as type checking and
     // validity checking?
     static const bool Validate = false;
 
     // Should the iterator produce output values?
     static const bool Output = false;
 
@@ -250,31 +250,31 @@ struct ExprIterPolicy
 
 // An iterator over the bytes of a function body. It performs validation
 // (if Policy::Validate is true) and unpacks the data into a usable form.
 //
 // The MOZ_STACK_CLASS attribute here is because of the use of DebugOnly.
 // There's otherwise nothing inherent in this class which would require
 // it to be used on the stack.
 template <typename Policy>
-class MOZ_STACK_CLASS ExprIter : private Policy
+class MOZ_STACK_CLASS OpIter : private Policy
 {
     static const bool Validate = Policy::Validate;
     static const bool Output = Policy::Output;
     typedef typename Policy::Value Value;
     typedef typename Policy::ControlItem ControlItem;
 
     Decoder& d_;
     const size_t offsetInModule_;
 
-    Vector<TypeAndValue<Value>, 0, SystemAllocPolicy> valueStack_;
-    Vector<ControlStackEntry<ControlItem>, 0, SystemAllocPolicy> controlStack_;
+    Vector<TypeAndValue<Value>, 8, SystemAllocPolicy> valueStack_;
+    Vector<ControlStackEntry<ControlItem>, 8, SystemAllocPolicy> controlStack_;
     bool reachable_;
 
-    DebugOnly<Expr> expr_;
+    DebugOnly<Op> op_;
     size_t offsetOfExpr_;
 
     MOZ_MUST_USE bool readFixedU8(uint8_t* out) {
         if (Validate)
             return d_.readFixedU8(out);
         *out = d_.uncheckedReadFixedU8();
         return true;
     }
@@ -498,48 +498,48 @@ class MOZ_STACK_CLASS ExprIter : private
         valueStack_.shrinkTo(controlStack_.back().valueStackStart());
         reachable_ = false;
     }
 
     bool checkBrValue(uint32_t relativeDepth, ExprType* type, Value* value);
     bool checkBrIfValues(uint32_t relativeDepth, Value* condition, ExprType* type, Value* value);
 
   public:
-    explicit ExprIter(Decoder& decoder, uint32_t offsetInModule = 0)
+    explicit OpIter(Decoder& decoder, uint32_t offsetInModule = 0)
       : d_(decoder), offsetInModule_(offsetInModule), reachable_(true),
-        expr_(Expr::Limit), offsetOfExpr_(0)
+        op_(Op::Limit), offsetOfExpr_(0)
     {}
 
     // Return the decoding byte offset.
     uint32_t currentOffset() const { return d_.currentOffset(); }
 
-    // Returning the offset within the entire module of the last-read Expr.
+    // Returning the offset within the entire module of the last-read Op.
     TrapOffset trapOffset() const {
         return TrapOffset(offsetInModule_ + offsetOfExpr_);
     }
 
     // Test whether the iterator has reached the end of the buffer.
     bool done() const { return d_.done(); }
 
     // Report a general failure.
     MOZ_MUST_USE bool fail(const char* msg) MOZ_COLD;
 
     // Report an unimplemented feature.
     MOZ_MUST_USE bool notYetImplemented(const char* what) MOZ_COLD;
 
     // Report an unrecognized opcode.
-    MOZ_MUST_USE bool unrecognizedOpcode(Expr expr) MOZ_COLD;
+    MOZ_MUST_USE bool unrecognizedOpcode(uint32_t expr) MOZ_COLD;
 
     // Test whether the iterator is currently in "reachable" code.
     bool inReachableCode() const { return reachable_; }
 
     // ------------------------------------------------------------------------
     // Decoding and validation interface.
 
-    MOZ_MUST_USE bool readExpr(Expr* expr);
+    MOZ_MUST_USE bool readOp(uint16_t* op);
     MOZ_MUST_USE bool readFunctionStart(ExprType ret);
     MOZ_MUST_USE bool readFunctionEnd();
     MOZ_MUST_USE bool readReturn(Value* value);
     MOZ_MUST_USE bool readBlock();
     MOZ_MUST_USE bool readLoop();
     MOZ_MUST_USE bool readIf(Value* condition);
     MOZ_MUST_USE bool readElse(ExprType* thenType, Value* thenValue);
     MOZ_MUST_USE bool readEnd(LabelKind* kind, ExprType* type, Value* value);
@@ -658,92 +658,92 @@ class MOZ_STACK_CLASS ExprIter : private
     // end of the function body.
     bool controlStackEmpty() const {
         return controlStack_.empty();
     }
 };
 
 template <typename Policy>
 bool
-ExprIter<Policy>::typeMismatch(ExprType actual, ExprType expected)
+OpIter<Policy>::typeMismatch(ExprType actual, ExprType expected)
 {
     MOZ_ASSERT(Validate);
     MOZ_ASSERT(reachable_);
 
     UniqueChars error(JS_smprintf("type mismatch: expression has type %s but expected %s",
                                   ToCString(actual), ToCString(expected)));
     if (!error)
         return false;
 
     return fail(error.get());
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::checkType(ValType actual, ValType expected)
+OpIter<Policy>::checkType(ValType actual, ValType expected)
 {
     return checkType(ToExprType(actual), ToExprType(expected));
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::checkType(ExprType actual, ExprType expected)
+OpIter<Policy>::checkType(ExprType actual, ExprType expected)
 {
     MOZ_ASSERT(reachable_);
 
     if (!Validate) {
         MOZ_ASSERT(actual == expected, "type mismatch");
         return true;
     }
 
     if (MOZ_LIKELY(actual == expected))
         return true;
 
     return typeMismatch(actual, expected);
 }
 
 template <typename Policy>
 bool
-ExprIter<Policy>::notYetImplemented(const char* what)
+OpIter<Policy>::notYetImplemented(const char* what)
 {
     UniqueChars error(JS_smprintf("not yet implemented: %s", what));
     if (!error)
         return false;
 
     return fail(error.get());
 }
 
 template <typename Policy>
 bool
-ExprIter<Policy>::unrecognizedOpcode(Expr expr)
+OpIter<Policy>::unrecognizedOpcode(uint32_t expr)
 {
-    UniqueChars error(JS_smprintf("unrecognized opcode: %x", uint32_t(expr)));
+    UniqueChars error(JS_smprintf("unrecognized opcode: %x", expr));
     if (!error)
         return false;
 
     return fail(error.get());
 }
 
 template <typename Policy>
 bool
-ExprIter<Policy>::fail(const char* msg)
+OpIter<Policy>::fail(const char* msg)
 {
     return d_.fail("%s", msg);
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::pushControl(LabelKind kind, ExprType type, bool reachable)
+OpIter<Policy>::pushControl(LabelKind kind, ExprType type, bool reachable)
 {
     return controlStack_.emplaceBack(kind, type, reachable, valueStack_.length());
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::mergeControl(LabelKind* kind, ExprType* type, Value* value)
+OpIter<Policy>::mergeControl(LabelKind* kind, ExprType* type, Value* value)
 {
     MOZ_ASSERT(!controlStack_.empty());
 
     ControlStackEntry<ControlItem>& controlItem = controlStack_.back();
     *kind = controlItem.kind();
 
     if (reachable_) {
         // Unlike branching, exiting a scope via fallthrough does not implicitly
@@ -781,17 +781,17 @@ ExprIter<Policy>::mergeControl(LabelKind
             *value = Value();
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::popControl(LabelKind* kind, ExprType* type, Value* value)
+OpIter<Policy>::popControl(LabelKind* kind, ExprType* type, Value* value)
 {
     if (!mergeControl(kind, type, value))
         return false;
 
     if (*kind == LabelKind::Then) {
         // A reachable If without an Else. Forbid a result value.
         if (reachable_) {
             if (Validate && !IsVoid(*type))
@@ -805,95 +805,97 @@ ExprIter<Policy>::popControl(LabelKind* 
     if (!reachable_ && !controlStack_.empty())
         valueStack_.shrinkTo(controlStack_.back().valueStackStart());
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBlockType(ExprType* type)
+OpIter<Policy>::readBlockType(ExprType* type)
 {
-    if (!d_.readBlockType(type))
+    uint8_t unchecked;
+    if (!d_.readBlockType(&unchecked))
         return fail("unable to read block signature");
 
     if (Validate) {
-        switch (*type) {
-          case ExprType::Void:
-          case ExprType::I32:
-          case ExprType::I64:
-          case ExprType::F32:
-          case ExprType::F64:
-          case ExprType::I8x16:
-          case ExprType::I16x8:
-          case ExprType::I32x4:
-          case ExprType::F32x4:
-          case ExprType::B8x16:
-          case ExprType::B16x8:
-          case ExprType::B32x4:
+        switch (unchecked) {
+          case uint8_t(ExprType::Void):
+          case uint8_t(ExprType::I32):
+          case uint8_t(ExprType::I64):
+          case uint8_t(ExprType::F32):
+          case uint8_t(ExprType::F64):
+          case uint8_t(ExprType::I8x16):
+          case uint8_t(ExprType::I16x8):
+          case uint8_t(ExprType::I32x4):
+          case uint8_t(ExprType::F32x4):
+          case uint8_t(ExprType::B8x16):
+          case uint8_t(ExprType::B16x8):
+          case uint8_t(ExprType::B32x4):
             break;
           default:
             return fail("invalid inline block type");
         }
     }
 
+    *type = ExprType(unchecked);
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readExpr(Expr* expr)
+OpIter<Policy>::readOp(uint16_t* op)
 {
     offsetOfExpr_ = d_.currentOffset();
 
     if (Validate) {
-        if (MOZ_UNLIKELY(!d_.readExpr(expr)))
+        if (MOZ_UNLIKELY(!d_.readOp(op)))
             return fail("unable to read opcode");
     } else {
-        *expr = d_.uncheckedReadExpr();
+        *op = uint16_t(d_.uncheckedReadOp());
     }
 
-    expr_ = *expr;
+    op_ = Op(*op);  // debug-only
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readFunctionStart(ExprType ret)
+OpIter<Policy>::readFunctionStart(ExprType ret)
 {
     MOZ_ASSERT(valueStack_.empty());
     MOZ_ASSERT(controlStack_.empty());
-    MOZ_ASSERT(Expr(expr_) == Expr::Limit);
+    MOZ_ASSERT(Op(op_) == Op::Limit);
     MOZ_ASSERT(reachable_);
 
     return pushControl(LabelKind::Block, ret, false);
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readFunctionEnd()
+OpIter<Policy>::readFunctionEnd()
 {
     if (Validate) {
         if (!controlStack_.empty())
             return fail("unbalanced function body control flow");
     } else {
         MOZ_ASSERT(controlStack_.empty());
     }
 
-    expr_ = Expr::Limit;
+    op_ = Op::Limit;
     valueStack_.clear();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readReturn(Value* value)
+OpIter<Policy>::readReturn(Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Return);
+    MOZ_ASSERT(Classify(op_) == OpKind::Return);
 
     if (MOZ_LIKELY(reachable_)) {
         ControlStackEntry<ControlItem>& controlItem = controlStack_[0];
         MOZ_ASSERT(controlItem.kind() == LabelKind::Block);
 
         controlItem.setReachable();
 
         if (!IsVoid(controlItem.type())) {
@@ -903,45 +905,45 @@ ExprIter<Policy>::readReturn(Value* valu
     }
 
     enterUnreachableCode();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBlock()
+OpIter<Policy>::readBlock()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Block);
+    MOZ_ASSERT(Classify(op_) == OpKind::Block);
 
     ExprType type = ExprType::Limit;
     if (!readBlockType(&type))
         return false;
 
     return pushControl(LabelKind::Block, type, false);
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readLoop()
+OpIter<Policy>::readLoop()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Loop);
+    MOZ_ASSERT(Classify(op_) == OpKind::Loop);
 
     ExprType type = ExprType::Limit;
     if (!readBlockType(&type))
         return false;
 
     return pushControl(LabelKind::Loop, type, reachable_);
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readIf(Value* condition)
+OpIter<Policy>::readIf(Value* condition)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::If);
+    MOZ_ASSERT(Classify(op_) == OpKind::If);
 
     ExprType type = ExprType::Limit;
     if (!readBlockType(&type))
         return false;
 
     if (MOZ_LIKELY(reachable_)) {
         if (!popWithType(ValType::I32, condition))
             return false;
@@ -949,19 +951,19 @@ ExprIter<Policy>::readIf(Value* conditio
         return pushControl(LabelKind::Then, type, false);
     }
 
     return pushControl(LabelKind::UnreachableThen, type, false);
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readElse(ExprType* thenType, Value* thenValue)
+OpIter<Policy>::readElse(ExprType* thenType, Value* thenValue)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Else);
+    MOZ_ASSERT(Classify(op_) == OpKind::Else);
 
     // Finish up the then arm.
     ExprType type = ExprType::Limit;
     LabelKind kind;
     if (!mergeControl(&kind, &type, thenValue))
         return false;
 
     if (Output)
@@ -981,36 +983,36 @@ ExprIter<Policy>::readElse(ExprType* the
 
     MOZ_ASSERT(valueStack_.length() == controlStack_.back().valueStackStart());
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readEnd(LabelKind* kind, ExprType* type, Value* value)
+OpIter<Policy>::readEnd(LabelKind* kind, ExprType* type, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::End);
+    MOZ_ASSERT(Classify(op_) == OpKind::End);
 
     LabelKind validateKind = static_cast<LabelKind>(-1);
     ExprType validateType = ExprType::Limit;
     if (!popControl(&validateKind, &validateType, value))
         return false;
 
     if (Output) {
         *kind = validateKind;
         *type = validateType;
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::checkBrValue(uint32_t relativeDepth, ExprType* type, Value* value)
+OpIter<Policy>::checkBrValue(uint32_t relativeDepth, ExprType* type, Value* value)
 {
     if (MOZ_LIKELY(reachable_)) {
         ControlStackEntry<ControlItem>* controlItem = nullptr;
         if (!getControl(relativeDepth, &controlItem))
             return false;
 
         if (controlItem->kind() != LabelKind::Loop) {
             controlItem->setReachable();
@@ -1029,19 +1031,19 @@ ExprIter<Policy>::checkBrValue(uint32_t 
         *value = Value();
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBr(uint32_t* relativeDepth, ExprType* type, Value* value)
+OpIter<Policy>::readBr(uint32_t* relativeDepth, ExprType* type, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Br);
+    MOZ_ASSERT(Classify(op_) == OpKind::Br);
 
     uint32_t validateRelativeDepth;
     if (!readVarU32(&validateRelativeDepth))
         return fail("unable to read br depth");
 
     if (!checkBrValue(validateRelativeDepth, type, value))
         return false;
 
@@ -1049,17 +1051,17 @@ ExprIter<Policy>::readBr(uint32_t* relat
         *relativeDepth = validateRelativeDepth;
 
     enterUnreachableCode();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::checkBrIfValues(uint32_t relativeDepth, Value* condition,
+OpIter<Policy>::checkBrIfValues(uint32_t relativeDepth, Value* condition,
                                   ExprType* type, Value* value)
 {
     if (MOZ_LIKELY(reachable_)) {
         if (!popWithType(ValType::I32, condition))
             return false;
 
         ControlStackEntry<ControlItem>* controlItem = nullptr;
         if (!getControl(relativeDepth, &controlItem))
@@ -1082,39 +1084,39 @@ ExprIter<Policy>::checkBrIfValues(uint32
         *value = Value();
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBrIf(uint32_t* relativeDepth, ExprType* type, Value* value, Value* condition)
+OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ExprType* type, Value* value, Value* condition)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::BrIf);
+    MOZ_ASSERT(Classify(op_) == OpKind::BrIf);
 
     uint32_t validateRelativeDepth;
     if (!readVarU32(&validateRelativeDepth))
         return fail("unable to read br_if depth");
 
     if (!checkBrIfValues(validateRelativeDepth, condition, type, value))
         return false;
 
     if (Output)
         *relativeDepth = validateRelativeDepth;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBrTable(uint32_t* tableLength, ExprType* type,
+OpIter<Policy>::readBrTable(uint32_t* tableLength, ExprType* type,
                               Value* value, Value* index)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::BrTable);
+    MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
 
     if (!readVarU32(tableLength))
         return fail("unable to read br_table table length");
 
     if (MOZ_LIKELY(reachable_)) {
         if (!popWithType(ValType::I32, index))
             return false;
     }
@@ -1124,19 +1126,19 @@ ExprIter<Policy>::readBrTable(uint32_t* 
     if (Output)
         *value = Value();
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBrTableEntry(ExprType* type, Value* value, uint32_t* depth)
+OpIter<Policy>::readBrTableEntry(ExprType* type, Value* value, uint32_t* depth)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::BrTable);
+    MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
 
     if (!readVarU32(depth))
         return false;
 
     ExprType knownType = *type;
 
     if (MOZ_LIKELY(reachable_)) {
         ControlStackEntry<ControlItem>* controlItem = nullptr;
@@ -1166,114 +1168,114 @@ ExprIter<Policy>::readBrTableEntry(ExprT
     *type = ExprType::Void;
     if (Output)
         *value = Value();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBrTableDefault(ExprType* type, Value* value, uint32_t* depth)
+OpIter<Policy>::readBrTableDefault(ExprType* type, Value* value, uint32_t* depth)
 {
     if (!readBrTableEntry(type, value, depth))
         return false;
 
     MOZ_ASSERT(!reachable_ || *type != ExprType::Limit);
 
     enterUnreachableCode();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readUnreachable()
+OpIter<Policy>::readUnreachable()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Unreachable);
+    MOZ_ASSERT(Classify(op_) == OpKind::Unreachable);
 
     enterUnreachableCode();
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readDrop()
+OpIter<Policy>::readDrop()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Drop);
+    MOZ_ASSERT(Classify(op_) == OpKind::Drop);
 
     if (!pop())
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readUnary(ValType operandType, Value* input)
+OpIter<Policy>::readUnary(ValType operandType, Value* input)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Unary);
+    MOZ_ASSERT(Classify(op_) == OpKind::Unary);
 
     if (!popWithType(operandType, input))
         return false;
 
     infalliblePush(operandType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readConversion(ValType operandType, ValType resultType, Value* input)
+OpIter<Policy>::readConversion(ValType operandType, ValType resultType, Value* input)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Conversion);
+    MOZ_ASSERT(Classify(op_) == OpKind::Conversion);
 
     if (!popWithType(operandType, input))
         return false;
 
     infalliblePush(resultType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readBinary(ValType operandType, Value* lhs, Value* rhs)
+OpIter<Policy>::readBinary(ValType operandType, Value* lhs, Value* rhs)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Binary);
+    MOZ_ASSERT(Classify(op_) == OpKind::Binary);
 
     if (!popWithType(operandType, rhs))
         return false;
 
     if (!popWithType(operandType, lhs))
         return false;
 
     infalliblePush(operandType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readComparison(ValType operandType, Value* lhs, Value* rhs)
+OpIter<Policy>::readComparison(ValType operandType, Value* lhs, Value* rhs)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Comparison);
+    MOZ_ASSERT(Classify(op_) == OpKind::Comparison);
 
     if (!popWithType(operandType, rhs))
         return false;
 
     if (!popWithType(operandType, lhs))
         return false;
 
     infalliblePush(ValType::I32);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr)
+OpIter<Policy>::readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr)
 {
     uint8_t alignLog2;
     if (!readFixedU8(&alignLog2))
         return fail("unable to read load alignment");
 
     uint32_t unusedOffset;
     if (!readVarU32(Output ? &addr->offset : &unusedOffset))
         return fail("unable to read load offset");
@@ -1288,96 +1290,95 @@ ExprIter<Policy>::readLinearMemoryAddres
     if (Output)
         addr->align = uint32_t(1) << alignLog2;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readLoad(ValType resultType, uint32_t byteSize,
-                           LinearMemoryAddress<Value>* addr)
+OpIter<Policy>::readLoad(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Load);
+    MOZ_ASSERT(Classify(op_) == OpKind::Load);
 
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     infalliblePush(resultType);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readStore(ValType resultType, uint32_t byteSize,
-                            LinearMemoryAddress<Value>* addr, Value* value)
+OpIter<Policy>::readStore(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr,
+                          Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Store);
+    MOZ_ASSERT(Classify(op_) == OpKind::Store);
 
     if (!popWithType(resultType, value))
         return false;
 
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readTeeStore(ValType resultType, uint32_t byteSize,
-                               LinearMemoryAddress<Value>* addr, Value* value)
+OpIter<Policy>::readTeeStore(ValType resultType, uint32_t byteSize, LinearMemoryAddress<Value>* addr,
+                             Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::TeeStore);
+    MOZ_ASSERT(Classify(op_) == OpKind::TeeStore);
 
     if (!popWithType(resultType, value))
         return false;
 
     if (!readLinearMemoryAddress(byteSize, addr))
         return false;
 
     infalliblePush(TypeAndValue<Value>(resultType, Output ? *value : Value()));
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readNop()
+OpIter<Policy>::readNop()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Nop);
+    MOZ_ASSERT(Classify(op_) == OpKind::Nop);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readCurrentMemory()
+OpIter<Policy>::readCurrentMemory()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::CurrentMemory);
+    MOZ_ASSERT(Classify(op_) == OpKind::CurrentMemory);
 
     uint32_t flags;
     if (!readVarU32(&flags))
         return false;
 
     if (Validate && flags != uint32_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
     if (!push(ValType::I32))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readGrowMemory(Value* input)
+OpIter<Policy>::readGrowMemory(Value* input)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::GrowMemory);
+    MOZ_ASSERT(Classify(op_) == OpKind::GrowMemory);
 
     uint32_t flags;
     if (!readVarU32(&flags))
         return false;
 
     if (Validate && flags != uint32_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
@@ -1386,19 +1387,19 @@ ExprIter<Policy>::readGrowMemory(Value* 
 
     infalliblePush(ValType::I32);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readSelect(ValType* type, Value* trueValue, Value* falseValue, Value* condition)
+OpIter<Policy>::readSelect(ValType* type, Value* trueValue, Value* falseValue, Value* condition)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Select);
+    MOZ_ASSERT(Classify(op_) == OpKind::Select);
 
     if (!popWithType(ValType::I32, condition))
         return false;
 
     TypeAndValue<Value> false_;
     if (!pop(&false_))
         return false;
 
@@ -1418,19 +1419,19 @@ ExprIter<Policy>::readSelect(ValType* ty
         *falseValue = false_.value();
     }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readGetLocal(const ValTypeVector& locals, uint32_t* id)
+OpIter<Policy>::readGetLocal(const ValTypeVector& locals, uint32_t* id)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::GetLocal);
+    MOZ_ASSERT(Classify(op_) == OpKind::GetLocal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= locals.length())
         return fail("get_local index out of range");
 
@@ -1440,19 +1441,19 @@ ExprIter<Policy>::readGetLocal(const Val
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
+OpIter<Policy>::readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::SetLocal);
+    MOZ_ASSERT(Classify(op_) == OpKind::SetLocal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= locals.length())
         return fail("set_local index out of range");
 
@@ -1462,19 +1463,19 @@ ExprIter<Policy>::readSetLocal(const Val
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
+OpIter<Policy>::readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::TeeLocal);
+    MOZ_ASSERT(Classify(op_) == OpKind::TeeLocal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= locals.length())
         return fail("set_local index out of range");
 
@@ -1484,19 +1485,19 @@ ExprIter<Policy>::readTeeLocal(const Val
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readGetGlobal(const GlobalDescVector& globals, uint32_t* id)
+OpIter<Policy>::readGetGlobal(const GlobalDescVector& globals, uint32_t* id)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::GetGlobal);
+    MOZ_ASSERT(Classify(op_) == OpKind::GetGlobal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= globals.length())
         return fail("get_global index out of range");