merge mozilla-inbound to mozilla-central a=merge FIREFOX_AURORA_52_BASE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Nov 2016 10:22:06 +0100
changeset 322374 1196bf3032e1
parent 322258 add9dada238e (current diff)
parent 322373 71825cbd0e25 (diff)
child 322375 67a7f044f3f9
child 322388 f9c01ced5685
child 322455 c29cedd0d3f8
push id30945
push usercbook@mozilla.com
push date2016-11-14 09:22 +0000
treeherdermozilla-central@1196bf3032e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
1196bf3032e1 / 52.0a1 / 20161114030203 / files
nightly linux64
1196bf3032e1 / 52.0a1 / 20161114030203 / files
nightly mac
1196bf3032e1 / 52.0a1 / 20161114030203 / files
nightly win32
1196bf3032e1 / 52.0a1 / 20161114030203 / files
nightly win64
1196bf3032e1 / 52.0a1 / 20161114030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
addon-sdk/source/lib/sdk/addon/installer.js
browser/app/profile/firefox.js
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
modules/libpref/init/all.js
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
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/extensions/internal/XPIProvider.jsm
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/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");
 
@@ -1506,19 +1507,19 @@ ExprIter<Policy>::readGetGlobal(const Gl
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readSetGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
+OpIter<Policy>::readSetGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::SetGlobal);
+    MOZ_ASSERT(Classify(op_) == OpKind::SetGlobal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= globals.length())
         return fail("set_global index out of range");
 
@@ -1531,19 +1532,19 @@ ExprIter<Policy>::readSetGlobal(const Gl
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readTeeGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
+OpIter<Policy>::readTeeGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::TeeGlobal);
+    MOZ_ASSERT(Classify(op_) == OpKind::TeeGlobal);
 
     uint32_t validateId;
     if (!readVarU32(&validateId))
         return false;
 
     if (Validate && validateId >= globals.length())
         return fail("set_global index out of range");
 
@@ -1556,207 +1557,207 @@ ExprIter<Policy>::readTeeGlobal(const Gl
     if (Output)
         *id = validateId;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readI32Const(int32_t* i32)
+OpIter<Policy>::readI32Const(int32_t* i32)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::I32);
+    MOZ_ASSERT(Classify(op_) == OpKind::I32);
 
     int32_t unused;
     if (!readVarS32(Output ? i32 : &unused))
         return false;
 
     if (!push(ValType::I32))
        return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readI64Const(int64_t* i64)
+OpIter<Policy>::readI64Const(int64_t* i64)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::I64);
+    MOZ_ASSERT(Classify(op_) == OpKind::I64);
 
     int64_t unused;
     if (!readVarS64(Output ? i64 : &unused))
         return false;
 
     if (!push(ValType::I64))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readF32Const(RawF32* f32)
+OpIter<Policy>::readF32Const(RawF32* f32)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::F32);
+    MOZ_ASSERT(Classify(op_) == OpKind::F32);
 
     RawF32 unused;
     if (!readFixedF32(Output ? f32 : &unused))
         return false;
 
     if (!push(ValType::F32))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readF64Const(RawF64* f64)
+OpIter<Policy>::readF64Const(RawF64* f64)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::F64);
+    MOZ_ASSERT(Classify(op_) == OpKind::F64);
 
     RawF64 unused;
     if (!readFixedF64(Output ? f64 : &unused))
        return false;
 
     if (!push(ValType::F64))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readI8x16Const(I8x16* i8x16)
+OpIter<Policy>::readI8x16Const(I8x16* i8x16)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::I8x16);
+    MOZ_ASSERT(Classify(op_) == OpKind::I8x16);
 
     I8x16 unused;
     if (!readFixedI8x16(Output ? i8x16 : &unused))
         return false;
 
     if (!push(ValType::I8x16))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readI16x8Const(I16x8* i16x8)
+OpIter<Policy>::readI16x8Const(I16x8* i16x8)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::I16x8);
+    MOZ_ASSERT(Classify(op_) == OpKind::I16x8);
 
     I16x8 unused;
     if (!readFixedI16x8(Output ? i16x8 : &unused))
         return false;
 
     if (!push(ValType::I16x8))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readI32x4Const(I32x4* i32x4)
+OpIter<Policy>::readI32x4Const(I32x4* i32x4)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::I32x4);
+    MOZ_ASSERT(Classify(op_) == OpKind::I32x4);
 
     I32x4 unused;
     if (!readFixedI32x4(Output ? i32x4 : &unused))
         return false;
 
     if (!push(ValType::I32x4))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readF32x4Const(F32x4* f32x4)
+OpIter<Policy>::readF32x4Const(F32x4* f32x4)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::F32x4);
+    MOZ_ASSERT(Classify(op_) == OpKind::F32x4);
 
     F32x4 unused;
     if (!readFixedF32x4(Output ? f32x4 : &unused))
         return false;
 
     if (!push(ValType::F32x4))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readB8x16Const(I8x16* i8x16)
+OpIter<Policy>::readB8x16Const(I8x16* i8x16)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::B8x16);
+    MOZ_ASSERT(Classify(op_) == OpKind::B8x16);
 
     I8x16 unused;
     if (!readFixedI8x16(Output ? i8x16 : &unused))
         return false;
 
     if (!push(ValType::B8x16))
         return false;
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readB16x8Const(I16x8* i16x8)
+OpIter<Policy>::readB16x8Const(I16x8* i16x8)
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::B16x8);
+    MOZ_ASSERT(Classify(op_) == OpKind::B16x8);
 
     I16x8 unused;
     if (!readFixedI16x8(Output ? i16x8 : &unused))
         return false;
 
     if (!push(ValType::B16x8))
         return false;
 
     return true;
 }