Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 25 Oct 2016 10:49:11 +0200
changeset 319280 2bcd92f4caa0987fd141d624aa3041701b145ed7
parent 319279 efba37187d2d41078def06a75991770f6058f26d (current diff)
parent 319261 78b863e9fcd9d44d75c817b6495b4585167ba255 (diff)
child 319281 e35c6a772ec4c3549fc596b48457dbd6f68b6a8b
push id30869
push userphilringnalda@gmail.com
push dateWed, 26 Oct 2016 04:57:48 +0000
treeherdermozilla-central@9471b3c49b2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
toolkit/obsolete/content/dialogOverlay.js
toolkit/obsolete/content/dialogOverlay.xul
toolkit/obsolete/content/globalOverlay.xul
toolkit/obsolete/content/inlineSpellCheckUI.js
toolkit/obsolete/content/nsClipboard.js
toolkit/obsolete/content/nsUserSettings.js
toolkit/obsolete/content/strres.js
toolkit/obsolete/jar.mn
toolkit/obsolete/moz.build
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1523,21 +1523,21 @@ pref("signon.schemeUpgrades", true);
 
 // "Simplify Page" feature in Print Preview. This feature is disabled by default
 // in toolkit.
 //
 // This feature is only enabled on Nightly for Linux until bug 1306295 is fixed.
 // For non-Linux, this feature is only enabled up to early Beta.
 #ifdef UNIX_BUT_NOT_MAC
 #if defined(NIGHTLY_BUILD)
-pref("print.user_simplify_page", true);
+pref("print.use_simplify_page", true);
 #endif
 #else
 #if defined(EARLY_BETA_OR_EARLIER)
-pref("print.user_simplify_page", true);
+pref("print.use_simplify_page", true);
 #endif
 #endif
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
 pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
 
 // Whether or not the browser should scan for unsubmitted
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -101,16 +101,22 @@ XPCOMUtils.defineLazyGetter(window, "gSh
   // Only show resizers on Windows 2000 and XP
   return AppConstants.isPlatformAndVersionAtMost("win", "5.9");
 });
 
 XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
   return Services.prefs;
 });
 
+XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", function() {
+  let tmp = {};
+  Cu.import("resource://gre/modules/InlineSpellChecker.jsm", tmp);
+  return new tmp.InlineSpellChecker();
+});
+
 XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuParent();
 });
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   let tmp = {};
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -3,17 +3,16 @@
 # 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/.
 
 <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
 <script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
 <script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser.js"/>
 <script type="application/javascript" src="chrome://browser/content/customizableui/panelUI.js"/>
-<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
 <script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
 
 <script type="application/javascript" src="chrome://browser/content/browser-addons.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-ctrlTab.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-customization.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-devedition.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullScreenAndPointerLock.js"/>
--- a/browser/base/content/test/general/browser_contextmenu_input.js
+++ b/browser/base/content/test/general/browser_contextmenu_input.js
@@ -174,19 +174,20 @@ add_task(function* test_tel_email_url_nu
        "context-delete",      false,
        "---",                 null,
        "context-selectall",   null],
       {skipFocusChange: true}
     );
   }
 });
 
-add_task(function* test_date_time_color_range_month_week_input() {
+add_task(function* test_date_time_color_range_month_week_datetimelocal_input() {
   for (let selector of ["#input_date", "#input_time", "#input_color",
-                        "#input_range", "#input_month"]) {
+                        "#input_range", "#input_month", "#input_week",
+                        "#input_datetime-local"]) {
     yield test_contextmenu(selector,
       ["context-navigation", null,
            ["context-back",         false,
             "context-forward",      false,
             "context-reload",       true,
             "context-bookmarkpage", true], null,
        "---",                  null,
        "context-savepage",     true,
@@ -215,27 +216,16 @@ add_task(function* test_search_input() {
      "---",                 null,
      "context-selectall",   null,
      "---",                 null,
      "spell-check-enabled", true],
     {skipFocusChange: true}
   );
 });
 
-add_task(function* test_datetime_datetimelocal_input_todos() {
-  for (let type of ["datetime", "datetime-local"]) {
-    let returnedType = yield ContentTask.spawn(gBrowser.selectedBrowser, type, function*(type) {
-      let doc = content.document;
-      let input = doc.getElementById("input_" + type);
-      return input.type;
-    });
-    todo_is(returnedType, type, `TODO: add test for ${type} input fields`);
-  }
-});
-
 add_task(function* test_text_input_readonly() {
   todo(false, "context-selectall is enabled on osx-e10s, and windows when" +
               " it should be disabled");
   todo(false, "spell-check should not be enabled for input[readonly]. see bug 1246296");
   yield test_contextmenu("#input_readonly",
     ["context-undo",        false,
      "---",                 null,
      "context-cut",         true,
--- a/browser/base/content/web-panels.xul
+++ b/browser/base/content/web-panels.xul
@@ -20,17 +20,16 @@
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="load()" onunload="unload()">
   <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-fxaccounts.js"/>
-  <script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
   <script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
   <script type="application/javascript" src="chrome://browser/content/web-panels.js"/>
 
   <stringbundleset id="stringbundleset"> 
     <stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
   </stringbundleset>
 
   <broadcasterset id="mainBroadcasterSet">
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -647,21 +647,24 @@ Inspector.prototype = {
         if (!value) {
           return;
         }
 
         this.onEyeDropperDone = this.onEyeDropperDone.bind(this);
         this.onEyeDropperButtonClicked = this.onEyeDropperButtonClicked.bind(this);
         this.eyeDropperButton = this.panelDoc
                                     .getElementById("inspector-eyedropper-toggle");
-        this.eyeDropperButton.style.display = "initial";
+        this.eyeDropperButton.disabled = false;
+        this.eyeDropperButton.title = INSPECTOR_L10N.getStr("inspector.eyedropper.label");
         this.eyeDropperButton.addEventListener("click", this.onEyeDropperButtonClicked);
       }, e => console.error(e));
     } else {
-      this.panelDoc.getElementById("inspector-eyedropper-toggle").style.display = "none";
+      let eyeDropperButton = this.panelDoc.getElementById("inspector-eyedropper-toggle");
+      eyeDropperButton.disabled = true;
+      eyeDropperButton.title = INSPECTOR_L10N.getStr("eyedropper.disabled.title");
     }
   },
 
   teardownToolbar: function () {
     this._sidebarToggle = null;
 
     if (this.addNodeButton) {
       this.addNodeButton.removeEventListener("click", this.addNode);
--- a/devtools/client/inspector/inspector.xhtml
+++ b/devtools/client/inspector/inspector.xhtml
@@ -41,17 +41,16 @@
         <span id="inspector-searchlabel"></span>
         <div id="inspector-search" class="devtools-searchbox has-clear-btn">
           <input id="inspector-searchbox" class="devtools-searchinput"
                       type="search"
                       data-localization="placeholder=inspectorSearchHTML.label3"/>
           <button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></button>
         </div>
         <button id="inspector-eyedropper-toggle"
-                     data-localization="title=inspector.eyedropper.label"
                      class="devtools-button command-button-invertable"></button>
         <div id="inspector-sidebar-toggle-box"></div>
       </div>
       <div id="markup-box"></div>
       <div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
         <div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
                   role="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
       </div>
--- a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
@@ -2,23 +2,25 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that the eyedropper icons in the toolbar and in the color picker aren't displayed
 // when the page isn't an HTML one.
 
 const TEST_URL = URL_ROOT + "doc_inspector_highlighter_xbl.xul";
+const TEST_URL_2 =
+  "data:text/html;charset=utf-8,<h1 style='color:red'>HTML test page</h1>";
 
 add_task(function* () {
-  let {inspector} = yield openInspectorForURL(TEST_URL);
+  let {inspector, toolbox} = yield openInspectorForURL(TEST_URL);
 
   info("Check the inspector toolbar");
   let button = inspector.panelDoc.querySelector("#inspector-eyedropper-toggle");
-  ok(!isVisible(button), "The button is hidden in the toolbar");
+  ok(isDisabled(button), "The button is hidden in the toolbar");
 
   info("Check the color picker");
   yield selectNode("#scale", inspector);
 
   // Find the color swatch in the rule-view.
   let ruleView = inspector.ruleview.view;
   let ruleViewDocument = ruleView.styleDocument;
   let swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
@@ -26,17 +28,41 @@ add_task(function* () {
   info("Open the color picker");
   let cPicker = ruleView.tooltips.colorPicker;
   let onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
   button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
   ok(isDisabled(button), "The button is disabled in the color picker");
-});
+
+  info("Navigate to a HTML document");
+  let navigated = toolbox.target.once("navigate");
+  let markuploaded = inspector.once("markuploaded");
+  navigateTo(toolbox, TEST_URL_2);
+  yield navigated;
+  yield markuploaded;
+
+  info("Check the inspector toolbar in HTML document");
+  button = inspector.panelDoc.querySelector("#inspector-eyedropper-toggle");
+  ok(!isDisabled(button), "The button is enabled in the toolbar");
 
-function isVisible(button) {
-  return button.getBoxQuads().length !== 0;
-}
+  info("Check the color picker in HTML document");
+  // Find the color swatch in the rule-view.
+  yield selectNode("h1", inspector);
+
+  ruleView = inspector.ruleview.view;
+  ruleViewDocument = ruleView.styleDocument;
+  swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
+
+  info("Open the color picker in HTML document");
+  cPicker = ruleView.tooltips.colorPicker;
+  onColorPickerReady = cPicker.once("ready");
+  swatchEl.click();
+  yield onColorPickerReady;
+
+  button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
+  ok(!isDisabled(button), "The button is enabled in the color picker");
+});
 
 function isDisabled(button) {
   return button.disabled;
 }
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -808,17 +808,17 @@ NetworkDetailsView.prototype = {
       } catch (ex) { // eslint-disable-line
       }
 
       if (jsonVal) {
         this._params.onlyEnumVisible = true;
         let jsonScopeName = L10N.getStr("jsonScopeName");
         let jsonScope = this._params.addScope(jsonScopeName);
         jsonScope.expanded = true;
-        let jsonItem = jsonScope.addItem("", { enumerable: true });
+        let jsonItem = jsonScope.addItem(undefined, { enumerable: true });
         jsonItem.populate(jsonVal, { sorted: true });
       } else {
         // This is really awkward, but hey, it works. Let's show an empty
         // scope in the params view and place the source editor containing
         // the raw post data directly underneath.
         $("#request-params-box").removeAttribute("flex");
         let paramsScope = this._params.addScope(this._paramsPostPayload);
         paramsScope.expanded = true;
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
   html_json-custom-mime-test-page.html
   html_json-long-test-page.html
   html_json-malformed-test-page.html
   html_json-text-mime-test-page.html
   html_jsonp-test-page.html
   html_navigate-test-page.html
   html_params-test-page.html
   html_post-data-test-page.html
+  html_post-json-test-page.html
   html_post-raw-test-page.html
   html_post-raw-with-headers-test-page.html
   html_simple-test-page.html
   html_single-get-page.html
   html_send-beacon.html
   html_sorting-test-page.html
   html_statistics-test-page.html
   html_status-codes-test-page.html
@@ -104,16 +105,17 @@ skip-if = (os == 'linux' && e10s && debu
 [browser_net_leak_on_tab_close.js]
 [browser_net_open_request_in_tab.js]
 [browser_net_page-nav.js]
 [browser_net_pane-collapse.js]
 [browser_net_pane-toggle.js]
 [browser_net_post-data-01.js]
 [browser_net_post-data-02.js]
 [browser_net_post-data-03.js]
+[browser_net_post-data-04.js]
 [browser_net_prefs-and-l10n.js]
 [browser_net_prefs-reload.js]
 [browser_net_raw_headers.js]
 [browser_net_reload-button.js]
 [browser_net_reload-markers.js]
 [browser_net_req-resp-bodies.js]
 [browser_net_resend_cors.js]
 [browser_net_resend_headers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if the POST requests display the correct information in the UI,
+ * for JSON payloads.
+ */
+
+add_task(function* () {
+  let { L10N } = require("devtools/client/netmonitor/l10n");
+
+  let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
+  info("Starting test... ");
+
+  let { document, EVENTS, NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu, NetworkDetails } = NetMonitorView;
+
+  RequestsMenu.lazyUpdate = false;
+  NetworkDetails._params.lazyEmpty = false;
+
+  let wait = waitForNetworkEvents(monitor, 0, 1);
+  yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+    content.wrappedJSObject.performRequests();
+  });
+  yield wait;
+
+  let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
+  NetMonitorView.toggleDetailsPane({ visible: true }, 2);
+  RequestsMenu.selectedIndex = 0;
+  yield onEvent;
+
+  let tabEl = document.querySelectorAll("#event-details-pane tab")[2];
+  let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2];
+
+  is(tabEl.getAttribute("selected"), "true",
+    "The params tab in the network details pane should be selected.");
+
+  is(tabpanel.querySelector("#request-params-box")
+    .hasAttribute("hidden"), false,
+    "The request params box doesn't have the intended visibility.");
+  is(tabpanel.querySelector("#request-post-data-textarea-box")
+    .hasAttribute("hidden"), true,
+    "The request post data textarea box doesn't have the intended visibility.");
+
+  is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
+    "There should be 1 param scopes displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+    "The empty notice should not be displayed in this tabpanel.");
+
+  let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
+  is(jsonScope.querySelector(".name").getAttribute("value"),
+    L10N.getStr("jsonScopeName"),
+    "The JSON scope doesn't have the correct title.");
+
+  let valueScope = tabpanel.querySelector(
+    ".variables-view-scope > .variables-view-element-details");
+
+  is(valueScope.querySelectorAll(".variables-view-variable").length, 1,
+    "There should be 1 value displayed in the JSON scope.");
+  is(valueScope.querySelector(".variables-view-property .name")
+    .getAttribute("value"),
+    "a", "The JSON var name was incorrect.");
+  is(valueScope.querySelector(".variables-view-property .value")
+    .getAttribute("value"),
+    "1", "The JSON var value was incorrect.");
+
+  let detailsParent = valueScope.querySelector(".variables-view-property .name")
+    .closest(".variables-view-element-details");
+  is(detailsParent.hasAttribute("open"), true, "The JSON value must be visible");
+
+  return teardown(monitor);
+});
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -17,16 +17,17 @@ const EXAMPLE_URL = "http://example.com/
 const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
 const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
 const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
 const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
+const POST_JSON_URL = EXAMPLE_URL + "html_post-json-test-page.html";
 const POST_RAW_URL = EXAMPLE_URL + "html_post-raw-test-page.html";
 const POST_RAW_WITH_HEADERS_URL = EXAMPLE_URL + "html_post-raw-with-headers-test-page.html";
 const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html";
 const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
 const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
 const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
 const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
 const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html";
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_post-json-test-page.html
@@ -0,0 +1,39 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>POST raw test</p>
+
+    <script type="text/javascript">
+      function post(address, message, callback) {
+        let xhr = new XMLHttpRequest();
+        xhr.open("POST", address, true);
+        xhr.setRequestHeader("Content-Type", "application/json");
+
+        xhr.onreadystatechange = function () {
+          if (this.readyState == this.DONE) {
+            callback();
+          }
+        };
+        xhr.send(message);
+      }
+
+      function performRequests() {
+        post("sjs_simple-test-server.sjs", JSON.stringify({a: 1}), function () {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -95,16 +95,18 @@ SwatchColorPickerTooltip.prototype = Her
       this.spectrum.updateUI();
     }
 
     let {target} = this.inspector;
     target.actorHasMethod("inspector", "pickColorFromPage").then(value => {
       let tooltipDoc = this.tooltip.doc;
       let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
       if (value && this.inspector.selection.nodeFront.isInHTMLDocument) {
+        eyeButton.disabled = false;
+        eyeButton.removeAttribute("title");
         eyeButton.addEventListener("click", this._openEyeDropper);
       } else {
         eyeButton.disabled = true;
         eyeButton.title = L10N.getStr("eyedropper.disabled.title");
       }
       this.emit("ready");
     }, e => console.error(e));
   }),
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -103,18 +103,18 @@ window {
 
 #inspector-search {
   flex: unset;
 }
 
 /* Eyedropper toolbar button */
 
 #inspector-eyedropper-toggle {
-  /* hidden by default, until we can check that the required highlighter exists */
-  display: none;
+  /* Required to display tooltip when eyedropper is disabled in non-HTML documents */
+  pointer-events: auto;
 }
 
 #inspector-eyedropper-toggle::before {
   background-image: var(--eyedropper-image);
 }
 
 #inspector-sidebar-toggle-box {
   line-height: initial;
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -1,14 +1,15 @@
 /* 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 { Cu } = require("chrome");
 const { extend } = require("sdk/core/heritage");
 const { AutoRefreshHighlighter } = require("./auto-refresh");
 const {
   CanvasFrameAnonymousContentHelper,
   createNode,
   createSVGNode,
   moveInfobar,
 } = require("./utils/markup");
@@ -40,17 +41,21 @@ const GRID_LINES_PROPERTIES = {
 const GRID_GAP_PATTERN_WIDTH = 14;
 const GRID_GAP_PATTERN_HEIGHT = 14;
 const GRID_GAP_PATTERN_LINE_DASH = [5, 3];
 const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB";
 
 /**
  * Cached used by `CssGridHighlighter.getGridGapPattern`.
  */
-const gCachedGridPattern = new Map();
+const gCachedGridPattern = new WeakMap();
+// WeakMap key for the Row grid pattern.
+const ROW_KEY = {};
+// WeakMap key for the Column grid pattern.
+const COLUMN_KEY = {};
 
 /**
  * The CssGridHighlighter is the class that overlays a visual grid on top of
  * display:grid elements.
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
@@ -88,16 +93,19 @@ const gCachedGridPattern = new Map();
  *   </div>
  * </div>
  */
 function CssGridHighlighter(highlighterEnv) {
   AutoRefreshHighlighter.call(this, highlighterEnv);
 
   this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
     this._buildMarkup.bind(this));
+
+  this.onNavigate = this.onNavigate.bind(this);
+  this.highlighterEnv.on("navigate", this.onNavigate);
 }
 
 CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
   typeName: "CssGridHighlighter",
 
   ID_CLASS_PREFIX: "css-grid-",
 
   _buildMarkup() {
@@ -198,19 +206,19 @@ CssGridHighlighter.prototype = extend(Au
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     return container;
   },
 
   destroy() {
-    AutoRefreshHighlighter.prototype.destroy.call(this);
+    this.highlighterEnv.off("navigate", this.onNavigate);
     this.markup.destroy();
-    gCachedGridPattern.clear();
+    AutoRefreshHighlighter.prototype.destroy.call(this);
   },
 
   getElement(id) {
     return this.markup.getElement(this.ID_CLASS_PREFIX + id);
   },
 
   get ctx() {
     return this.canvas.getCanvasContext("2d");
@@ -218,51 +226,61 @@ CssGridHighlighter.prototype = extend(Au
 
   get canvas() {
     return this.getElement("canvas");
   },
 
   /**
    * Gets the grid gap pattern used to render the gap regions.
    *
-   * @param  {String} dimensionType
-   *         The grid dimension type which is either the constant COLUMNS or ROWS.
+   * @param  {Object} dimension
+   *         Refers to the WeakMap key for the grid dimension type which is either the
+   *         constant COLUMN or ROW.
    * @return {CanvasPattern} grid gap pattern.
    */
-  getGridGapPattern(dimensionType) {
-    if (gCachedGridPattern.has(dimensionType)) {
-      return gCachedGridPattern.get(dimensionType);
+  getGridGapPattern(dimension) {
+    if (gCachedGridPattern.has(dimension)) {
+      return gCachedGridPattern.get(dimension);
     }
 
     // Create the diagonal lines pattern for the rendering the grid gaps.
     let canvas = createNode(this.win, { nodeType: "canvas" });
     canvas.width = GRID_GAP_PATTERN_WIDTH;
     canvas.height = GRID_GAP_PATTERN_HEIGHT;
 
     let ctx = canvas.getContext("2d");
     ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH);
     ctx.beginPath();
     ctx.translate(.5, .5);
 
-    if (dimensionType === COLUMNS) {
+    if (dimension === COLUMN_KEY) {
       ctx.moveTo(0, 0);
       ctx.lineTo(GRID_GAP_PATTERN_WIDTH, GRID_GAP_PATTERN_HEIGHT);
     } else {
       ctx.moveTo(GRID_GAP_PATTERN_WIDTH, 0);
       ctx.lineTo(0, GRID_GAP_PATTERN_HEIGHT);
     }
 
     ctx.strokeStyle = GRID_GAP_PATTERN_STROKE_STYLE;
     ctx.stroke();
 
     let pattern = ctx.createPattern(canvas, "repeat");
-    gCachedGridPattern.set(dimensionType, pattern);
+    gCachedGridPattern.set(dimension, pattern);
     return pattern;
   },
 
+  /**
+   * Called when the page navigates. Used to clear the cached gap patterns and avoid
+   * using DeadWrapper objects as gap patterns the next time.
+   */
+  onNavigate() {
+    gCachedGridPattern.delete(ROW_KEY);
+    gCachedGridPattern.delete(COLUMN_KEY);
+  },
+
   _show() {
     if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
       this.hide();
       return false;
     }
 
     return this._update();
   },
@@ -597,21 +615,22 @@ CssGridHighlighter.prototype = extend(Au
    *         The end position of the cross side of the grid line.
    * @param  {Number} breadth
    *         The grid line breadth value.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
    */
   renderGridGap(linePos, startPos, endPos, breadth, dimensionType) {
     this.ctx.save();
-    this.ctx.fillStyle = this.getGridGapPattern(dimensionType);
 
     if (dimensionType === COLUMNS) {
+      this.ctx.fillStyle = this.getGridGapPattern(COLUMN_KEY);
       this.ctx.fillRect(linePos, startPos, breadth, endPos - startPos);
     } else {
+      this.ctx.fillStyle = this.getGridGapPattern(ROW_KEY);
       this.ctx.fillRect(startPos, linePos, endPos - startPos, breadth);
     }
 
     this.ctx.restore();
   },
 
   /**
    * Render the grid area highlight for the given area name or for all the grid areas.
@@ -705,16 +724,20 @@ exports.CssGridHighlighter = CssGridHigh
  * Stringify CSS Grid data as returned by node.getGridFragments.
  * This is useful to compare grid state at each update and redraw the highlighter if
  * needed.
  *
  * @param  {Object} Grid Fragments
  * @return {String} representation of the CSS grid fragment data.
  */
 function stringifyGridFragments(fragments = []) {
+  if (fragments[0] && Cu.isDeadWrapper(fragments[0])) {
+    return {};
+  }
+
   return JSON.stringify(fragments.map(getStringifiableFragment));
 }
 
 function getStringifiableFragment(fragment) {
   return {
     cols: getStringifiableDimension(fragment.cols),
     rows: getStringifiableDimension(fragment.rows)
   };
--- a/devtools/server/actors/highlighters/eye-dropper.js
+++ b/devtools/server/actors/highlighters/eye-dropper.js
@@ -1,534 +1,534 @@
-/* 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";
-
-// Eye-dropper tool. This is implemented as a highlighter so it can be displayed in the
-// content page.
-// It basically displays a magnifier that tracks mouse moves and shows a magnified version
-// of the page. On click, it samples the color at the pixel being hovered.
-
-const {Ci, Cc} = require("chrome");
-const {CanvasFrameAnonymousContentHelper, createNode} = require("./utils/markup");
-const Services = require("Services");
-const EventEmitter = require("devtools/shared/event-emitter");
-const {rgbToHsl, rgbToColorName} = require("devtools/shared/css/color").colorUtils;
-const {getCurrentZoom, getFrameOffsets} = require("devtools/shared/layout/utils");
-
-loader.lazyGetter(this, "clipboardHelper",
-  () => Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper));
-loader.lazyGetter(this, "l10n",
-  () => Services.strings.createBundle("chrome://devtools/locale/eyedropper.properties"));
-
-const ZOOM_LEVEL_PREF = "devtools.eyedropper.zoom";
-const FORMAT_PREF = "devtools.defaultColorUnit";
-// Width of the canvas.
-const MAGNIFIER_WIDTH = 96;
-// Height of the canvas.
-const MAGNIFIER_HEIGHT = 96;
-// Start position, when the tool is first shown. This should match the top/left position
-// defined in CSS.
-const DEFAULT_START_POS_X = 100;
-const DEFAULT_START_POS_Y = 100;
-// How long to wait before closing after copy.
-const CLOSE_DELAY = 750;
-
-/**
- * The EyeDropper is the class that draws the gradient line and
- * color stops as an overlay on top of a linear-gradient background-image.
- */
-function EyeDropper(highlighterEnv) {
-  EventEmitter.decorate(this);
-
-  this.highlighterEnv = highlighterEnv;
-  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
-                                                      this._buildMarkup.bind(this));
-
-  // Get a couple of settings from prefs.
-  this.format = Services.prefs.getCharPref(FORMAT_PREF);
-  this.eyeDropperZoomLevel = Services.prefs.getIntPref(ZOOM_LEVEL_PREF);
-}
-
-EyeDropper.prototype = {
-  typeName: "EyeDropper",
-
-  ID_CLASS_PREFIX: "eye-dropper-",
-
-  get win() {
-    return this.highlighterEnv.window;
-  },
-
-  _buildMarkup() {
-    // Highlighter main container.
-    let container = createNode(this.win, {
-      attributes: {"class": "highlighter-container"}
-    });
-
-    // Wrapper element.
-    let wrapper = createNode(this.win, {
-      parent: container,
-      attributes: {
-        "id": "root",
-        "class": "root",
-        "hidden": "true"
-      },
-      prefix: this.ID_CLASS_PREFIX
-    });
-
-    // The magnifier canvas element.
-    createNode(this.win, {
-      parent: wrapper,
-      nodeType: "canvas",
-      attributes: {
-        "id": "canvas",
-        "class": "canvas",
-        "width": MAGNIFIER_WIDTH,
-        "height": MAGNIFIER_HEIGHT
-      },
-      prefix: this.ID_CLASS_PREFIX
-    });
-
-    // The color label element.
-    let colorLabelContainer = createNode(this.win, {
-      parent: wrapper,
-      attributes: {"class": "color-container"},
-      prefix: this.ID_CLASS_PREFIX
-    });
-    createNode(this.win, {
-      nodeType: "div",
-      parent: colorLabelContainer,
-      attributes: {"id": "color-preview", "class": "color-preview"},
-      prefix: this.ID_CLASS_PREFIX
-    });
-    createNode(this.win, {
-      nodeType: "div",
-      parent: colorLabelContainer,
-      attributes: {"id": "color-value", "class": "color-value"},
-      prefix: this.ID_CLASS_PREFIX
-    });
-
-    return container;
-  },
-
-  destroy() {
-    this.hide();
-    this.markup.destroy();
-  },
-
-  getElement(id) {
-    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
-  },
-
-  /**
-   * Show the eye-dropper highlighter.
-   * @param {DOMNode} node The node which document the highlighter should be inserted in.
-   * @param {Object} options The options object may contain the following properties:
-   * - {Boolean} copyOnSelect Whether selecting a color should copy it to the clipboard.
-   */
-  show(node, options = {}) {
-    if (this.highlighterEnv.isXUL) {
-      return false;
-    }
-
-    this.options = options;
-
-    // Get the page's current zoom level.
-    this.pageZoom = getCurrentZoom(this.win);
-
-    // Take a screenshot of the viewport. This needs to be done first otherwise the
-    // eyedropper UI will appear in the screenshot itself (since the UI is injected as
-    // native anonymous content in the page).
-    // Once the screenshot is ready, the magnified area will be drawn.
-    this.prepareImageCapture();
-
-    // Start listening for user events.
-    let {pageListenerTarget} = this.highlighterEnv;
-    pageListenerTarget.addEventListener("mousemove", this);
-    pageListenerTarget.addEventListener("click", this);
-    pageListenerTarget.addEventListener("keydown", this);
-    pageListenerTarget.addEventListener("DOMMouseScroll", this);
-    pageListenerTarget.addEventListener("FullZoomChange", this);
-
-    // Show the eye-dropper.
-    this.getElement("root").removeAttribute("hidden");
-
-    // Prepare the canvas context on which we're drawing the magnified page portion.
-    this.ctx = this.getElement("canvas").getCanvasContext();
-    this.ctx.imageSmoothingEnabled = false;
-
-    this.magnifiedArea = {width: MAGNIFIER_WIDTH, height: MAGNIFIER_HEIGHT,
-                          x: DEFAULT_START_POS_X, y: DEFAULT_START_POS_Y};
-
-    this.moveTo(DEFAULT_START_POS_X, DEFAULT_START_POS_Y);
-
-    // Focus the content so the keyboard can be used.
-    this.win.focus();
-
-    return true;
-  },
-
-  /**
-   * Hide the eye-dropper highlighter.
-   */
-  hide() {
-    if (this.highlighterEnv.isXUL) {
-      return;
-    }
-
-    this.pageImage = null;
-
-    let {pageListenerTarget} = this.highlighterEnv;
-    pageListenerTarget.removeEventListener("mousemove", this);
-    pageListenerTarget.removeEventListener("click", this);
-    pageListenerTarget.removeEventListener("keydown", this);
-    pageListenerTarget.removeEventListener("DOMMouseScroll", this);
-    pageListenerTarget.removeEventListener("FullZoomChange", this);
-
-    this.getElement("root").setAttribute("hidden", "true");
-    this.getElement("root").removeAttribute("drawn");
-
-    this.emit("hidden");
-  },
-
-  prepareImageCapture() {
-    // Get the image data from the content window.
-    let imageData = getWindowAsImageData(this.win);
-
-    // We need to transform imageData to something drawWindow will consume. An ImageBitmap
-    // works well. We could have used an Image, but doing so results in errors if the page
-    // defines CSP headers.
-    this.win.createImageBitmap(imageData).then(image => {
-      this.pageImage = image;
-      // We likely haven't drawn anything yet (no mousemove events yet), so start now.
-      this.draw();
-
-      // Set an attribute on the root element to be able to run tests after the first draw
-      // was done.
-      this.getElement("root").setAttribute("drawn", "true");
-    });
-  },
-
-  /**
-   * Get the number of cells (blown-up pixels) per direction in the grid.
-   */
-  get cellsWide() {
-    // Canvas will render whole "pixels" (cells) only, and an even number at that. Round
-    // up to the nearest even number of pixels.
-    let cellsWide = Math.ceil(this.magnifiedArea.width / this.eyeDropperZoomLevel);
-    cellsWide += cellsWide % 2;
-
-    return cellsWide;
-  },
-
-  /**
-   * Get the size of each cell (blown-up pixel) in the grid.
-   */
-  get cellSize() {
-    return this.magnifiedArea.width / this.cellsWide;
-  },
-
-  /**
-   * Get index of cell in the center of the grid.
-   */
-  get centerCell() {
-    return Math.floor(this.cellsWide / 2);
-  },
-
-  /**
-   * Get color of center cell in the grid.
-   */
-  get centerColor() {
-    let pos = (this.centerCell * this.cellSize) + (this.cellSize / 2);
-    let rgb = this.ctx.getImageData(pos, pos, 1, 1).data;
-    return rgb;
-  },
-
-  draw() {
-    // If the image of the page isn't ready yet, bail out, we'll draw later on mousemove.
-    if (!this.pageImage) {
-      return;
-    }
-
-    let {width, height, x, y} = this.magnifiedArea;
-
-    let zoomedWidth = width / this.eyeDropperZoomLevel;
-    let zoomedHeight = height / this.eyeDropperZoomLevel;
-
-    let sx = x - (zoomedWidth / 2);
-    let sy = y - (zoomedHeight / 2);
-    let sw = zoomedWidth;
-    let sh = zoomedHeight;
-
-    this.ctx.drawImage(this.pageImage, sx, sy, sw, sh, 0, 0, width, height);
-
-    // Draw the grid on top, but only at 3x or more, otherwise it's too busy.
-    if (this.eyeDropperZoomLevel > 2) {
-      this.drawGrid();
-    }
-
-    this.drawCrosshair();
-
-    // Update the color preview and value.
-    let rgb = this.centerColor;
-    this.getElement("color-preview").setAttribute("style",
-      `background-color:${toColorString(rgb, "rgb")};`);
-    this.getElement("color-value").setTextContent(toColorString(rgb, this.format));
-  },
-
-  /**
-   * Draw a grid on the canvas representing pixel boundaries.
-   */
-  drawGrid() {
-    let {width, height} = this.magnifiedArea;
-
-    this.ctx.lineWidth = 1;
-    this.ctx.strokeStyle = "rgba(143, 143, 143, 0.2)";
-
-    for (let i = 0; i < width; i += this.cellSize) {
-      this.ctx.beginPath();
-      this.ctx.moveTo(i - .5, 0);
-      this.ctx.lineTo(i - .5, height);
-      this.ctx.stroke();
-
-      this.ctx.beginPath();
-      this.ctx.moveTo(0, i - .5);
-      this.ctx.lineTo(width, i - .5);
-      this.ctx.stroke();
-    }
-  },
-
-  /**
-   * Draw a box on the canvas to highlight the center cell.
-   */
-  drawCrosshair() {
-    let pos = this.centerCell * this.cellSize;
-
-    this.ctx.lineWidth = 1;
-    this.ctx.lineJoin = "miter";
-    this.ctx.strokeStyle = "rgba(0, 0, 0, 1)";
-    this.ctx.strokeRect(pos - 1.5, pos - 1.5, this.cellSize + 2, this.cellSize + 2);
-
-    this.ctx.strokeStyle = "rgba(255, 255, 255, 1)";
-    this.ctx.strokeRect(pos - 0.5, pos - 0.5, this.cellSize, this.cellSize);
-  },
-
-  handleEvent(e) {
-    switch (e.type) {
-      case "mousemove":
-        // We might be getting an event from a child frame, so account for the offset.
-        let [xOffset, yOffset] = getFrameOffsets(this.win, e.target);
-        let x = xOffset + e.pageX - this.win.scrollX;
-        let y = yOffset + e.pageY - this.win.scrollY;
-        // Update the zoom area.
-        this.magnifiedArea.x = x * this.pageZoom;
-        this.magnifiedArea.y = y * this.pageZoom;
-        // Redraw the portion of the screenshot that is now under the mouse.
-        this.draw();
-        // And move the eye-dropper's UI so it follows the mouse.
-        this.moveTo(x, y);
-        break;
-      case "click":
-        this.selectColor();
-        break;
-      case "keydown":
-        this.handleKeyDown(e);
-        break;
-      case "DOMMouseScroll":
-        // Prevent scrolling. That's because we only took a screenshot of the viewport, so
-        // scrolling out of the viewport wouldn't draw the expected things. In the future
-        // we can take the screenshot again on scroll, but for now it doesn't seem
-        // important.
-        e.preventDefault();
-        break;
-      case "FullZoomChange":
-        this.hide();
-        this.show();
-        break;
-    }
-  },
-
-  moveTo(x, y) {
-    let root = this.getElement("root");
-    root.setAttribute("style", `top:${y}px;left:${x}px;`);
-
-    // Move the label container to the top if the magnifier is close to the bottom edge.
-    if (y >= this.win.innerHeight - MAGNIFIER_HEIGHT) {
-      root.setAttribute("top", "");
-    } else {
-      root.removeAttribute("top");
-    }
-
-    // Also offset the label container to the right or left if the magnifier is close to
-    // the edge.
-    root.removeAttribute("left");
-    root.removeAttribute("right");
-    if (x <= MAGNIFIER_WIDTH) {
-      root.setAttribute("right", "");
-    } else if (x >= this.win.innerWidth - MAGNIFIER_WIDTH) {
-      root.setAttribute("left", "");
-    }
-  },
-
-  /**
-   * Select the current color that's being previewed. Depending on the current options,
-   * selecting might mean copying to the clipboard and closing the
-   */
-  selectColor() {
-    let onColorSelected = Promise.resolve();
-    if (this.options.copyOnSelect) {
-      onColorSelected = this.copyColor();
-    }
-
-    this.emit("selected", toColorString(this.centerColor, this.format));
-    onColorSelected.then(() => this.hide(), e => console.error(e));
-  },
-
-  /**
-   * Handler for the keydown event. Either select the color or move the panel in a
-   * direction depending on the key pressed.
-   */
-  handleKeyDown(e) {
-    // Bail out early if any unsupported modifier is used, so that we let
-    // keyboard shortcuts through.
-    if (e.metaKey || e.ctrlKey || e.altKey) {
-      return;
-    }
-
-    if (e.keyCode === e.DOM_VK_RETURN) {
-      this.selectColor();
-      e.preventDefault();
-      return;
-    }
-
-    if (e.keyCode === e.DOM_VK_ESCAPE) {
-      this.emit("canceled");
-      this.hide();
-      e.preventDefault();
-      return;
-    }
-
-    let offsetX = 0;
-    let offsetY = 0;
-    let modifier = 1;
-
-    if (e.keyCode === e.DOM_VK_LEFT) {
-      offsetX = -1;
-    } else if (e.keyCode === e.DOM_VK_RIGHT) {
-      offsetX = 1;
-    } else if (e.keyCode === e.DOM_VK_UP) {
-      offsetY = -1;
-    } else if (e.keyCode === e.DOM_VK_DOWN) {
-      offsetY = 1;
-    }
-
-    if (e.shiftKey) {
-      modifier = 10;
-    }
-
-    offsetY *= modifier;
-    offsetX *= modifier;
-
-    if (offsetX !== 0 || offsetY !== 0) {
-      this.magnifiedArea.x = cap(this.magnifiedArea.x + offsetX,
-                                 0, this.win.innerWidth * this.pageZoom);
-      this.magnifiedArea.y = cap(this.magnifiedArea.y + offsetY, 0,
-                                 this.win.innerHeight * this.pageZoom);
-
-      this.draw();
-
-      this.moveTo(this.magnifiedArea.x / this.pageZoom,
-                  this.magnifiedArea.y / this.pageZoom);
-
-      e.preventDefault();
-    }
-  },
-
-  /**
-   * Copy the currently inspected color to the clipboard.
-   * @return {Promise} Resolves when the copy has been done (after a delay that is used to
-   * let users know that something was copied).
-   */
-  copyColor() {
-    // Copy to the clipboard.
-    let color = toColorString(this.centerColor, this.format);
-    clipboardHelper.copyString(color);
-
-    // Provide some feedback.
-    this.getElement("color-value").setTextContent(
-      "✓ " + l10n.GetStringFromName("colorValue.copied"));
-
-    // Hide the tool after a delay.
-    clearTimeout(this._copyTimeout);
-    return new Promise(resolve => {
-      this._copyTimeout = setTimeout(resolve, CLOSE_DELAY);
-    });
-  }
-};
-
-exports.EyeDropper = EyeDropper;
-
-/**
- * Draw the visible portion of the window on a canvas and get the resulting ImageData.
- * @param {Window} win
- * @return {ImageData} The image data for the window.
- */
-function getWindowAsImageData(win) {
-  let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-  let scale = getCurrentZoom(win);
-  let width = win.innerWidth;
-  let height = win.innerHeight;
-  canvas.width = width * scale;
-  canvas.height = height * scale;
-  canvas.mozOpaque = true;
-
-  let ctx = canvas.getContext("2d");
-
-  ctx.scale(scale, scale);
-  ctx.drawWindow(win, win.scrollX, win.scrollY, width, height, "#fff");
-
-  return ctx.getImageData(0, 0, canvas.width, canvas.height);
-}
-
-/**
- * Get a formatted CSS color string from a color value.
- * @param {array} rgb Rgb values of a color to format.
- * @param {string} format Format of string. One of "hex", "rgb", "hsl", "name".
- * @return {string} Formatted color value, e.g. "#FFF" or "hsl(20, 10%, 10%)".
- */
-function toColorString(rgb, format) {
-  let [r, g, b] = rgb;
-
-  switch (format) {
-    case "hex":
-      return hexString(rgb);
-    case "rgb":
-      return "rgb(" + r + ", " + g + ", " + b + ")";
-    case "hsl":
-      let [h, s, l] = rgbToHsl(rgb);
-      return "hsl(" + h + ", " + s + "%, " + l + "%)";
-    case "name":
-      let str;
-      try {
-        str = rgbToColorName(r, g, b);
-      } catch (e) {
-        str = hexString(rgb);
-      }
-      return str;
-    default:
-      return hexString(rgb);
-  }
-}
-
-/**
- * Produce a hex-formatted color string from rgb values.
- * @param {array} rgb Rgb values of color to stringify.
- * @return {string} Hex formatted string for color, e.g. "#FFEE00".
- */
-function hexString([r, g, b]) {
-  let val = (1 << 24) + (r << 16) + (g << 8) + (b << 0);
-  return "#" + val.toString(16).substr(-6).toUpperCase();
-}
-
-function cap(value, min, max) {
-  return Math.max(min, Math.min(value, max));
-}
+/* 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";
+
+// Eye-dropper tool. This is implemented as a highlighter so it can be displayed in the
+// content page.
+// It basically displays a magnifier that tracks mouse moves and shows a magnified version
+// of the page. On click, it samples the color at the pixel being hovered.
+
+const {Ci, Cc} = require("chrome");
+const {CanvasFrameAnonymousContentHelper, createNode} = require("./utils/markup");
+const Services = require("Services");
+const EventEmitter = require("devtools/shared/event-emitter");
+const {rgbToHsl, rgbToColorName} = require("devtools/shared/css/color").colorUtils;
+const {getCurrentZoom, getFrameOffsets} = require("devtools/shared/layout/utils");
+
+loader.lazyGetter(this, "clipboardHelper",
+  () => Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper));
+loader.lazyGetter(this, "l10n",
+  () => Services.strings.createBundle("chrome://devtools/locale/eyedropper.properties"));
+
+const ZOOM_LEVEL_PREF = "devtools.eyedropper.zoom";
+const FORMAT_PREF = "devtools.defaultColorUnit";
+// Width of the canvas.
+const MAGNIFIER_WIDTH = 96;
+// Height of the canvas.
+const MAGNIFIER_HEIGHT = 96;
+// Start position, when the tool is first shown. This should match the top/left position
+// defined in CSS.
+const DEFAULT_START_POS_X = 100;
+const DEFAULT_START_POS_Y = 100;
+// How long to wait before closing after copy.
+const CLOSE_DELAY = 750;
+
+/**
+ * The EyeDropper is the class that draws the gradient line and
+ * color stops as an overlay on top of a linear-gradient background-image.
+ */
+function EyeDropper(highlighterEnv) {
+  EventEmitter.decorate(this);
+
+  this.highlighterEnv = highlighterEnv;
+  this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
+                                                      this._buildMarkup.bind(this));
+
+  // Get a couple of settings from prefs.
+  this.format = Services.prefs.getCharPref(FORMAT_PREF);
+  this.eyeDropperZoomLevel = Services.prefs.getIntPref(ZOOM_LEVEL_PREF);
+}
+
+EyeDropper.prototype = {
+  typeName: "EyeDropper",
+
+  ID_CLASS_PREFIX: "eye-dropper-",
+
+  get win() {
+    return this.highlighterEnv.window;
+  },
+
+  _buildMarkup() {
+    // Highlighter main container.
+    let container = createNode(this.win, {
+      attributes: {"class": "highlighter-container"}
+    });
+
+    // Wrapper element.
+    let wrapper = createNode(this.win, {
+      parent: container,
+      attributes: {
+        "id": "root",
+        "class": "root",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // The magnifier canvas element.
+    createNode(this.win, {
+      parent: wrapper,
+      nodeType: "canvas",
+      attributes: {
+        "id": "canvas",
+        "class": "canvas",
+        "width": MAGNIFIER_WIDTH,
+        "height": MAGNIFIER_HEIGHT
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // The color label element.
+    let colorLabelContainer = createNode(this.win, {
+      parent: wrapper,
+      attributes: {"class": "color-container"},
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "div",
+      parent: colorLabelContainer,
+      attributes: {"id": "color-preview", "class": "color-preview"},
+      prefix: this.ID_CLASS_PREFIX
+    });
+    createNode(this.win, {
+      nodeType: "div",
+      parent: colorLabelContainer,
+      attributes: {"id": "color-value", "class": "color-value"},
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    return container;
+  },
+
+  destroy() {
+    this.hide();
+    this.markup.destroy();
+  },
+
+  getElement(id) {
+    return this.markup.getElement(this.ID_CLASS_PREFIX + id);
+  },
+
+  /**
+   * Show the eye-dropper highlighter.
+   * @param {DOMNode} node The node which document the highlighter should be inserted in.
+   * @param {Object} options The options object may contain the following properties:
+   * - {Boolean} copyOnSelect Whether selecting a color should copy it to the clipboard.
+   */
+  show(node, options = {}) {
+    if (this.highlighterEnv.isXUL) {
+      return false;
+    }
+
+    this.options = options;
+
+    // Get the page's current zoom level.
+    this.pageZoom = getCurrentZoom(this.win);
+
+    // Take a screenshot of the viewport. This needs to be done first otherwise the
+    // eyedropper UI will appear in the screenshot itself (since the UI is injected as
+    // native anonymous content in the page).
+    // Once the screenshot is ready, the magnified area will be drawn.
+    this.prepareImageCapture();
+
+    // Start listening for user events.
+    let {pageListenerTarget} = this.highlighterEnv;
+    pageListenerTarget.addEventListener("mousemove", this);
+    pageListenerTarget.addEventListener("click", this, true);
+    pageListenerTarget.addEventListener("keydown", this);
+    pageListenerTarget.addEventListener("DOMMouseScroll", this);
+    pageListenerTarget.addEventListener("FullZoomChange", this);
+
+    // Show the eye-dropper.
+    this.getElement("root").removeAttribute("hidden");
+
+    // Prepare the canvas context on which we're drawing the magnified page portion.
+    this.ctx = this.getElement("canvas").getCanvasContext();
+    this.ctx.imageSmoothingEnabled = false;
+
+    this.magnifiedArea = {width: MAGNIFIER_WIDTH, height: MAGNIFIER_HEIGHT,
+                          x: DEFAULT_START_POS_X, y: DEFAULT_START_POS_Y};
+
+    this.moveTo(DEFAULT_START_POS_X, DEFAULT_START_POS_Y);
+
+    // Focus the content so the keyboard can be used.
+    this.win.focus();
+
+    return true;
+  },
+
+  /**
+   * Hide the eye-dropper highlighter.
+   */
+  hide() {
+    if (this.highlighterEnv.isXUL) {
+      return;
+    }
+
+    this.pageImage = null;
+
+    let {pageListenerTarget} = this.highlighterEnv;
+    pageListenerTarget.removeEventListener("mousemove", this);
+    pageListenerTarget.removeEventListener("click", this, true);
+    pageListenerTarget.removeEventListener("keydown", this);
+    pageListenerTarget.removeEventListener("DOMMouseScroll", this);
+    pageListenerTarget.removeEventListener("FullZoomChange", this);
+
+    this.getElement("root").setAttribute("hidden", "true");
+    this.getElement("root").removeAttribute("drawn");
+
+    this.emit("hidden");
+  },
+
+  prepareImageCapture() {
+    // Get the image data from the content window.
+    let imageData = getWindowAsImageData(this.win);
+
+    // We need to transform imageData to something drawWindow will consume. An ImageBitmap
+    // works well. We could have used an Image, but doing so results in errors if the page
+    // defines CSP headers.
+    this.win.createImageBitmap(imageData).then(image => {
+      this.pageImage = image;
+      // We likely haven't drawn anything yet (no mousemove events yet), so start now.
+      this.draw();
+
+      // Set an attribute on the root element to be able to run tests after the first draw
+      // was done.
+      this.getElement("root").setAttribute("drawn", "true");
+    });
+  },
+
+  /**
+   * Get the number of cells (blown-up pixels) per direction in the grid.
+   */
+  get cellsWide() {
+    // Canvas will render whole "pixels" (cells) only, and an even number at that. Round
+    // up to the nearest even number of pixels.
+    let cellsWide = Math.ceil(this.magnifiedArea.width / this.eyeDropperZoomLevel);
+    cellsWide += cellsWide % 2;
+
+    return cellsWide;
+  },
+
+  /**
+   * Get the size of each cell (blown-up pixel) in the grid.
+   */
+  get cellSize() {
+    return this.magnifiedArea.width / this.cellsWide;
+  },
+
+  /**
+   * Get index of cell in the center of the grid.
+   */
+  get centerCell() {
+    return Math.floor(this.cellsWide / 2);
+  },
+
+  /**
+   * Get color of center cell in the grid.
+   */
+  get centerColor() {
+    let pos = (this.centerCell * this.cellSize) + (this.cellSize / 2);
+    let rgb = this.ctx.getImageData(pos, pos, 1, 1).data;
+    return rgb;
+  },
+
+  draw() {
+    // If the image of the page isn't ready yet, bail out, we'll draw later on mousemove.
+    if (!this.pageImage) {
+      return;
+    }
+
+    let {width, height, x, y} = this.magnifiedArea;
+
+    let zoomedWidth = width / this.eyeDropperZoomLevel;
+    let zoomedHeight = height / this.eyeDropperZoomLevel;
+
+    let sx = x - (zoomedWidth / 2);
+    let sy = y - (zoomedHeight / 2);
+    let sw = zoomedWidth;
+    let sh = zoomedHeight;
+
+    this.ctx.drawImage(this.pageImage, sx, sy, sw, sh, 0, 0, width, height);
+
+    // Draw the grid on top, but only at 3x or more, otherwise it's too busy.
+    if (this.eyeDropperZoomLevel > 2) {
+      this.drawGrid();
+    }
+
+    this.drawCrosshair();
+
+    // Update the color preview and value.
+    let rgb = this.centerColor;
+    this.getElement("color-preview").setAttribute("style",
+      `background-color:${toColorString(rgb, "rgb")};`);
+    this.getElement("color-value").setTextContent(toColorString(rgb, this.format));
+  },
+
+  /**
+   * Draw a grid on the canvas representing pixel boundaries.
+   */
+  drawGrid() {
+    let {width, height} = this.magnifiedArea;
+
+    this.ctx.lineWidth = 1;
+    this.ctx.strokeStyle = "rgba(143, 143, 143, 0.2)";
+
+    for (let i = 0; i < width; i += this.cellSize) {
+      this.ctx.beginPath();
+      this.ctx.moveTo(i - .5, 0);
+      this.ctx.lineTo(i - .5, height);
+      this.ctx.stroke();
+
+      this.ctx.beginPath();
+      this.ctx.moveTo(0, i - .5);
+      this.ctx.lineTo(width, i - .5);
+      this.ctx.stroke();
+    }
+  },
+
+  /**
+   * Draw a box on the canvas to highlight the center cell.
+   */
+  drawCrosshair() {
+    let pos = this.centerCell * this.cellSize;
+
+    this.ctx.lineWidth = 1;
+    this.ctx.lineJoin = "miter";
+    this.ctx.strokeStyle = "rgba(0, 0, 0, 1)";
+    this.ctx.strokeRect(pos - 1.5, pos - 1.5, this.cellSize + 2, this.cellSize + 2);
+
+    this.ctx.strokeStyle = "rgba(255, 255, 255, 1)";
+    this.ctx.strokeRect(pos - 0.5, pos - 0.5, this.cellSize, this.cellSize);
+  },
+
+  handleEvent(e) {
+    switch (e.type) {
+      case "mousemove":
+        // We might be getting an event from a child frame, so account for the offset.
+        let [xOffset, yOffset] = getFrameOffsets(this.win, e.target);
+        let x = xOffset + e.pageX - this.win.scrollX;
+        let y = yOffset + e.pageY - this.win.scrollY;
+        // Update the zoom area.
+        this.magnifiedArea.x = x * this.pageZoom;
+        this.magnifiedArea.y = y * this.pageZoom;
+        // Redraw the portion of the screenshot that is now under the mouse.
+        this.draw();
+        // And move the eye-dropper's UI so it follows the mouse.
+        this.moveTo(x, y);
+        break;
+      case "click":
+        this.selectColor();
+        break;
+      case "keydown":
+        this.handleKeyDown(e);
+        break;
+      case "DOMMouseScroll":
+        // Prevent scrolling. That's because we only took a screenshot of the viewport, so
+        // scrolling out of the viewport wouldn't draw the expected things. In the future
+        // we can take the screenshot again on scroll, but for now it doesn't seem
+        // important.
+        e.preventDefault();
+        break;
+      case "FullZoomChange":
+        this.hide();
+        this.show();
+        break;
+    }
+  },
+
+  moveTo(x, y) {
+    let root = this.getElement("root");
+    root.setAttribute("style", `top:${y}px;left:${x}px;`);
+
+    // Move the label container to the top if the magnifier is close to the bottom edge.
+    if (y >= this.win.innerHeight - MAGNIFIER_HEIGHT) {
+      root.setAttribute("top", "");
+    } else {
+      root.removeAttribute("top");
+    }
+
+    // Also offset the label container to the right or left if the magnifier is close to
+    // the edge.
+    root.removeAttribute("left");
+    root.removeAttribute("right");
+    if (x <= MAGNIFIER_WIDTH) {
+      root.setAttribute("right", "");
+    } else if (x >= this.win.innerWidth - MAGNIFIER_WIDTH) {
+      root.setAttribute("left", "");
+    }
+  },
+
+  /**
+   * Select the current color that's being previewed. Depending on the current options,
+   * selecting might mean copying to the clipboard and closing the
+   */
+  selectColor() {
+    let onColorSelected = Promise.resolve();
+    if (this.options.copyOnSelect) {
+      onColorSelected = this.copyColor();
+    }
+
+    this.emit("selected", toColorString(this.centerColor, this.format));
+    onColorSelected.then(() => this.hide(), e => console.error(e));
+  },
+
+  /**
+   * Handler for the keydown event. Either select the color or move the panel in a
+   * direction depending on the key pressed.
+   */
+  handleKeyDown(e) {
+    // Bail out early if any unsupported modifier is used, so that we let
+    // keyboard shortcuts through.
+    if (e.metaKey || e.ctrlKey || e.altKey) {
+      return;
+    }
+
+    if (e.keyCode === e.DOM_VK_RETURN) {
+      this.selectColor();
+      e.preventDefault();
+      return;
+    }
+
+    if (e.keyCode === e.DOM_VK_ESCAPE) {
+      this.emit("canceled");
+      this.hide();
+      e.preventDefault();
+      return;
+    }
+
+    let offsetX = 0;
+    let offsetY = 0;
+    let modifier = 1;
+
+    if (e.keyCode === e.DOM_VK_LEFT) {
+      offsetX = -1;
+    } else if (e.keyCode === e.DOM_VK_RIGHT) {
+      offsetX = 1;
+    } else if (e.keyCode === e.DOM_VK_UP) {
+      offsetY = -1;
+    } else if (e.keyCode === e.DOM_VK_DOWN) {
+      offsetY = 1;
+    }
+
+    if (e.shiftKey) {
+      modifier = 10;
+    }
+
+    offsetY *= modifier;
+    offsetX *= modifier;
+
+    if (offsetX !== 0 || offsetY !== 0) {
+      this.magnifiedArea.x = cap(this.magnifiedArea.x + offsetX,
+                                 0, this.win.innerWidth * this.pageZoom);
+      this.magnifiedArea.y = cap(this.magnifiedArea.y + offsetY, 0,
+                                 this.win.innerHeight * this.pageZoom);
+
+      this.draw();
+
+      this.moveTo(this.magnifiedArea.x / this.pageZoom,
+                  this.magnifiedArea.y / this.pageZoom);
+
+      e.preventDefault();
+    }
+  },
+
+  /**
+   * Copy the currently inspected color to the clipboard.
+   * @return {Promise} Resolves when the copy has been done (after a delay that is used to
+   * let users know that something was copied).
+   */
+  copyColor() {
+    // Copy to the clipboard.
+    let color = toColorString(this.centerColor, this.format);
+    clipboardHelper.copyString(color);
+
+    // Provide some feedback.
+    this.getElement("color-value").setTextContent(
+      "✓ " + l10n.GetStringFromName("colorValue.copied"));
+
+    // Hide the tool after a delay.
+    clearTimeout(this._copyTimeout);
+    return new Promise(resolve => {
+      this._copyTimeout = setTimeout(resolve, CLOSE_DELAY);
+    });
+  }
+};
+
+exports.EyeDropper = EyeDropper;
+
+/**
+ * Draw the visible portion of the window on a canvas and get the resulting ImageData.
+ * @param {Window} win
+ * @return {ImageData} The image data for the window.
+ */
+function getWindowAsImageData(win) {
+  let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+  let scale = getCurrentZoom(win);
+  let width = win.innerWidth;
+  let height = win.innerHeight;
+  canvas.width = width * scale;
+  canvas.height = height * scale;
+  canvas.mozOpaque = true;
+
+  let ctx = canvas.getContext("2d");
+
+  ctx.scale(scale, scale);
+  ctx.drawWindow(win, win.scrollX, win.scrollY, width, height, "#fff");
+
+  return ctx.getImageData(0, 0, canvas.width, canvas.height);
+}
+
+/**
+ * Get a formatted CSS color string from a color value.
+ * @param {array} rgb Rgb values of a color to format.
+ * @param {string} format Format of string. One of "hex", "rgb", "hsl", "name".
+ * @return {string} Formatted color value, e.g. "#FFF" or "hsl(20, 10%, 10%)".
+ */
+function toColorString(rgb, format) {
+  let [r, g, b] = rgb;
+
+  switch (format) {
+    case "hex":
+      return hexString(rgb);
+    case "rgb":
+      return "rgb(" + r + ", " + g + ", " + b + ")";
+    case "hsl":
+      let [h, s, l] = rgbToHsl(rgb);
+      return "hsl(" + h + ", " + s + "%, " + l + "%)";
+    case "name":
+      let str;
+      try {
+        str = rgbToColorName(r, g, b);
+      } catch (e) {
+        str = hexString(rgb);
+      }
+      return str;
+    default:
+      return hexString(rgb);
+  }
+}
+
+/**
+ * Produce a hex-formatted color string from rgb values.
+ * @param {array} rgb Rgb values of color to stringify.
+ * @return {string} Hex formatted string for color, e.g. "#FFEE00".
+ */
+function hexString([r, g, b]) {
+  let val = (1 << 24) + (r << 16) + (g << 8) + (b << 0);
+  return "#" + val.toString(16).substr(-6).toUpperCase();
+}
+
+function cap(value, min, max) {
+  return Math.max(min, Math.min(value, max));
+}
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -1003,21 +1003,18 @@ MakePropertyValuePair(nsCSSPropertyID aP
       new ThreadSafeURIHolder(aDocument->GetDocumentURI());
     RefPtr<ThreadSafePrincipalHolder> principal =
       new ThreadSafePrincipalHolder(aDocument->NodePrincipal());
 
     nsCString baseString;
     aDocument->GetDocumentURI()->GetSpec(baseString);
 
     RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
-      Servo_ParseProperty(
-        reinterpret_cast<const uint8_t*>(name.get()), name.Length(),
-        reinterpret_cast<const uint8_t*>(value.get()), value.Length(),
-        reinterpret_cast<const uint8_t*>(baseString.get()), baseString.Length(),
-        base, referrer, principal).Consume();
+      Servo_ParseProperty(&name, &value, &baseString,
+                          base, referrer, principal).Consume();
 
     if (servoDeclarationBlock) {
       result.mServoDeclarationBlock = servoDeclarationBlock.forget();
       return result;
     }
   }
 
   nsCSSValue value;
--- a/dom/gamepad/GamepadPlatformService.cpp
+++ b/dom/gamepad/GamepadPlatformService.cpp
@@ -65,16 +65,24 @@ GamepadPlatformService::NotifyGamepadCha
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(!NS_IsMainThread());
 
   GamepadChangeEvent e(aInfo);
 
   // mChannelParents may be accessed by background thread in the
   // same time, we use mutex to prevent possible race condtion
   MutexAutoLock autoLock(mMutex);
+
+  // Buffer all events if we have no Channel to dispatch, which
+  // may happen when performing Mochitest.
+  if (mChannelParents.IsEmpty()) {
+    mPendingEvents.AppendElement(e);
+    return;
+  }
+
   for(uint32_t i = 0; i < mChannelParents.Length(); ++i) {
     mChannelParents[i]->DispatchUpdateEvent(e);
   }
 }
 
 uint32_t
 GamepadPlatformService::AddGamepad(const char* aID,
                                    GamepadMappingType aMapping,
@@ -158,16 +166,37 @@ GamepadPlatformService::AddChannelParent
   // is created or removed in Background thread
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParent);
   MOZ_ASSERT(!mChannelParents.Contains(aParent));
 
   // We use mutex here to prevent race condition with monitor thread
   MutexAutoLock autoLock(mMutex);
   mChannelParents.AppendElement(aParent);
+  FlushPendingEvents();
+}
+
+void
+GamepadPlatformService::FlushPendingEvents()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mChannelParents.IsEmpty());
+
+  if (mPendingEvents.IsEmpty()) {
+    return;
+  }
+
+  // NOTE: This method must be called with mMutex held because it accesses
+  // mChannelParents.
+  for (uint32_t i=0; i<mChannelParents.Length(); ++i) {
+    for (uint32_t j=0; j<mPendingEvents.Length();++j) {
+      mChannelParents[i]->DispatchUpdateEvent(mPendingEvents[j]);
+    }
+  }
+  mPendingEvents.Clear();
 }
 
 void
 GamepadPlatformService::RemoveChannelParent(GamepadEventChannelParent* aParent)
 {
   // mChannelParents can only be modified once GamepadEventChannelParent
   // is created or removed in Background thread
   AssertIsOnBackgroundThread();
--- a/dom/gamepad/GamepadPlatformService.h
+++ b/dom/gamepad/GamepadPlatformService.h
@@ -69,27 +69,36 @@ class GamepadPlatformService final
   bool HasGamepadListeners();
 
   void MaybeShutdown();
 
  private:
   GamepadPlatformService();
   ~GamepadPlatformService();
   template<class T> void NotifyGamepadChange(const T& aInfo);
+
+  // Flush all pending events buffered in mPendingEvents, must be called
+  // with mMutex held
+  void FlushPendingEvents();
   void Cleanup();
 
   // mGamepadIndex can only be accessed by monitor thread
   uint32_t mGamepadIndex;
 
   // mChannelParents stores all the GamepadEventChannelParent instances
   // which may be accessed by both background thread and monitor thread
   // simultaneously, so we have a mutex to prevent race condition
   nsTArray<RefPtr<GamepadEventChannelParent>> mChannelParents;
 
   // This mutex protects mChannelParents from race condition
   // between background and monitor thread
   Mutex mMutex;
+
+  // In mochitest, it is possible that the test Events is synthesized
+  // before GamepadEventChannel created, we need to buffer all events
+  // until the channel is created if that happens.
+  nsTArray<GamepadChangeEvent> mPendingEvents;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/html/HTMLFormControlsCollection.cpp
+++ b/dom/html/HTMLFormControlsCollection.cpp
@@ -48,16 +48,17 @@ HTMLFormControlsCollection::ShouldBeInEl
   case NS_FORM_INPUT_TEL :
   case NS_FORM_INPUT_URL :
   case NS_FORM_INPUT_NUMBER :
   case NS_FORM_INPUT_RANGE :
   case NS_FORM_INPUT_DATE :
   case NS_FORM_INPUT_TIME :
   case NS_FORM_INPUT_MONTH :
   case NS_FORM_INPUT_WEEK :
+  case NS_FORM_INPUT_DATETIME_LOCAL :
   case NS_FORM_SELECT :
   case NS_FORM_TEXTAREA :
   case NS_FORM_FIELDSET :
   case NS_FORM_OBJECT :
   case NS_FORM_OUTPUT :
     return true;
   }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -157,16 +157,17 @@ namespace dom {
 static int32_t gSelectTextFieldOnFocus;
 UploadLastDir* HTMLInputElement::gUploadLastDir;
 
 static const nsAttrValue::EnumTable kInputTypeTable[] = {
   { "button", NS_FORM_INPUT_BUTTON },
   { "checkbox", NS_FORM_INPUT_CHECKBOX },
   { "color", NS_FORM_INPUT_COLOR },
   { "date", NS_FORM_INPUT_DATE },
+  { "datetime-local", NS_FORM_INPUT_DATETIME_LOCAL },
   { "email", NS_FORM_INPUT_EMAIL },
   { "file", NS_FORM_INPUT_FILE },
   { "hidden", NS_FORM_INPUT_HIDDEN },
   { "reset", NS_FORM_INPUT_RESET },
   { "image", NS_FORM_INPUT_IMAGE },
   { "month", NS_FORM_INPUT_MONTH },
   { "number", NS_FORM_INPUT_NUMBER },
   { "password", NS_FORM_INPUT_PASSWORD },
@@ -178,17 +179,17 @@ static const nsAttrValue::EnumTable kInp
   { "text", NS_FORM_INPUT_TEXT },
   { "time", NS_FORM_INPUT_TIME },
   { "url", NS_FORM_INPUT_URL },
   { "week", NS_FORM_INPUT_WEEK },
   { nullptr, 0 }
 };
 
 // Default type is 'text'.
-static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[17];
+static const nsAttrValue::EnumTable* kInputDefaultType = &kInputTypeTable[18];
 
 static const uint8_t NS_INPUT_INPUTMODE_AUTO              = 0;
 static const uint8_t NS_INPUT_INPUTMODE_NUMERIC           = 1;
 static const uint8_t NS_INPUT_INPUTMODE_DIGIT             = 2;
 static const uint8_t NS_INPUT_INPUTMODE_UPPERCASE         = 3;
 static const uint8_t NS_INPUT_INPUTMODE_LOWERCASE         = 4;
 static const uint8_t NS_INPUT_INPUTMODE_TITLECASE         = 5;
 static const uint8_t NS_INPUT_INPUTMODE_AUTOCAPITALIZED   = 6;
@@ -2198,17 +2199,18 @@ HTMLInputElement::ConvertNumberToString(
       return false;
   }
 }
 
 
 Nullable<Date>
 HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
 {
-  if (!IsDateTimeInputType(mType)) {
+  // TODO: this is temporary until bug 888331 is fixed.
+  if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
     return Nullable<Date>();
   }
 
   switch (mType) {
     case NS_FORM_INPUT_DATE:
     {
       uint32_t year, month, day;
       nsAutoString value;
@@ -2266,17 +2268,18 @@ HTMLInputElement::GetValueAsDate(ErrorRe
   MOZ_ASSERT(false, "Unrecognized input type");
   aRv.Throw(NS_ERROR_UNEXPECTED);
   return Nullable<Date>();
 }
 
 void
 HTMLInputElement::SetValueAsDate(Nullable<Date> aDate, ErrorResult& aRv)
 {
-  if (!IsDateTimeInputType(mType)) {
+  // TODO: this is temporary until bug 888331 is fixed.
+  if (!IsDateTimeInputType(mType) || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aDate.IsNull() || aDate.Value().IsUndefined()) {
     aRv = SetValue(EmptyString());
     return;
   }
@@ -2522,17 +2525,18 @@ HTMLInputElement::IsExperimentalMobileTy
 }
 
 bool
 HTMLInputElement::IsDateTimeInputType(uint8_t aType)
 {
   return aType == NS_FORM_INPUT_DATE ||
          aType == NS_FORM_INPUT_TIME ||
          aType == NS_FORM_INPUT_MONTH ||
-         aType == NS_FORM_INPUT_WEEK;
+         aType == NS_FORM_INPUT_WEEK ||
+         aType == NS_FORM_INPUT_DATETIME_LOCAL;
 }
 
 NS_IMETHODIMP
 HTMLInputElement::StepDown(int32_t n, uint8_t optional_argc)
 {
   return ApplyStep(optional_argc ? -n : -1);
 }
 
@@ -5620,17 +5624,18 @@ IsDateTimeEnabled(int32_t aNewType)
   return (aNewType == NS_FORM_INPUT_DATE &&
           (Preferences::GetBool("dom.forms.datetime", false) ||
            Preferences::GetBool("dom.experimental_forms", false) ||
            Preferences::GetBool("dom.forms.datepicker", false))) ||
          (aNewType == NS_FORM_INPUT_TIME &&
           (Preferences::GetBool("dom.forms.datetime", false) ||
            Preferences::GetBool("dom.experimental_forms", false))) ||
          ((aNewType == NS_FORM_INPUT_MONTH ||
-           aNewType == NS_FORM_INPUT_WEEK) &&
+           aNewType == NS_FORM_INPUT_WEEK ||
+           aNewType == NS_FORM_INPUT_DATETIME_LOCAL) &&
           Preferences::GetBool("dom.forms.datetime", false));
 }
 
 bool
 HTMLInputElement::ParseAttribute(int32_t aNamespaceID,
                                  nsIAtom* aAttribute,
                                  const nsAString& aValue,
                                  nsAttrValue& aResult)
@@ -7140,16 +7145,17 @@ HTMLInputElement::GetValueMode() const
     case NS_FORM_INPUT_URL:
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_RANGE:
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_TIME:
     case NS_FORM_INPUT_COLOR:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return VALUE_MODE_VALUE;
     default:
       NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()");
       return VALUE_MODE_VALUE;
 #else // DEBUG
     default:
       return VALUE_MODE_VALUE;
 #endif // DEBUG
@@ -7187,16 +7193,17 @@ HTMLInputElement::DoesReadOnlyApply() co
     case NS_FORM_INPUT_TEL:
     case NS_FORM_INPUT_EMAIL:
     case NS_FORM_INPUT_URL:
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_TIME:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return true;
     default:
       NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()");
       return true;
 #else // DEBUG
     default:
       return true;
 #endif // DEBUG
@@ -7226,16 +7233,17 @@ HTMLInputElement::DoesRequiredApply() co
     case NS_FORM_INPUT_TEL:
     case NS_FORM_INPUT_EMAIL:
     case NS_FORM_INPUT_URL:
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_TIME:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return true;
     default:
       NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
       return true;
 #else // DEBUG
     default:
       return true;
 #endif // DEBUG
@@ -7269,18 +7277,17 @@ HTMLInputElement::DoesMinMaxApply() cons
   switch (mType)
   {
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_TIME:
     case NS_FORM_INPUT_RANGE:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
-    // TODO:
-    // All date/time types.
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return true;
 #ifdef DEBUG
     case NS_FORM_INPUT_RESET:
     case NS_FORM_INPUT_SUBMIT:
     case NS_FORM_INPUT_IMAGE:
     case NS_FORM_INPUT_BUTTON:
     case NS_FORM_INPUT_HIDDEN:
     case NS_FORM_INPUT_RADIO:
@@ -7318,16 +7325,17 @@ HTMLInputElement::DoesAutocompleteApply(
     case NS_FORM_INPUT_PASSWORD:
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_TIME:
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_RANGE:
     case NS_FORM_INPUT_COLOR:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return true;
 #ifdef DEBUG
     case NS_FORM_INPUT_RESET:
     case NS_FORM_INPUT_SUBMIT:
     case NS_FORM_INPUT_IMAGE:
     case NS_FORM_INPUT_BUTTON:
     case NS_FORM_INPUT_RADIO:
     case NS_FORM_INPUT_CHECKBOX:
@@ -7525,17 +7533,18 @@ HTMLInputElement::HasPatternMismatch() c
   nsIDocument* doc = OwnerDoc();
 
   return !nsContentUtils::IsPatternMatching(value, pattern, doc);
 }
 
 bool
 HTMLInputElement::IsRangeOverflow() const
 {
-  if (!DoesMinMaxApply()) {
+  // TODO: this is temporary until bug 888331 is fixed.
+  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
     return false;
   }
 
   Decimal maximum = GetMaximum();
   if (maximum.isNaN()) {
     return false;
   }
 
@@ -7545,17 +7554,18 @@ HTMLInputElement::IsRangeOverflow() cons
   }
 
   return value > maximum;
 }
 
 bool
 HTMLInputElement::IsRangeUnderflow() const
 {
-  if (!DoesMinMaxApply()) {
+  // TODO: this is temporary until bug 888331 is fixed.
+  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
     return false;
   }
 
   Decimal minimum = GetMinimum();
   if (minimum.isNaN()) {
     return false;
   }
 
@@ -8553,17 +8563,18 @@ HTMLInputElement::UpdateHasRange()
 {
   /*
    * There is a range if min/max applies for the type and if the element
    * currently have a valid min or max.
    */
 
   mHasRange = false;
 
-  if (!DoesMinMaxApply()) {
+  // TODO: this is temporary until bug 888331 is fixed.
+  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_DATETIME_LOCAL) {
     return;
   }
 
   Decimal minimum = GetMinimum();
   if (!minimum.isNaN()) {
     mHasRange = true;
     return;
   }
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1051,27 +1051,35 @@ protected:
   /**
    * Returns if the min and max attributes apply for the current type.
    */
   bool DoesMinMaxApply() const;
 
   /**
    * Returns if the step attribute apply for the current type.
    */
-  bool DoesStepApply() const { return DoesMinMaxApply(); }
+  bool DoesStepApply() const
+  {
+    // TODO: this is temporary until bug 888331 is fixed.
+    return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL;
+  }
 
   /**
    * Returns if stepDown and stepUp methods apply for the current type.
    */
   bool DoStepDownStepUpApply() const { return DoesStepApply(); }
 
   /**
    * Returns if valueAsNumber attribute applies for the current type.
    */
-  bool DoesValueAsNumberApply() const { return DoesMinMaxApply(); }
+  bool DoesValueAsNumberApply() const
+  {
+    // TODO: this is temporary until bug 888331 is fixed.
+    return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL;
+  }
 
   /**
    * Returns if autocomplete attribute applies for the current type.
    */
   bool DoesAutocompleteApply() const;
 
   /**
    * Returns if the minlength or maxlength attributes apply for the current type.
--- a/dom/html/nsIFormControl.h
+++ b/dom/html/nsIFormControl.h
@@ -62,16 +62,17 @@ enum InputElementTypes : uint8_t {
   NS_FORM_INPUT_SEARCH,
   NS_FORM_INPUT_SUBMIT,
   NS_FORM_INPUT_TEL,
   NS_FORM_INPUT_TEXT,
   NS_FORM_INPUT_TIME,
   NS_FORM_INPUT_URL,
   NS_FORM_INPUT_RANGE,
   NS_FORM_INPUT_WEEK,
+  NS_FORM_INPUT_DATETIME_LOCAL,
   eInputElementTypesMax
 };
 
 static_assert(static_cast<uint32_t>(eFormControlsWithoutSubTypesMax) <
               static_cast<uint32_t>(NS_FORM_BUTTON_ELEMENT),
               "Too many FormControlsTypes without sub-types");
 static_assert(static_cast<uint32_t>(eButtonElementTypesMax) <
               static_cast<uint32_t>(NS_FORM_INPUT_ELEMENT),
@@ -268,16 +269,17 @@ nsIFormControl::IsSingleLineTextControl(
          // TODO: those are temporary until bug 773205 is fixed.
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
          // On Android/B2G, date/time input appears as a normal text box.
          aType == NS_FORM_INPUT_TIME ||
 #endif
          aType == NS_FORM_INPUT_DATE ||
          aType == NS_FORM_INPUT_MONTH ||
          aType == NS_FORM_INPUT_WEEK ||
+         aType == NS_FORM_INPUT_DATETIME_LOCAL ||
          (!aExcludePassword && aType == NS_FORM_INPUT_PASSWORD);
 }
 
 bool
 nsIFormControl::IsSubmittableControl() const
 {
   // TODO: keygen should be in that list, see bug 101019.
   uint32_t type = GetType();
--- a/dom/html/test/forms/test_input_attributes_reflection.html
+++ b/dom/html/test/forms/test_input_attributes_reflection.html
@@ -230,20 +230,19 @@ reflectString({
 
 // .type
 reflectLimitedEnumerated({
   element: document.createElement("input"),
   attribute: "type",
   validValues: [ "hidden", "text", "search", "tel", "url", "email", "password",
                  "checkbox", "radio", "file", "submit", "image", "reset",
                  "button", "date", "time", "number", "range", "color", "month",
-                 "week" ],
+                 "week", "datetime-local" ],
   invalidValues: [ "this-is-probably-a-wrong-type", "", "tulip" ],
-  defaultValue: "text",
-  unsupportedValues: [ "datetime", "datetime-local" ]
+  defaultValue: "text"
 });
 
 // .defaultValue
 reflectString({
   element: document.createElement("input"),
   attribute: { idl: "defaultValue", content: "value" },
   otherValues: [ "foo\nbar", "foo\rbar", "foo\r\nbar" ],
 });
--- a/dom/html/test/forms/test_input_sanitization.html
+++ b/dom/html/test/forms/test_input_sanitization.html
@@ -69,22 +69,17 @@ function flushDelayedTests(description)
 }
 
 // We are excluding "file" because it's too different from the other types.
 // And it has no sanitizing algorithm.
 var inputTypes =
 [
   "text", "password", "search", "tel", "hidden", "checkbox", "radio",
   "submit", "image", "reset", "button", "email", "url", "number", "date",
-  "time", "range", "color", "month", "week"
-];
-
-var todoTypes =
-[
-  "datetime", "datetime-local",
+  "time", "range", "color", "month", "week",
 ];
 
 var valueModeValue =
 [
   "text", "search", "url", "tel", "email", "password", "date", "datetime",
   "month", "week", "time", "datetime-local", "number", "range", "color",
 ];
 
@@ -479,25 +474,12 @@ for (type of inputTypes) {
   checkSanitizing(element, "type=" + type + ", frame, editor");
 
   element.style.display = "none";
   checkSanitizing(element, "type=" + type + ", no frame, editor");
 
   form.removeChild(element);
 }
 
-for (type of todoTypes) {
-  // The only meaning of this is to have a failure when new types are introduced
-  // so we will know we have to write the tests here.
-  var form = document.forms[0];
-  var element = document.createElement("input");
-  element.type = type;
-  form.appendChild(element);
-
-  todo_is(element.type, type, "");
-
-  form.removeChild(element);
-}
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/forms/test_input_types_pref.html
+++ b/dom/html/test/forms/test_input_types_pref.html
@@ -67,16 +67,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     }, {
       prefs: [["dom.forms.datetime", false]],
       inputType: "week",
       expectedType: "text"
     }, {
       prefs: [["dom.forms.datetime", true]],
       inputType: "week",
       expectedType: "week"
+    }, {
+      prefs: [["dom.forms.datetime", false]],
+      inputType: "datetime-local",
+      expectedType: "text"
+    }, {
+      prefs: [["dom.forms.datetime", true]],
+      inputType: "datetime-local",
+      expectedType: "datetime-local"
     }
   ];
 
   function testInputTypePreference(aData) {
     return SpecialPowers.pushPrefEnv({'set': aData.prefs})
       .then(() => {
         // Change the type of input to text and then back to the tested input type,
         // so that HTMLInputElement::ParseAttribute gets called with the pref enabled.
--- a/dom/html/test/forms/test_input_typing_sanitization.html
+++ b/dom/html/test/forms/test_input_typing_sanitization.html
@@ -190,29 +190,21 @@ function runTest()
         'week',
         '2016-30',
         '2010-W80',
         '2000/W30',
         '1985-W00',
         '1000-W'
       ]
     },
-    { type: 'datetime', todo: true },
-    { type: 'datetime-local', todo: true },
   ];
 
   for (test of data) {
     gCurrentTest = test;
 
-    if (test.todo) {
-      input.type = test.type;
-      todo_is(input.type, test.type, test.type + " is not implemented");
-      continue;
-    }
-
     input.type = test.type;
     gValidData = test.validData;
     gInvalidData = test.invalidData;
 
     for (data of gValidData) {
       input.value = "";
       input.focus();
       sendString(data);
--- a/dom/html/test/forms/test_max_attribute.html
+++ b/dom/html/test/forms/test_max_attribute.html
@@ -22,22 +22,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 var data = [
   { type: 'hidden', apply: false },
   { type: 'text', apply: false },
   { type: 'search', apply: false },
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
-  { type: 'datetime', apply: true, todo: true },
   { type: 'date', apply: true },
   { type: 'month', apply: true },
   { type: 'week', apply: true },
   { type: 'time', apply: true },
-  { type: 'datetime-local', apply: true, todo: true },
+  // TODO: temporary set to false until bug 888331 is fixed.
+  { type: 'datetime-local', apply: false },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
   { type: 'file', apply: false },
   { type: 'submit', apply: false },
   { type: 'image', apply: false },
@@ -93,21 +93,16 @@ function checkValidity(aElement, aValidi
        ":out-of-range matches status should be " + !aValidity);
   }
 }
 
 for (var test of data) {
   input.type = test.type;
   var apply = test.apply;
 
-  if (test.todo) {
-    todo_is(input.type, test.type, test.type + " isn't implemented yet");
-    continue;
-  }
-
   // The element should be valid. Range should not apply when @min and @max are
   // undefined, except if the input type is 'range' (since that type has a
   // default minimum and maximum).
   if (input.type == 'range') {
     checkValidity(input, true, apply, true);
   } else {
     checkValidity(input, true, apply, false);
   }
@@ -147,16 +142,19 @@ for (var test of data) {
       input.max = '10';
       break;
     case 'month':
       input.max = '2016-12';
       break;
     case 'week':
       input.max = '2016-W39';
       break;
+    case 'datetime-local':
+      // TODO: this is temporary until bug 888331 is fixed.
+      break;
     default:
       ok(false, 'please, add a case for this new type (' + input.type + ')');
   }
 
   checkValidity(input, true, apply, apply);
 
   switch (input.type) {
     case 'text':
@@ -417,16 +415,20 @@ for (var test of data) {
 
       input.max = '';
       checkValidity(input, true, apply, false);
 
       input.max = 'foo';
       checkValidity(input, true, apply, false);
 
       break;
+    case 'datetime-local':
+      // TODO: this is temporary until bug 888331 is fixed.
+
+      break;
   }
 
   // Cleaning up,
   input.removeAttribute('max');
   input.value = '';
 }
 
 </script>
--- a/dom/html/test/forms/test_min_attribute.html
+++ b/dom/html/test/forms/test_min_attribute.html
@@ -22,22 +22,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 var data = [
   { type: 'hidden', apply: false },
   { type: 'text', apply: false },
   { type: 'search', apply: false },
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
-  { type: 'datetime', apply: true, todo: true },
   { type: 'date', apply: true },
   { type: 'month', apply: true },
   { type: 'week', apply: true },
   { type: 'time', apply: true },
-  { type: 'datetime-local', apply: true, todo: true },
+  // TODO: temporary set to false until bug 888331 is fixed.
+  { type: 'datetime-local', apply: false },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
   { type: 'file', apply: false },
   { type: 'submit', apply: false },
   { type: 'image', apply: false },
@@ -143,16 +143,19 @@ for (var test of data) {
       // suffer from overflow.
       break;
     case 'month':
       input.min = '2016-06';
       break;
     case 'week':
       input.min = "2016-W39";
       break;
+    case 'datetime-local':
+      // TODO: this is temporary until bug 888331 is fixed.
+      break;
     default:
       ok(false, 'please, add a case for this new type (' + input.type + ')');
   }
 
   // The element should still be valid and range should apply if it can.
   checkValidity(input, true, apply, apply);
 
   switch (input.type) {
@@ -411,16 +414,19 @@ for (var test of data) {
       checkValidity(input, false, apply, apply);
 
       input.min = '';
       checkValidity(input, true, apply, false);
 
       input.min = 'foo';
       checkValidity(input, true, apply, false);
       break;
+    case 'datetime-local':
+      // TODO: this is temporary until bug 888331 is fixed.
+      break;
     default:
       ok(false, 'write tests for ' + input.type);
   }
 
   // Cleaning up,
   input.removeAttribute('min');
   input.value = '';
 }
--- a/dom/html/test/forms/test_mozistextfield.html
+++ b/dom/html/test/forms/test_mozistextfield.html
@@ -49,25 +49,16 @@ var gInputTestData = [
   ['url',      true],
   ['number',   false],
   ['range',    false],
   ['date',     false],
   ['time',     false],
   ['color',    false],
   ['month',    false],
   ['week',     false],
-];
-
-/**
- * TODO: the next types are not yet in the tree.
- * The value is only a suggestion.
- */
-var gInputTodoData = [
-/* type        expected result */
-  ['datetime', false],
   ['datetime-local', false],
 ];
 
 function checkMozIsTextFieldDefined(aElement, aResult)
 {
   var element = document.createElement(aElement);
 
   var msg = "mozIsTextField should be "
@@ -109,18 +100,12 @@ for (data of gElementTestData) {
 
 // Check if the method returns the correct value.
 var input = document.createElement('input');
 for (data of gInputTestData) {
   input.type = data[0];
   checkMozIsTextFieldValue(input, data[1]);
 }
 
-// Check for the todo's.
-for (data of gInputTodoData) {
-  input.type = data[0];
-  checkMozIsTextFieldValueTodo(input, data[1]);
-}
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/forms/test_pattern_attribute.html
+++ b/dom/html/test/forms/test_pattern_attribute.html
@@ -294,19 +294,18 @@ function checkPatternValidity(element)
 
 var input = document.getElementById('i');
 
 // |validTypes| are the types which accept @pattern
 // and |invalidTypes| are the ones which do not accept it.
 var validTypes = Array('text', 'password', 'search', 'tel', 'email', 'url');
 var barredTypes = Array('hidden', 'reset', 'button');
 var invalidTypes = Array('checkbox', 'radio', 'file', 'number', 'range', 'date',
-                         'time', 'color', 'submit', 'image', 'month', 'week');
-// TODO: 'datetime' and 'datetime-local'
-//       do not accept the @pattern too but are not implemented yet.
+                         'time', 'color', 'submit', 'image', 'month', 'week',
+                         'datetime-local');
 
 for (type of validTypes) {
   input.type = type;
   completeValidityCheck(input, false);
   checkPatternValidity(input);
 }
 
 for (type of barredTypes) {
--- a/dom/html/test/forms/test_required_attribute.html
+++ b/dom/html/test/forms/test_required_attribute.html
@@ -359,20 +359,19 @@ for (type of typeBarredFromConstraintVal
 
 // Then, checks for the types which do not use the required attribute.
 var typeRequireNotApply = ['range', 'color', 'submit', 'image'];
 for (type of typeRequireNotApply) {
   checkInputRequiredNotApply(type, false);
 }
 
 // Now, checking for all types which accept the required attribute.
-// TODO: check 'datetime' and 'datetime-local'
-//       when they will be implemented.
 var typeRequireApply = ["text", "password", "search", "tel", "email", "url",
-                        "number", "date", "time", "month", "week"];
+                        "number", "date", "time", "month", "week",
+                        "datetime-local"];
 
 for (type of typeRequireApply) {
   checkInputRequiredValidity(type);
 }
 
 checkInputRequiredValidityForCheckbox();
 checkInputRequiredValidityForRadio();
 checkInputRequiredValidityForFile();
--- a/dom/html/test/forms/test_step_attribute.html
+++ b/dom/html/test/forms/test_step_attribute.html
@@ -22,22 +22,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 var data = [
   { type: 'hidden', apply: false },
   { type: 'text', apply: false },
   { type: 'search', apply: false },
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
-  { type: 'datetime', apply: true, todo: true },
   { type: 'date', apply: true },
   { type: 'month', apply: true },
   { type: 'week', apply: true },
   { type: 'time', apply: true },
-  { type: 'datetime-local', apply: true, todo: true },
+  // TODO: temporary set to false until bug 888331 is fixed.
+  { type: 'datetime-local', apply: false },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
   { type: 'file', apply: false },
   { type: 'submit', apply: false },
   { type: 'image', apply: false },
@@ -944,16 +944,20 @@ for (var test of data) {
 
       input.value = '1970-W01';
       checkValidity(input, false, apply, { low: "1969-W52", high: "1970-W02" });
 
       input.value = '1970-W02';
       checkValidity(input, true, apply);
 
       break;
+    case 'datetime-local':
+      // TODO: this is temporary until bug 888331 is fixed.
+
+      break;
     default:
       ok(false, "Implement the tests for <input type='" + test.type + " >");
       break;
   }
 }
 
 </script>
 </pre>
--- a/dom/html/test/forms/test_valueasdate_attribute.html
+++ b/dom/html/test/forms/test_valueasdate_attribute.html
@@ -42,27 +42,22 @@ var validTypes =
   ["button", false],
   ["number", false],
   ["range", false],
   ["date", true],
   ["time", true],
   ["color", false],
   ["month", true],
   ["week", true],
-];
-
-var todoTypes =
-[
-  ["datetime", true],
-  ["datetime-local", true],
+  // TODO: temporary set to false until bug 888331 is fixed.
+  ["datetime-local", false],
 ];
 
 function checkAvailability()
 {
-
   for (let data of validTypes) {
     var exceptionCatched = false;
     element.type = data[0];
     try {
       element.valueAsDate;
     } catch (e) {
       exceptionCatched = true;
     }
@@ -73,37 +68,16 @@ function checkAvailability()
     try {
       element.valueAsDate = new Date();
     } catch (e) {
       exceptionCatched = true;
     }
     is(exceptionCatched, !data[1], "valueAsDate for " + data[0] +
                                    " availability is not correct");
   }
-
-  for (let data of todoTypes) {
-    var exceptionCatched = false;
-    element.type = data[0];
-    try {
-      element.valueAsDate;
-    } catch (e) {
-      exceptionCatched = true;
-    }
-    is(exceptionCatched, false,
-       "valueAsDate shouldn't throw exception on getting");
-
-    exceptionCatched = false;
-    try {
-      element.valueAsDate= 42;
-    } catch (e) {
-      exceptionCatched = true;
-    }
-    todo_is(exceptionCatched, !data[1],
-            "valueAsDate for " + data[0] + " availability is not correct");
-  }
 }
 
 function checkGarbageValues()
 {
   for (let type of validTypes) {
     if (!type[1]) {
       continue;
     }
--- a/dom/html/test/forms/test_valueasnumber_attribute.html
+++ b/dom/html/test/forms/test_valueasnumber_attribute.html
@@ -40,24 +40,19 @@ function checkAvailability()
     ["reset", false],
     ["button", false],
     ["number", true],
     ["range", true],
     ["date", true],
     ["time", true],
     ["color", false],
     ["month", true],
-    // TODO: temporary set to false until bug 888316 is fixed.
     ["week", true],
-  ];
-
-  var todoList =
-  [
-    ["datetime", true],
-    ["datetime-local", true],
+     // TODO: temporary set to false until bug 888331 is fixed.
+    ["datetime-local", false],
   ];
 
   var element = document.createElement('input');
 
   for (let data of testData) {
     var exceptionCatched = false;
     element.type = data[0];
     try {
@@ -72,37 +67,16 @@ function checkAvailability()
     try {
       element.valueAsNumber = 42;
     } catch (e) {
       exceptionCatched = true;
     }
     is(exceptionCatched, !data[1], "valueAsNumber for " + data[0] +
                                    " availability is not correct");
   }
-
-  for (let data of todoList) {
-    var exceptionCatched = false;
-    element.type = data[0];
-    try {
-      element.valueAsNumber;
-    } catch (e) {
-      exceptionCatched = true;
-    }
-    is(exceptionCatched, false,
-       "valueAsNumber shouldn't throw exception on getting");
-
-    exceptionCatched = false;
-    try {
-      element.valueAsNumber = 42;
-    } catch (e) {
-      exceptionCatched = true;
-    }
-    todo_is(exceptionCatched, !data[1],
-            "valueAsNumber for " + data[0] + " availability is not correct");
-  }
 }
 
 function checkNumberGet()
 {
   var testData =
   [
     ["42", 42],
     ["-42", -42], // should work for negative values
--- a/dom/html/test/test_bug590363.html
+++ b/dom/html/test/test_bug590363.html
@@ -35,24 +35,21 @@ var testData = [
   [ "search",   true ],
   [ "password", true ],
   [ "number",   true ],
   [ "date",     true ],
   [ "time",     true ],
   [ "range",    true ],
   [ "color",    true ],
   [ 'month',    true ],
-  [ 'week',     true ]
+  [ 'week',     true ],
+  [ 'datetime-local', true ]
   // 'file' is treated separatly.
 ];
 
-var todoTypes = [
-  "datetime", "datetime-local"
-];
-
 var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month', 'week' ];
 
 var length = testData.length;
 for (var i=0; i<length; ++i) {
   for (var j=0; j<length; ++j) {
     var e = document.createElement('input');
     e.type = testData[i][0];
 
@@ -120,19 +117,12 @@ for (var data of testData) {
     is(e.value, '', ".value should have been reset to the empty string after " +
        "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
   } else {
     is(e.value, 'foo', ".value should still return the same value after " +
        "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
   }
 }
 
-// TODO checks
-for (var type of todoTypes) {
-  var e = document.createElement('input');
-  e.type = type;
-  todo_is(e.type, type, type + " type isn't supported yet");
-}
-
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/test/test_bug598643.html
+++ b/dom/html/test/test_bug598643.html
@@ -33,19 +33,18 @@ function testFileControl(aElement)
      "the file control shouldn't suffer from being too long");
 }
 
 var types = [
   // These types can be too long.
   [ "text", "email", "password", "url", "search", "tel" ],
   // These types can't be too long.
   [ "radio", "checkbox", "submit", "button", "reset", "image", "hidden",
-    'number', 'range', 'date', 'time', 'color', 'month', 'week' ],
-  // These types can't be too long but are not implemented yet.
-  [ "datetime", 'datetime-local' ]
+    'number', 'range', 'date', 'time', 'color', 'month', 'week',
+    'datetime-local' ],
 ];
 
 var input = document.createElement("input");
 input.maxLength = 1;
 input.value = "foo";
 
 // Too long types.
 for (type of types[0]) {
@@ -68,20 +67,14 @@ for (type of types[0]) {
 // Not too long types.
 for (type of types[1]) {
   input.type = type
   ok(input.validity.valid, "the element should be valid [type=" + type + "]");
   ok(!input.validity.tooLong,
      "the element shouldn't suffer from being too long [type=" + type + "]");
 }
 
-// Not too long types but TODO.
-for (type of types[2]) {
-  input.type = type
-  todo_is(input.type, type, type + " should not be implemented");
-}
-
 testFileControl(input);
 
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
index 0000000000000000000000000000000000000000..f9635fc9f516ac8346920b6c5a4f430bbe7c3e14
GIT binary patch
literal 4360
zc%03bc{tSj9>>R#I3?qxBYQ`R%ATE=B7|&_VJs74XN+ZRV>>EzvQ+jZ9Lr$r$-a!F
zp)gdIVQeRiHDqE8!?>yY+<We+ll$Di?&tS>pXdAiJ)iIE{r%_tecoRqJ;p;P0000B
zASc&cC;Ud&S}-F3kiiT9umSi0FmDH2qyyC6PQn=pcfV=+BY^RkF`Idz5u3Shz)=9h
zp$!HA;PZa;&C&b3+Uy|&u<81l@Y>GXR=<dfmyY1X&KOiC%?KXqPcKjE>OpQ&G)ws?
zlRhZN(r%)G`f2v~f~5f^-{Qt<>f)5$RU~}VRHYSIH6k2;Ei!gM`AN>qIe1!V?~;~l
zqgNSc31FmPrnRD2xzeC&npjiz)VQG%6MWLG@%17jGZ;S@?TYmBl7qOLj3xqyW{+5K
z>fC4ti^lzyJ2<g=o~HjgazU*=`G?i{3`F!jGF)vBWxR3En4weoo?#*-D`on{0}QId
zzcCEeFwKq|Az6heNSJPp4E+c^7yce@cS^+M5}gB~zQ%cEV$5c;nYN4{UrbI$`hWEP
zxjME{12a8Bnm=Uc+sz`7lE1orBy{1Z5qe;aq*H3yI>$30@nMN$*y6gR#J%yv=~L{)
zcxtL8F;{%TqMZT6s(=O^&#P8XcFNDe;a-HtxB;RQ+aZ=ZJe7YqnsyU^Qa~-=X0yuG
z)77j?f4$_+kpnG1(<d$3D1X-u=^{D41mXFO{k5G0-;RE2Z1ONe>)T@-7iJ-z?r#M0
z*)7?eZdJ;KUL{n<W@?RJKn-eYUvtJSW<K<o5nPqK=tz8(m#d<|lyI%n+`I9zF)x?#
zNhc#9QiNP&Rv0D52x7^}6sXd>#_^1O*hz@UgMV=`Z%FRkU9NWHyFYjY=@2rjCsg8N
zeRcYZ9?5Yjcc)spDXH1dyeZ;nsP(M%Rk=6TAWX}@<2+D25BAWtaZxSgV@OP_X5x*@
z=a=gBZ|Cw&@N&LO^@`L=9$*9spZ=ZSCO-F+kNC9+%UMRmyR+5z&mVrq5_3#;E56X}
zv{q3}Bo8nF63oiFz+!cq-p*ez{|JxQBV8KRNqgr4x~q}u?N=X}%-sZ#ig7xg5lFI`
z&P}3y^g}Go6{Rkv!*-&mEuOG*8n_m7`8z$uxiW8`4sgwnof$0Sr7r|L67G{2L|&t4
z-oNs$jq7RTuurle)j7?g1Zl;TFq!Q|msT0!>t_qvzOjOs3FRq^P2;P37Bib&J-e40
zvwFPFVbY)gdQS*-p}S^rb3Sai8aH&KO;q-xbK()Sh+dtE_i%^cs14rsWcHOLCJV`p
z1y+B^O9mdLvV4b>{u$UG_R5b{4XqR;$l4rPj_Xr0XR<dpp)RUEp2izt0eLuLnyS*M
zB0PCoV>xBIMDu3c_V5abENS$~!$$!7m6T(y1Nc)N`Q%|24||6I2m7n)lAry{ZOCT6
zKmOw1e=}14!uaVw7%*pV1l-{ZivJUa|9hya{QpEre+wloC9SA<QC3Pu9_i!Y>Lam#
z-}u;ic-uO8dV1ar{37+il+Ap9d_(HX?e+ANl-f@&^no7%pVtcigOr4im%9tn;j<w5
zlNkAH!DWKAyCoq6@WJPJf39i2@mroQVna)*P&?!%+MsFxPZX=x9EF@vN+JadX6RW|
z<FDqPh$p|7Ejv?pKJu7(co{#L{Z_lc{oD`j0+Cohu1GajO{o)_PonI&nwQHMZ|BZO
z+FtfA0%la5>BRrQZW#)CNJyFc7)GBPbEB=#b-I<g`h{Yp!7dKKC=xp?;B>b-OT+py
zP+>d)`()amMn^nfCOsFa2rI2z-}unhVQITy*t#>P*B89t1I1Et2rMNVOy7-$*59D*
z=7HI4=qY+(feTc-vJE>Q%$nIW@2ZqA;P2}hZQ?DV9kyCN*Y%P)NR9jf8{ivOV%+4J
zLsW!jlx104rDCd!omB2WfAY%PrTDSFeq5%aaaK>QOL9`2Av#^g@IftaR=PPnfgdE7
zmZaBd;FwUDh0jv8$a*Q<)fXpQJlBVp9I_mlt1@)Z?iT3~u6jcbP})#Y3tYt1WMS6q
zl}hmW@`WAR+O9IKNl7AFV4M1iIUzLT+TzEh{$9gYQ&oa;#%avsOiZsC;_!g6j~i9R
z9|FapWL<vd*RQj<u2MB<HQ<;&GQ8kEHDD_5<ro|sVqDlS)mc!raTQ*=ZRAw(Zjho;
z65T}fUtOHSlGEUvbxNcD_4T73Z`R&x&98>$U>t(9_Xs-kTrlU5LYn`?0;I(PULA_F
zcVDT_ih1%|Kj{_5-{4u3rF|35FFNR5R#tcjZK~^X$wgI5IH9I>m$xk?E-r<Tbz5VB
zfh!~RLLGs;J~M2Bel_=0q`MF3q#%$JON{+B(u}gY%pE{)t0#}vY+v6X+qF=-?9v-U
zT9c#nmC}@&LP~;$VB8s8;-HlAkzba@JY3TFiDSrl(NnvZ`<~qab`YR8);q~;mPaV=
zy4~^|rQ`ngu0ki<N6=mG?c`LHLl>R3ZJjPlf-A<#QKMH><fmq5mVDscA<l{_GSZYS
z8IriZiN3NE=?Mo%hBi-^aCc{aH)6_sYrVr6KNJiLc@sqxHy96q35aH@nem7t1QwGM
z5i+-$TxpLYbtA?ZodjZReQRe$pVlCAMWf5MYN)jW5mB`#mc^I11|t=l(M6TVX{f_l
zVjW40B@wHh>mFudxBA;!?r;QU<e_{8CuNMpItZXAC{sjjb0DE<!IKVEPFfuw;SB`>
zC)-<^m<>+&D=TGc&(4NS4obdX;Z_V*4E0@3s*+nNJFA*OfxGSYA|~+gy3yZI4&~|k
zN!b~T=k%07O-3%(%{9#*|3JtLj7_<hn3dJ>fNL>h0TpA%-#`F$D<8yT{l@0`DJ1j0
zm2fBEYPF>0M}NwPp{kuhjV9D#HokjM{`s*&3a8)LZ0%^;hW4U0LPuqn-*DV{D`26A
zszr)zC5#5iJTB|??e4FG6(d-81m<W8K26LWY|R>2g23IlQ%zujh7zY+o>DfuFOo%a
zKGIkC4flF=Jo>UIYgT#6;PKf>eg*Gsj7k8&Lm0VH#1^u>GR|noHt}YGh%EO*@3p^2
zDN$J8Po4rtnx%{xv?I}h(|t|y;=XxE_>?8A*%U78_Hkph6khbuF-C8X^DXmt+`V7)
zFj`H(W+AME=$;855<Pkp+$(0~Z0cAnBq;|IyxED)j#_8<KH}HSBy8$YFDOfy>(jxM
z&`$Io%L3y8va1SPO`ABgnvwev>q4OmFu{-mHh9c!Oh&5;PMoCA=Ww-dQckVg1n-oy
z`$a(M-j{l!w2C4d9!5aSS@4yqqdsK2Mnpk;gsCM^27@)GdX6RF3&=h>#~}@O3}yRw
zt+pL8nsTgBVw>;k>%!DF+(>FcVaX_b)QGw&*3(`ZFO`WJG+8n$no%2QeW*L@qy6Bq
z-r0VPb+T5fzFZJF7TLlHi8!fYMjhG}!W2B#b)4ee!Ws3cz6PaqU`RYIAYu*I6bSby
z_<YX{HVMjdGy+4tLl=8&h=<1R@rCiVX7YedmS62OoS@fG^QJU%(whWCfcoewjwaTe
z5;NG$E`x$UPCW?Y+pF1h+k&LecH*FtY$u^a6T@h6iMgd5r2r|pC0fs`;IloX-Y5<l
zGzL*X-f9EWmRu_J?trHWD-{vo=XUk+LDkf&_vO1oWUYE*BcF_gMLE8+^J=C-oM$1p
zuyY%QQW@uapouF-=e)!CB@#C|_fBK^yBa#5KNktR5qzmuw@FL=I=%vEZNM0E>Fm<A
zW;4FNLVfNXTgb9qxeYNR`^TiVge_Oym(%A<S6O#;f<>*mYNq_vOD;h!nLobn7N~;d
z)@?|epg-#N|GBU|qD@=(D2^~CW2?N0d>OhKyhg*#ZI7x9_k<|$KXeUL?pY2Jvf{gR
zqdBW!S8L7x?L)#_6sLFp8fu~MJ}4S-*QpCszgcnN!HMj9lZdE9w?wuM1wG)+stN6B
z#3ht;eq28~`^DY`o9@0y+;bS(4X1IQn=n0z>*Ev=I3}GP;d2L6whQCgsHuM#c0r7d
zUP>LGV19135nKIxG>O?xRotcdBF~<@thcs$!*)N_btiBwYLgyfRlZ~Ebvo3!z6PbM
zZ3Ya0Zg-%ecMtYO+JyBQmTP)*A})f`N0Nv-$Q9N@1gx0963YQ?^Hu7!sxi_p2ekd(
zy-iFZNiZL9%>Q>)EXARr`^o`rms5WJ?^G!uBRz&61Q`GB5zO_uZ6EV%`2Os#PhzGo
zPvWnF=|1!CLOItzXY7DyS6+Nl`g25Q`V!H<!}~g*|Cz^f2=J}A&h#a&e+T<@SpPHD
z<XhN-qdU`==>ARFzYFeM|2o32;n@E{_Fplc=}V0NN}TENKAh>(xBBTPBXEE94~Z}7
AMF0Q*
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_snappyUpgrade.js
@@ -0,0 +1,44 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = "test_snappyUpgrade.js";
+  const objectStoreName = "test";
+  const testString = "Lorem ipsum his ponderum delicatissimi ne, at noster dolores urbanitas pro, cibo elaboraret no his. Ea dicunt maiorum usu. Ad appareat facilisis mediocritatem eos. Tale graeci mentitum in eos, hinc insolens at nam. Graecis nominavi aliquyam eu vix. Id solet assentior sadipscing pro. Et per atqui graecis, usu quot viris repudiandae ei, mollis evertitur an nam. At nam dolor ignota, liber labore omnesque ea mei, has movet voluptaria in. Vel an impetus omittantur. Vim movet option salutandi ex, ne mei ignota corrumpit. Mucius comprehensam id per. Est ea putant maiestatis.";
+
+  info("Installing profile");
+
+  clearAllDatabases(continueToNextStepSync);
+  yield undefined;
+
+  installPackagedProfile("snappyUpgrade_profile");
+
+  info("Opening database");
+
+  let request = indexedDB.open(name);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = unexpectedSuccessHandler;
+  request.onsuccess = continueToNextStepSync;
+  yield undefined;
+
+  // success
+  let db = request.result;
+  db.onerror = errorHandler;
+
+  info("Getting string");
+
+  request = db.transaction([objectStoreName])
+              .objectStore(objectStoreName).get(1);
+  request.onsuccess = continueToNextStepSync;
+  yield undefined;
+
+  is(request.result, testString);
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -19,16 +19,17 @@ support-files =
   GlobalObjectsComponent.manifest
   GlobalObjectsModule.jsm
   GlobalObjectsSandbox.js
   metadata2Restore_profile.zip
   metadataRestore_profile.zip
   schema18upgrade_profile.zip
   schema21upgrade_profile.zip
   schema23upgrade_profile.zip
+  snappyUpgrade_profile.zip
   storagePersistentUpgrade_profile.zip
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_blob_file_backed.js]
 [test_bug1056939.js]
 [test_cleanup_transaction.js]
@@ -47,12 +48,13 @@ skip-if = true
 [test_metadataRestore.js]
 [test_mutableFileUpgrade.js]
 [test_oldDirectories.js]
 [test_quotaExceeded_recovery.js]
 [test_readwriteflush_disabled.js]
 [test_schema18upgrade.js]
 [test_schema21upgrade.js]
 [test_schema23upgrade.js]
+[test_snappyUpgrade.js]
 [test_storagePersistentUpgrade.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -36,16 +36,17 @@
 #include "GLLibraryLoader.h"
 #include "nsISupportsImpl.h"
 #include "plstr.h"
 #include "GLContextTypes.h"
 #include "SurfaceTypes.h"
 #include "GLContextSymbols.h"
 #include "base/platform_thread.h"       // for PlatformThreadId
 #include "mozilla/GenericRefCounted.h"
+#include "mozilla/WeakPtr.h"
 #include "gfx2DGlue.h"
 #include "GeckoProfiler.h"
 
 class nsIWidget;
 
 namespace android {
     class GraphicBuffer;
 } // namespace android
@@ -185,17 +186,21 @@ enum class GLRenderer {
     IntelHD3000,
     MicrosoftBasicRenderDriver,
     Other
 };
 
 class GLContext
     : public GLLibraryLoader
     , public GenericAtomicRefCounted
+    , public SupportsWeakPtr<GLContext>
 {
+public:
+    MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
+
 // -----------------------------------------------------------------------------
 // basic enums
 public:
 
 // -----------------------------------------------------------------------------
 // basic getters
 public:
 
--- a/gfx/gl/SharedSurface.h
+++ b/gfx/gl/SharedSurface.h
@@ -55,17 +55,17 @@ class ShSurfHandle;
 class SharedSurface
 {
 public:
     static void ProdCopy(SharedSurface* src, SharedSurface* dest,
                          SurfaceFactory* factory);
 
     const SharedSurfaceType mType;
     const AttachmentType mAttachType;
-    GLContext* const mGL;
+    const WeakPtr<GLContext> mGL;
     const gfx::IntSize mSize;
     const bool mHasAlpha;
     const bool mCanRecycle;
 protected:
     bool mIsLocked;
     bool mIsProducerAcquired;
 #ifdef DEBUG
     nsIThread* const mOwningThread;
--- a/gfx/gl/SharedSurfaceD3D11Interop.cpp
+++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp
@@ -334,17 +334,17 @@ SharedSurface_D3D11Interop::SharedSurfac
     , mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish())
     , mLockedForGL(false)
 { }
 
 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
 {
     MOZ_ASSERT(!IsProducerAcquired());
 
-    if (!mGL->MakeCurrent())
+    if (!mGL || !mGL->MakeCurrent())
         return;
 
     if (!mInterop->UnregisterObject(mLockHandle)) {
         NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
     }
 
     mGL->fDeleteRenderbuffers(1, &mProdRB);
 }
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -89,17 +89,17 @@ SharedSurface_EGLImage::~SharedSurface_E
 
     if (mSync) {
         // We can't call this unless we have the ext, but we will always have
         // the ext if we have something to destroy.
         mEGL->fDestroySync(Display(), mSync);
         mSync = 0;
     }
 
-    if (!mGL->MakeCurrent())
+    if (!mGL || !mGL->MakeCurrent())
         return;
 
     mGL->fDeleteTextures(1, &mProdTex);
     mProdTex = 0;
 }
 
 layers::TextureFlags
 SharedSurface_EGLImage::GetTextureFlags() const
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -80,17 +80,17 @@ SharedSurface_Basic::SharedSurface_Basic
                               0);
 
     DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
 }
 
 SharedSurface_Basic::~SharedSurface_Basic()
 {
-    if (!mGL->MakeCurrent())
+    if (!mGL || !mGL->MakeCurrent())
         return;
 
     if (mFB)
         mGL->fDeleteFramebuffers(1, &mFB);
 
     if (mOwnsTex)
         mGL->fDeleteTextures(1, &mTex);
 }
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -141,17 +141,17 @@ SharedSurface_Gralloc::HasExtensions(GLL
     return egl->HasKHRImageBase() &&
            gl->IsExtensionSupported(GLContext::OES_EGL_image);
 }
 
 SharedSurface_Gralloc::~SharedSurface_Gralloc()
 {
     DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this);
 
-    if (!mGL->MakeCurrent())
+    if (!mGL || !mGL->MakeCurrent())
         return;
 
     mGL->fDeleteTextures(1, &mProdTex);
 
     if (mSync) {
         MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) );
         mSync = 0;
     }
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -160,17 +160,17 @@ SharedSurface_IOSurface::SharedSurface_I
     gl->MakeCurrent();
     mProdTex = 0;
     gl->fGenTextures(1, &mProdTex);
     BackTextureWithIOSurf(gl, mProdTex, mIOSurf);
 }
 
 SharedSurface_IOSurface::~SharedSurface_IOSurface()
 {
-    if (!mGL->MakeCurrent())
+    if (!mGL || !mGL->MakeCurrent())
         return;
 
     mGL->fDeleteTextures(1, &mProdTex);
 }
 
 bool
 SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -39,17 +39,17 @@ namespace layers {
  *             +---+         +---+
  *             | E |         | F |
  *             +---+         +---+
  *
  * In this layer tree, there are six layers with A being the root and B,D,E,F
  * being leaf nodes. Layer C is in the middle and has n+1 FrameMetrics, labelled
  * FM0...FMn. FM0 is the FrameMetrics you get by calling c->GetFrameMetrics(0)
  * and FMn is the FrameMetrics you can obtain by calling
- * c->GetFrameMetrics(c->GetFrameMetricsCount() - 1). This layer tree is
+ * c->GetFrameMetrics(c->GetScrollMetadataCount() - 1). This layer tree is
  * conceptually equivalent to this one below:
  *
  *                    +---+
  *                    | A |
  *                    +---+
  *                   /  |  \
  *                  /   |   \
  *                 /    |    \
@@ -108,18 +108,18 @@ namespace layers {
  * in loops and recursion).
  *
  * This class purposely does not expose the wrapped layer directly to avoid
  * user code from accidentally calling functions directly on it. Instead
  * any necessary functions should be wrapped in this class. It does expose
  * the wrapped layer as a void* for printf purposes.
  *
  * The implementation may look like it special-cases mIndex == 0 and/or
- * GetFrameMetricsCount() == 0. This is an artifact of the fact that both
- * mIndex and GetFrameMetricsCount() are uint32_t and GetFrameMetricsCount()
+ * GetScrollMetadataCount() == 0. This is an artifact of the fact that both
+ * mIndex and GetScrollMetadataCount() are uint32_t and GetScrollMetadataCount()
  * can return 0 but mIndex cannot store -1. This seems better than the
  * alternative of making mIndex a int32_t that can store -1, but then having
  * to cast to uint32_t all over the place.
  */
 class MOZ_STACK_CLASS LayerMetricsWrapper {
 public:
   enum StartAt {
     TOP,
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1728,17 +1728,17 @@ public:
    * composited.
    */
   void ClearInvalidRect() { mInvalidRegion.SetEmpty(); }
 
   // These functions allow attaching an AsyncPanZoomController to this layer,
   // and can be used anytime.
   // A layer has an APZC at index aIndex only-if GetFrameMetrics(aIndex).IsScrollable();
   // attempting to get an APZC for a non-scrollable metrics will return null.
-  // The aIndex for these functions must be less than GetFrameMetricsCount().
+  // The aIndex for these functions must be less than GetScrollMetadataCount().
   void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller);
   AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const;
   // The ScrollMetadataChanged function is used internally to ensure the APZC array length
   // matches the frame metrics array length.
 private:
   void ScrollMetadataChanged();
 public:
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/CompositorController.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et 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/. */
+
+#ifndef mozilla_layers_CompositorController_h
+#define mozilla_layers_CompositorController_h
+
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class CompositorController
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+  virtual void ScheduleRenderOnCompositorThread() = 0;
+  virtual void ScheduleHideAllPluginWindows() = 0;
+  virtual void ScheduleShowAllPluginWindows() = 0;
+
+protected:
+  virtual ~CompositorController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorController_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/MetricsSharingController.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 et 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/. */
+
+#ifndef mozilla_layers_MetricsSharingController_h
+#define mozilla_layers_MetricsSharingController_h
+
+#include "FrameMetrics.h" // for FrameMetrics
+#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutexHandle
+#include "mozilla/ipc/SharedMemoryBasic.h" // for SharedMemoryBasic
+#include "mozilla/RefCountType.h" // for MozExternalRefCountType
+#include "nscore.h" // for NS_IMETHOD_
+
+namespace mozilla {
+namespace layers {
+
+class MetricsSharingController
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
+
+  virtual base::ProcessId RemotePid() = 0;
+  virtual bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+                                   CrossProcessMutexHandle aMutexHandle,
+                                   uint64_t aLayersId,
+                                   uint32_t aApzcId) = 0;
+  virtual bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+                                  uint32_t aApzcId) = 0;
+
+protected:
+  virtual ~MetricsSharingController() {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_MetricsSharingController_h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -51,28 +51,28 @@ namespace layers {
 
 typedef mozilla::gfx::Point Point;
 typedef mozilla::gfx::Point4D Point4D;
 typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 
 float APZCTreeManager::sDPI = 160.0;
 
 struct APZCTreeManager::TreeBuildingState {
-  TreeBuildingState(CompositorBridgeParent* aCompositor,
+  TreeBuildingState(const CompositorBridgeParent::LayerTreeState* aLayerTreeState,
                     bool aIsFirstPaint, uint64_t aOriginatingLayersId,
                     APZTestData* aTestData, uint32_t aPaintSequence)
-    : mCompositor(aCompositor)
+    : mLayerTreeState(aLayerTreeState)
     , mIsFirstPaint(aIsFirstPaint)
     , mOriginatingLayersId(aOriginatingLayersId)
     , mPaintLogger(aTestData, aPaintSequence)
   {
   }
 
   // State that doesn't change as we recurse in the tree building
-  CompositorBridgeParent* const mCompositor;
+  const CompositorBridgeParent::LayerTreeState* mLayerTreeState;
   const bool mIsFirstPaint;
   const uint64_t mOriginatingLayersId;
   const APZPaintLogHelper mPaintLogger;
 
   // State that is updated as we perform the tree build
 
   // A list of nodes that need to be destroyed at the end of the tree building.
   // This is initialized with all nodes in the old tree, and nodes are removed
@@ -207,17 +207,17 @@ APZCTreeManager::GetFrameTime()
 void
 APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
                                          const nsTArray<TouchBehaviorFlags> &aValues)
 {
   mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
 }
 
 void
-APZCTreeManager::UpdateHitTestingTree(CompositorBridgeParent* aCompositor,
+APZCTreeManager::UpdateHitTestingTree(uint64_t aRootLayerTreeId,
                                       Layer* aRoot,
                                       bool aIsFirstPaint,
                                       uint64_t aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
   MutexAutoLock lock(mTreeLock);
@@ -227,17 +227,20 @@ APZCTreeManager::UpdateHitTestingTree(Co
   APZTestData* testData = nullptr;
   if (gfxPrefs::APZTestLoggingEnabled()) {
     if (CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(aOriginatingLayersId)) {
       testData = &state->mApzTestData;
       testData->StartNewPaint(aPaintSequenceNumber);
     }
   }
 
-  TreeBuildingState state(aCompositor, aIsFirstPaint, aOriginatingLayersId,
+  const CompositorBridgeParent::LayerTreeState* treeState =
+    CompositorBridgeParent::GetIndirectShadowTree(aRootLayerTreeId);
+  MOZ_ASSERT(treeState);
+  TreeBuildingState state(treeState, aIsFirstPaint, aOriginatingLayersId,
                           testData, aPaintSequenceNumber);
 
   // We do this business with collecting the entire tree into an array because otherwise
   // it's very hard to determine which APZC instances need to be destroyed. In the worst
   // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
   // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
   // completely different place. In scenario (a) we would want to destroy the APZC while
   // walking the layer tree and noticing that the layer/APZC is no longer there. But if
@@ -254,19 +257,17 @@ APZCTreeManager::UpdateHitTestingTree(Co
       });
   mRootNode = nullptr;
 
   if (aRoot) {
     std::stack<gfx::TreeAutoIndent> indents;
     std::stack<gfx::Matrix4x4> ancestorTransforms;
     HitTestingTreeNode* parent = nullptr;
     HitTestingTreeNode* next = nullptr;
-
-    // aCompositor is null in gtest scenarios
-    uint64_t layersId = aCompositor ? aCompositor->RootLayerTreeId() : 0;
+    uint64_t layersId = aRootLayerTreeId;
     ancestorTransforms.push(Matrix4x4());
 
     mApzcTreeLog << "[start]\n";
     LayerMetricsWrapper root(aRoot);
     mTreeLock.AssertCurrentThreadOwns();
 
     ForEachNode<ReverseIterator>(root,
         [&](LayerMetricsWrapper aLayerMetrics)
@@ -544,20 +545,23 @@ APZCTreeManager::PrepareNodeForLayer(con
     }
 
     // The APZC we get off the layer may have been destroyed previously if the
     // layer was inactive or omitted from the layer tree for whatever reason
     // from a layers update. If it later comes back it will have a reference to
     // a destroyed APZC and so we need to throw that out and make a new one.
     bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
     if (newApzc) {
+      MOZ_ASSERT(aState.mLayerTreeState);
       apzc = NewAPZCInstance(aLayersId, state->mController);
-      apzc->SetCompositorBridgeParent(aState.mCompositor);
-      if (state->mCrossProcessParent != nullptr) {
-        apzc->ShareFrameMetricsAcrossProcesses();
+      apzc->SetCompositorController(aState.mLayerTreeState->GetCompositorController());
+      if (state->mCrossProcessParent) {
+        apzc->SetMetricsSharingController(state->CrossProcessSharingController());
+      } else {
+        apzc->SetMetricsSharingController(aState.mLayerTreeState->InProcessSharingController());
       }
       MOZ_ASSERT(node == nullptr);
       node = new HitTestingTreeNode(apzc, true, aLayersId);
     } else {
       // If we are re-using a node for this layer clear the tree pointers
       // so that it doesn't continue pointing to nodes that might no longer
       // be in the tree. These pointers will get reset properly as we continue
       // building the tree. Also remove it from the set of nodes that are going
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -102,31 +102,31 @@ public:
 
   /**
    * Rebuild the hit-testing tree based on the layer update that just came up.
    * Preserve nodes and APZC instances where possible, but retire those whose
    * layers are no longer in the layer tree.
    *
    * This must be called on the compositor thread as it walks the layer tree.
    *
-   * @param aCompositor A pointer to the compositor parent instance that owns
-   *                    this APZCTreeManager
+   * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
+   *                         to this APZCTreeManager
    * @param aRoot The root of the (full) layer tree
    * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint
    *                            applies.
    * @param aIsFirstPaint True if the layers update that this is called in response
    *                      to included a first-paint. If this is true, the part of
    *                      the tree that is affected by the first-paint flag is
    *                      indicated by the aFirstPaintLayersId parameter.
    * @param aPaintSequenceNumber The sequence number of the paint that triggered
    *                             this layer update. Note that every layer child
    *                             process' layer subtree has its own sequence
    *                             numbers.
    */
-  void UpdateHitTestingTree(CompositorBridgeParent* aCompositor,
+  void UpdateHitTestingTree(uint64_t aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
                             uint64_t aOriginatingLayersId,
                             uint32_t aPaintSequenceNumber);
 
   /**
    * Walk the tree of APZCs and flushes the repaint requests for all the APZCS
    * corresponding to the given layers id. Finally, sends a flush complete
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -48,18 +48,19 @@
 #include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread, etc
 #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
 #include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
 #include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
-#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/CompositorController.h" // for CompositorController
 #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
+#include "mozilla/layers/MetricsSharingController.h" // for MetricsSharingController
 #include "mozilla/layers/ScrollInputMethods.h" // for ScrollInputMethod
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Unused.h"             // for unused
 #include "mozilla/FloatingPoint.h"      // for FuzzyEquals*
 #include "nsAlgorithm.h"                // for clamped
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
@@ -693,17 +694,16 @@ AsyncPanZoomController::AsyncPanZoomCont
                                                const RefPtr<InputQueue>& aInputQueue,
                                                GeckoContentController* aGeckoContentController,
                                                GestureBehavior aGestures)
   :  mLayersId(aLayersId),
      mGeckoContentController(aGeckoContentController),
      mRefPtrMonitor("RefPtrMonitor"),
      // mTreeManager must be initialized before GetFrameTime() is called
      mTreeManager(aTreeManager),
-     mSharingFrameMetricsAcrossProcesses(false),
      mFrameMetrics(mScrollMetadata.GetMetrics()),
      mMonitor("AsyncPanZoomController"),
      mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
      mX(this),
      mY(this),
      mPanDirRestricted(false),
      mZoomConstraints(false, false,
         mFrameMetrics.GetDevPixelsPerCSSPixel() * kViewportMinScale / ParentLayerToScreenScale(1),
@@ -725,31 +725,16 @@ AsyncPanZoomController::AsyncPanZoomCont
   }
 }
 
 AsyncPanZoomController::~AsyncPanZoomController()
 {
   MOZ_ASSERT(IsDestroyed());
 }
 
-PCompositorBridgeParent*
-AsyncPanZoomController::GetSharedFrameMetricsCompositor()
-{
-  APZThreadUtils::AssertOnCompositorThread();
-
-  if (mSharingFrameMetricsAcrossProcesses) {
-    // |state| may be null here if the CrossProcessCompositorBridgeParent has already been destroyed.
-    if (const CompositorBridgeParent::LayerTreeState* state = CompositorBridgeParent::GetIndirectShadowTree(mLayersId)) {
-      return state->CrossProcessPCompositorBridge();
-    }
-    return nullptr;
-  }
-  return mCompositorBridgeParent.get();
-}
-
 PlatformSpecificStateBase*
 AsyncPanZoomController::GetPlatformSpecificState()
 {
   if (!mPlatformSpecificState) {
     mPlatformSpecificState = MakeUnique<PlatformSpecificState>();
   }
   return mPlatformSpecificState.get();
 }
@@ -783,20 +768,19 @@ AsyncPanZoomController::Destroy()
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
   mParent = nullptr;
   mTreeManager = nullptr;
 
-  PCompositorBridgeParent* compositor = GetSharedFrameMetricsCompositor();
   // Only send the release message if the SharedFrameMetrics has been created.
-  if (compositor && mSharedFrameMetricsBuffer) {
-    Unused << compositor->SendReleaseSharedCompositorFrameMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
+  if (mMetricsSharingController && mSharedFrameMetricsBuffer) {
+    Unused << mMetricsSharingController->StopSharingMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
   }
 
   { // scope the lock
     ReentrantMonitorAutoEnter lock(mMonitor);
     mSharedFrameMetricsBuffer = nullptr;
     delete mSharedLock;
     mSharedLock = nullptr;
   }
@@ -2649,22 +2633,24 @@ void AsyncPanZoomController::CancelAnima
 }
 
 void AsyncPanZoomController::ClearOverscroll() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   mX.ClearOverscroll();
   mY.ClearOverscroll();
 }
 
-void AsyncPanZoomController::SetCompositorBridgeParent(CompositorBridgeParent* aCompositorBridgeParent) {
-  mCompositorBridgeParent = aCompositorBridgeParent;
+void AsyncPanZoomController::SetCompositorController(CompositorController* aCompositorController)
+{
+  mCompositorController = aCompositorController;
 }
 
-void AsyncPanZoomController::ShareFrameMetricsAcrossProcesses() {
-  mSharingFrameMetricsAcrossProcesses = true;
+void AsyncPanZoomController::SetMetricsSharingController(MetricsSharingController* aMetricsSharingController)
+{
+  mMetricsSharingController = aMetricsSharingController;
 }
 
 void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShift)
 {
   ReentrantMonitorAutoEnter lock(mMonitor);
   CSSPoint adjustment =
     ViewAs<ParentLayerPixel>(aShift, PixelCastJustification::ScreenIsParentLayerForRoot)
     / mFrameMetrics.GetZoom();
@@ -2821,18 +2807,18 @@ const ScreenMargin AsyncPanZoomControlle
   cssMargins.top = -displayPort.y;
   cssMargins.right = displayPort.width - compositionSize.width - cssMargins.left;
   cssMargins.bottom = displayPort.height - compositionSize.height - cssMargins.top;
 
   return cssMargins * aFrameMetrics.DisplayportPixelsPerCSSPixel();
 }
 
 void AsyncPanZoomController::ScheduleComposite() {
-  if (mCompositorBridgeParent) {
-    mCompositorBridgeParent->ScheduleRenderOnCompositorThread();
+  if (mCompositorController) {
+    mCompositorController->ScheduleRenderOnCompositorThread();
   }
 }
 
 void AsyncPanZoomController::ScheduleCompositeAndMaybeRepaint() {
   ScheduleComposite();
   RequestContentRepaint();
 }
 
@@ -3768,26 +3754,26 @@ void AsyncPanZoomController::DispatchSta
 
   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
     if (!IsTransformingState(aOldState) && IsTransformingState(aNewState)) {
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::eTransformBegin);
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
       // Let the compositor know about scroll state changes so it can manage
       // windowed plugins.
-      if (gfxPrefs::HidePluginsForScroll() && mCompositorBridgeParent) {
-        mCompositorBridgeParent->ScheduleHideAllPluginWindows();
+      if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+        mCompositorController->ScheduleHideAllPluginWindows();
       }
 #endif
     } else if (IsTransformingState(aOldState) && !IsTransformingState(aNewState)) {
       controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::eTransformEnd);
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
-      if (gfxPrefs::HidePluginsForScroll() && mCompositorBridgeParent) {
-        mCompositorBridgeParent->ScheduleShowAllPluginWindows();
+      if (gfxPrefs::HidePluginsForScroll() && mCompositorController) {
+        mCompositorController->ScheduleShowAllPluginWindows();
       }
 #endif
     }
   }
 }
 
 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
   return !(aState == NOTHING || aState == TOUCHING);
@@ -3870,54 +3856,54 @@ void AsyncPanZoomController::UpdateShare
 
   if (frame && mSharedLock && gfxPrefs::ProgressivePaint()) {
     mSharedLock->Lock();
     *frame = mFrameMetrics;
     mSharedLock->Unlock();
   }
 }
 
-void AsyncPanZoomController::ShareCompositorFrameMetrics() {
-
-  PCompositorBridgeParent* compositor = GetSharedFrameMetricsCompositor();
+void AsyncPanZoomController::ShareCompositorFrameMetrics()
+{
+  APZThreadUtils::AssertOnCompositorThread();
 
   // Only create the shared memory buffer if it hasn't already been created,
   // we are using progressive tile painting, and we have a
-  // compositor to pass the shared memory back to the content process/thread.
-  if (!mSharedFrameMetricsBuffer && compositor && gfxPrefs::ProgressivePaint()) {
+  // controller to pass the shared memory back to the content process/thread.
+  if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
 
     // Create shared memory and initialize it with the current FrameMetrics value
     mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
     FrameMetrics* frame = nullptr;
     mSharedFrameMetricsBuffer->Create(sizeof(FrameMetrics));
     mSharedFrameMetricsBuffer->Map(sizeof(FrameMetrics));
     frame = static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory());
 
     if (frame) {
 
       { // scope the monitor, only needed to copy the FrameMetrics.
         ReentrantMonitorAutoEnter lock(mMonitor);
         *frame = mFrameMetrics;
       }
 
       // Get the process id of the content process
-      base::ProcessId otherPid = compositor->OtherPid();
+      base::ProcessId otherPid = mMetricsSharingController->RemotePid();
       ipc::SharedMemoryBasic::Handle mem = ipc::SharedMemoryBasic::NULLHandle();
 
       // Get the shared memory handle to share with the content process
       mSharedFrameMetricsBuffer->ShareToProcess(otherPid, &mem);
 
       // Get the cross process mutex handle to share with the content process
       mSharedLock = new CrossProcessMutex("AsyncPanZoomControlLock");
       CrossProcessMutexHandle handle = mSharedLock->ShareToProcess(otherPid);
 
       // Send the shared memory handle and cross process handle to the content
       // process by an asynchronous ipc call. Include the APZC unique ID
       // so the content process know which APZC sent this shared FrameMetrics.
-      if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mLayersId, mAPZCId)) {
+      if (!mMetricsSharingController->StartSharingMetrics(mem, handle, mLayersId, mAPZCId)) {
         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
       }
     }
   }
 }
 
 Maybe<CSSPoint> AsyncPanZoomController::FindSnapPointNear(
     const CSSPoint& aDestination, nsIScrollableFrame::ScrollUnit aUnit) {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -39,19 +39,19 @@ namespace ipc {
 class SharedMemoryBasic;
 
 } // namespace ipc
 
 namespace layers {
 
 class AsyncDragMetrics;
 struct ScrollableLayerGuid;
-class CompositorBridgeParent;
+class CompositorController;
+class MetricsSharingController;
 class GestureEventListener;
-class PCompositorBridgeParent;
 struct AsyncTransform;
 class AsyncPanZoomAnimation;
 class AndroidFlingAnimation;
 class GenericFlingAnimation;
 class InputBlockState;
 class TouchBlockState;
 class PanGestureBlockState;
 class OverscrollHandoffChain;
@@ -182,27 +182,26 @@ public:
    * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the scroll metadata being sent with this call are
    * the initial metadata and the initial paint of the frame has just happened.
    */
   void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
                            bool aThisLayerTreeUpdated);
 
   /**
-   * The platform implementation must set the compositor parent so that we can
+   * The platform implementation must set the compositor controller so that we can
    * request composites.
    */
-  void SetCompositorBridgeParent(CompositorBridgeParent* aCompositorBridgeParent);
+  void SetCompositorController(CompositorController* aCompositorController);
 
   /**
-   * Inform this APZC that it will be sharing its FrameMetrics with a cross-process
-   * compositor so that the associated content process can access it. This is only
-   * relevant when progressive painting is enabled.
+   * If we need to share the frame metrics with some other thread, this controller
+   * needs to be set and provides relevant information/APIs.
    */
-  void ShareFrameMetricsAcrossProcesses();
+  void SetMetricsSharingController(MetricsSharingController* aMetricsSharingController);
 
   // --------------------------------------------------------------------------
   // These methods can be called from any thread.
   //
 
   /**
    * Shut down the controller/UI thread state and prepare to be
    * deleted (which may happen from any thread).
@@ -509,18 +508,17 @@ protected:
    * Scales the viewport by an amount (note that it multiplies this scale in to
    * the current scale, it doesn't set it to |aScale|). Also considers a focus
    * point so that the page zooms inward/outward from that point.
    */
   void ScaleWithFocus(float aScale,
                       const CSSPoint& aFocus);
 
   /**
-   * Schedules a composite on the compositor thread. Wrapper for
-   * CompositorBridgeParent::ScheduleRenderOnCompositorThread().
+   * Schedules a composite on the compositor thread.
    */
   void ScheduleComposite();
 
   /**
    * Schedules a composite, and if enough time has elapsed since the last
    * paint, a paint.
    */
   void ScheduleCompositeAndMaybeRepaint();
@@ -642,17 +640,18 @@ protected:
   nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType,
                                   const ScreenIntPoint& aPoint,
                                   mozilla::Modifiers aModifiers);
 
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
 
   uint64_t mLayersId;
-  RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
+  RefPtr<CompositorController> mCompositorController;
+  RefPtr<MetricsSharingController> mMetricsSharingController;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      compositor thread. */
   RefPtr<GeckoContentController> mGeckoContentController;
   RefPtr<GestureEventListener> mGestureEventListener;
   mutable Monitor mRefPtrMonitor;
 
@@ -662,22 +661,16 @@ protected:
   // The APZCTreeManager owns the lifetime of the APZCs, so nulling this
   // pointer out in Destroy() will prevent accessing deleted memory.
   Atomic<APZCTreeManager*> mTreeManager;
 
   /* Utility functions that return a addrefed pointer to the corresponding fields. */
   already_AddRefed<GeckoContentController> GetGeckoContentController() const;
   already_AddRefed<GestureEventListener> GetGestureEventListener() const;
 
-  // If we are sharing our frame metrics with content across processes
-  bool mSharingFrameMetricsAcrossProcesses;
-  /* Utility function to get the Compositor with which we share the FrameMetrics.
-     This function is only callable from the compositor thread. */
-  PCompositorBridgeParent* GetSharedFrameMetricsCompositor();
-
   PlatformSpecificStateBase* GetPlatformSpecificState();
 
 protected:
   // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
   // monitor. Do not read from or modify either of them without locking.
   ScrollMetadata mScrollMetadata;
   FrameMetrics& mFrameMetrics;  // for convenience, refers to mScrollMetadata.mMetrics
 
--- a/gfx/layers/apz/test/gtest/TestEventRegions.cpp
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -37,17 +37,17 @@ protected:
     root->SetEventRegions(regions);
     regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100));
     regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
     layers[1]->SetEventRegions(regions);
     regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
     layers[2]->SetEventRegions(regions);
 
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
   }
 
   void CreateEventRegionsLayerTree2() {
     const char* layerTreeSyntax = "c(t)";
     nsIntRegion layerVisibleRegions[] = {
       nsIntRegion(IntRect(0, 0, 100, 500)),
       nsIntRegion(IntRect(0, 150, 100, 100)),
@@ -58,17 +58,17 @@ protected:
     // Set up the event regions so that the child thebes layer is positioned far
     // away from the scrolling container layer.
     EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
     root->SetEventRegions(regions);
     regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
     layers[1]->SetEventRegions(regions);
 
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
   }
 
   void CreateObscuringLayerTree() {
     const char* layerTreeSyntax = "c(c(t)t)";
     // LayerID                     0 1 2 3
     // 0 is the root.
     // 1 is a parent scrollable layer.
@@ -92,17 +92,17 @@ protected:
     EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
     root->SetEventRegions(regions);
     regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
     layers[1]->SetEventRegions(regions);
     regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
     layers[2]->SetEventRegions(regions);
 
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
   }
 
   void CreateBug1119497LayerTree() {
     const char* layerTreeSyntax = "c(tt)";
     // LayerID                     0 12
     // 0 is the root and has an APZC
     // 1 is behind 2 and has an APZC
@@ -114,17 +114,17 @@ protected:
       nsIntRegion(IntRect(0, 0, 100, 100)),
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
 
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
 
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
   }
 
   void CreateBug1117712LayerTree() {
     const char* layerTreeSyntax = "c(c(t)t)";
     // LayerID                     0 1 2 3
     // 0 is the root
     // 1 is a container layer whose sole purpose to make a non-empty ancestor
     //   transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
@@ -151,17 +151,17 @@ protected:
 
     EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
     layers[2]->SetEventRegions(regions);
     regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
     regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
     layers[3]->SetEventRegions(regions);
 
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
   }
 };
 
 TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) {
   CreateEventRegionsLayerTree1();
 
   TestAsyncPanZoomController* root = ApzcOf(layers[0]);
   TestAsyncPanZoomController* left = ApzcOf(layers[1]);
@@ -224,17 +224,17 @@ TEST_F(APZEventRegionsTester, HitRegionA
   EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1);
   Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
 }
 
 TEST_F(APZEventRegionsTester, Obscuration) {
   CreateObscuringLayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   TestAsyncPanZoomController* parent = ApzcOf(layers[1]);
   TestAsyncPanZoomController* child = ApzcOf(layers[2]);
 
   ApzcPanNoFling(parent, 75, 25);
 
   HitTestResult result;
   RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result);
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp
+++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp
@@ -112,41 +112,41 @@ TEST_F(APZHitTestingTester, HitTesting1)
   EXPECT_EQ(nullAPZC, hit.get());
   EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
   EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
 
   uint32_t paintSequenceNumber = 0;
 
   // Now we have a root APZC that will match the page
   SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
+  manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
   hit = GetTargetAPZC(ScreenPoint(15, 15));
   EXPECT_EQ(ApzcOf(root), hit.get());
   // expect hit point at LayerIntPoint(15, 15)
   EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
   EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
 
   // Now we have a sub APZC with a better fit
   SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
+  manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
   EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
   hit = GetTargetAPZC(ScreenPoint(25, 25));
   EXPECT_EQ(ApzcOf(layers[3]), hit.get());
   // expect hit point at LayerIntPoint(25, 25)
   EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
   EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
 
   // At this point, layers[4] obscures layers[3] at the point (15, 15) so
   // hitting there should hit the root APZC
   hit = GetTargetAPZC(ScreenPoint(15, 15));
   EXPECT_EQ(ApzcOf(root), hit.get());
 
   // Now test hit testing when we have two scrollable layers
   SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
+  manager->UpdateHitTestingTree(0, root, false, 0, paintSequenceNumber++);
   hit = GetTargetAPZC(ScreenPoint(15, 15));
   EXPECT_EQ(ApzcOf(layers[4]), hit.get());
   // expect hit point at LayerIntPoint(15, 15)
   EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc.TransformPoint(ScreenPoint(15, 15)));
   EXPECT_EQ(ScreenPoint(15, 15), transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
 
   // Hit test ouside the reach of layer[3,4] but inside root
   hit = GetTargetAPZC(ScreenPoint(90, 90));
@@ -168,17 +168,17 @@ TEST_F(APZHitTestingTester, HitTesting1)
 
 // A more involved hit testing test that involves css and async transforms.
 TEST_F(APZHitTestingTester, HitTesting2) {
   SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests
 
   CreateHitTesting2LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   // At this point, the following holds (all coordinates in screen pixels):
   // layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
   // layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
   // layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
   // layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
 
   TestAsyncPanZoomController* apzcroot = ApzcOf(root);
@@ -275,17 +275,17 @@ TEST_F(APZHitTestingTester, HitTesting2)
   EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25)));
   // transformToGecko unapplies the full async transform of -100 pixels
   EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
 }
 
 TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
   CreateComplexMultiLayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   /* The layer tree looks like this:
 
                 0
         |----|--+--|----|
         1    2     4    5
              |         /|\
              3        6 8 9
@@ -365,17 +365,17 @@ TEST_F(APZHitTestingTester, TestRepaintF
 
   // The main purpose of this test is to verify that touch-start events (or anything
   // that starts a new input block) don't ever get untransformed. This should always
   // hold because the APZ code should flush repaints when we start a new input block
   // and the transform to gecko space should be empty.
 
   CreateSimpleScrollingLayer();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   TestAsyncPanZoomController* apzcroot = ApzcOf(root);
 
   // At this point, the following holds (all coordinates in screen pixels):
   // layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
 
   MockFunction<void(std::string checkPointName)> check;
 
   {
@@ -430,17 +430,17 @@ TEST_F(APZHitTestingTester, TestRepaintF
 }
 
 TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
   // The purpose of this test is to ensure that wheel events trigger a repaint
   // flush as per bug 1166871, and that the wheel event untransform is a no-op.
 
   CreateSimpleScrollingLayer();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   TestAsyncPanZoomController* apzcroot = ApzcOf(root);
 
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
   ScreenPoint origin(100, 50);
   for (int i = 0; i < 3; i++) {
     ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
       ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
       origin, 0, 10, false);
@@ -458,17 +458,17 @@ TEST_F(APZHitTestingTester, TestRepaintF
     mcc->AdvanceByMillis(5);
   }
 }
 
 TEST_F(APZHitTestingTester, TestForceDisableApz) {
   CreateSimpleScrollingLayer();
   DisableApzOn(root);
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   TestAsyncPanZoomController* apzcroot = ApzcOf(root);
 
   ScreenPoint origin(100, 50);
   ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
     ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
     origin, 0, 10, false);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
   EXPECT_EQ(origin, swi.mOrigin);
@@ -499,17 +499,17 @@ TEST_F(APZHitTestingTester, TestForceDis
     origin, 0, 0, false);
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
   EXPECT_EQ(origin, swi.mOrigin);
 }
 
 TEST_F(APZHitTestingTester, Bug1148350) {
   CreateBug1148350LayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
     EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped without transform"));
     EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, ApzcOf(layers[1])->GetGuid(), _)).Times(1);
     EXPECT_CALL(check, Call("Tapped with interleaved transform"));
@@ -523,17 +523,17 @@ TEST_F(APZHitTestingTester, Bug1148350) 
   TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time(), &blockId);
   if (gfxPrefs::TouchActionEnabled()) {
     SetDefaultAllowedTouchBehavior(manager, blockId);
   }
   mcc->AdvanceByMillis(100);
 
   layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
   layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
   mcc->RunThroughDelayedTasks();
   check.Call("Tapped with interleaved transform");
 }
 
 TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
   // Create the layer tree.
@@ -562,17 +562,17 @@ TEST_F(APZHitTestingTester, HitTestingRe
       ParentLayerRect(0,0,200,100));
   subframeMetadata.SetScrollClip(Some(LayerClip(ParentLayerIntRect(0,0,200,100))));
   layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
   layers[2]->SetClipRect(Some(ParentLayerIntRect(0,0,200,200)));
   SetEventRegionsBasedOnBottommostMetrics(layers[2]);
 
   // Build the hit testing tree.
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   // Pan on a region that's inside layers[2]'s layer clip, but outside
   // its subframe metadata's scroll clip.
   Pan(manager, 120, 110);
 
   // Test that the subframe hasn't scrolled.
   EXPECT_EQ(CSSPoint(0,0), ApzcOf(layers[2], 0)->GetFrameMetrics().GetScrollOffset());
 }
--- a/gfx/layers/apz/test/gtest/TestInputQueue.cpp
+++ b/gfx/layers/apz/test/gtest/TestInputQueue.cpp
@@ -9,17 +9,17 @@
 #include "InputUtils.h"
 
 // Test of scenario described in bug 1269067 - that a continuing mouse drag
 // doesn't interrupt a wheel scrolling animation
 TEST_F(APZCTreeManagerTester, WheelInterruptedByMouseDrag) {
   // Set up a scrollable layer
   CreateSimpleScrollingLayer();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
 
   uint64_t dragBlockId = 0;
   uint64_t wheelBlockId = 0;
   uint64_t tmpBlockId = 0;
 
   // First start the mouse drag
   MouseDown(apzc, ScreenIntPoint(5, 5), mcc->Time(), &dragBlockId);
--- a/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
+++ b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp
@@ -19,17 +19,17 @@ protected:
       nsIntRegion(IntRect(0, 0, 100, 100)),
       nsIntRegion(IntRect(0, 50, 100, 50))
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
     SetScrollHandoff(layers[1], root);
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
     rootApzc->GetFrameMetrics().SetIsRootContent(true);  // make root APZC zoomable
   }
 
   void CreateScrollHandoffLayerTree2() {
     const char* layerTreeSyntax = "c(c(t))";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),
@@ -40,17 +40,17 @@ protected:
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
     SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
     SetScrollHandoff(layers[1], root);
     SetScrollHandoff(layers[2], layers[1]);
     // No ScopedLayerTreeRegistration as that just needs to be done once per test
     // and this is the second layer tree for a particular test.
     MOZ_ASSERT(registration);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
   }
 
   void CreateScrollHandoffLayerTree3() {
     const char* layerTreeSyntax = "c(c(t)c(t))";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),  // root
       nsIntRegion(IntRect(0, 0, 100, 50)),   // scrolling parent 1
@@ -64,32 +64,32 @@ protected:
     SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100));
     SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
     SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 4, CSSRect(0, 50, 100, 100));
     SetScrollHandoff(layers[1], layers[0]);
     SetScrollHandoff(layers[3], layers[0]);
     SetScrollHandoff(layers[2], layers[1]);
     SetScrollHandoff(layers[4], layers[3]);
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
   }
 
   void CreateScrollgrabLayerTree(bool makeParentScrollable = true) {
     const char* layerTreeSyntax = "c(t)";
     nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 100, 100)),  // scroll-grabbing parent
       nsIntRegion(IntRect(0, 20, 100, 80))   // child
     };
     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
     float parentHeight = makeParentScrollable ? 120 : 100;
     SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight));
     SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
     SetScrollHandoff(layers[1], root);
     registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-    manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+    manager->UpdateHitTestingTree(0, root, false, 0, 0);
     rootApzc = ApzcOf(root);
     rootApzc->GetScrollMetadata().SetHasScrollgrab(true);
   }
 
   void TestFlingAcceleration() {
     // Jack up the fling acceleration multiplier so we can easily determine
     // whether acceleration occured.
     const float kAcceleration = 100.0f;
--- a/gfx/layers/apz/test/gtest/TestSnapping.cpp
+++ b/gfx/layers/apz/test/gtest/TestSnapping.cpp
@@ -28,17 +28,17 @@ TEST_F(APZCSnappingTester, Bug1265510)
   snap.mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_MANDATORY;
   snap.mScrollSnapIntervalY = Some(100 * AppUnitsPerCSSPixel());
 
   ScrollMetadata metadata = root->GetScrollMetadata(0);
   metadata.SetSnapInfo(ScrollSnapInfo(snap));
   root->SetScrollMetadata(metadata);
 
   UniquePtr<ScopedLayerTreeRegistration> registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   TestAsyncPanZoomController* outer = ApzcOf(layers[0]);
   TestAsyncPanZoomController* inner = ApzcOf(layers[1]);
 
   // Position the mouse near the bottom of the outer frame and scroll by 60px.
   // (6 lines of 10px each). APZC will actually scroll to y=100 because of the
   // mandatory snap coordinate there.
   TimeStamp now = mcc->Time();
--- a/gfx/layers/apz/test/gtest/TestTreeManager.cpp
+++ b/gfx/layers/apz/test/gtest/TestTreeManager.cpp
@@ -10,42 +10,42 @@
 
 TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
   CreateSimpleMultiLayerTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
   // both layers have the same scrollId
   SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
   SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   TestAsyncPanZoomController* nullAPZC = nullptr;
   // so they should have the same APZC
   EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
   EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
   EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
   EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
 
   // Change the scrollId of layers[1], and verify the APZC changes
   SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[2]));
 
   // Change the scrollId of layers[2] to match that of layers[1], ensure we get the same
   // APZC for both again
   SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
 }
 
 TEST_F(APZCTreeManagerTester, Bug1068268) {
   CreatePotentiallyLeakingTree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
 
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
   RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
   RefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
   RefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
 
   EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
   EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
   EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
   EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
@@ -56,17 +56,17 @@ TEST_F(APZCTreeManagerTester, Bug1068268
   EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->GetApzc());
   EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
   EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
 }
 
 TEST_F(APZCTreeManagerTester, Bug1194876) {
   CreateBug1194876Tree();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   uint64_t blockId;
   nsTArray<ScrollableLayerGuid> targets;
 
   // First touch goes down, APZCTM will hit layers[1] because it is on top of
   // layers[0], but we tell it the real target APZC is layers[0].
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
@@ -94,17 +94,17 @@ TEST_F(APZCTreeManagerTester, Bug1194876
   EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
 }
 
 TEST_F(APZCTreeManagerTester, Bug1198900) {
   // This is just a test that cancels a wheel event to make sure it doesn't
   // crash.
   CreateSimpleDTCScrollingLayer();
   ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
-  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  manager->UpdateHitTestingTree(0, root, false, 0, 0);
 
   ScreenPoint origin(100, 50);
   ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
     ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
     origin, 0, 10, false);
   uint64_t blockId;
   manager->ReceiveInputEvent(swi, nullptr, &blockId);
   manager->ContentReceivedInputBlock(blockId, /* preventDefault= */ true);
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -329,16 +329,19 @@ public:
   virtual void DeallocateDeviceData() override{}
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
   virtual Compositor* GetCompositor() override;
 
   virtual gfx::SurfaceFormat GetFormat() const override{ return gfx::SurfaceFormat::YUV; }
 
+  // Bug 1305906 fixes YUVColorSpace handling
+  virtual YUVColorSpace GetYUVColorSpace() const override { return YUVColorSpace::BT601; }
+
   virtual bool Lock() override;
 
   virtual void Unlock() override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
   {
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -352,16 +352,19 @@ public:
   virtual void DeallocateDeviceData() override {}
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
   virtual Compositor* GetCompositor() override;
 
   virtual gfx::SurfaceFormat GetFormat() const override { return gfx::SurfaceFormat::YUV; }
 
+  // Bug 1305906 fixes YUVColorSpace handling
+  virtual YUVColorSpace GetYUVColorSpace() const override { return YUVColorSpace::BT601; }
+
   virtual bool Lock() override;
   virtual void Unlock() override;
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
   {
     return nullptr;
   }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -174,16 +174,40 @@ CompositorBridgeParentBase::AllocUnsafeS
 }
 
 void
 CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem)
 {
   PCompositorBridgeParent::DeallocShmem(aShmem);
 }
 
+base::ProcessId
+CompositorBridgeParentBase::RemotePid()
+{
+  return OtherPid();
+}
+
+bool
+CompositorBridgeParentBase::StartSharingMetrics(ipc::SharedMemoryBasic::Handle aHandle,
+                                                CrossProcessMutexHandle aMutexHandle,
+                                                uint64_t aLayersId,
+                                                uint32_t aApzcId)
+{
+  return PCompositorBridgeParent::SendSharedCompositorFrameMetrics(
+    aHandle, aMutexHandle, aLayersId, aApzcId);
+}
+
+bool
+CompositorBridgeParentBase::StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+                                               uint32_t aApzcId)
+{
+  return PCompositorBridgeParent::SendReleaseSharedCompositorFrameMetrics(
+    aScrollId, aApzcId);
+}
+
 CompositorBridgeParent::LayerTreeState::LayerTreeState()
   : mApzcTreeManagerParent(nullptr)
   , mParent(nullptr)
   , mLayerManager(nullptr)
   , mCrossProcessParent(nullptr)
   , mLayerTree(nullptr)
   , mUpdatedPluginDataAvailable(false)
   , mPendingCompositorUpdates(0)
@@ -1002,17 +1026,16 @@ CompositorBridgeParent::ActorDestroy(Act
   // after this function returns while some ipdl code still needs to run on
   // this thread.
   // We must keep the compositor parent alive untill the code handling message
   // reception is finished on this thread.
   mSelfRef = this;
   MessageLoop::current()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::DeferredDestroy));
 }
 
-
 void
 CompositorBridgeParent::ScheduleRenderOnCompositorThread()
 {
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::ScheduleComposition));
 }
 
 void
@@ -1188,18 +1211,18 @@ CompositorBridgeParent::NotifyShadowTree
     // If plugins haven't been updated, stop waiting.
     if (!pluginsUpdatedFlag) {
       mWaitForPluginsUntil = TimeStamp();
       mHaveBlockedForPlugins = false;
     }
 #endif
 
     if (mApzcTreeManager && aHitTestUpdate) {
-      mApzcTreeManager->UpdateHitTestingTree(this, mLayerManager->GetRoot(),
-          aIsFirstPaint, aId, aPaintSequenceNumber);
+      mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID,
+          mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
     }
 
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
 }
@@ -1541,17 +1564,17 @@ CompositorBridgeParent::ShadowLayersUpda
 
   mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
   if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) {
     AutoResolveRefLayers resolve(mCompositionManager);
 
-    mApzcTreeManager->UpdateHitTestingTree(this, root, aIsFirstPaint,
+    mApzcTreeManager->UpdateHitTestingTree(mRootLayerTreeID, root, aIsFirstPaint,
         mRootLayerTreeID, aPaintSequenceNumber);
   }
 
   // The transaction ID might get reset to 1 if the page gets reloaded, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
   // Otherwise, it should be continually increasing.
   MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransaction);
   mPendingTransaction = aTransactionId;
@@ -2260,22 +2283,34 @@ private:
 
   RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
   // If true, we should send a RemotePaintIsReady message when the layer transaction
   // is received
   bool mNotifyAfterRemotePaint;
   bool mDestroyCalled;
 };
 
-PCompositorBridgeParent*
-CompositorBridgeParent::LayerTreeState::CrossProcessPCompositorBridge() const
+CompositorController*
+CompositorBridgeParent::LayerTreeState::GetCompositorController() const
+{
+  return mParent;
+}
+
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::CrossProcessSharingController() const
 {
   return mCrossProcessParent;
 }
 
+MetricsSharingController*
+CompositorBridgeParent::LayerTreeState::InProcessSharingController() const
+{
+  return mParent;
+}
+
 void
 CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
                                      TimeStamp& aCompositeEnd)
 {
   Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd);
   mPendingTransaction = 0;
 
   if (mLayerManager) {
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -22,19 +22,21 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"            // for Monitor
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/CompositorController.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator
 #include "mozilla/layers/LayersMessages.h"  // for TargetConfig
+#include "mozilla/layers/MetricsSharingController.h"
 #include "mozilla/layers/PCompositorBridgeParent.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsISupportsImpl.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 #include "mozilla/VsyncDispatcher.h"
 
 class MessageLoop;
@@ -188,17 +190,18 @@ private:
   mozilla::Monitor mSetDisplayMonitor;
   RefPtr<CancelableRunnable> mSetDisplayTask;
 #endif
 #endif
 };
 
 class CompositorBridgeParentBase : public PCompositorBridgeParent,
                                    public HostIPCAllocator,
-                                   public ShmemAllocator
+                                   public ShmemAllocator,
+                                   public MetricsSharingController
 {
 public:
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& aTargetConfig,
                                    const InfallibleTArray<PluginWindowData>& aPlugins,
                                    bool aIsFirstPaint,
                                    bool aScheduleComposite,
@@ -221,43 +224,57 @@ public:
                               APZTestData* aOutData) { }
   virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
                                       const uint64_t& aInputBlockId,
                                       const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
   virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
 
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
+  virtual bool RecvSyncWithCompositor() override { return true; }
+
   // HostIPCAllocator
   virtual base::ProcessId GetChildProcessId() override;
   virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
   virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
 
   // ShmemAllocator
   virtual bool AllocShmem(size_t aSize,
                           mozilla::ipc::SharedMemory::SharedMemoryType aType,
                           mozilla::ipc::Shmem* aShmem) override;
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aType,
                                 mozilla::ipc::Shmem* aShmem) override;
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
-  virtual bool RecvSyncWithCompositor() override { return true; }
-
+  // MetricsSharingController
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return HostIPCAllocator::AddRef(); }
+  NS_IMETHOD_(MozExternalRefCountType) Release() override { return HostIPCAllocator::Release(); }
+  base::ProcessId RemotePid() override;
+  bool StartSharingMetrics(mozilla::ipc::SharedMemoryBasic::Handle aHandle,
+                           CrossProcessMutexHandle aMutexHandle,
+                           uint64_t aLayersId,
+                           uint32_t aApzcId) override;
+  bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
+                          uint32_t aApzcId) override;
 };
 
 class CompositorBridgeParent final : public CompositorBridgeParentBase
+                                   , public CompositorController
 {
   friend class CompositorVsyncScheduler;
   friend class CompositorThreadHolder;
   friend class InProcessCompositorSession;
   friend class gfx::GPUProcessManager;
   friend class gfx::GPUParent;
 
 public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return CompositorBridgeParentBase::AddRef(); }
+  NS_IMETHOD_(MozExternalRefCountType) Release() override { return CompositorBridgeParentBase::Release(); }
+
   explicit CompositorBridgeParent(CSSToLayoutDeviceScale aScale,
                                   const TimeDuration& aVsyncRate,
                                   bool aUseExternalSurfaceSize,
                                   const gfx::IntSize& aSurfaceSize);
 
   // Must only be called by CompositorBridgeChild. After invoking this, the
   // IPC channel is active and RecvWillStop/ActorDestroy must be called to
   // free the compositor.
@@ -370,17 +387,17 @@ public:
 
   static void SetShadowProperties(Layer* aLayer);
 
   void NotifyChildCreated(uint64_t aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
-  void ScheduleRenderOnCompositorThread();
+  void ScheduleRenderOnCompositorThread() override;
   void SchedulePauseOnCompositorThread();
   void InvalidateOnCompositorThread();
   /**
    * Returns true if a surface was obtained and the resume succeeded; false
    * otherwise.
    */
   bool ScheduleResumeOnCompositorThread();
   bool ScheduleResumeOnCompositorThread(int width, int height);
@@ -457,17 +474,19 @@ public:
     LayerTransactionParent* mLayerTree;
     nsTArray<PluginWindowData> mPluginData;
     bool mUpdatedPluginDataAvailable;
 
     // Number of times the compositor has been reset without having been
     // acknowledged by the child.
     uint32_t mPendingCompositorUpdates;
 
-    PCompositorBridgeParent* CrossProcessPCompositorBridge() const;
+    CompositorController* GetCompositorController() const;
+    MetricsSharingController* CrossProcessSharingController() const;
+    MetricsSharingController* InProcessSharingController() const;
   };
 
   /**
    * Lookup the indirect shadow tree for |aId| and return it if it
    * exists.  Otherwise null is returned.  This must only be called on
    * the compositor thread.
    */
   static LayerTreeState* GetIndirectShadowTree(uint64_t aId);
@@ -496,20 +515,23 @@ public:
    * and visibility via ipc.
    */
   bool UpdatePluginWindowState(uint64_t aId);
 
   /**
    * Plugin visibility helpers for the apz (main thread) and compositor
    * thread.
    */
-  void ScheduleShowAllPluginWindows();
-  void ScheduleHideAllPluginWindows();
+  void ScheduleShowAllPluginWindows() override;
+  void ScheduleHideAllPluginWindows() override;
   void ShowAllPluginWindows();
   void HideAllPluginWindows();
+#else
+  void ScheduleShowAllPluginWindows() override {}
+  void ScheduleHideAllPluginWindows() override {}
 #endif
 
   /**
    * Main thread response for a plugin visibility request made by the
    * compositor thread.
    */
   virtual bool RecvRemotePluginsReady() override;
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -93,18 +93,20 @@ EXPORTS.gfxipc += [
     'ipc/ShadowLayerUtils.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'apz/util/CheckerboardReportService.h',
 ]
 
 EXPORTS.mozilla.layers += [
+    'apz/public/CompositorController.h',
     'apz/public/GeckoContentController.h',
     'apz/public/IAPZCTreeManager.h',
+    'apz/public/MetricsSharingController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZCTreeManager.h',
     'apz/src/APZUtils.h',
     'apz/src/AsyncDragMetrics.h',
     'apz/src/AsyncPanZoomAnimation.h',
     'apz/src/TouchCounter.h',
     'apz/testutil/APZTestData.h',
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -24,16 +24,17 @@
 
 #include "jsmath.h"
 #include "jsprf.h"
 #include "jsstr.h"
 #include "jsutil.h"
 
 #include "jswrapper.h"
 
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmGenerator.h"
 #include "asmjs/WasmInstance.h"
 #include "asmjs/WasmJS.h"
 #include "asmjs/WasmSerialize.h"
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
 #include "js/MemoryMetrics.h"
@@ -4906,17 +4907,17 @@ CheckFFICall(FunctionValidator& f, Parse
         return false;
 
     Sig sig(Move(args), ret.canonicalToExprType());
 
     uint32_t importIndex;
     if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &importIndex))
         return false;
 
-    if (!f.writeCall(callNode, Expr::CallImport))
+    if (!f.writeCall(callNode, Expr::OldCallImport))
         return false;
 
     // Import index
     if (!f.encoder().writeVarU32(importIndex))
         return false;
 
     *type = Type::ret(ret);
     return true;
@@ -7572,18 +7573,16 @@ ValidateGlobalVariable(JSContext* cx, co
           case ValType::B32x4: {
             SimdConstant simdConstant;
             if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
                 return false;
             // Bool32x4 uses the same data layout as Int32x4.
             *val = Val(simdConstant.asInt32x4());
             return true;
           }
-          case ValType::Limit:
-            MOZ_CRASH("Limit");
         }
       }
     }
 
     MOZ_CRASH("unreachable");
 }
 
 static bool
--- a/js/src/asmjs/WasmAST.h
+++ b/js/src/asmjs/WasmAST.h
@@ -190,32 +190,33 @@ enum class AstExprKind
     Block,
     Branch,
     BranchTable,
     Call,
     CallIndirect,
     ComparisonOperator,
     Const,
     ConversionOperator,
+    CurrentMemory,
     Drop,
     First,
     GetGlobal,
     GetLocal,
+    GrowMemory,
     If,
     Load,
     Nop,
     Pop,
     Return,
     SetGlobal,
     SetLocal,
     TeeLocal,
     Store,
     TernaryOperator,
     UnaryOperator,
-    NullaryOperator,
     Unreachable
 };
 
 class AstExpr : public AstNode
 {
     const AstExprKind kind_;
     ExprType type_;
 
@@ -540,16 +541,38 @@ class AstStore : public AstExpr
         value_(value)
     {}
 
     Expr expr() const { return expr_; }
     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_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::GrowMemory;
+    explicit AstGrowMemory(AstExpr* op)
+      : AstExpr(Kind, ExprType::I32), op_(op)
+    {}
+
+    AstExpr* op() const { return op_; }
+};
+
 class AstBranchTable : public AstExpr
 {
     AstExpr& index_;
     AstRef default_;
     AstRefVector table_;
     AstExpr* value_;
 
   public:
@@ -590,31 +613,31 @@ class AstFunc : public AstNode
     const AstNameVector& locals() const { return localNames_; }
     const AstExprVector& body() const { return body_; }
     AstName name() const { return name_; }
 };
 
 class AstGlobal : public AstNode
 {
     AstName name_;
-    uint32_t flags_;
+    bool isMutable_;
     ValType type_;
     Maybe<AstExpr*> init_;
 
   public:
-    AstGlobal() : flags_(0), type_(ValType::Limit)
+    AstGlobal() : isMutable_(false), type_(ValType(TypeCode::Limit))
     {}
 
-    explicit AstGlobal(AstName name, ValType type, uint32_t flags,
+    explicit AstGlobal(AstName name, ValType type, bool isMutable,
                        Maybe<AstExpr*> init = Maybe<AstExpr*>())
-      : name_(name), flags_(flags), type_(type), init_(init)
+      : name_(name), isMutable_(isMutable), type_(type), init_(init)
     {}
 
     AstName name() const { return name_; }
-    uint32_t flags() const { return flags_; }
+    bool isMutable() const { return isMutable_; }
     ValType type() const { return type_; }
 
     bool hasInit() const { return !!init_; }
     AstExpr& init() const { MOZ_ASSERT(hasInit()); return **init_; }
 };
 
 typedef AstVector<AstGlobal*> AstGlobalVector;
 
@@ -868,30 +891,16 @@ class AstModule : public AstNode
     bool append(AstGlobal* glob) {
         return globals_.append(glob);
     }
     const AstGlobalVector& globals() const {
         return globals_;
     }
 };
 
-class AstNullaryOperator final : public AstExpr
-{
-    Expr expr_;
-
-  public:
-    static const AstExprKind Kind = AstExprKind::NullaryOperator;
-    explicit AstNullaryOperator(Expr expr)
-      : AstExpr(Kind, ExprType::Limit),
-        expr_(expr)
-    {}
-
-    Expr expr() const { return expr_; }
-};
-
 class AstUnaryOperator final : public AstExpr
 {
     Expr expr_;
     AstExpr* op_;
 
   public:
     static const AstExprKind Kind = AstExprKind::UnaryOperator;
     explicit AstUnaryOperator(Expr expr, AstExpr* op)
--- a/js/src/asmjs/WasmBaselineCompile.cpp
+++ b/js/src/asmjs/WasmBaselineCompile.cpp
@@ -89,16 +89,17 @@
  *   assignments are desirable because they are not flushed to memory
  *   by the pre-block sync() call.)
  */
 
 #include "asmjs/WasmBaselineCompile.h"
 
 #include "mozilla/MathAlgorithms.h"
 
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmBinaryIterator.h"
 #include "asmjs/WasmGenerator.h"
 #include "asmjs/WasmSignalHandlers.h"
 #include "jit/AtomicOp.h"
 #include "jit/IonTypes.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/Label.h"
 #include "jit/MacroAssembler.h"
@@ -2296,16 +2297,19 @@ class BaseCompiler
     void builtinCall(SymbolicAddress builtin, const FunctionCall& call)
     {
         callSymbolic(builtin, call);
     }
 
     void builtinInstanceMethodCall(SymbolicAddress builtin, const ABIArg& instanceArg,
                                    const FunctionCall& call)
     {
+        // Builtin method calls assumed the TLS register has been set.
+        loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer));
+
         CallSiteDesc desc(call.lineOrBytecode_, CallSiteDesc::Symbolic);
         masm.wasmCallBuiltinInstanceMethod(instanceArg, builtin);
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Sundry low-level code generators.
 
@@ -3300,17 +3304,17 @@ class BaseCompiler
     bool emitReturn();
     MOZ_MUST_USE
     bool emitCallArgs(const ValTypeVector& args, FunctionCall& baselineCall);
     MOZ_MUST_USE
     bool emitCallImportCommon(uint32_t lineOrBytecode, uint32_t funcImportIndex);
     MOZ_MUST_USE
     bool emitCall();
     MOZ_MUST_USE
-    bool emitCallImport();
+    bool emitOldCallImport();
     MOZ_MUST_USE
     bool emitCallIndirect(bool oldStyle);
     MOZ_MUST_USE
     bool emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
     MOZ_MUST_USE
     bool emitBinaryMathBuiltinCall(SymbolicAddress callee, ValType operandType);
 #ifdef QUOT_REM_I64_CALLOUT
     MOZ_MUST_USE
@@ -5262,24 +5266,24 @@ BaseCompiler::emitCall()
 
     if (!IsVoid(sig.ret()))
         pushReturned(baselineCall, sig.ret());
 
     return true;
 }
 
 bool
-BaseCompiler::emitCallImport()
+BaseCompiler::emitOldCallImport()
 {
     MOZ_ASSERT(!mg_.firstFuncDefIndex);
 
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
     uint32_t funcImportIndex;
-    if (!iter_.readCallImport(&funcImportIndex))
+    if (!iter_.readCall(&funcImportIndex))
         return false;
 
     return emitCallImportCommon(lineOrBytecode, funcImportIndex);
 }
 
 bool
 BaseCompiler::emitCallIndirect(bool oldStyle)
 {
@@ -6223,33 +6227,33 @@ BaseCompiler::emitTeeStoreWithCoercion(V
 }
 
 bool
 BaseCompiler::emitGrowMemory()
 {
     if (deadCode_)
         return true;
 
-    uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
+    Nothing arg;
+    if (!iter_.readGrowMemory(&arg))
+        return false;
 
     sync();
 
     uint32_t numArgs = 1;
     size_t stackSpace = stackConsumed(numArgs);
 
-    FunctionCall baselineCall(lineOrBytecode);
+    FunctionCall baselineCall(readCallSiteLineOrBytecode());
     beginCall(baselineCall, EscapesSandbox(true), IsBuiltinCall(true));
 
     ABIArg instanceArg = reserveArgument(baselineCall);
 
-    if (!emitCallArgs(SigI_, baselineCall))
-        return false;
-
-    if (!iter_.readCallReturn(ExprType::I32))
-        return false;
+    startCallArgs(baselineCall, stackArgAreaSize(SigI_));
+
+    passArg(baselineCall, ValType::I32, peek(0));
 
     builtinInstanceMethodCall(SymbolicAddress::GrowMemory, instanceArg, baselineCall);
 
     endCall(baselineCall);
 
     popValueStackBy(numArgs);
     masm.freeStack(stackSpace);
 
@@ -6259,30 +6263,27 @@ BaseCompiler::emitGrowMemory()
 }
 
 bool
 BaseCompiler::emitCurrentMemory()
 {
     if (deadCode_)
         return true;
 
-    uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
+    if (!iter_.readCurrentMemory())
+        return false;
 
     sync();
 
-    FunctionCall baselineCall(lineOrBytecode);
+    FunctionCall baselineCall(readCallSiteLineOrBytecode());
     beginCall(baselineCall, EscapesSandbox(true), IsBuiltinCall(true));
 
     ABIArg instanceArg = reserveArgument(baselineCall);
 
-    if (!emitCallArgs(Sig_, baselineCall))
-        return false;
-
-    if (!iter_.readCallReturn(ExprType::I32))
-        return false;
+    startCallArgs(baselineCall, stackArgAreaSize(Sig_));
 
     builtinInstanceMethodCall(SymbolicAddress::CurrentMemory, instanceArg, baselineCall);
 
     endCall(baselineCall);
 
     pushReturned(baselineCall, ExprType::I32);
 
     return true;
@@ -6381,18 +6382,18 @@ BaseCompiler::emitBody()
 
           // Calls
           case Expr::Call:
             CHECK_NEXT(emitCall());
           case Expr::CallIndirect:
             CHECK_NEXT(emitCallIndirect(/* oldStyle = */ false));
           case Expr::OldCallIndirect:
             CHECK_NEXT(emitCallIndirect(/* oldStyle = */ true));
-          case Expr::CallImport:
-            CHECK_NEXT(emitCallImport());
+          case Expr::OldCallImport:
+            CHECK_NEXT(emitOldCallImport());
 
           // Locals and globals
           case Expr::GetLocal:
             CHECK_NEXT(emitGetLocal());
           case Expr::SetLocal:
             CHECK_NEXT(emitSetLocal());
           case Expr::TeeLocal:
             CHECK_NEXT(emitTeeLocal());
--- a/js/src/asmjs/WasmBinary.cpp
+++ b/js/src/asmjs/WasmBinary.cpp
@@ -36,151 +36,15 @@ Decoder::fail(const char* msg, ...) {
         return false;
 
     return fail(Move(str));
 }
 
 bool
 Decoder::fail(UniqueChars msg) {
     MOZ_ASSERT(error_);
-    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s",
-                                          currentOffset(), msg.get()));
+    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
     if (!strWithOffset)
         return false;
 
     *error_ = Move(strWithOffset);
     return false;
 }
-
-bool
-wasm::DecodePreamble(Decoder& d)
-{
-    uint32_t u32;
-    if (!d.readFixedU32(&u32) || u32 != MagicNumber)
-        return d.fail("failed to match magic number");
-
-    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::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
-{
-    uint32_t numLocalEntries = 0;
-    ValType prev = ValType::Limit;
-    for (ValType t : locals) {
-        if (t != prev) {
-            numLocalEntries++;
-            prev = t;
-        }
-    }
-
-    if (!e.writeVarU32(numLocalEntries))
-        return false;
-
-    if (numLocalEntries) {
-        prev = locals[0];
-        uint32_t count = 1;
-        for (uint32_t i = 1; i < locals.length(); i++, count++) {
-            if (prev != locals[i]) {
-                if (!e.writeVarU32(count))
-                    return false;
-                if (!e.writeValType(prev))
-                    return false;
-                prev = locals[i];
-                count = 0;
-            }
-        }
-        if (!e.writeVarU32(count))
-            return false;
-        if (!e.writeValType(prev))
-            return false;
-    }
-
-    return true;
-}
-
-bool
-wasm::DecodeLocalEntries(Decoder& d, ValTypeVector* locals)
-{
-    uint32_t numLocalEntries;
-    if (!d.readVarU32(&numLocalEntries))
-        return false;
-
-    for (uint32_t i = 0; i < numLocalEntries; i++) {
-        uint32_t count;
-        if (!d.readVarU32(&count))
-            return false;
-
-        if (MaxLocals - locals->length() < count)
-            return false;
-
-        ValType type;
-        if (!d.readValType(&type))
-            return false;
-
-        if (!locals->appendN(type, count))
-            return false;
-    }
-
-    return true;
-}
-
-bool
-wasm::DecodeGlobalType(Decoder& d, ValType* type, uint32_t* flags)
-{
-    if (!d.readValType(type))
-        return d.fail("bad global type");
-
-    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");
-
-    return true;
-}
-
-bool
-wasm::DecodeLimits(Decoder& d, Limits* limits)
-{
-    uint32_t flags;
-    if (!d.readVarU32(&flags))
-        return d.fail("expected flags");
-
-    // TODO (bug 1310149): tighten this check (s/3/1) when the AngryBots demo
-    // gets updated.
-    if (flags & ~uint32_t(0x3))
-        return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x3)));
-
-    if (!d.readVarU32(&limits->initial))
-        return d.fail("expected initial length");
-
-    if (flags & 0x1) {
-        uint32_t maximum;
-        if (!d.readVarU32(&maximum))
-            return d.fail("expected maximum length");
-
-        if (limits->initial > maximum) {
-            return d.fail("memory size minimum must not be greater than maximum; "
-                          "maximum length %" PRIu32 " is less than initial length %" PRIu32 ,
-                          maximum, limits->initial);
-        }
-
-        limits->maximum.emplace(maximum);
-    }
-
-    return true;
-}
-
-bool
-wasm::DecodeUnknownSections(Decoder& d)
-{
-    while (!d.done()) {
-        if (!d.skipUserDefinedSection())
-            return false;
-    }
-
-    return true;
-}
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -63,17 +63,17 @@ using RawF32 = Raw<float>;
 // or WebAssembly is used.
 
 enum class Telemetry {
     ASMJS = 0,
     WASM = 1
 };
 
 static const uint32_t MagicNumber        = 0x6d736100; // "\0asm"
-static const uint32_t EncodingVersion    = 0x0c;
+static const uint32_t EncodingVersion    = 0x0d;
 
 enum class SectionId {
     UserDefined = 0,
     Type        = 1,
     Import      = 2,
     Function    = 3,
     Table       = 4,
     Memory      = 5,
@@ -82,254 +82,280 @@ enum class SectionId {
     Start       = 8,
     Elem        = 9,
     Code        = 10,
     Data        = 11
 };
 
 static const char NameSectionName[] = "name";
 
-enum class ValType
+enum class TypeCode
 {
-    I32                                  = 0x01,
-    I64                                  = 0x02,
-    F32                                  = 0x03,
-    F64                                  = 0x04,
+    I32                                  = 0x7f,  // SLEB128(-0x01)
+    I64                                  = 0x7e,  // SLEB128(-0x02)
+    F32                                  = 0x7d,  // SLEB128(-0x03)
+    F64                                  = 0x7c,  // SLEB128(-0x04)
+
+    // Only emitted internally for asm.js, likely to get collapsed into I128
+    I8x16                                = 0x7b,
+    I16x8                                = 0x7a,
+    I32x4                                = 0x79,
+    F32x4                                = 0x78,
+    B8x16                                = 0x77,
+    B16x8                                = 0x76,
+    B32x4                                = 0x75,
+
+    // A function pointer with any signature
+    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
+{
+    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
     // compiling asm.js and are rejected by wasm validation.
 
-    I8x16,
-    I16x8,
-    I32x4,
-    F32x4,
-    B8x16,
-    B16x8,
-    B32x4,
-
-    Limit
+    I8x16                                = uint8_t(TypeCode::I8x16),
+    I16x8                                = uint8_t(TypeCode::I16x8),
+    I32x4                                = uint8_t(TypeCode::I32x4),
+    F32x4                                = uint8_t(TypeCode::F32x4),
+    B8x16                                = uint8_t(TypeCode::B8x16),
+    B16x8                                = uint8_t(TypeCode::B16x8),
+    B32x4                                = uint8_t(TypeCode::B32x4)
 };
 
-enum class TypeConstructor
-{
-    AnyFunc                              = 0x20,
-    Function                             = 0x40
-};
+typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
 
 enum class DefinitionKind
 {
     Function                             = 0x00,
     Table                                = 0x01,
     Memory                               = 0x02,
     Global                               = 0x03
 };
 
 enum class GlobalFlags
 {
     IsMutable                            = 0x1,
     AllowedMask                          = 0x1
 };
 
-enum class Expr
+enum class MemoryTableFlags
+{
+    Default                              = 0x0
+};
+
+enum class Expr : uint32_t // fix type so we can cast from any u16 in decoder
 {
     // Control flow operators
     Unreachable                          = 0x00,
-    Block                                = 0x01,
-    Loop                                 = 0x02,
-    If                                   = 0x03,
-    Else                                 = 0x04,
-    Select                               = 0x05,
-    Br                                   = 0x06,
-    BrIf                                 = 0x07,
-    BrTable                              = 0x08,
-    Return                               = 0x09,
-    Nop                                  = 0x0a,
-    Drop                                 = 0x0b,
-    End                                  = 0x0f,
+    Nop                                  = 0x01,
+    Block                                = 0x02,
+    Loop                                 = 0x03,
+    If                                   = 0x04,
+    Else                                 = 0x05,
+    End                                  = 0x0b,
+    Br                                   = 0x0c,
+    BrIf                                 = 0x0d,
+    BrTable                              = 0x0e,
+    Return                               = 0x0f,
 
-    // Basic operators
-    I32Const                             = 0x10,
-    I64Const                             = 0x11,
-    F64Const                             = 0x12,
-    F32Const                             = 0x13,
-    GetLocal                             = 0x14,
-    SetLocal                             = 0x15,
-    Call                                 = 0x16,
-    CallIndirect                         = 0x17,
-    CallImport                           = 0x18,
-    TeeLocal                             = 0x19,
+    // Call operators
+    Call                                 = 0x10,
+    CallIndirect                         = 0x11,
+
+    // Parametric operators
+    Drop                                 = 0x1a,
+    Select                               = 0x1b,
+
+    // Variable access
+    GetLocal                             = 0x20,
+    SetLocal                             = 0x21,
+    TeeLocal                             = 0x22,
+    GetGlobal                            = 0x23,
+    SetGlobal                            = 0x24,
 
     // Memory-related operators
-    I32Load8S                            = 0x20,
-    I32Load8U                            = 0x21,
-    I32Load16S                           = 0x22,
-    I32Load16U                           = 0x23,
-    I64Load8S                            = 0x24,
-    I64Load8U                            = 0x25,
-    I64Load16S                           = 0x26,
-    I64Load16U                           = 0x27,
-    I64Load32S                           = 0x28,
-    I64Load32U                           = 0x29,
-    I32Load                              = 0x2a,
-    I64Load                              = 0x2b,
-    F32Load                              = 0x2c,
-    F64Load                              = 0x2d,
-    I32Store8                            = 0x2e,
-    I32Store16                           = 0x2f,
-    I64Store8                            = 0x30,
-    I64Store16                           = 0x31,
-    I64Store32                           = 0x32,
-    I32Store                             = 0x33,
-    I64Store                             = 0x34,
-    F32Store                             = 0x35,
-    F64Store                             = 0x36,
-    CurrentMemory                        = 0x3b,
-    GrowMemory                           = 0x39,
+    I32Load                              = 0x28,
+    I64Load                              = 0x29,
+    F32Load                              = 0x2a,
+    F64Load                              = 0x2b,
+    I32Load8S                            = 0x2c,
+    I32Load8U                            = 0x2d,
+    I32Load16S                           = 0x2e,
+    I32Load16U                           = 0x2f,
+    I64Load8S                            = 0x30,
+    I64Load8U                            = 0x31,
+    I64Load16S                           = 0x32,
+    I64Load16U                           = 0x33,
+    I64Load32S                           = 0x34,
+    I64Load32U                           = 0x35,
+    I32Store                             = 0x36,
+    I64Store                             = 0x37,
+    F32Store                             = 0x38,
+    F64Store                             = 0x39,
+    I32Store8                            = 0x3a,
+    I32Store16                           = 0x3b,
+    I64Store8                            = 0x3c,
+    I64Store16                           = 0x3d,
+    I64Store32                           = 0x3e,
+    CurrentMemory                        = 0x3f,
+    GrowMemory                           = 0x40,
 
-    // i32 operators
-    I32Add                               = 0x40,
-    I32Sub                               = 0x41,
-    I32Mul                               = 0x42,
-    I32DivS                              = 0x43,
-    I32DivU                              = 0x44,
-    I32RemS                              = 0x45,
-    I32RemU                              = 0x46,
-    I32And                               = 0x47,
-    I32Or                                = 0x48,
-    I32Xor                               = 0x49,
-    I32Shl                               = 0x4a,
-    I32ShrU                              = 0x4b,
-    I32ShrS                              = 0x4c,
-    I32Eq                                = 0x4d,
-    I32Ne                                = 0x4e,
-    I32LtS                               = 0x4f,
-    I32LeS                               = 0x50,
-    I32LtU                               = 0x51,
-    I32LeU                               = 0x52,
-    I32GtS                               = 0x53,
-    I32GeS                               = 0x54,
-    I32GtU                               = 0x55,
-    I32GeU                               = 0x56,
-    I32Clz                               = 0x57,
-    I32Ctz                               = 0x58,
-    I32Popcnt                            = 0x59,
-    I32Eqz                               = 0x5a,
+    // Constants
+    I32Const                             = 0x41,
+    I64Const                             = 0x42,
+    F32Const                             = 0x43,
+    F64Const                             = 0x44,
 
-    // i64 operators
-    I64Add                               = 0x5b,
-    I64Sub                               = 0x5c,
-    I64Mul                               = 0x5d,
-    I64DivS                              = 0x5e,
-    I64DivU                              = 0x5f,
-    I64RemS                              = 0x60,
-    I64RemU                              = 0x61,
-    I64And                               = 0x62,
-    I64Or                                = 0x63,
-    I64Xor                               = 0x64,
-    I64Shl                               = 0x65,
-    I64ShrU                              = 0x66,
-    I64ShrS                              = 0x67,
-    I64Eq                                = 0x68,
-    I64Ne                                = 0x69,
-    I64LtS                               = 0x6a,
-    I64LeS                               = 0x6b,
-    I64LtU                               = 0x6c,
-    I64LeU                               = 0x6d,
-    I64GtS                               = 0x6e,
-    I64GeS                               = 0x6f,
-    I64GtU                               = 0x70,
-    I64GeU                               = 0x71,
-    I64Clz                               = 0x72,
-    I64Ctz                               = 0x73,
-    I64Popcnt                            = 0x74,
+    // Comparison operators
+    I32Eqz                               = 0x45,
+    I32Eq                                = 0x46,
+    I32Ne                                = 0x47,
+    I32LtS                               = 0x48,
+    I32LtU                               = 0x49,
+    I32GtS                               = 0x4a,
+    I32GtU                               = 0x4b,
+    I32LeS                               = 0x4c,
+    I32LeU                               = 0x4d,
+    I32GeS                               = 0x4e,
+    I32GeU                               = 0x4f,
+    I64Eqz                               = 0x50,
+    I64Eq                                = 0x51,
+    I64Ne                                = 0x52,
+    I64LtS                               = 0x53,
+    I64LtU                               = 0x54,
+    I64GtS                               = 0x55,
+    I64GtU                               = 0x56,
+    I64LeS                               = 0x57,
+    I64LeU                               = 0x58,
+    I64GeS                               = 0x59,
+    I64GeU                               = 0x5a,
+    F32Eq                                = 0x5b,
+    F32Ne                                = 0x5c,
+    F32Lt                                = 0x5d,
+    F32Gt                                = 0x5e,
+    F32Le                                = 0x5f,
+    F32Ge                                = 0x60,
+    F64Eq                                = 0x61,
+    F64Ne                                = 0x62,
+    F64Lt                                = 0x63,
+    F64Gt                                = 0x64,
+    F64Le                                = 0x65,
+    F64Ge                                = 0x66,
 
-    // f32 opcodes
-    F32Add                               = 0x75,
-    F32Sub                               = 0x76,
-    F32Mul                               = 0x77,
-    F32Div                               = 0x78,
-    F32Min                               = 0x79,
-    F32Max                               = 0x7a,
-    F32Abs                               = 0x7b,
-    F32Neg                               = 0x7c,
-    F32CopySign                          = 0x7d,
-    F32Ceil                              = 0x7e,
-    F32Floor                             = 0x7f,
-    F32Trunc                             = 0x80,
-    F32Nearest                           = 0x81,
-    F32Sqrt                              = 0x82,
-    F32Eq                                = 0x83,
-    F32Ne                                = 0x84,
-    F32Lt                                = 0x85,
-    F32Le                                = 0x86,
-    F32Gt                                = 0x87,
-    F32Ge                                = 0x88,
-
-    // f64 opcodes
-    F64Add                               = 0x89,
-    F64Sub                               = 0x8a,
-    F64Mul                               = 0x8b,
-    F64Div                               = 0x8c,
-    F64Min                               = 0x8d,
-    F64Max                               = 0x8e,
-    F64Abs                               = 0x8f,
-    F64Neg                               = 0x90,
-    F64CopySign                          = 0x91,
-    F64Ceil                              = 0x92,
-    F64Floor                             = 0x93,
-    F64Trunc                             = 0x94,
-    F64Nearest                           = 0x95,
-    F64Sqrt                              = 0x96,
-    F64Eq                                = 0x97,
-    F64Ne                                = 0x98,
-    F64Lt                                = 0x99,
-    F64Le                                = 0x9a,
-    F64Gt                                = 0x9b,
-    F64Ge                                = 0x9c,
+    // Numeric operators
+    I32Clz                               = 0x67,
+    I32Ctz                               = 0x68,
+    I32Popcnt                            = 0x69,
+    I32Add                               = 0x6a,
+    I32Sub                               = 0x6b,
+    I32Mul                               = 0x6c,
+    I32DivS                              = 0x6d,
+    I32DivU                              = 0x6e,
+    I32RemS                              = 0x6f,
+    I32RemU                              = 0x70,
+    I32And                               = 0x71,
+    I32Or                                = 0x72,
+    I32Xor                               = 0x73,
+    I32Shl                               = 0x74,
+    I32ShrS                              = 0x75,
+    I32ShrU                              = 0x76,
+    I32Rotl                              = 0x77,
+    I32Rotr                              = 0x78,
+    I64Clz                               = 0x79,
+    I64Ctz                               = 0x7a,
+    I64Popcnt                            = 0x7b,
+    I64Add                               = 0x7c,
+    I64Sub                               = 0x7d,
+    I64Mul                               = 0x7e,
+    I64DivS                              = 0x7f,
+    I64DivU                              = 0x80,
+    I64RemS                              = 0x81,
+    I64RemU                              = 0x82,
+    I64And                               = 0x83,
+    I64Or                                = 0x84,
+    I64Xor                               = 0x85,
+    I64Shl                               = 0x86,
+    I64ShrS                              = 0x87,
+    I64ShrU                              = 0x88,
+    I64Rotl                              = 0x89,
+    I64Rotr                              = 0x8a,
+    F32Abs                               = 0x8b,
+    F32Neg                               = 0x8c,
+    F32Ceil                              = 0x8d,
+    F32Floor                             = 0x8e,
+    F32Trunc                             = 0x8f,
+    F32Nearest                           = 0x90,
+    F32Sqrt                              = 0x91,
+    F32Add                               = 0x92,
+    F32Sub                               = 0x93,
+    F32Mul                               = 0x94,
+    F32Div                               = 0x95,
+    F32Min                               = 0x96,
+    F32Max                               = 0x97,
+    F32CopySign                          = 0x98,
+    F64Abs                               = 0x99,
+    F64Neg                               = 0x9a,
+    F64Ceil                              = 0x9b,
+    F64Floor                             = 0x9c,
+    F64Trunc                             = 0x9d,
+    F64Nearest                           = 0x9e,
+    F64Sqrt                              = 0x9f,
+    F64Add                               = 0xa0,
+    F64Sub                               = 0xa1,
+    F64Mul                               = 0xa2,
+    F64Div                               = 0xa3,
+    F64Min                               = 0xa4,
+    F64Max                               = 0xa5,
+    F64CopySign                          = 0xa6,
 
     // Conversions
-    I32TruncSF32                         = 0x9d,
-    I32TruncSF64                         = 0x9e,
-    I32TruncUF32                         = 0x9f,
-    I32TruncUF64                         = 0xa0,
-    I32WrapI64                           = 0xa1,
-    I64TruncSF32                         = 0xa2,
-    I64TruncSF64                         = 0xa3,
-    I64TruncUF32                         = 0xa4,
-    I64TruncUF64                         = 0xa5,
-    I64ExtendSI32                        = 0xa6,
-    I64ExtendUI32                        = 0xa7,
-    F32ConvertSI32                       = 0xa8,
-    F32ConvertUI32                       = 0xa9,
-    F32ConvertSI64                       = 0xaa,
-    F32ConvertUI64                       = 0xab,
-    F32DemoteF64                         = 0xac,
-    F32ReinterpretI32                    = 0xad,
-    F64ConvertSI32                       = 0xae,
-    F64ConvertUI32                       = 0xaf,
-    F64ConvertSI64                       = 0xb0,
-    F64ConvertUI64                       = 0xb1,
-    F64PromoteF32                        = 0xb2,
-    F64ReinterpretI64                    = 0xb3,
-    I32ReinterpretF32                    = 0xb4,
-    I64ReinterpretF64                    = 0xb5,
+    I32WrapI64                           = 0xa7,
+    I32TruncSF32                         = 0xa8,
+    I32TruncUF32                         = 0xa9,
+    I32TruncSF64                         = 0xaa,
+    I32TruncUF64                         = 0xab,
+    I64ExtendSI32                        = 0xac,
+    I64ExtendUI32                        = 0xad,
+    I64TruncSF32                         = 0xae,
+    I64TruncUF32                         = 0xaf,
+    I64TruncSF64                         = 0xb0,
+    I64TruncUF64                         = 0xb1,
+    F32ConvertSI32                       = 0xb2,
+    F32ConvertUI32                       = 0xb3,
+    F32ConvertSI64                       = 0xb4,
+    F32ConvertUI64                       = 0xb5,
+    F32DemoteF64                         = 0xb6,
+    F64ConvertSI32                       = 0xb7,
+    F64ConvertUI32                       = 0xb8,
+    F64ConvertSI64                       = 0xb9,
+    F64ConvertUI64                       = 0xba,
+    F64PromoteF32                        = 0xbb,
 
-    // Bitwise rotates.
-    I32Rotr                              = 0xb6,
-    I32Rotl                              = 0xb7,
-    I64Rotr                              = 0xb8,
-    I64Rotl                              = 0xb9,
-
-    // i64.eqz.
-    I64Eqz                               = 0xba,
-
-    // Global access.
-    GetGlobal                            = 0xbb,
-    SetGlobal                            = 0xbc,
+    // Reinterpretations
+    I32ReinterpretF32                    = 0xbc,
+    I64ReinterpretF64                    = 0xbd,
+    F32ReinterpretI32                    = 0xbe,
+    F64ReinterpretI64                    = 0xbf,
 
     // ------------------------------------------------------------------------
     // The rest of these operators are currently only emitted internally when
     // compiling asm.js and are rejected by wasm validation.
 
     // asm.js-specific operators
     TeeGlobal                            = 0xc8,
     I32Min,
@@ -358,16 +384,21 @@ enum class Expr
     F64Exp,
     F64Log,
     F64Pow,
     F64Atan2,
 
     // asm.js-style call_indirect with the callee evaluated first.
     OldCallIndirect,
 
+    // asm.js-style call to an import; asm.js imports are not (and cannot be,
+    // due to streaming compilation and lazy discovery) injected into the
+    // function index space so Expr::Call cannot be used.
+    OldCallImport,
+
     // Atomics
     I32AtomicsCompareExchange,
     I32AtomicsExchange,
     I32AtomicsLoad,
     I32AtomicsStore,
     I32AtomicsBinOp,
 
     // SIMD
@@ -449,32 +480,34 @@ enum class Expr
     Limit
 };
 
 // The ExprType enum represents the type of a WebAssembly expression or return
 // value and may either be a value type or void. Soon, expression types will be
 // generalized to a list of ValType and this enum will go away, replaced,
 // wherever it is used, by a varU32 + list of ValType.
 
-enum class ExprType
+enum class ExprType : uint32_t // fix type so we can cast from any u8 in decoder
 {
-    Void  = 0x00,
-    I32   = uint8_t(ValType::I32),
-    I64   = uint8_t(ValType::I64),
-    F32   = uint8_t(ValType::F32),
-    F64   = uint8_t(ValType::F64),
-    I8x16 = uint8_t(ValType::I8x16),
-    I16x8 = uint8_t(ValType::I16x8),
-    I32x4 = uint8_t(ValType::I32x4),
-    F32x4 = uint8_t(ValType::F32x4),
-    B8x16 = uint8_t(ValType::B8x16),
-    B16x8 = uint8_t(ValType::B16x8),
-    B32x4 = uint8_t(ValType::B32x4),
+    Void  = uint8_t(TypeCode::BlockVoid),
+
+    I32   = uint8_t(TypeCode::I32),
+    I64   = uint8_t(TypeCode::I64),
+    F32   = uint8_t(TypeCode::F32),
+    F64   = uint8_t(TypeCode::F64),
 
-    Limit
+    I8x16 = uint8_t(TypeCode::I8x16),
+    I16x8 = uint8_t(TypeCode::I16x8),
+    I32x4 = uint8_t(TypeCode::I32x4),
+    F32x4 = uint8_t(TypeCode::F32x4),
+    B8x16 = uint8_t(TypeCode::B8x16),
+    B16x8 = uint8_t(TypeCode::B16x8),
+    B32x4 = uint8_t(TypeCode::B32x4),
+
+    Limit = uint8_t(TypeCode::Limit)
 };
 
 typedef int8_t I8x16[16];
 typedef int16_t I16x8[8];
 typedef int32_t I32x4[4];
 typedef float F32x4[4];
 typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytes;
 
@@ -606,22 +639,24 @@ 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(ValType::Limit) <= INT8_MAX, "fits");
-        return writeFixedU8(size_t(type));
+        static_assert(size_t(TypeCode::Max) <= INT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) <= size_t(TypeCode::Max));
+        return writeFixedU8(uint8_t(type));
     }
-    MOZ_MUST_USE bool writeExprType(ExprType type) {
-        static_assert(size_t(ExprType::Limit) <= INT8_MAX, "fits");
-        return writeFixedU8(size_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));
+        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));
         return writeFixedU8(UINT8_MAX) &&
                writeFixedU8(size_t(expr) - UINT8_MAX);
     }
@@ -795,16 +830,19 @@ class Decoder
         return size_t(end_ - cur_);
     }
     const uint8_t* currentPosition() const {
         return cur_;
     }
     size_t currentOffset() const {
         return cur_ - beg_;
     }
+    const uint8_t* begin() const {
+        return beg_;
+    }
 
     // Fixed-size encoding operations simply copy the literal bytes (without
     // attempting to align).
 
     MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
         return read<uint8_t>(i);
     }
     MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
@@ -847,37 +885,45 @@ class Decoder
     }
     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(ValType::Limit) <= INT8_MAX, "fits");
+        static_assert(uint8_t(TypeCode::Max) <= INT8_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);
             return true;
         }
         if (!readFixedU8(&u8))
             return false;
         if (u8 == UINT8_MAX)
             return false;
-        *expr = Expr(u8 + UINT8_MAX);
+        *expr = Expr(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_;
@@ -1067,37 +1113,12 @@ class Decoder
     }
     void uncheckedReadFixedF32x4(F32x4* f32x4) {
         struct T { F32x4 v; };
         T t = uncheckedRead<T>();
         memcpy(f32x4, &t, sizeof(t));
     }
 };
 
-// Reusable macro encoding/decoding functions reused by both the two
-// encoders (AsmJS/WasmText) and decoders (Wasm/WasmIonCompile).
-
-typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
-
-MOZ_MUST_USE bool
-DecodePreamble(Decoder& d);
-
-MOZ_MUST_USE bool
-EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
-
-MOZ_MUST_USE bool
-DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
-
-MOZ_MUST_USE bool
-DecodeGlobalType(Decoder& d, ValType* type, uint32_t* flags);
-
-struct Limits;
-
-MOZ_MUST_USE bool
-DecodeLimits(Decoder& d, Limits* limits);
-
-MOZ_MUST_USE bool
-DecodeUnknownSections(Decoder& d);
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_binary_h
copy from js/src/asmjs/WasmBinary.cpp
copy to js/src/asmjs/WasmBinaryFormat.cpp
--- a/js/src/asmjs/WasmBinary.cpp
+++ b/js/src/asmjs/WasmBinaryFormat.cpp
@@ -11,51 +11,24 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include "asmjs/WasmBinary.h"
-
-#include <stdarg.h>
+#include "asmjs/WasmBinaryFormat.h"
 
 #include "jsprf.h"
-#include "asmjs/WasmTypes.h"
 
 using namespace js;
 using namespace js::wasm;
 
 bool
-Decoder::fail(const char* msg, ...) {
-    va_list ap;
-    va_start(ap, msg);
-    UniqueChars str(JS_vsmprintf(msg, ap));
-    va_end(ap);
-    if (!str)
-        return false;
-
-    return fail(Move(str));
-}
-
-bool
-Decoder::fail(UniqueChars msg) {
-    MOZ_ASSERT(error_);
-    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s",
-                                          currentOffset(), msg.get()));
-    if (!strWithOffset)
-        return false;
-
-    *error_ = Move(strWithOffset);
-    return false;
-}
-
-bool
 wasm::DecodePreamble(Decoder& d)
 {
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
         return d.fail("failed to match magic number");
 
     if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
         return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
@@ -63,17 +36,17 @@ wasm::DecodePreamble(Decoder& d)
 
     return true;
 }
 
 bool
 wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
 {
     uint32_t numLocalEntries = 0;
-    ValType prev = ValType::Limit;
+    ValType prev = ValType(TypeCode::Limit);
     for (ValType t : locals) {
         if (t != prev) {
             numLocalEntries++;
             prev = t;
         }
     }
 
     if (!e.writeVarU32(numLocalEntries))
@@ -123,27 +96,92 @@ wasm::DecodeLocalEntries(Decoder& d, Val
         if (!locals->appendN(type, count))
             return false;
     }
 
     return true;
 }
 
 bool
-wasm::DecodeGlobalType(Decoder& d, ValType* type, uint32_t* flags)
+wasm::DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
 {
     if (!d.readValType(type))
         return d.fail("bad global type");
 
-    if (!d.readVarU32(flags))
+    uint32_t flags;
+    if (!d.readVarU32(&flags))
         return d.fail("expected global flags");
 
-    if (*flags & ~uint32_t(GlobalFlags::AllowedMask))
+    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))
+        return d.fail("failed to read initializer type");
+
+    switch (expr) {
+      case Expr::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: {
+        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: {
+        RawF32 f32;
+        if (!d.readFixedF32(&f32))
+            return d.fail("failed to read initializer f32 expression");
+        *init = InitExpr(Val(f32));
+        break;
+      }
+      case Expr::F64Const: {
+        RawF64 f64;
+        if (!d.readFixedF64(&f64))
+            return d.fail("failed to read initializer f64 expression");
+        *init = InitExpr(Val(f64));
+        break;
+      }
+      case Expr::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());
+        break;
+      }
+      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)
+        return d.fail("failed to read end of initializer expression");
+
     return true;
 }
 
 bool
 wasm::DecodeLimits(Decoder& d, Limits* limits)
 {
     uint32_t flags;
     if (!d.readVarU32(&flags))
@@ -159,27 +197,83 @@ wasm::DecodeLimits(Decoder& d, Limits* l
 
     if (flags & 0x1) {
         uint32_t maximum;
         if (!d.readVarU32(&maximum))
             return d.fail("expected maximum length");
 
         if (limits->initial > maximum) {
             return d.fail("memory size minimum must not be greater than maximum; "
-                          "maximum length %" PRIu32 " is less than initial length %" PRIu32 ,
+                          "maximum length %" PRIu32 " is less than initial length %" PRIu32,
                           maximum, limits->initial);
         }
 
         limits->maximum.emplace(maximum);
     }
 
     return true;
 }
 
 bool
+wasm::DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLength,
+                        const GlobalDescVector& globals, DataSegmentVector* segments)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Data, &sectionStart, &sectionSize, "data"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    if (!usesMemory)
+        return d.fail("data section requires a memory section");
+
+    uint32_t numSegments;
+    if (!d.readVarU32(&numSegments))
+        return d.fail("failed to read number of data segments");
+
+    if (numSegments > MaxDataSegments)
+        return d.fail("too many data segments");
+
+    for (uint32_t i = 0; i < numSegments; i++) {
+        uint32_t linearMemoryIndex;
+        if (!d.readVarU32(&linearMemoryIndex))
+            return d.fail("expected linear memory index");
+
+        if (linearMemoryIndex != 0)
+            return d.fail("linear memory index must currently be 0");
+
+        DataSegment seg;
+        if (!DecodeInitializerExpression(d, globals, ValType::I32, &seg.offset))
+            return false;
+
+        if (!d.readVarU32(&seg.length))
+            return d.fail("expected segment size");
+
+        if (seg.offset.isVal()) {
+            uint32_t off = seg.offset.val().i32();
+            if (off > minMemoryByteLength || minMemoryByteLength - off < seg.length)
+                return d.fail("data segment does not fit");
+        }
+
+        seg.bytecodeOffset = d.currentOffset();
+
+        if (!d.readBytes(seg.length))
+            return d.fail("data segment shorter than declared");
+
+        if (!segments->append(seg))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "data"))
+        return false;
+
+    return true;
+}
+
+bool
 wasm::DecodeUnknownSections(Decoder& d)
 {
     while (!d.done()) {
         if (!d.skipUserDefinedSection())
             return false;
     }
 
     return true;
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinaryFormat.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_binary_format_h
+#define wasm_binary_format_h
+
+#include "asmjs/WasmBinary.h"
+#include "asmjs/WasmTypes.h"
+
+namespace js {
+namespace wasm {
+
+// Reusable macro encoding/decoding functions reused by both the two
+// encoders (AsmJS/WasmTextToBinary) and all the decoders
+// (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
+
+MOZ_MUST_USE bool
+DecodePreamble(Decoder& d);
+
+MOZ_MUST_USE bool
+EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
+
+MOZ_MUST_USE bool
+DecodeLocalEntries(Decoder& d, 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);
+
+MOZ_MUST_USE bool
+DecodeLimits(Decoder& d, Limits* limits);
+
+MOZ_MUST_USE bool
+DecodeUnknownSections(Decoder& d);
+
+MOZ_MUST_USE bool
+DecodeDataSection(Decoder& d, bool usesMemory, uint32_t minMemoryByteLength,
+                  const GlobalDescVector& globals, DataSegmentVector* segments);
+
+} // namespace wasm
+} // namespace js
+
+#endif // wasm_binary_format_h
--- a/js/src/asmjs/WasmBinaryIterator.cpp
+++ b/js/src/asmjs/WasmBinaryIterator.cpp
@@ -105,17 +105,16 @@ wasm::Classify(Expr expr)
       case Expr::F32x4neg:
       case Expr::F32x4sqrt:
       case Expr::F32x4abs:
       case Expr::F32x4reciprocalApproximation:
       case Expr::F32x4reciprocalSqrtApproximation:
       case Expr::B8x16not:
       case Expr::B16x8not:
       case Expr::B32x4not:
-      case Expr::GrowMemory:
         return ExprKind::Unary;
       case Expr::I32Add:
       case Expr::I32Sub:
       case Expr::I32Mul:
       case Expr::I32DivS:
       case Expr::I32DivU:
       case Expr::I32RemS:
       case Expr::I32RemU:
@@ -349,23 +348,22 @@ wasm::Classify(Expr expr)
         return ExprKind::TeeLocal;
       case Expr::GetGlobal:
         return ExprKind::GetGlobal;
       case Expr::SetGlobal:
         return ExprKind::SetGlobal;
       case Expr::TeeGlobal:
         return ExprKind::TeeGlobal;
       case Expr::Call:
+      case Expr::OldCallImport:
         return ExprKind::Call;
       case Expr::CallIndirect:
         return ExprKind::CallIndirect;
       case Expr::OldCallIndirect:
         return ExprKind::OldCallIndirect;
-      case Expr::CallImport:
-        return ExprKind::CallImport;
       case Expr::Return:
       case Expr::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;
@@ -487,13 +485,15 @@ wasm::Classify(Expr expr)
       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::Nullary;
+        return ExprKind::CurrentMemory;
+      case Expr::GrowMemory:
+        return ExprKind::GrowMemory;
     }
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unimplemented opcode");
 }
 #endif
--- a/js/src/asmjs/WasmBinaryIterator.h
+++ b/js/src/asmjs/WasmBinaryIterator.h
@@ -63,27 +63,28 @@ enum class ExprKind {
     Nullary,
     Unary,
     Binary,
     Comparison,
     Conversion,
     Load,
     Store,
     TeeStore,
+    CurrentMemory,
+    GrowMemory,
     Select,
     GetLocal,
     SetLocal,
     TeeLocal,
     GetGlobal,
     SetGlobal,
     TeeGlobal,
     Call,
     CallIndirect,
     OldCallIndirect,
-    CallImport,
     Return,
     If,
     Else,
     End,
     AtomicLoad,
     AtomicStore,
     AtomicBinOp,
     AtomicCompareExchange,
@@ -187,17 +188,17 @@ class ControlStackEntry<Nothing>
 
 template <typename Value>
 class TypeAndValue
 {
     ValType type_;
     Value value_;
 
   public:
-    TypeAndValue() : type_(ValType::Limit), value_() {}
+    TypeAndValue() : type_(ValType(TypeCode::Limit)), value_() {}
     explicit TypeAndValue(ValType type)
       : type_(type), value_()
     {}
     TypeAndValue(ValType type, Value value)
       : type_(type), value_(value)
     {}
     ValType type() const {
         return type_;
@@ -212,17 +213,17 @@ class TypeAndValue
 
 // Specialization for when there is no additional data needed.
 template <>
 class TypeAndValue<Nothing>
 {
     ValType type_;
 
   public:
-    TypeAndValue() : type_(ValType::Limit) {}
+    TypeAndValue() : type_(ValType(TypeCode::Limit)) {}
     explicit TypeAndValue(ValType type) : type_(type) {}
 
     TypeAndValue(ValType type, Nothing value)
       : type_(type)
     {}
 
     ValType type() const { return type_; }
     Nothing value() const { return Nothing(); }
@@ -370,17 +371,17 @@ class MOZ_STACK_CLASS ExprIter : private
                 return fail("unrecognized atomic binop");
             }
         }
         *op = jit::AtomicOp(x);
         return true;
     }
 
     MOZ_MUST_USE bool readLinearMemoryAddress(uint32_t byteSize, LinearMemoryAddress<Value>* addr);
-    MOZ_MUST_USE bool readExprType(ExprType* expr);
+    MOZ_MUST_USE bool readBlockType(ExprType* expr);
 
     MOZ_MUST_USE bool typeMismatch(ExprType actual, ExprType expected) MOZ_COLD;
     MOZ_MUST_USE bool checkType(ValType actual, ValType expected);
     MOZ_MUST_USE bool checkType(ExprType actual, ExprType expected);
 
     MOZ_MUST_USE bool pushControl(LabelKind kind, ExprType type, bool reachable);
     MOZ_MUST_USE bool mergeControl(LabelKind* kind, ExprType* type, Value* value);
     MOZ_MUST_USE bool popControl(LabelKind* kind, ExprType* type, Value* value);
@@ -557,17 +558,18 @@ class MOZ_STACK_CLASS ExprIter : private
     MOZ_MUST_USE bool readComparison(ValType operandType, Value* lhs, Value* rhs);
     MOZ_MUST_USE bool readLoad(ValType resultType, uint32_t byteSize,
                                LinearMemoryAddress<Value>* addr);
     MOZ_MUST_USE bool readStore(ValType resultType, uint32_t byteSize,
                                 LinearMemoryAddress<Value>* addr, Value* value);
     MOZ_MUST_USE bool readTeeStore(ValType resultType, uint32_t byteSize,
                                    LinearMemoryAddress<Value>* addr, Value* value);
     MOZ_MUST_USE bool readNop();
-    MOZ_MUST_USE bool readNullary(ValType retType);
+    MOZ_MUST_USE bool readCurrentMemory();
+    MOZ_MUST_USE bool readGrowMemory(Value* input);
     MOZ_MUST_USE bool readSelect(ValType* type,
                                  Value* trueValue, Value* falseValue, Value* condition);
     MOZ_MUST_USE bool readGetLocal(const ValTypeVector& locals, uint32_t* id);
     MOZ_MUST_USE bool readSetLocal(const ValTypeVector& locals, uint32_t* id, Value* value);
     MOZ_MUST_USE bool readTeeLocal(const ValTypeVector& locals, uint32_t* id, Value* value);
     MOZ_MUST_USE bool readGetGlobal(const GlobalDescVector& globals, uint32_t* id);
     MOZ_MUST_USE bool readSetGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value);
     MOZ_MUST_USE bool readTeeGlobal(const GlobalDescVector& globals, uint32_t* id, Value* value);
@@ -580,17 +582,16 @@ class MOZ_STACK_CLASS ExprIter : private
     MOZ_MUST_USE bool readI32x4Const(I32x4* i32x4);
     MOZ_MUST_USE bool readF32x4Const(F32x4* f32x4);
     MOZ_MUST_USE bool readB8x16Const(I8x16* i8x16);
     MOZ_MUST_USE bool readB16x8Const(I16x8* i16x8);
     MOZ_MUST_USE bool readB32x4Const(I32x4* i32x4);
     MOZ_MUST_USE bool readCall(uint32_t* calleeIndex);
     MOZ_MUST_USE bool readCallIndirect(uint32_t* sigIndex, Value* callee);
     MOZ_MUST_USE bool readOldCallIndirect(uint32_t* sigIndex);
-    MOZ_MUST_USE bool readCallImport(uint32_t* importIndex);
     MOZ_MUST_USE bool readCallArg(ValType type, uint32_t numArgs, uint32_t argIndex, Value* arg);
     MOZ_MUST_USE bool readCallArgsEnd(uint32_t numArgs);
     MOZ_MUST_USE bool readOldCallIndirectCallee(Value* callee);
     MOZ_MUST_USE bool readCallReturn(ExprType ret);
     MOZ_MUST_USE bool readAtomicLoad(LinearMemoryAddress<Value>* addr,
                                      Scalar::Type* viewType);
     MOZ_MUST_USE bool readAtomicStore(LinearMemoryAddress<Value>* addr,
                                       Scalar::Type* viewType,
@@ -804,26 +805,40 @@ ExprIter<Policy>::popControl(LabelKind* 
     if (!reachable_ && !controlStack_.empty())
         valueStack_.shrinkTo(controlStack_.back().valueStackStart());
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readExprType(ExprType* type)
+ExprIter<Policy>::readBlockType(ExprType* type)
 {
-    uint8_t byte;
-    if (!readFixedU8(&byte))
+    if (!d_.readBlockType(type))
         return fail("unable to read block signature");
 
-    if (Validate && byte >= uint8_t(ExprType::Limit))
-        return fail("invalid inline type");
-
-    *type = ExprType(byte);
+    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:
+            break;
+          default:
+            return fail("invalid inline block type");
+        }
+    }
 
     return true;
 }
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::readExpr(Expr* expr)
 {
@@ -893,43 +908,43 @@ ExprIter<Policy>::readReturn(Value* valu
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::readBlock()
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::Block);
 
     ExprType type = ExprType::Limit;
-    if (!readExprType(&type))
+    if (!readBlockType(&type))
         return false;
 
     return pushControl(LabelKind::Block, type, false);
 }
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::readLoop()
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::Loop);
 
     ExprType type = ExprType::Limit;
-    if (!readExprType(&type))
+    if (!readBlockType(&type))
         return false;
 
     return pushControl(LabelKind::Loop, type, reachable_);
 }
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::readIf(Value* condition)
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::If);
 
     ExprType type = ExprType::Limit;
-    if (!readExprType(&type))
+    if (!readBlockType(&type))
         return false;
 
     if (MOZ_LIKELY(reachable_)) {
         if (!popWithType(ValType::I32, condition))
             return false;
 
         return pushControl(LabelKind::Then, type, false);
     }
@@ -1331,23 +1346,51 @@ ExprIter<Policy>::readNop()
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::Nop);
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readNullary(ValType retType)
+ExprIter<Policy>::readCurrentMemory()
 {
-    MOZ_ASSERT(Classify(expr_) == ExprKind::Nullary);
+    MOZ_ASSERT(Classify(expr_) == ExprKind::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;
+}
 
-    if (!push(retType))
+template <typename Policy>
+inline bool
+ExprIter<Policy>::readGrowMemory(Value* input)
+{
+    MOZ_ASSERT(Classify(expr_) == ExprKind::GrowMemory);
+
+    uint32_t flags;
+    if (!readVarU32(&flags))
         return false;
 
+    if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+        return fail("unexpected flags");
+
+    if (!popWithType(ValType::I32, input))
+        return false;
+
+    infalliblePush(ValType::I32);
+
     return true;
 }
 
 template <typename Policy>
 inline bool
 ExprIter<Policy>::readSelect(ValType* type, Value* trueValue, Value* falseValue, Value* condition)
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::Select);
@@ -1708,16 +1751,23 @@ template <typename Policy>
 inline bool
 ExprIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee)
 {
     MOZ_ASSERT(Classify(expr_) == ExprKind::CallIndirect);
 
     if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
+    uint32_t flags;
+    if (!readVarU32(&flags))
+        return false;
+
+    if (Validate && flags != uint32_t(MemoryTableFlags::Default))
+        return fail("unexpected flags");
+
     if (reachable_) {
         if (!popWithType(ValType::I32, callee))
             return false;
     }
 
     return true;
 }
 
@@ -1730,28 +1780,16 @@ ExprIter<Policy>::readOldCallIndirect(ui
     if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
     return true;
 }
 
 template <typename Policy>
 inline bool
-ExprIter<Policy>::readCallImport(uint32_t* importIndex)
-{
-    MOZ_ASSERT(Classify(expr_) == ExprKind::CallImport);
-
-    if (!readVarU32(importIndex))
-        return fail("unable to read call_import import index");
-
-    return true;
-}
-
-template <typename Policy>
-inline bool
 ExprIter<Policy>::readCallArg(ValType type, uint32_t numArgs, uint32_t argIndex, Value* arg)
 {
     MOZ_ASSERT(reachable_);
 
     TypeAndValue<Value> tv;
 
     if (!peek(numArgs - argIndex, &tv))
         return false;
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -19,16 +19,17 @@
 #include "asmjs/WasmBinaryToAST.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Sprintf.h"
 
 #include "jscntxt.h"
 
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmBinaryIterator.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
 using mozilla::FloorLog2;
 
@@ -91,16 +92,17 @@ class AstDecodeContext
 
   private:
     AstModule& module_;
     AstIndexVector funcSigs_;
     AstDecodeExprIter *iter_;
     AstDecodeStack exprs_;
     DepthStack depths_;
     const ValTypeVector* locals_;
+    GlobalDescVector globals_;
     AstNameVector blockLabels_;
     uint32_t currentLabelIndex_;
     ExprType retType_;
 
   public:
     AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module,
                      bool generateNames)
      : cx(cx),
@@ -118,19 +120,38 @@ class AstDecodeContext
        retType_(ExprType::Limit)
     {}
 
     AstModule& module() { return module_; }
     AstIndexVector& funcSigs() { return funcSigs_; }
     AstDecodeExprIter& iter() { return *iter_; }
     AstDecodeStack& exprs() { return exprs_; }
     DepthStack& depths() { return depths_; }
-    const ValTypeVector& locals() { return *locals_; }
+
     AstNameVector& blockLabels() { return blockLabels_; }
+
     ExprType retType() const { return retType_; }
+    const ValTypeVector& locals() const { return *locals_; }
+
+    bool addGlobalDesc(ValType type, bool isMutable, bool isImport) {
+        if (isImport)
+            return globals_.append(GlobalDesc(type, isMutable, globals_.length()));
+        // No need to have the precise init expr value; we just need the right
+        // type.
+        Val dummy;
+        switch (type) {
+          case ValType::I32: dummy = Val(uint32_t(0)); break;
+          case ValType::I64: dummy = Val(uint64_t(0)); break;
+          case ValType::F32: dummy = Val(RawF32(0.f)); break;
+          case ValType::F64: dummy = Val(RawF64(0.0)); break;
+          default:           return false;
+        }
+        return globals_.append(GlobalDesc(InitExpr(dummy), isMutable));
+    }
+    const GlobalDescVector& globalDescs() const { return globals_; }
 
     void popBack() { return exprs().popBack(); }
     AstDecodeStackItem popCopy() { return exprs().popCopy(); }
     AstDecodeStackItem& top() { return exprs().back(); }
     MOZ_MUST_USE bool push(AstDecodeStackItem item) { return exprs().append(item); }
 
     bool needFirst() {
         for (size_t i = depths().back(); i < exprs().length(); ++i) {
@@ -300,16 +321,19 @@ AstDecodeDrop(AstDecodeContext& c)
 
 static bool
 AstDecodeCall(AstDecodeContext& c)
 {
     uint32_t calleeIndex;
     if (!c.iter().readCall(&calleeIndex))
         return false;
 
+    if (!c.iter().inReachableCode())
+        return true;
+
     uint32_t sigIndex;
     AstRef funcRef;
     if (calleeIndex < c.module().funcImportNames().length()) {
         AstImport* import = c.module().imports()[calleeIndex];
         sigIndex = import->funcSig().index();
         funcRef = AstRef(import->name(), AstNoIndex);
     } else {
         uint32_t funcDefIndex = calleeIndex - c.module().funcImportNames().length();
@@ -317,19 +341,16 @@ AstDecodeCall(AstDecodeContext& c)
             return c.iter().fail("callee index out of range");
 
         sigIndex = c.funcSigs()[funcDefIndex];
 
         if (!AstDecodeGenerateRef(c, AstName(u"func"), calleeIndex, &funcRef))
             return false;
     }
 
-    if (!c.iter().inReachableCode())
-        return false;
-
     const AstSig* sig = c.module().sigs()[sigIndex];
 
     AstExprVector args(c.lifo);
     if (!AstDecodeCallArgs(c, *sig, &args))
         return false;
 
     if (!AstDecodeCallReturn(c, *sig))
         return false;
@@ -350,22 +371,22 @@ AstDecodeCall(AstDecodeContext& c)
 
 static bool
 AstDecodeCallIndirect(AstDecodeContext& c)
 {
     uint32_t sigIndex;
     if (!c.iter().readCallIndirect(&sigIndex, nullptr))
         return false;
 
+    if (!c.iter().inReachableCode())
+        return true;
+
     if (sigIndex >= c.module().sigs().length())
         return c.iter().fail("signature index out of range");
 
-    if (!c.iter().inReachableCode())
-        return false;
-
     AstDecodeStackItem index = c.popCopy();
 
     AstRef sigRef;
     if (!AstDecodeGenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
         return false;
 
     const AstSig* sig = c.module().sigs()[sigIndex];
     AstExprVector args(c.lifo);
@@ -386,56 +407,16 @@ AstDecodeCallIndirect(AstDecodeContext& 
 
     if (!c.push(AstDecodeStackItem(result)))
         return false;
 
     return true;
 }
 
 static bool
-AstDecodeCallImport(AstDecodeContext& c)
-{
-    uint32_t importIndex;
-    if (!c.iter().readCallImport(&importIndex))
-        return false;
-
-    if (importIndex >= c.module().imports().length())
-        return c.iter().fail("import index out of range");
-
-    if (!c.iter().inReachableCode())
-        return false;
-
-    AstImport* import = c.module().imports()[importIndex];
-    AstSig* sig = c.module().sigs()[import->funcSig().index()];
-    AstRef funcRef;
-    if (!AstDecodeGenerateRef(c, AstName(u"import"), importIndex, &funcRef))
-        return false;
-
-    AstExprVector args(c.lifo);
-    if (!AstDecodeCallArgs(c, *sig, &args))
-        return false;
-
-    if (!AstDecodeCallReturn(c, *sig))
-        return false;
-
-    AstCall* call = new(c.lifo) AstCall(Expr::CallImport, sig->ret(), funcRef, Move(args));
-    if (!call)
-        return false;
-
-    AstExpr* result = call;
-    if (IsVoid(sig->ret()))
-        result = c.handleVoidExpr(call);
-
-    if (!c.push(AstDecodeStackItem(result)))
-        return false;
-
-    return true;
-}
-
-static bool
 AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref)
 {
     if (!c.generateNames || depth >= c.blockLabels().length()) {
         // Also ignoring if it's a function body label.
         *ref = AstRef(AstName(), depth);
         return true;
     }
 
@@ -687,32 +668,16 @@ AstDecodeUnary(AstDecodeContext& c, ValT
 
     if (!c.push(AstDecodeStackItem(unary)))
         return false;
 
     return true;
 }
 
 static bool
-AstDecodeNullary(AstDecodeContext& c, ValType type, Expr expr)
-{
-    if (!c.iter().readNullary(type))
-        return false;
-
-    AstNullaryOperator* nullary = new(c.lifo) AstNullaryOperator(expr);
-    if (!nullary)
-        return false;
-
-    if (!c.push(AstDecodeStackItem(nullary)))
-        return false;
-
-    return true;
-}
-
-static bool
 AstDecodeBinary(AstDecodeContext& c, ValType type, Expr expr)
 {
     if (!c.iter().readBinary(type, nullptr, nullptr))
         return false;
 
     AstDecodeStackItem rhs = c.popCopy();
     AstDecodeStackItem lhs = c.popCopy();
 
@@ -830,16 +795,50 @@ AstDecodeStore(AstDecodeContext& c, ValT
 
     if (!c.push(AstDecodeStackItem(wrapped)))
         return false;
 
     return true;
 }
 
 static bool
+AstDecodeCurrentMemory(AstDecodeContext& c)
+{
+    if (!c.iter().readCurrentMemory())
+        return false;
+
+    AstCurrentMemory* gm = new(c.lifo) AstCurrentMemory();
+    if (!gm)
+        return false;
+
+    if (!c.push(AstDecodeStackItem(gm)))
+        return false;
+
+    return true;
+}
+
+static bool
+AstDecodeGrowMemory(AstDecodeContext& c)
+{
+    if (!c.iter().readGrowMemory(nullptr))
+        return false;
+
+    AstDecodeStackItem op = c.popCopy();
+
+    AstGrowMemory* gm = new(c.lifo) AstGrowMemory(op.expr);
+    if (!gm)
+        return false;
+
+    if (!c.push(AstDecodeStackItem(gm)))
+        return false;
+
+    return true;
+}
+
+static bool
 AstDecodeBranch(AstDecodeContext& c, Expr expr)
 {
     MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
 
     uint32_t depth;
     ExprType type;
     AstDecodeStackItem value;
     AstDecodeStackItem cond;
@@ -939,16 +938,64 @@ AstDecodeTeeLocal(AstDecodeContext& c)
 
     if (!c.push(AstDecodeStackItem(teeLocal)))
         return false;
 
     return true;
 }
 
 static bool
+AstDecodeGetGlobal(AstDecodeContext& c)
+{
+    uint32_t globalId;
+    if (!c.iter().readGetGlobal(c.globalDescs(), &globalId))
+        return false;
+
+    AstRef globalRef;
+    if (!AstDecodeGenerateRef(c, AstName(u"global"), globalId, &globalRef))
+        return false;
+
+    auto* getGlobal = new(c.lifo) AstGetGlobal(globalRef);
+    if (!getGlobal)
+        return false;
+
+    if (!c.push(AstDecodeStackItem(getGlobal)))
+        return false;
+
+    return true;
+}
+
+static bool
+AstDecodeSetGlobal(AstDecodeContext& c)
+{
+    uint32_t globalId;
+    if (!c.iter().readSetGlobal(c.globalDescs(), &globalId, nullptr))
+        return false;
+
+    AstDecodeStackItem value = c.popCopy();
+
+    AstRef globalRef;
+    if (!AstDecodeGenerateRef(c, AstName(u"global"), globalId, &globalRef))
+        return false;
+
+    auto* setGlobal = new(c.lifo) AstSetGlobal(globalRef, *value.expr);
+    if (!setGlobal)
+        return false;
+
+    AstExpr* expr = c.handleVoidExpr(setGlobal);
+    if (!expr)
+        return false;
+
+    if (!c.push(AstDecodeStackItem(expr)))
+        return false;
+
+    return true;
+}
+
+static bool
 AstDecodeReturn(AstDecodeContext& c)
 {
     if (!c.iter().readReturn(nullptr))
         return false;
 
     AstDecodeStackItem result;
     if (!IsVoid(c.retType()))
        result = c.popCopy();
@@ -984,20 +1031,16 @@ AstDecodeExpr(AstDecodeContext& c)
       case Expr::Call:
         if (!AstDecodeCall(c))
             return false;
         break;
       case Expr::CallIndirect:
         if (!AstDecodeCallIndirect(c))
             return false;
         break;
-      case Expr::CallImport:
-        if (!AstDecodeCallImport(c))
-            return false;
-        break;
       case Expr::I32Const:
         int32_t i32;
         if (!c.iter().readI32Const(&i32))
             return false;
         tmp = new(c.lifo) AstConst(Val((uint32_t)i32));
         if (!tmp || !c.push(AstDecodeStackItem(tmp)))
             return false;
         break;
@@ -1058,17 +1101,16 @@ AstDecodeExpr(AstDecodeContext& c)
         break;
       case Expr::End:
         if (!AstDecodeEnd(c))
             return false;
         break;
       case Expr::I32Clz:
       case Expr::I32Ctz:
       case Expr::I32Popcnt:
-      case Expr::GrowMemory:
         if (!AstDecodeUnary(c, ValType::I32, expr))
             return false;
         break;
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
         if (!AstDecodeUnary(c, ValType::I64, expr))
             return false;
@@ -1193,16 +1235,17 @@ AstDecodeExpr(AstDecodeContext& c)
         if (!AstDecodeComparison(c, ValType::F64, expr))
             return false;
         break;
       case Expr::I32Eqz:
         if (!AstDecodeConversion(c, ValType::I32, ValType::I32, expr))
             return false;
         break;
       case Expr::I64Eqz:
+      case Expr::I32WrapI64:
         if (!AstDecodeConversion(c, ValType::I64, ValType::I32, expr))
             return false;
         break;
       case Expr::I32TruncSF32:
       case Expr::I32TruncUF32:
       case Expr::I32ReinterpretF32:
         if (!AstDecodeConversion(c, ValType::F32, ValType::I32, expr))
             return false;
@@ -1330,33 +1373,45 @@ AstDecodeExpr(AstDecodeContext& c)
       case Expr::F32Store:
         if (!AstDecodeStore(c, ValType::F32, 4, expr))
             return false;
         break;
       case Expr::F64Store:
         if (!AstDecodeStore(c, ValType::F64, 8, expr))
             return false;
         break;
+      case Expr::CurrentMemory:
+        if (!AstDecodeCurrentMemory(c))
+            return false;
+        break;
+      case Expr::GrowMemory:
+        if (!AstDecodeGrowMemory(c))
+            return false;
+        break;
+      case Expr::SetGlobal:
+        if (!AstDecodeSetGlobal(c))
+            return false;
+        break;
+      case Expr::GetGlobal:
+        if (!AstDecodeGetGlobal(c))
+            return false;
+        break;
       case Expr::Br:
       case Expr::BrIf:
         if (!AstDecodeBranch(c, expr))
             return false;
         break;
       case Expr::BrTable:
         if (!AstDecodeBrTable(c))
             return false;
         break;
       case Expr::Return:
         if (!AstDecodeReturn(c))
             return false;
         break;
-      case Expr::CurrentMemory:
-        if (!AstDecodeNullary(c, ValType::I32, expr))
-            return false;
-        break;
       case Expr::Unreachable:
         if (!c.iter().readUnreachable())
             return false;
         tmp = new(c.lifo) AstUnreachable();
         if (!tmp)
             return false;
         if (!c.push(AstDecodeStackItem(tmp)))
             return false;
@@ -1389,17 +1444,17 @@ AstDecodeTypeSection(AstDecodeContext& c
     if (!c.d.readVarU32(&numSigs))
         return c.d.fail("expected number of signatures");
 
     if (numSigs > MaxSigs)
         return c.d.fail("too many signatures");
 
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
         uint32_t form;
-        if (!c.d.readVarU32(&form) || form != uint32_t(TypeConstructor::Function))
+        if (!c.d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
             return c.d.fail("expected function form");
 
         uint32_t numArgs;
         if (!c.d.readVarU32(&numArgs))
             return c.d.fail("bad number of function args");
 
         if (numArgs > MaxArgsPerFunc)
             return c.d.fail("too many arguments in signature");
@@ -1503,17 +1558,17 @@ AstDecodeTableSection(AstDecodeContext& 
 
     if (numTables != 1)
         return c.d.fail("the number of tables must be exactly one");
 
     uint32_t typeConstructorValue;
     if (!c.d.readVarU32(&typeConstructorValue))
         return c.d.fail("expected type constructor kind");
 
-    if (typeConstructorValue != uint32_t(TypeConstructor::AnyFunc))
+    if (typeConstructorValue != uint32_t(TypeCode::AnyFunc))
         return c.d.fail("unknown type constructor kind");
 
     Limits table;
     if (!DecodeLimits(c.d, &table))
         return false;
 
     if (table.initial > MaxTableElems)
         return c.d.fail("too many table elements");
@@ -1548,17 +1603,17 @@ AstDecodeName(AstDecodeContext& c, AstNa
 
 static bool
 AstDecodeLimitsTable(AstDecodeContext& c, Limits* limits)
 {
     uint32_t kind;
     if (!c.d.readVarU32(&kind))
         return false;
 
-    if (kind != uint32_t(TypeConstructor::AnyFunc))
+    if (kind != uint32_t(TypeCode::AnyFunc))
         return c.d.fail("unknown type constructor kind");
 
     if (!DecodeLimits(c.d, limits))
         return false;
 
     return true;
 }
 
@@ -1571,60 +1626,72 @@ AstDecodeImport(AstDecodeContext& c, uin
 
     if (moduleName.empty())
         return c.d.fail("module name cannot be empty");
 
     AstName fieldName;
     if (!AstDecodeName(c, &fieldName))
         return c.d.fail("expected import field name");
 
-    AstName importName;
-    if (!AstDecodeGenerateName(c, AstName(u"import"), importIndex, &importName))
-        return false;
-
     uint32_t kind;
     if (!c.d.readVarU32(&kind))
         return c.d.fail("expected import kind");
 
     switch (kind) {
       case uint32_t(DefinitionKind::Function): {
+        AstName importName;
+        if (!AstDecodeGenerateName(c, AstName(u"import"), importIndex, &importName))
+            return false;
+
         uint32_t sigIndex = AstNoIndex;
         if (!AstDecodeSignatureIndex(c, &sigIndex))
             return false;
 
         AstRef sigRef;
         if (!AstDecodeGenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
             return false;
 
         *import = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef);
         break;
       }
       case uint32_t(DefinitionKind::Global): {
-        ValType type;
-        if (!c.d.readValType(&type))
+        AstName importName;
+        if (!AstDecodeGenerateName(c, AstName(u"global"), importIndex, &importName))
             return false;
 
-        uint32_t flags;
-        if (!c.d.readVarU32(&flags))
+        ValType type;
+        bool isMutable;
+        if (!DecodeGlobalType(c.d, &type, &isMutable))
+            return false;
+
+        if (!c.addGlobalDesc(type, isMutable, /* import */ true))
             return false;
 
         *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
-                                        AstGlobal(importName, type, flags));
+                                        AstGlobal(importName, type, isMutable));
         break;
       }
       case uint32_t(DefinitionKind::Table): {
+        AstName importName;
+        if (!AstDecodeGenerateName(c, AstName(u"table"), importIndex, &importName))
+            return false;
+
         Limits table;
         if (!AstDecodeLimitsTable(c, &table))
             return false;
 
         *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
                                         DefinitionKind::Table, table);
         break;
       }
       case uint32_t(DefinitionKind::Memory): {
+        AstName importName;
+        if (!AstDecodeGenerateName(c, AstName(u"memory"), importIndex, &importName))
+            return false;
+
         Limits memory;
         if (!DecodeLimits(c.d, &memory))
             return false;
 
         *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
                                         DefinitionKind::Memory, memory);
         break;
       }
@@ -1690,117 +1757,85 @@ AstDecodeMemorySection(AstDecodeContext&
 
     if (!c.d.finishSection(sectionStart, sectionSize, "memory"))
         return false;
 
     c.module().setMemory(memory);
     return true;
 }
 
-static bool
-AstDecodeInitializerExpression(AstDecodeContext& c, AstExpr** init, ValType type)
+static AstExpr*
+ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr)
 {
-    Expr expr;
-    if (!c.d.readExpr(&expr))
-        return c.d.fail("missing initializer opcode");
-
-    switch (expr) {
-      case Expr::I32Const: {
-        int32_t i32;
-        if (!c.d.readVarS32(&i32))
-            return c.d.fail("missing initializer value");
-        *init = new(c.lifo) AstConst(Val((uint32_t)i32));
-        if (!*init)
-            return false;
-        if (type != ValType::I32)
-            return c.d.fail("initializer expression has incorrect type");
-        break;
+    switch (initExpr.kind()) {
+      case InitExpr::Kind::Constant: {
+        return new(c.lifo) AstConst(Val(initExpr.val()));
       }
-      case Expr::I64Const: {
-        int64_t i64;
-        if (!c.d.readVarS64(&i64))
-            return c.d.fail("missing initializer value");
-        *init = new(c.lifo) AstConst(Val((uint64_t)i64));
-        if (!*init)
-            return false;
-        if (type != ValType::I64)
-            return c.d.fail("initializer expression has incorrect type");
-        break;
+      case InitExpr::Kind::GetGlobal: {
+        AstRef globalRef;
+        if (!AstDecodeGenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef))
+            return nullptr;
+        return new(c.lifo) AstGetGlobal(globalRef);
       }
-      case Expr::F32Const: {
-        RawF32 f32;
-        if (!c.d.readFixedF32(&f32))
-            return c.d.fail("missing initializer value");
-        *init = new(c.lifo) AstConst(Val(f32));
-        if (!*init)
-            return false;
-        if (type != ValType::F32)
-            return c.d.fail("initializer expression has incorrect type");
-        break;
-      }
-      case Expr::F64Const: {
-        RawF64 f64;
-        if (!c.d.readFixedF64(&f64))
-            return c.d.fail("missing initializer value");
-        *init = new(c.lifo) AstConst(Val(f64));
-        if (!*init)
-            return false;
-        if (type != ValType::F64)
-            return c.d.fail("initializer expression has incorrect type");
-        break;
-      }
-      default:
-        return c.d.fail("unknown initializer opcode");
     }
-
-    if (!c.d.readExpr(&expr))
-        return c.d.fail("missing initializer end");
+    return nullptr;
+}
 
-    if (expr != Expr::End)
-        return c.d.fail("initializer end isn't an end");
+static bool
+AstDecodeInitializerExpression(AstDecodeContext& c, ValType type, AstExpr** init)
+{
+    InitExpr initExpr;
+    if (!DecodeInitializerExpression(c.d, c.globalDescs(), type, &initExpr))
+        return false;
 
-    return true;
+    *init = ToAstExpr(c, initExpr);
+    return !!*init;
 }
 
 static bool
 AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
 {
     AstName name;
     if (!AstDecodeGenerateName(c, AstName(u"global"), i, &name))
         return false;
 
     ValType type;
-    uint32_t flags;
-    if (!DecodeGlobalType(c.d, &type, &flags))
+    bool isMutable;
+    if (!DecodeGlobalType(c.d, &type, &isMutable))
         return false;
 
     AstExpr* init;
-    if (!AstDecodeInitializerExpression(c, &init, type))
-        return c.d.fail("missing initializer expression");
+    if (!AstDecodeInitializerExpression(c, type, &init))
+        return false;
 
-    *global = AstGlobal(name, type, flags, Some(init));
+    if (!c.addGlobalDesc(type, isMutable, /* import */ false))
+        return false;
+
+    *global = AstGlobal(name, type, isMutable, Some(init));
     return true;
 }
 
 static bool
 AstDecodeGlobalSection(AstDecodeContext& c)
 {
     uint32_t sectionStart, sectionSize;
     if (!c.d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numGlobals;
     if (!c.d.readVarU32(&numGlobals))
         return c.d.fail("expected number of globals");
 
+    uint32_t numImported = c.globalDescs().length();
+
     for (uint32_t i = 0; i < numGlobals; i++) {
         auto* global = new(c.lifo) AstGlobal;
-        if (!AstDecodeGlobal(c, i, global))
+        if (!AstDecodeGlobal(c, i + numImported, global))
             return false;
         if (!c.module().append(global))
             return false;
     }
 
     if (!c.d.finishSection(sectionStart, sectionSize, "global"))
         return false;
 
@@ -1989,65 +2024,41 @@ AstDecodeCodeSection(AstDecodeContext &c
         return false;
 
     return true;
 }
 
 static bool
 AstDecodeDataSection(AstDecodeContext &c)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(SectionId::Data, &sectionStart, &sectionSize, "data"))
+    DataSegmentVector segments;
+    bool hasMemory = c.module().hasMemory();
+    uint32_t memByteLength = hasMemory ? c.module().memory().initial * PageSize : 0;
+    if (!DecodeDataSection(c.d, hasMemory, memByteLength, c.globalDescs(), &segments))
         return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numSegments;
-    if (!c.d.readVarU32(&numSegments))
-        return c.d.fail("failed to read number of data segments");
-
-    const uint32_t heapLength = c.module().hasMemory() ? c.module().memory().initial : 0;
 
-    for (uint32_t i = 0; i < numSegments; i++) {
-        uint32_t dstOffset;
-        if (!c.d.readVarU32(&dstOffset))
-            return c.d.fail("expected segment destination offset");
-
-        uint32_t numBytes;
-        if (!c.d.readVarU32(&numBytes))
-            return c.d.fail("expected segment size");
-
-        if (dstOffset > heapLength || heapLength - dstOffset < numBytes)
-            return c.d.fail("data segment does not fit in memory");
-
-        const uint8_t* src;
-        if (!c.d.readBytes(numBytes, &src))
-            return c.d.fail("data segment shorter than declared");
-
-        char16_t *buffer = static_cast<char16_t *>(c.lifo.alloc(numBytes * sizeof(char16_t)));
-        for (size_t i = 0; i < numBytes; i++)
+    for (DataSegment& s : segments) {
+        const uint8_t* src = c.d.begin() + s.bytecodeOffset;
+        char16_t* buffer = static_cast<char16_t*>(c.lifo.alloc(s.length * sizeof(char16_t)));
+        for (size_t i = 0; i < s.length; i++)
             buffer[i] = src[i];
 
-        AstExpr* offset = new(c.lifo) AstConst(Val(dstOffset));
+        AstExpr* offset = ToAstExpr(c, s.offset);
         if (!offset)
             return false;
 
-        AstName name(buffer, numBytes);
+        AstName name(buffer, s.length);
         AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, name);
         if (!segment || !c.module().append(segment))
             return false;
     }
 
-    if (!c.d.finishSection(sectionStart, sectionSize, "data"))
-        return false;
-
     return true;
 }
 
-
 static bool
 AstDecodeElemSection(AstDecodeContext &c)
 {
     uint32_t sectionStart, sectionSize;
     if (!c.d.startSection(SectionId::Elem, &sectionStart, &sectionSize, "elem"))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
@@ -2060,18 +2071,18 @@ AstDecodeElemSection(AstDecodeContext &c
         uint32_t tableIndex;
         if (!c.d.readVarU32(&tableIndex))
             return c.d.fail("expected table index for element");
 
         if (tableIndex != 0)
             return c.d.fail("non-zero table index for element");
 
         AstExpr* offset;
-        if (!AstDecodeInitializerExpression(c, &offset, ValType::I32))
-            return c.d.fail("missing initializer expression");
+        if (!AstDecodeInitializerExpression(c, ValType::I32, &offset))
+            return false;
 
         uint32_t count;
         if (!c.d.readVarU32(&count))
             return c.d.fail("expected element count");
 
         AstRefVector elems(c.lifo);
         if (!elems.resize(count))
             return false;
--- a/js/src/asmjs/WasmBinaryToExperimentalText.cpp
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp
@@ -312,29 +312,16 @@ PrintBlockLevelExpr(WasmPrintContext& c,
     }
     return c.buffer.append('\n');
 }
 
 /*****************************************************************************/
 // binary format parsing and rendering
 
 static bool
-PrintNullaryOperator(WasmPrintContext& c, AstNullaryOperator& op)
-{
-    const char* opStr;
-
-    switch (op.expr()) {
-        case Expr::CurrentMemory:   opStr = "curent_memory"; break;
-        default:  return false;
-    }
-
-    return c.buffer.append(opStr, strlen(opStr));
-}
-
-static bool
 PrintNop(WasmPrintContext& c)
 {
     return c.buffer.append("nop");
 }
 
 static bool
 PrintDrop(WasmPrintContext& c, AstDrop& drop)
 {
@@ -374,19 +361,16 @@ PrintCallArgs(WasmPrintContext& c, const
 }
 
 static bool
 PrintCall(WasmPrintContext& c, AstCall& call)
 {
     if (call.expr() == Expr::Call) {
         if (!c.buffer.append("call "))
             return false;
-    } else if (call.expr() == Expr::CallImport) {
-        if (!c.buffer.append("call_import "))
-            return false;
     } else {
         return false;
     }
 
     if (!PrintRef(c, call.func()))
         return false;
 
     if (!c.buffer.append(" "))
@@ -652,17 +636,16 @@ PrintUnaryOperator(WasmPrintContext& c, 
       case Expr::F32Sqrt:    opStr = "f32.sqrt"; break;
       case Expr::F32Trunc:   opStr = "f32.trunc"; break;
       case Expr::F32Nearest: opStr = "f32.nearest"; break;
       case Expr::F64Abs:     opStr = "f64.abs"; break;
       case Expr::F64Neg:     opStr = "f64.neg"; prefixStr = "-"; precedence = NegatePrecedence; break;
       case Expr::F64Ceil:    opStr = "f64.ceil"; break;
       case Expr::F64Floor:   opStr = "f64.floor"; break;
       case Expr::F64Sqrt:    opStr = "f64.sqrt"; break;
-      case Expr::GrowMemory: opStr = "grow_memory"; break;
       default: return false;
     }
 
     if (c.f.allowAsciiOperators && prefixStr) {
         if (!c.f.reduceParens || lastPrecedence > precedence) {
             if (!c.buffer.append("("))
                 return false;
         }
@@ -1360,32 +1343,55 @@ PrintFirst(WasmPrintContext& c, AstFirst
 
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
 static bool
+PrintCurrentMemory(WasmPrintContext& c, AstCurrentMemory& cm)
+{
+    return c.buffer.append("current_memory");
+}
+
+static bool
+PrintGrowMemory(WasmPrintContext& c, AstGrowMemory& gm)
+{
+    if (!c.buffer.append("grow_memory("))
+        return false;
+
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+    c.currentPrecedence = ExpressionPrecedence;
+
+    if (!PrintExpr(c, *gm.op()))
+        return false;
+
+    if (!c.buffer.append(")"))
+        return false;
+
+    c.currentPrecedence = lastPrecedence;
+    return true;
+}
+
+static bool
 PrintExpr(WasmPrintContext& c, AstExpr& expr)
 {
     if (c.maybeSourceMap) {
         uint32_t lineno = c.buffer.lineno();
         uint32_t column = c.buffer.column();
         if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset()))
             return false;
     }
 
     switch (expr.kind()) {
       case AstExprKind::Nop:
         return PrintNop(c);
       case AstExprKind::Drop:
         return PrintDrop(c, expr.as<AstDrop>());
-      case AstExprKind::NullaryOperator:
-        return PrintNullaryOperator(c, expr.as<AstNullaryOperator>());
       case AstExprKind::Unreachable:
         return PrintUnreachable(c, expr.as<AstUnreachable>());
       case AstExprKind::Call:
         return PrintCall(c, expr.as<AstCall>());
       case AstExprKind::CallIndirect:
         return PrintCallIndirect(c, expr.as<AstCallIndirect>());
       case AstExprKind::Const:
         return PrintConst(c, expr.as<AstConst>());
@@ -1416,16 +1422,20 @@ PrintExpr(WasmPrintContext& c, AstExpr& 
       case AstExprKind::Branch:
         return PrintBranch(c, expr.as<AstBranch>());
       case AstExprKind::BranchTable:
         return PrintBrTable(c, expr.as<AstBranchTable>());
       case AstExprKind::Return:
         return PrintReturn(c, expr.as<AstReturn>());
       case AstExprKind::First:
         return PrintFirst(c, expr.as<AstFirst>());
+      case AstExprKind::CurrentMemory:
+        return PrintCurrentMemory(c, expr.as<AstCurrentMemory>());
+      case AstExprKind::GrowMemory:
+        return PrintGrowMemory(c, expr.as<AstGrowMemory>());
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
         break;
     }
 
     return false;
 }
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -219,17 +219,17 @@ RenderBlockNameAndSignature(WasmRenderCo
         if (!RenderExprType(c, type))
             return false;
     }
 
     return true;
 }
 
 static bool
-RenderExpr(WasmRenderContext& c, AstExpr& expr);
+RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true);
 
 #define MAP_AST_EXPR(c, expr)                                                         \
     if (c.maybeSourceMap) {                                                           \
         uint32_t lineno = c.buffer.lineno();                                          \
         uint32_t column = c.buffer.column();                                          \
         if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \
             return false;                                                             \
     }
@@ -238,40 +238,38 @@ RenderExpr(WasmRenderContext& c, AstExpr
 // binary format parsing and rendering
 
 static bool
 RenderNop(WasmRenderContext& c, AstNop& nop)
 {
     if (!RenderIndent(c))
         return false;
     MAP_AST_EXPR(c, nop);
-    return c.buffer.append("nop\n");
+    return c.buffer.append("nop");
 }
 
 static bool
 RenderDrop(WasmRenderContext& c, AstDrop& drop)
 {
     if (!RenderExpr(c, drop.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
     MAP_AST_EXPR(c, drop);
-    if (!c.buffer.append("drop\n"))
-        return false;
-    return true;
+    return c.buffer.append("drop");
 }
 
 static bool
 RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable)
 {
     if (!RenderIndent(c))
         return false;
     MAP_AST_EXPR(c, unreachable);
-    return c.buffer.append("unreachable\n");
+    return c.buffer.append("unreachable");
 }
 
 static bool
 RenderCallArgs(WasmRenderContext& c, const AstExprVector& args)
 {
     for (uint32_t i = 0; i < args.length(); i++) {
         if (!RenderExpr(c, *args[i]))
             return false;
@@ -288,30 +286,21 @@ RenderCall(WasmRenderContext& c, AstCall
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, call);
     if (call.expr() == Expr::Call) {
         if (!c.buffer.append("call "))
             return false;
-    } else if (call.expr() == Expr::CallImport) {
-        if (!c.buffer.append("call_import "))
-            return false;
     } else {
         return false;
     }
 
-    if (!RenderRef(c, call.func()))
-        return false;
-
-    if (!c.buffer.append('\n'))
-        return false;
-
-    return true;
+    return RenderRef(c, call.func());
 }
 
 static bool
 RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call)
 {
     if (!RenderCallArgs(c, call.args()))
         return false;
 
@@ -319,117 +308,114 @@ RenderCallIndirect(WasmRenderContext& c,
         return false;
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, call);
     if (!c.buffer.append("call_indirect "))
         return false;
-    if (!RenderRef(c, call.sig()))
-        return false;
-
-    if (!c.buffer.append('\n'))
-        return false;
-
-    return true;
+    return RenderRef(c, call.sig());
 }
 
 static bool
 RenderConst(WasmRenderContext& c, AstConst& cst)
 {
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, cst);
     if (!RenderValType(c, cst.val().type()))
         return false;
     if (!c.buffer.append(".const "))
         return false;
 
     switch (ToExprType(cst.val().type())) {
       case ExprType::I32:
-        if (!RenderInt32(c, (uint32_t)cst.val().i32()))
-            return false;
-        break;
+        return RenderInt32(c, (int32_t)cst.val().i32());
       case ExprType::I64:
-        if (!RenderInt64(c, (uint32_t)cst.val().i64()))
-            return false;
-        break;
+        return RenderInt64(c, (int64_t)cst.val().i64());
       case ExprType::F32:
-        if (!RenderFloat32(c, cst.val().f32()))
-            return false;
-        break;
+        return RenderFloat32(c, cst.val().f32());
       case ExprType::F64:
-        if (!RenderDouble(c, cst.val().f64()))
-            return false;
+        return RenderDouble(c, cst.val().f64());
+      default:
         break;
-      default:
-        return false;
     }
 
-    if (!c.buffer.append('\n'))
-        return false;
-    return true;
+    return false;
 }
 
 static bool
 RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl)
 {
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, gl);
     if (!c.buffer.append("get_local "))
         return false;
-    if (!RenderRef(c, gl.local()))
-        return false;
-    if (!c.buffer.append('\n'))
-        return false;
-    return true;
+    return RenderRef(c, gl.local());
 }
 
 static bool
 RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl)
- {
+{
     if (!RenderExpr(c, sl.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, sl);
     if (!c.buffer.append("set_local "))
         return false;
-    if (!RenderRef(c, sl.local()))
-        return false;
-
-    if (!c.buffer.append('\n'))
-        return false;
-    return true;
+    return RenderRef(c, sl.local());
 }
 
 static bool
 RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl)
 {
     if (!RenderExpr(c, tl.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, tl);
     if (!c.buffer.append("tee_local "))
         return false;
-    if (!RenderRef(c, tl.local()))
+    return RenderRef(c, tl.local());
+}
+
+static bool
+RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg)
+{
+    if (!RenderIndent(c))
         return false;
 
-    if (!c.buffer.append('\n'))
+    MAP_AST_EXPR(c, gg);
+    if (!c.buffer.append("get_global "))
         return false;
-    return true;
+    return RenderRef(c, gg.global());
+}
+
+static bool
+RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg)
+{
+    if (!RenderExpr(c, sg.value()))
+        return false;
+
+    if (!RenderIndent(c))
+        return false;
+
+    MAP_AST_EXPR(c, sg);
+    if (!c.buffer.append("set_global "))
+        return false;
+    return RenderRef(c, sg.global());
 }
 
 static bool
 RenderExprList(WasmRenderContext& c, const AstExprVector& exprs)
 {
     for (uint32_t i = 0; i < exprs.length(); i++) {
         if (!RenderExpr(c, *exprs[i]))
             return false;
@@ -445,61 +431,63 @@ RenderBlock(WasmRenderContext& c, AstBlo
 
     MAP_AST_EXPR(c, block);
     if (block.expr() == Expr::Block) {
         if (!c.buffer.append("block"))
             return false;
     } else if (block.expr() == Expr::Loop) {
         if (!c.buffer.append("loop"))
             return false;
-    } else
+    } else {
         return false;
+    }
 
     if (!RenderBlockNameAndSignature(c, block.name(), block.type()))
         return false;
 
     if (!c.buffer.append('\n'))
         return false;
 
     c.indent++;
     if (!RenderExprList(c, block.exprs()))
         return false;
     c.indent--;
 
     if (!RenderIndent(c))
         return false;
 
-    return c.buffer.append("end\n");
+    return c.buffer.append("end");
 }
 
 static bool
 RenderFirst(WasmRenderContext& c, AstFirst& first)
 {
-    if (!RenderExprList(c, first.exprs()))
-        return false;
-
-    return true;
+    return RenderExprList(c, first.exprs());
 }
 
 static bool
-RenderNullaryOperator(WasmRenderContext& c, AstNullaryOperator& op)
+RenderCurrentMemory(WasmRenderContext& c, AstCurrentMemory& cm)
 {
     if (!RenderIndent(c))
-      return false;
-
-    const char* opStr;
-    switch (op.expr()) {
-      case Expr::CurrentMemory:     opStr = "current_memory"; break;
-      default: return false;
-    }
-
-    if (!c.buffer.append(opStr, strlen(opStr)))
         return false;
 
-    return c.buffer.append('\n');
+    return c.buffer.append("current_memory\n");
+}
+
+static bool
+RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm)
+{
+    if (!RenderExpr(c, *gm.op()))
+        return false;
+
+    if (!RenderIndent(c))
+        return false;
+
+    MAP_AST_EXPR(c, gm);
+    return c.buffer.append("grow_memory\n");
 }
 
 static bool
 RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& op)
 {
     if (!RenderExpr(c, *op.op()))
         return false;
 
@@ -522,24 +510,23 @@ RenderUnaryOperator(WasmRenderContext& c
       case Expr::F32Floor:   opStr = "f32.floor"; break;
       case Expr::F32Sqrt:    opStr = "f32.sqrt"; break;
       case Expr::F32Trunc:   opStr = "f32.trunc"; break;
       case Expr::F32Nearest: opStr = "f32.nearest"; break;
       case Expr::F64Abs:     opStr = "f64.abs"; break;
       case Expr::F64Neg:     opStr = "f64.neg"; break;
       case Expr::F64Ceil:    opStr = "f64.ceil"; break;
       case Expr::F64Floor:   opStr = "f64.floor"; break;
+      case Expr::F64Nearest: opStr = "f64.nearest"; break;
       case Expr::F64Sqrt:    opStr = "f64.sqrt"; break;
-      case Expr::GrowMemory: opStr = "grow_memory"; break;
+      case Expr::F64Trunc:   opStr = "f64.trunc"; break;
       default: return false;
     }
-    if (!c.buffer.append(opStr, strlen(opStr)))
-        return false;
 
-    return c.buffer.append('\n');
+    return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
 RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& op)
 {
     if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!RenderExpr(c, *op.rhs()))
@@ -585,25 +572,21 @@ RenderBinaryOperator(WasmRenderContext& 
       case Expr::F32Max:      opStr = "f32.max"; break;
       case Expr::F32CopySign: opStr = "f32.copysign"; break;
       case Expr::F64Add:      opStr = "f64.add"; break;
       case Expr::F64Sub:      opStr = "f64.sub"; break;
       case Expr::F64Mul:      opStr = "f64.mul"; break;
       case Expr::F64Div:      opStr = "f64.div"; break;
       case Expr::F64Min:      opStr = "f64.min"; break;
       case Expr::F64Max:      opStr = "f64.max"; break;
+      case Expr::F64CopySign: opStr = "f64.copysign"; break;
       default: return false;
     }
-    if (!c.buffer.append(opStr, strlen(opStr)))
-        return false;
 
-    if (!c.buffer.append('\n'))
-        return false;
-
-    return true;
+    return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
 RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& op)
 {
     if (!RenderExpr(c, *op.op0()))
         return false;
     if (!RenderExpr(c, *op.op1()))
@@ -615,23 +598,18 @@ RenderTernaryOperator(WasmRenderContext&
         return false;
 
     MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::Select: opStr = "select"; break;
       default: return false;
     }
-    if (!c.buffer.append(opStr, strlen(opStr)))
-        return false;
 
-    if (!c.buffer.append('\n'))
-        return false;
-
-    return true;
+    return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
 RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& op)
 {
     if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!RenderExpr(c, *op.rhs()))
@@ -672,23 +650,18 @@ RenderComparisonOperator(WasmRenderConte
       case Expr::F64Eq:  opStr = "f64.eq"; break;
       case Expr::F64Ne:  opStr = "f64.ne"; break;
       case Expr::F64Lt:  opStr = "f64.lt"; break;
       case Expr::F64Le:  opStr = "f64.le"; break;
       case Expr::F64Gt:  opStr = "f64.gt"; break;
       case Expr::F64Ge:  opStr = "f64.ge"; break;
       default: return false;
     }
-    if (!c.buffer.append(opStr, strlen(opStr)))
-        return false;
 
-    if (!c.buffer.append('\n'))
-        return false;
-
-    return true;
+    return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
 RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& op)
 {
     if (!RenderExpr(c, *op.op()))
         return false;
 
@@ -722,46 +695,33 @@ RenderConversionOperator(WasmRenderConte
       case Expr::F64ConvertSI64:    opStr = "f64.convert_s/i64"; break;
       case Expr::F64ConvertUI64:    opStr = "f64.convert_u/i64"; break;
       case Expr::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
       case Expr::F64PromoteF32:     opStr = "f64.promote/f32"; break;
       case Expr::I32Eqz:            opStr = "i32.eqz"; break;
       case Expr::I64Eqz:            opStr = "i64.eqz"; break;
       default: return false;
     }
-    if (!c.buffer.append(opStr, strlen(opStr)))
-        return false;
-
-    return c.buffer.append('\n');
+    return c.buffer.append(opStr, strlen(opStr));
 }
 
 static bool
 RenderIf(WasmRenderContext& c, AstIf& if_)
 {
     if (!RenderExpr(c, if_.cond()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, if_);
     if (!c.buffer.append("if"))
         return false;
-
     if (!RenderBlockNameAndSignature(c, if_.name(), if_.type()))
         return false;
-
-    if (!if_.name().empty()) {
-        if (!c.buffer.append(' '))
-            return false;
-
-        if (!RenderName(c, if_.name()))
-            return false;
-    }
-
     if (!c.buffer.append('\n'))
         return false;
 
     c.indent++;
     if (!RenderExprList(c, if_.thenExprs()))
         return false;
     c.indent--;
 
@@ -776,44 +736,41 @@ RenderIf(WasmRenderContext& c, AstIf& if
         if (!RenderExprList(c, if_.elseExprs()))
             return false;
         c.indent--;
     }
 
     if (!RenderIndent(c))
         return false;
 
-    return c.buffer.append("end\n");
+    return c.buffer.append("end");
 }
 
 static bool
 RenderLoadStoreBase(WasmRenderContext& c, const AstLoadStoreAddress& lsa)
 {
-    if (!RenderExpr(c, lsa.base()))
-        return false;
-
-    return true;
+    return RenderExpr(c, lsa.base());
 }
 
 static bool
 RenderLoadStoreAddress(WasmRenderContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2)
 {
     if (lsa.offset() != 0) {
-      if (!c.buffer.append(" offset="))
-          return false;
-      if (!RenderInt32(c, lsa.offset()))
-          return false;
+        if (!c.buffer.append(" offset="))
+            return false;
+        if (!RenderInt32(c, lsa.offset()))
+            return false;
     }
 
     uint32_t alignLog2 = lsa.flags();
     if (defaultAlignLog2 != alignLog2) {
-      if (!c.buffer.append(" align="))
-          return false;
-      if (!RenderInt32(c, 1 << alignLog2))
-          return false;
+        if (!c.buffer.append(" align="))
+            return false;
+        if (!RenderInt32(c, 1 << alignLog2))
+            return false;
     }
 
     return true;
 }
 
 static bool
 RenderLoad(WasmRenderContext& c, AstLoad& load)
 {
@@ -895,20 +852,17 @@ RenderLoad(WasmRenderContext& c, AstLoad
         if (!c.buffer.append("f64.load"))
             return false;
         defaultAlignLog2 = 3;
         break;
       default:
         return false;
     }
 
-    if (!RenderLoadStoreAddress(c, load.address(), defaultAlignLog2))
-        return false;
-
-    return c.buffer.append('\n');
+    return RenderLoadStoreAddress(c, load.address(), defaultAlignLog2);
 }
 
 static bool
 RenderStore(WasmRenderContext& c, AstStore& store)
 {
     if (!RenderLoadStoreBase(c, store.address()))
         return false;
 
@@ -965,20 +919,17 @@ RenderStore(WasmRenderContext& c, AstSto
         if (!c.buffer.append("f64.store"))
             return false;
         defaultAlignLog2 = 3;
         break;
       default:
         return false;
     }
 
-    if (!RenderLoadStoreAddress(c, store.address(), defaultAlignLog2))
-        return false;
-
-    return c.buffer.append('\n');
+    return RenderLoadStoreAddress(c, store.address(), defaultAlignLog2);
 }
 
 static bool
 RenderBranch(WasmRenderContext& c, AstBranch& branch)
 {
     Expr expr = branch.expr();
     MOZ_ASSERT(expr == Expr::BrIf || expr == Expr::Br);
 
@@ -994,28 +945,25 @@ RenderBranch(WasmRenderContext& c, AstBr
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, branch);
     if (expr == Expr::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
         return false;
 
-    if (!RenderRef(c, branch.target()))
-        return false;
-
-    return c.buffer.append('\n');
+    return RenderRef(c, branch.target());
 }
 
 static bool
 RenderBrTable(WasmRenderContext& c, AstBranchTable& table)
 {
     if (table.maybeValue()) {
-      if (!RenderExpr(c, *(table.maybeValue())))
-          return false;
+        if (!RenderExpr(c, *(table.maybeValue())))
+            return false;
     }
 
     // Index
     if (!RenderExpr(c, table.index()))
         return false;
 
     if (!RenderIndent(c))
         return false;
@@ -1028,133 +976,186 @@ RenderBrTable(WasmRenderContext& c, AstB
     for (uint32_t i = 0; i < tableLength; i++) {
         if (!RenderRef(c, table.table()[i]))
             return false;
 
         if (!c.buffer.append(" "))
             return false;
     }
 
-    if (!RenderRef(c, table.def()))
-        return false;
-
-    return c.buffer.append('\n');
+    return RenderRef(c, table.def());
 }
 
 static bool
 RenderReturn(WasmRenderContext& c, AstReturn& ret)
 {
     if (ret.maybeExpr()) {
         if (!RenderExpr(c, *(ret.maybeExpr())))
             return false;
     }
 
     if (!RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, ret);
-    if (!c.buffer.append("return"))
-        return false;
-
-    return c.buffer.append('\n');
+    return c.buffer.append("return");
 }
 
 static bool
-RenderExpr(WasmRenderContext& c, AstExpr& expr)
+RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine /* = true */)
 {
     switch (expr.kind()) {
       case AstExprKind::Drop:
-        return RenderDrop(c, expr.as<AstDrop>());
+        if (!RenderDrop(c, expr.as<AstDrop>()))
+            return false;
+        break;
       case AstExprKind::Nop:
-        return RenderNop(c, expr.as<AstNop>());
+        if (!RenderNop(c, expr.as<AstNop>()))
+            return false;
+        break;
       case AstExprKind::Unreachable:
-        return RenderUnreachable(c, expr.as<AstUnreachable>());
+        if (!RenderUnreachable(c, expr.as<AstUnreachable>()))
+            return false;
+        break;
       case AstExprKind::Call:
-        return RenderCall(c, expr.as<AstCall>());
+        if (!RenderCall(c, expr.as<AstCall>()))
+            return false;
+        break;
       case AstExprKind::CallIndirect:
-        return RenderCallIndirect(c, expr.as<AstCallIndirect>());
+        if (!RenderCallIndirect(c, expr.as<AstCallIndirect>()))
+            return false;
+        break;
       case AstExprKind::Const:
-        return RenderConst(c, expr.as<AstConst>());
+        if (!RenderConst(c, expr.as<AstConst>()))
+            return false;
+        break;
       case AstExprKind::GetLocal:
-        return RenderGetLocal(c, expr.as<AstGetLocal>());
+        if (!RenderGetLocal(c, expr.as<AstGetLocal>()))
+            return false;
+        break;
       case AstExprKind::SetLocal:
-        return RenderSetLocal(c, expr.as<AstSetLocal>());
+        if (!RenderSetLocal(c, expr.as<AstSetLocal>()))
+            return false;
+        break;
+      case AstExprKind::GetGlobal:
+        if (!RenderGetGlobal(c, expr.as<AstGetGlobal>()))
+            return false;
+        break;
+      case AstExprKind::SetGlobal:
+        if (!RenderSetGlobal(c, expr.as<AstSetGlobal>()))
+            return false;
+        break;
       case AstExprKind::TeeLocal:
-        return RenderTeeLocal(c, expr.as<AstTeeLocal>());
+        if (!RenderTeeLocal(c, expr.as<AstTeeLocal>()))
+            return false;
+        break;
       case AstExprKind::Block:
-        return RenderBlock(c, expr.as<AstBlock>());
+        if (!RenderBlock(c, expr.as<AstBlock>()))
+            return false;
+        break;
       case AstExprKind::If:
-        return RenderIf(c, expr.as<AstIf>());
-      case AstExprKind::NullaryOperator:
-        return RenderNullaryOperator(c, expr.as<AstNullaryOperator>());
+        if (!RenderIf(c, expr.as<AstIf>()))
+            return false;
+        break;
       case AstExprKind::UnaryOperator:
-        return RenderUnaryOperator(c, expr.as<AstUnaryOperator>());
+        if (!RenderUnaryOperator(c, expr.as<AstUnaryOperator>()))
+            return false;
+        break;
       case AstExprKind::BinaryOperator:
-        return RenderBinaryOperator(c, expr.as<AstBinaryOperator>());
+        if (!RenderBinaryOperator(c, expr.as<AstBinaryOperator>()))
+            return false;
+        break;
       case AstExprKind::TernaryOperator:
-        return RenderTernaryOperator(c, expr.as<AstTernaryOperator>());
+        if (!RenderTernaryOperator(c, expr.as<AstTernaryOperator>()))
+            return false;
+        break;
       case AstExprKind::ComparisonOperator:
-        return RenderComparisonOperator(c, expr.as<AstComparisonOperator>());
+        if (!RenderComparisonOperator(c, expr.as<AstComparisonOperator>()))
+            return false;
+        break;
       case AstExprKind::ConversionOperator:
-        return RenderConversionOperator(c, expr.as<AstConversionOperator>());
+        if (!RenderConversionOperator(c, expr.as<AstConversionOperator>()))
+            return false;
+        break;
       case AstExprKind::Load:
-        return RenderLoad(c, expr.as<AstLoad>());
+        if (!RenderLoad(c, expr.as<AstLoad>()))
+            return false;
+        break;
       case AstExprKind::Store:
-        return RenderStore(c, expr.as<AstStore>());
+        if (!RenderStore(c, expr.as<AstStore>()))
+            return false;
+        break;
       case AstExprKind::Branch:
-        return RenderBranch(c, expr.as<AstBranch>());
+        if (!RenderBranch(c, expr.as<AstBranch>()))
+            return false;
+        break;
       case AstExprKind::BranchTable:
-        return RenderBrTable(c, expr.as<AstBranchTable>());
+        if (!RenderBrTable(c, expr.as<AstBranchTable>()))
+            return false;
+        break;
       case AstExprKind::Return:
-        return RenderReturn(c, expr.as<AstReturn>());
+        if (!RenderReturn(c, expr.as<AstReturn>()))
+            return false;
+        break;
       case AstExprKind::First:
-        return RenderFirst(c, expr.as<AstFirst>());
+        newLine = false;
+        if (!RenderFirst(c, expr.as<AstFirst>()))
+            return false;
+        break;
+      case AstExprKind::CurrentMemory:
+        if (!RenderCurrentMemory(c, expr.as<AstCurrentMemory>()))
+            return false;
+        break;
+      case AstExprKind::GrowMemory:
+        if (!RenderGrowMemory(c, expr.as<AstGrowMemory>()))
+            return false;
+        break;
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
-        break;
+        return false;
     }
 
-    return false;
+    return !newLine || c.buffer.append("\n");
 }
 
 static bool
 RenderSignature(WasmRenderContext& c, const AstSig& sig, const AstNameVector* maybeLocals = nullptr)
 {
     uint32_t paramsNum = sig.args().length();
 
     if (maybeLocals) {
-      for (uint32_t i = 0; i < paramsNum; i++) {
-          if (!c.buffer.append(" (param "))
-              return false;
-          const AstName& name = (*maybeLocals)[i];
-          if (!name.empty()) {
-              if (!RenderName(c, name))
-                  return false;
-              if (!c.buffer.append(" "))
-                  return false;
-          }
-          ValType arg = sig.args()[i];
-          if (!RenderValType(c, arg))
-              return false;
-          if (!c.buffer.append(")"))
-              return false;
-      }
+        for (uint32_t i = 0; i < paramsNum; i++) {
+            if (!c.buffer.append(" (param "))
+                return false;
+            const AstName& name = (*maybeLocals)[i];
+            if (!name.empty()) {
+                if (!RenderName(c, name))
+                    return false;
+                if (!c.buffer.append(" "))
+                    return false;
+            }
+            ValType arg = sig.args()[i];
+            if (!RenderValType(c, arg))
+                return false;
+            if (!c.buffer.append(")"))
+                return false;
+        }
     } else if (paramsNum > 0) {
-      if (!c.buffer.append(" (param"))
-          return false;
-      for (uint32_t i = 0; i < paramsNum; i++) {
-          if (!c.buffer.append(" "))
-              return false;
-          ValType arg = sig.args()[i];
-          if (!RenderValType(c, arg))
-              return false;
-      }
-      if (!c.buffer.append(")"))
-          return false;
+        if (!c.buffer.append(" (param"))
+            return false;
+        for (uint32_t i = 0; i < paramsNum; i++) {
+            if (!c.buffer.append(" "))
+                return false;
+            ValType arg = sig.args()[i];
+            if (!RenderValType(c, arg))
+                return false;
+        }
+        if (!c.buffer.append(")"))
+            return false;
     }
     if (sig.ret() != ExprType::Void) {
         if (!c.buffer.append(" (result "))
             return false;
         if (!RenderExprType(c, sig.ret()))
             return false;
         if (!c.buffer.append(")"))
             return false;
@@ -1203,39 +1204,106 @@ RenderTableSection(WasmRenderContext& c,
         return false;
 
     if (!c.buffer.append("(table anyfunc (elem "))
         return false;
 
     for (const AstRef& elem : segment.elems()) {
         if (!c.buffer.append(" "))
             return false;
+
         uint32_t index = elem.index();
         AstName name = index < module.funcImportNames().length()
-                           ? module.funcImportNames()[index]
-                           : module.funcs()[index - module.funcImportNames().length()]->name();
+                       ? module.funcImportNames()[index]
+                       : module.funcs()[index - module.funcImportNames().length()]->name();
+
         if (name.empty()) {
             if (!RenderInt32(c, index))
                 return false;
         } else {
-          if (!RenderName(c, name))
-              return false;
+            if (!RenderName(c, name))
+                return false;
         }
     }
 
-    if (!c.buffer.append("))\n"))
+    return c.buffer.append("))\n");
+}
+
+static bool
+RenderInlineExpr(WasmRenderContext& c, AstExpr& expr)
+{
+    if (!c.buffer.append("("))
+        return false;
+
+    uint32_t prevIndent = c.indent;
+    c.indent = 0;
+    if (!RenderExpr(c, expr, /* newLine */ false))
+        return false;
+    c.indent = prevIndent;
+
+    return c.buffer.append(")");
+}
+
+static bool
+RenderGlobal(WasmRenderContext& c, const AstGlobal& glob, bool inImport = false)
+{
+    if (!c.buffer.append("(global "))
         return false;
 
+    if (!inImport) {
+        if (!RenderName(c, glob.name()))
+            return false;
+        if (!c.buffer.append(" "))
+            return false;
+    }
+
+    if (glob.isMutable()) {
+        if (!c.buffer.append("(mut "))
+            return false;
+        if (!RenderValType(c, glob.type()))
+            return false;
+        if (!c.buffer.append(")"))
+            return false;
+    } else {
+        if (!RenderValType(c, glob.type()))
+            return false;
+    }
+
+    if (glob.hasInit()) {
+        if (!c.buffer.append(" "))
+            return false;
+        if (!RenderInlineExpr(c, glob.init()))
+            return false;
+    }
+
+    if (!c.buffer.append(")"))
+        return false;
+
+    return inImport || c.buffer.append("\n");
+}
+
+static bool
+RenderGlobalSection(WasmRenderContext& c, const AstModule& module)
+{
+    if (module.globals().empty())
+        return true;
+
+    for (const AstGlobal* global : module.globals()) {
+        if (!RenderIndent(c))
+            return false;
+        if (!RenderGlobal(c, *global))
+            return false;
+    }
+
     return true;
 }
 
 static bool
-RenderImport(WasmRenderContext& c, AstImport& import, const AstModule::SigVector& sigs)
+RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module)
 {
-    const AstSig* sig = sigs[import.funcSig().index()];
     if (!RenderIndent(c))
         return false;
     if (!c.buffer.append("(import "))
         return false;
     if (!RenderName(c, import.name()))
         return false;
     if (!c.buffer.append(" \""))
         return false;
@@ -1246,38 +1314,52 @@ RenderImport(WasmRenderContext& c, AstIm
 
     if (!c.buffer.append("\" \""))
         return false;
 
     const AstName& fieldName = import.field();
     if (!RenderEscapedString(c, fieldName))
         return false;
 
-    if (!c.buffer.append("\""))
+    if (!c.buffer.append("\" "))
         return false;
 
-    if (!RenderSignature(c, *sig))
-        return false;
-    if (!c.buffer.append(")\n"))
-        return false;
+    switch (import.kind()) {
+      case DefinitionKind::Function: {
+        const AstSig* sig = module.sigs()[import.funcSig().index()];
+        if (!RenderSignature(c, *sig))
+            return false;
+        break;
+      }
+      case DefinitionKind::Table: {
+        // TODO next patch
+        break;
+      }
+      case DefinitionKind::Memory: {
+        // TODO next patch
+        break;
+      }
+      case DefinitionKind::Global: {
+        const AstGlobal& glob = import.global();
+        if (!RenderGlobal(c, glob, /* inImport */ true))
+            return false;
+        break;
+      }
+    }
 
-    return true;
+    return c.buffer.append(")\n");
 }
 
-
 static bool
-RenderImportSection(WasmRenderContext& c, const AstModule::ImportVector& imports, const AstModule::SigVector& sigs)
+RenderImportSection(WasmRenderContext& c, const AstModule& module)
 {
-    uint32_t numImports = imports.length();
-
-    for (uint32_t i = 0; i < numImports; i++) {
-        if (!RenderImport(c, *imports[i], sigs))
+    for (AstImport* import : module.imports()) {
+        if (!RenderImport(c, *import, module))
             return false;
     }
-
     return true;
 }
 
 static bool
 RenderExport(WasmRenderContext& c, AstExport& export_,
              const AstModule::NameVector& funcImportNames,
              const AstModule::FuncVector& funcs)
 {
@@ -1300,20 +1382,17 @@ RenderExport(WasmRenderContext& c, AstEx
         if (name.empty()) {
             if (!RenderInt32(c, index))
                 return false;
         } else {
             if (!RenderName(c, name))
                 return false;
         }
     }
-    if (!c.buffer.append(")\n"))
-        return false;
-
-    return true;
+    return c.buffer.append(")\n");
 }
 
 static bool
 RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports,
                     const AstModule::NameVector& funcImportNames,
                     const AstModule::FuncVector& funcs)
 {
     uint32_t numExports = exports.length();
@@ -1337,36 +1416,36 @@ RenderFunctionBody(WasmRenderContext& c,
     if (localsNum > 0) {
         if (!RenderIndent(c))
             return false;
         for (uint32_t i = 0; i < localsNum; i++) {
             if (!c.buffer.append("(local "))
                 return false;
             const AstName& name = func.locals()[argsNum + i];
             if (!name.empty()) {
-              if (!RenderName(c, name))
-                  return false;
-              if (!c.buffer.append(" "))
-                  return false;
+                if (!RenderName(c, name))
+                    return false;
+                if (!c.buffer.append(" "))
+                    return false;
             }
             ValType local = func.vars()[i];
             if (!RenderValType(c, local))
                 return false;
             if (!c.buffer.append(") "))
                 return false;
         }
         if (!c.buffer.append("\n"))
             return false;
     }
 
 
     uint32_t exprsNum = func.body().length();
     for (uint32_t i = 0; i < exprsNum; i++) {
-      if (!RenderExpr(c, *func.body()[i]))
-          return false;
+        if (!RenderExpr(c, *func.body()[i]))
+            return false;
     }
 
     size_t endExprIndex = c.maybeSourceMap ? c.maybeSourceMap->exprlocs().length() : 0;
     uint32_t endLineno = c.buffer.lineno();
 
     if (c.maybeSourceMap) {
         if (!c.maybeSourceMap->functionlocs().emplaceBack(startExprIndex, endExprIndex, startLineno, endLineno))
             return false;
@@ -1384,18 +1463,18 @@ RenderCodeSection(WasmRenderContext& c, 
         uint32_t sigIndex = func->sig().index();
         AstSig* sig = sigs[sigIndex];
 
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append("(func "))
             return false;
         if (!func->name().empty()) {
-          if (!RenderName(c, func->name()))
-              return false;
+            if (!RenderName(c, func->name()))
+                return false;
         }
 
         if (!RenderSignature(c, *sig, &(func->locals())))
             return false;
         if (!c.buffer.append("\n"))
             return false;
 
         c.currentFuncIndex = funcIndex;
@@ -1405,93 +1484,86 @@ RenderCodeSection(WasmRenderContext& c, 
             return false;
         c.indent--;
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append(")\n"))
             return false;
     }
 
-   return true;
+    return true;
 }
 
-
 static bool
 RenderDataSection(WasmRenderContext& c, const AstModule& module)
 {
     if (!module.hasMemory())
         return true;
 
     if (!RenderIndent(c))
         return false;
     if (!c.buffer.append("(memory "))
         return false;
+
     if (!RenderInt32(c, module.memory().initial))
-       return false;
+        return false;
+
     Maybe<uint32_t> memMax = module.memory().maximum;
     if (memMax) {
         if (!c.buffer.append(" "))
             return false;
         if (!RenderInt32(c, *memMax))
             return false;
     }
 
-    c.indent++;
+    if (!c.buffer.append(")\n"))
+        return false;
 
     uint32_t numSegments = module.dataSegments().length();
-    if (!numSegments) {
-      if (!c.buffer.append(")\n"))
-          return false;
-      return true;
-    }
-    if (!c.buffer.append("\n"))
-        return false;
+    if (!numSegments)
+        return true;
 
-    for (uint32_t i = 0; i < numSegments; i++) {
-        const AstDataSegment* segment = module.dataSegments()[i];
-
+    for (const AstDataSegment* seg : module.dataSegments()) {
         if (!RenderIndent(c))
             return false;
-        if (!c.buffer.append("(segment "))
-           return false;
-        if (!RenderInt32(c, segment->offset()->as<AstConst>().val().i32()))
-           return false;
+        if (!c.buffer.append("(data "))
+            return false;
+        if (!RenderInlineExpr(c, *seg->offset()))
+            return false;
         if (!c.buffer.append(" \""))
-           return false;
-
-        RenderEscapedString(c, segment->text());
-
+            return false;
+        if (!RenderEscapedString(c, seg->text()))
+            return false;
         if (!c.buffer.append("\")\n"))
-           return false;
+            return false;
     }
 
-    c.indent--;
-    if (!c.buffer.append(")\n"))
-        return false;
-
     return true;
 }
 
 static bool
 RenderModule(WasmRenderContext& c, AstModule& module)
 {
     if (!c.buffer.append("(module\n"))
         return false;
 
     c.indent++;
 
     if (!RenderTypeSection(c, module.sigs()))
         return false;
 
-    if (!RenderImportSection(c, module.imports(), module.sigs()))
+    if (!RenderImportSection(c, module))
         return false;
 
     if (!RenderTableSection(c, module))
         return false;
 
+    if (!RenderGlobalSection(c, module))
+        return false;
+
     if (!RenderExportSection(c, module.exports(), module.funcImportNames(), module.funcs()))
         return false;
 
     if (!RenderCodeSection(c, module.funcs(), module.sigs()))
         return false;
 
     if (!RenderDataSection(c, module))
         return false;
@@ -1507,17 +1579,16 @@ RenderModule(WasmRenderContext& c, AstMo
 #undef MAP_AST_EXPR
 
 /*****************************************************************************/
 // Top-level functions
 
 bool
 wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, GeneratedSourceMap* sourceMap)
 {
-
     LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
 
     AstModule* module;
     if (!BinaryToAst(cx, bytes, length, lifo, &module))
         return false;
 
     WasmPrintBuffer buf(buffer);
     WasmRenderContext c(cx, module, buf, sourceMap);
@@ -1525,9 +1596,8 @@ wasm::BinaryToText(JSContext* cx, const 
     if (!RenderModule(c, *module)) {
         if (!cx->isExceptionPending())
             ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
-
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -17,16 +17,17 @@
  */
 
 #include "asmjs/WasmCompile.h"
 
 #include "mozilla/CheckedInt.h"
 
 #include "jsprf.h"
 
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmBinaryIterator.h"
 #include "asmjs/WasmGenerator.h"
 #include "asmjs/WasmSignalHandlers.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
@@ -142,52 +143,16 @@ DecodeCallIndirect(FunctionDecoder& f)
     const Sig& sig = f.mg().sig(sigIndex);
     if (!DecodeCallArgs(f, sig))
         return false;
 
     return DecodeCallReturn(f, sig);
 }
 
 static bool
-DecodeBlock(FunctionDecoder& f)
-{
-    if (!f.iter().readBlock())
-        return false;
-
-    if (f.iter().controlType() > ExprType::F64)
-        return f.iter().fail("unknown block signature type");
-
-    return true;
-}
-
-static bool
-DecodeLoop(FunctionDecoder& f)
-{
-    if (!f.iter().readLoop())
-        return false;
-
-    if (f.iter().controlType() > ExprType::F64)
-        return f.iter().fail("unknown loop signature type");
-
-    return true;
-}
-
-static bool
-DecodeIf(FunctionDecoder& f)
-{
-    if (!f.iter().readIf(nullptr))
-        return false;
-
-    if (f.iter().controlType() > ExprType::F64)
-        return f.iter().fail("unknown if signature type");
-
-    return true;
-}
-
-static bool
 DecodeBrTable(FunctionDecoder& f)
 {
     uint32_t tableLength;
     ExprType type = ExprType::Limit;
     if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
         return false;
 
     uint32_t depth;
@@ -241,21 +206,21 @@ DecodeFunctionBodyExprs(FunctionDecoder&
             CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
           case Expr::GetGlobal:
             CHECK(f.iter().readGetGlobal(f.mg().globals(), nullptr));
           case Expr::SetGlobal:
             CHECK(f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr));
           case Expr::Select:
             CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
           case Expr::Block:
-            CHECK(DecodeBlock(f));
+            CHECK(f.iter().readBlock());
           case Expr::Loop:
-            CHECK(DecodeLoop(f));
+            CHECK(f.iter().readLoop());
           case Expr::If:
-            CHECK(DecodeIf(f));
+            CHECK(f.iter().readIf(nullptr));
           case Expr::Else:
             CHECK(f.iter().readElse(nullptr, nullptr));
           case Expr::I32Clz:
           case Expr::I32Ctz:
           case Expr::I32Popcnt:
             CHECK(f.iter().readUnary(ValType::I32, nullptr));
           case Expr::I64Clz:
           case Expr::I64Ctz:
@@ -438,19 +403,19 @@ DecodeFunctionBodyExprs(FunctionDecoder&
             CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
           case Expr::I64Store:
             CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
           case Expr::F32Store:
             CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
           case Expr::F64Store:
             CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
           case Expr::GrowMemory:
-            CHECK(f.checkHasMemory() && f.iter().readUnary(ValType::I32, nullptr));
+            CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
           case Expr::CurrentMemory:
-            CHECK(f.checkHasMemory() && f.iter().readNullary(ValType::I32));
+            CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
           case Expr::Br:
             CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
           case Expr::BrIf:
             CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
           case Expr::BrTable:
             CHECK(DecodeBrTable(f));
           case Expr::Return:
             CHECK(f.iter().readReturn(nullptr));
@@ -482,17 +447,17 @@ DecodeTypeSection(Decoder& d, ModuleGene
     if (numSigs > MaxSigs)
         return d.fail("too many signatures");
 
     if (!init->sigs.resize(numSigs))
         return false;
 
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
         uint32_t form;
-        if (!d.readVarU32(&form) || form != uint32_t(TypeConstructor::Function))
+        if (!d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
             return d.fail("expected function form");
 
         uint32_t numArgs;
         if (!d.readVarU32(&numArgs))
             return d.fail("bad number of function args");
 
         if (numArgs > MaxArgsPerFunc)
             return d.fail("too many arguments in signature");
@@ -636,17 +601,17 @@ DecodeResizableMemory(Decoder& d, Module
 
 static bool
 DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
 {
     uint32_t elementType;
     if (!d.readVarU32(&elementType))
         return d.fail("expected table element type");
 
-    if (elementType != uint32_t(TypeConstructor::AnyFunc))
+    if (elementType != uint32_t(TypeCode::AnyFunc))
         return d.fail("expected 'anyfunc' element type");
 
     Limits limits;
     if (!DecodeLimits(d, &limits))
         return false;
 
     if (!init->tables.empty())
         return d.fail("already have default table");
@@ -707,20 +672,19 @@ DecodeImport(Decoder& d, ModuleGenerator
       }
       case DefinitionKind::Memory: {
         if (!DecodeResizableMemory(d, init))
             return false;
         break;
       }
       case DefinitionKind::Global: {
         ValType type;
-        uint32_t flags;
-        if (!DecodeGlobalType(d, &type, &flags))
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
             return false;
-        bool isMutable = flags & uint32_t(GlobalFlags::IsMutable);
         if (!GlobalIsJSCompatible(d, type, isMutable))
             return false;
         if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
             return false;
         break;
       }
       default:
         return d.fail("unsupported import kind");
@@ -802,79 +766,16 @@ DecodeMemorySection(Decoder& d, ModuleGe
 
     if (!d.finishSection(sectionStart, sectionSize, "memory"))
         return false;
 
     return true;
 }
 
 static bool
-DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
-                            InitExpr* init)
-{
-    Expr expr;
-    if (!d.readExpr(&expr))
-        return d.fail("failed to read initializer type");
-
-    switch (expr) {
-      case Expr::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: {
-        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: {
-        RawF32 f32;
-        if (!d.readFixedF32(&f32))
-            return d.fail("failed to read initializer f32 expression");
-        *init = InitExpr(Val(f32));
-        break;
-      }
-      case Expr::F64Const: {
-        RawF64 f64;
-        if (!d.readFixedF64(&f64))
-            return d.fail("failed to read initializer f64 expression");
-        *init = InitExpr(Val(f64));
-        break;
-      }
-      case Expr::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());
-        break;
-      }
-      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)
-        return d.fail("failed to read end of initializer expression");
-
-    return true;
-}
-
-static bool
 DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
@@ -882,25 +783,24 @@ DecodeGlobalSection(Decoder& d, ModuleGe
     if (!d.readVarU32(&numGlobals))
         return d.fail("expected number of globals");
 
     if (numGlobals > MaxGlobals)
         return d.fail("too many globals");
 
     for (uint32_t i = 0; i < numGlobals; i++) {
         ValType type;
-        uint32_t flags;
-        if (!DecodeGlobalType(d, &type, &flags))
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
             return false;
 
         InitExpr initializer;
         if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
             return false;
 
-        bool isMutable = flags & uint32_t(GlobalFlags::IsMutable);
         if (!init->globals.append(GlobalDesc(initializer, isMutable)))
             return false;
     }
 
     if (!d.finishSection(sectionStart, sectionSize, "global"))
         return false;
 
     return true;
@@ -1198,72 +1098,16 @@ DecodeElemSection(Decoder& d, Uint32Vect
     }
 
     if (!d.finishSection(sectionStart, sectionSize, "elem"))
         return false;
 
     return true;
 }
 
-static bool
-DecodeDataSection(Decoder& d, ModuleGenerator& mg)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Data, &sectionStart, &sectionSize, "data"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    if (!mg.usesMemory())
-        return d.fail("data section requires a memory section");
-
-    uint32_t numSegments;
-    if (!d.readVarU32(&numSegments))
-        return d.fail("failed to read number of data segments");
-
-    if (numSegments > MaxDataSegments)
-        return d.fail("too many data segments");
-
-    uint32_t max = mg.minMemoryLength();
-    for (uint32_t i = 0; i < numSegments; i++) {
-        uint32_t linearMemoryIndex;
-        if (!d.readVarU32(&linearMemoryIndex))
-            return d.fail("expected linear memory index");
-
-        if (linearMemoryIndex != 0)
-            return d.fail("linear memory index must currently be 0");
-
-        DataSegment seg;
-        if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &seg.offset))
-            return false;
-
-        if (!d.readVarU32(&seg.length))
-            return d.fail("expected segment size");
-
-        if (seg.offset.isVal()) {
-            uint32_t off = seg.offset.val().i32();
-            if (off > max || max - off < seg.length)
-                return d.fail("data segment does not fit");
-        }
-
-        seg.bytecodeOffset = d.currentOffset();
-
-        if (!d.readBytes(seg.length))
-            return d.fail("data segment shorter than declared");
-
-        if (!mg.addDataSegment(seg))
-            return false;
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "data"))
-        return false;
-
-    return true;
-}
-
 static void
 MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg)
 {
     // For simplicity, ignore all failures, even OOM. Failure will simply result
     // in the names section not being included for this module.
 
     uint32_t numFuncNames;
     if (!d.readVarU32(&numFuncNames))
@@ -1301,16 +1145,27 @@ MaybeDecodeNameSectionBody(Decoder& d, M
                 return;
         }
     }
 
     mg.setFuncNames(Move(funcNames));
 }
 
 static bool
+DecodeDataSection(Decoder& d, ModuleGenerator& mg)
+{
+    DataSegmentVector dataSegments;
+    if (!DecodeDataSection(d, mg.usesMemory(), mg.minMemoryLength(), mg.globals(), &dataSegments))
+        return false;
+
+    mg.setDataSegments(Move(dataSegments));
+    return true;
+}
+
+static bool
 DecodeNameSection(Decoder& d, ModuleGenerator& mg)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startUserDefinedSection(NameSectionName, &sectionStart, &sectionSize))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
@@ -1376,17 +1231,17 @@ wasm::Compile(const ShareableBytes& byte
         return nullptr;
 
     if (!DecodeElemSection(d, Move(oldElems), mg))
         return nullptr;
 
     if (!DecodeCodeSection(d, mg))
         return nullptr;
 
-    if (!DecodeDataSection(d, mg))
+    if (!::DecodeDataSection(d, mg))
         return nullptr;
 
     if (!DecodeNameSection(d, mg))
         return nullptr;
 
     if (!DecodeUnknownSections(d))
         return nullptr;
 
--- a/js/src/asmjs/WasmGeneratedSourceMap.h
+++ b/js/src/asmjs/WasmGeneratedSourceMap.h
@@ -120,16 +120,18 @@ class WasmPrintBuffer
             processChar(*p);
         return stringBuffer_.append(begin, end);
     }
     bool append(const char16_t* str, size_t length) {
         return append(str, str + length);
     }
     template <size_t ArrayLength>
     bool append(const char (&array)[ArrayLength]) {
+        static_assert(ArrayLength > 0, "null-terminated");
+        MOZ_ASSERT(array[ArrayLength - 1] == '\0');
         return append(array, ArrayLength - 1);
     }
     char16_t getChar(size_t index) {
         return stringBuffer_.getChar(index);
     }
     size_t length() {
         return stringBuffer_.length();
     }
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -665,19 +665,16 @@ ModuleGenerator::allocateGlobal(GlobalDe
       case ValType::I16x8:
       case ValType::I32x4:
       case ValType::F32x4:
       case ValType::B8x16:
       case ValType::B16x8:
       case ValType::B32x4:
         width = 16;
         break;
-      case ValType::Limit:
-        MOZ_CRASH("Limit");
-        break;
     }
 
     uint32_t offset;
     if (!allocateGlobalBytes(width, width, &offset))
         return false;
 
     global->setOffset(offset);
     return true;
@@ -855,16 +852,23 @@ ModuleGenerator::addElemSegment(InitExpr
             shared_->tables[0].external = true;
             break;
         }
     }
 
     return elemSegments_.emplaceBack(0, offset, Move(elemFuncIndices));
 }
 
+void
+ModuleGenerator::setDataSegments(DataSegmentVector&& segments)
+{
+    MOZ_ASSERT(dataSegments_.empty());
+    dataSegments_ = Move(segments);
+}
+
 bool
 ModuleGenerator::startFuncDefs()
 {
     MOZ_ASSERT(!startedFuncDefs_);
     MOZ_ASSERT(!finishedFuncDefs_);
 
     // Now that it is known whether tables are internal or external, mark the
     // elements of any external table as exported since they may be called from
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -187,17 +187,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     MOZ_MUST_USE bool startFuncDef(uint32_t lineOrBytecode, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDef(uint32_t funcDefIndex, FunctionGenerator* fg);
     MOZ_MUST_USE bool finishFuncDefs();
 
     // Start function:
     bool setStartFunction(uint32_t funcIndex);
 
     // Segments:
-    MOZ_MUST_USE bool addDataSegment(DataSegment s) { return dataSegments_.append(s); }
+    void setDataSegments(DataSegmentVector&& segments);
     MOZ_MUST_USE bool addElemSegment(InitExpr offset, Uint32Vector&& elemFuncIndices);
 
     // Function names:
     void setFuncNames(NameInBytecodeVector&& funcNames);
 
     // asm.js lazy initialization:
     void initSig(uint32_t sigIndex, Sig&& sig);
     void initFuncDefSig(uint32_t funcIndex, uint32_t sigIndex);
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -161,17 +161,16 @@ Instance::callImport(JSContext* cx, uint
           }
           case ValType::I8x16:
           case ValType::I16x8:
           case ValType::I32x4:
           case ValType::F32x4:
           case ValType::B8x16:
           case ValType::B16x8:
           case ValType::B32x4:
-          case ValType::Limit:
             MOZ_CRASH("unhandled type in callImport");
         }
     }
 
     FuncImportTls& import = funcImportTls(fi);
     RootedFunction importFun(cx, &import.obj->as<JSFunction>());
     RootedValue fval(cx, ObjectValue(*import.obj));
     RootedValue thisv(cx, UndefinedValue());
@@ -234,17 +233,16 @@ Instance::callImport(JSContext* cx, uint
           case ValType::F64:   type = TypeSet::DoubleType(); break;
           case ValType::I8x16: MOZ_CRASH("NYI");
           case ValType::I16x8: MOZ_CRASH("NYI");
           case ValType::I32x4: MOZ_CRASH("NYI");
           case ValType::F32x4: MOZ_CRASH("NYI");
           case ValType::B8x16: MOZ_CRASH("NYI");
           case ValType::B16x8: MOZ_CRASH("NYI");
           case ValType::B32x4: MOZ_CRASH("NYI");
-          case ValType::Limit: MOZ_CRASH("Limit");
         }
         if (!TypeScript::ArgTypes(script, i)->hasType(type))
             return true;
     }
 
     // Let's optimize it!
     if (!script->baselineScript()->addDependentWasmImport(cx, *this, funcImportIndex))
         return false;
@@ -635,18 +633,16 @@ Instance::callExport(JSContext* cx, uint
           case ValType::B32x4: {
             SimdConstant simd;
             if (!ToSimdConstant<Bool32x4>(cx, v, &simd))
                 return false;
             // Bool32x4 uses the same representation as Int32x4.
             memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize);
             break;
           }
-          case ValType::Limit:
-            MOZ_CRASH("Limit");
         }
     }
 
     {
         // Push a WasmActivation to describe the wasm frames we're about to push
         // when running this module. Additionally, push a JitActivation so that
         // the optimized wasm-to-Ion FFI call path (which we want to be very
         // fast) can avoid doing so. The JitActivation is marked as inactive so
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -16,16 +16,17 @@
  * limitations under the License.
  */
 
 #include "asmjs/WasmIonCompile.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "asmjs/WasmBaselineCompile.h"
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmBinaryIterator.h"
 #include "asmjs/WasmGenerator.h"
 #include "asmjs/WasmSignalHandlers.h"
 
 #include "jit/CodeGenerator.h"
 
 using namespace js;
 using namespace js::jit;
@@ -258,18 +259,16 @@ class FunctionCompiler
               case ValType::B16x8:
                 // Bool16x8 uses the same data layout as Int16x8.
                 ins = MSimdConstant::New(alloc(), SimdConstant::SplatX8(0), MIRType::Bool16x8);
                 break;
               case ValType::B32x4:
                 // Bool32x4 uses the same data layout as Int32x4.
                 ins = MSimdConstant::New(alloc(), SimdConstant::SplatX4(0), MIRType::Bool32x4);
                 break;
-              case ValType::Limit:
-                MOZ_CRASH("Limit");
             }
 
             curBlock_->add(ins);
             curBlock_->initSlot(info().localSlot(i), ins);
             if (!mirGen_.ensureBallast())
                 return false;
         }
 
@@ -1947,24 +1946,24 @@ EmitCall(FunctionCompiler& f)
     if (IsVoid(sig.ret()))
         return true;
 
     f.iter().setResult(def);
     return true;
 }
 
 static bool
-EmitCallImport(FunctionCompiler& f)
+EmitOldCallImport(FunctionCompiler& f)
 {
     MOZ_ASSERT(!f.mg().firstFuncDefIndex);
 
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     uint32_t funcImportIndex;
-    if (!f.iter().readCallImport(&funcImportIndex))
+    if (!f.iter().readCall(&funcImportIndex))
         return false;
 
     if (f.inDeadCode())
         return true;
 
     return EmitCallImportCommon(f, lineOrBytecode, funcImportIndex);
 }
 
@@ -2679,17 +2678,17 @@ SimdToLaneType(ValType type)
       case ValType::F32x4:  return ValType::F32;
       case ValType::B8x16:
       case ValType::B16x8:
       case ValType::B32x4:  return ValType::I32; // Boolean lanes are Int32 in asm.
       case ValType::I32:
       case ValType::I64:
       case ValType::F32:
       case ValType::F64:
-      case ValType::Limit:;
+        break;
     }
     MOZ_CRASH("bad simd type");
 }
 
 static bool
 EmitExtractLane(FunctionCompiler& f, ValType operandType, SimdSign sign)
 {
     uint8_t lane;
@@ -2965,17 +2964,16 @@ EmitSimdCtor(FunctionCompiler& f, ValTyp
         f.iter().setResult(f.constructSimd<MSimdValueX4>(args[0], args[1], args[2], args[3],
                            MIRType::Bool32x4));
         return true;
       }
       case ValType::I32:
       case ValType::I64:
       case ValType::F32:
       case ValType::F64:
-      case ValType::Limit:
         break;
     }
     MOZ_CRASH("unexpected SIMD type");
 }
 
 static bool
 EmitSimdOp(FunctionCompiler& f, ValType type, SimdOperation op, SimdSign sign)
 {
@@ -3077,17 +3075,17 @@ EmitGrowMemory(FunctionCompiler& f)
     CallCompileState args(f, lineOrBytecode);
     if (!f.startCall(&args))
         return false;
 
     if (!f.passInstance(&args))
         return false;
 
     MDefinition* delta;
-    if (!f.iter().readUnary(ValType::I32, &delta))
+    if (!f.iter().readGrowMemory(&delta))
         return false;
 
     if (!f.passArg(delta, ValType::I32, &args))
         return false;
 
     // As a short-cut, pretend this is an inter-module call so that any pinned
     // heap pointer will be reloaded after the call. This hack will go away once
     // we can stop pinning registers.
@@ -3103,17 +3101,17 @@ EmitGrowMemory(FunctionCompiler& f)
 
 static bool
 EmitCurrentMemory(FunctionCompiler& f)
 {
     uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
 
     CallCompileState args(f, lineOrBytecode);
 
-    if (!f.iter().readNullary(ValType::I32))
+    if (!f.iter().readCurrentMemory())
         return false;
 
     if (!f.startCall(&args))
         return false;
 
     if (!f.passInstance(&args))
         return false;
 
@@ -3169,18 +3167,18 @@ EmitExpr(FunctionCompiler& f)
 
       // Calls
       case Expr::Call:
         return EmitCall(f);
       case Expr::CallIndirect:
         return EmitCallIndirect(f, /* oldStyle = */ false);
       case Expr::OldCallIndirect:
         return EmitCallIndirect(f, /* oldStyle = */ true);
-      case Expr::CallImport:
-        return EmitCallImport(f);
+      case Expr::OldCallImport:
+        return EmitOldCallImport(f);
 
       // Locals and globals
       case Expr::GetLocal:
         return EmitGetLocal(f);
       case Expr::SetLocal:
         return EmitSetLocal(f);
       case Expr::TeeLocal:
         return EmitTeeLocal(f);
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -67,16 +67,19 @@ wasm::HasCompilerSupport(ExclusiveContex
 
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     return false;
 #else
     return true;
 #endif
 }
 
+// ============================================================================
+// Imports
+
 template<typename T>
 JSObject*
 js::wasm::CreateCustomNaNObject(JSContext* cx, T* addr)
 {
     MOZ_ASSERT(IsNaN(*addr));
 
     RootedObject obj(cx, JS_NewPlainObject(cx));
     if (!obj)
@@ -177,19 +180,16 @@ wasm::ReadI64Object(JSContext* cx, Handl
     if (!JS_GetProperty(cx, obj, "high", &val))
         return false;
     if (!ToInt32(cx, val, &i32[1]))
         return false;
 
     return true;
 }
 
-// ============================================================================
-// (Temporary) Wasm class and static methods
-
 static bool
 ThrowBadImportArg(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_ARG);
     return false;
 }
 
 static bool
@@ -327,16 +327,19 @@ GetImports(JSContext* cx,
         }
     }
 
     MOZ_ASSERT(globalIndex == globals.length() || !globals[globalIndex].isImport());
 
     return true;
 }
 
+// ============================================================================
+// Fuzzing support
+
 static bool
 DescribeScriptedCaller(JSContext* cx, ScriptedCaller* scriptedCaller)
 {
     // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
     // found, not whether an error was thrown. This wrapper function converts
     // back to the more ordinary false-if-error form.
 
     JS::AutoFilename af;
@@ -390,66 +393,16 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
     RootedWasmMemoryObject memory(cx);
     ValVector globals;
     if (!GetImports(cx, *module, importObj, &funcs, &table, &memory, &globals))
         return false;
 
     return module->instantiate(cx, funcs, table, memory, globals, nullptr, instanceObj);
 }
 
-#if JS_HAS_TOSOURCE
-static bool
-wasm_toSource(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setString(cx->names().Wasm);
-    return true;
-}
-#endif
-
-static const JSFunctionSpec wasm_static_methods[] = {
-#if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,     wasm_toSource,     0, 0),
-#endif
-    JS_FS_END
-};
-
-const Class js::WasmClass = {
-    js_Wasm_str,
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Wasm)
-};
-
-JSObject*
-js::InitWasmClass(JSContext* cx, HandleObject global)
-{
-    MOZ_RELEASE_ASSERT(HasCompilerSupport(cx));
-    MOZ_ASSERT(cx->options().wasm());
-
-    RootedObject proto(cx, global->as<GlobalObject>().getOrCreateObjectPrototype(cx));
-    if (!proto)
-        return nullptr;
-
-    RootedObject Wasm(cx, NewObjectWithGivenProto(cx, &WasmClass, proto, SingletonObject));
-    if (!Wasm)
-        return nullptr;
-
-    if (!JS_DefineProperty(cx, global, js_Wasm_str, Wasm, JSPROP_RESOLVING))
-        return nullptr;
-
-    RootedValue version(cx, Int32Value(EncodingVersion));
-    if (!JS_DefineProperty(cx, Wasm, "experimentalVersion", version, JSPROP_RESOLVING))
-        return nullptr;
-
-    if (!JS_DefineFunctions(cx, Wasm, wasm_static_methods))
-        return nullptr;
-
-    global->as<GlobalObject>().setConstructor(JSProto_Wasm, ObjectValue(*Wasm));
-    return Wasm;
-}
-
 // ============================================================================
 // Common functions
 
 static bool
 ToNonWrappingUint32(JSContext* cx, HandleValue v, uint32_t max, const char* kind, const char* noun,
                     uint32_t* u32)
 {
     double dbl;
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -126,28 +126,16 @@ class Export
     uint32_t funcIndex() const;
     uint32_t globalIndex() const;
 
     WASM_DECLARE_SERIALIZABLE(Export)
 };
 
 typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
 
-// DataSegment describes the offset of a data segment in the bytecode that is
-// to be copied at a given offset into linear memory upon instantiation.
-
-struct DataSegment
-{
-    InitExpr offset;
-    uint32_t bytecodeOffset;
-    uint32_t length;
-};
-
-typedef Vector<DataSegment, 0, SystemAllocPolicy> DataSegmentVector;
-
 // ElemSegment represents an element segment in the module where each element
 // describes both its function index and its code range.
 
 struct ElemSegment
 {
     uint32_t tableIndex;
     InitExpr offset;
     Uint32Vector elemFuncIndices;
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -23,17 +23,17 @@
 #include "mozilla/Maybe.h"
 
 #include "jsdtoa.h"
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsstr.h"
 
 #include "asmjs/WasmAST.h"
-#include "asmjs/WasmBinary.h"
+#include "asmjs/WasmBinaryFormat.h"
 #include "asmjs/WasmTypes.h"
 #include "ds/LifoAlloc.h"
 #include "js/CharacterEncoding.h"
 #include "js/HashTable.h"
 
 using namespace js;
 using namespace js::wasm;
 
@@ -68,36 +68,37 @@ class WasmToken
         Align,
         AnyFunc,
         BinaryOpcode,
         Block,
         Br,
         BrIf,
         BrTable,
         Call,
-        CallImport,
         CallIndirect,
         CloseParen,
         ComparisonOpcode,
         Const,
         ConversionOpcode,
+        CurrentMemory,
         Data,
         Drop,
         Elem,
         Else,
         End,
         EndOfFile,
         Equal,
         Error,
         Export,
         Float,
         Func,
         GetGlobal,
         GetLocal,
         Global,
+        GrowMemory,
         If,
         Import,
         Index,
         Memory,
         NegativeZero,
         Load,
         Local,
         Loop,
@@ -105,29 +106,27 @@ class WasmToken
         Mutable,
         Name,
         Nop,
         Offset,
         OpenParen,
         Param,
         Result,
         Return,
-        Segment,
         SetGlobal,
         SetLocal,
         SignedInteger,
         Start,
         Store,
         Table,
         TeeLocal,
         TernaryOpcode,
         Text,
         Then,
         Type,
-        NullaryOpcode,
         UnaryOpcode,
         Unreachable,
         UnsignedInteger,
         ValueType
     };
   private:
     Kind kind_;
     const char16_t* begin_;
@@ -200,17 +199,17 @@ class WasmToken
     explicit WasmToken(Kind kind, Expr expr, const char16_t* begin, const char16_t* end)
       : kind_(kind),
         begin_(begin),
         end_(end)
     {
         MOZ_ASSERT(begin != end);
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == TernaryOpcode ||
                    kind_ == ComparisonOpcode || kind_ == ConversionOpcode ||
-                   kind_ == Load || kind_ == Store || kind_ == NullaryOpcode);
+                   kind_ == Load || kind_ == Store);
         u.expr_ = expr;
     }
     explicit WasmToken(const char16_t* begin)
       : kind_(Error),
         begin_(begin),
         end_(begin)
     {}
     Kind kind() const {
@@ -251,46 +250,46 @@ class WasmToken
     }
     ValType valueType() const {
         MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
         return u.valueType_;
     }
     Expr expr() const {
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == TernaryOpcode ||
                    kind_ == ComparisonOpcode || kind_ == ConversionOpcode ||
-                   kind_ == Load || kind_ == Store || kind_ == NullaryOpcode);
+                   kind_ == Load || kind_ == Store);
         return u.expr_;
     }
     bool isOpcode() const {
         switch (kind_) {
           case BinaryOpcode:
           case Block:
           case Br:
           case BrIf:
           case BrTable:
           case Call:
-          case CallImport:
           case CallIndirect:
           case ComparisonOpcode:
           case Const:
           case ConversionOpcode:
+          case CurrentMemory:
           case Drop:
           case GetGlobal:
           case GetLocal:
+          case GrowMemory:
           case If:
           case Load:
           case Loop:
           case Nop:
           case Return:
           case SetGlobal:
           case SetLocal:
           case Store:
           case TeeLocal:
           case TernaryOpcode:
-          case NullaryOpcode:
           case UnaryOpcode:
           case Unreachable:
             return true;
           case Align:
           case AnyFunc:
           case CloseParen:
           case Data:
           case Elem:
@@ -310,17 +309,16 @@ class WasmToken
           case NegativeZero:
           case Local:
           case Module:
           case Name:
           case Offset:
           case OpenParen:
           case Param:
           case Result:
-          case Segment:
           case SignedInteger:
           case Start:
           case Table:
           case Text:
           case Then:
           case Type:
           case UnsignedInteger:
           case ValueType:
@@ -850,22 +848,20 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Br, begin, cur_);
         }
         break;
 
       case 'c':
         if (consume(u"call")) {
             if (consume(u"_indirect"))
                 return WasmToken(WasmToken::CallIndirect, begin, cur_);
-            if (consume(u"_import"))
-                return WasmToken(WasmToken::CallImport, begin, cur_);
             return WasmToken(WasmToken::Call, begin, cur_);
         }
         if (consume(u"current_memory"))
-            return WasmToken(WasmToken::NullaryOpcode, Expr::CurrentMemory, begin, cur_);
+            return WasmToken(WasmToken::CurrentMemory, begin, cur_);
         break;
 
       case 'd':
         if (consume(u"data"))
             return WasmToken(WasmToken::Data, begin, cur_);
         if (consume(u"drop"))
             return WasmToken(WasmToken::Drop, begin, cur_);
         break;
@@ -1092,17 +1088,17 @@ WasmTokenStream::next()
       case 'g':
         if (consume(u"get_global"))
             return WasmToken(WasmToken::GetGlobal, begin, cur_);
         if (consume(u"get_local"))
             return WasmToken(WasmToken::GetLocal, begin, cur_);
         if (consume(u"global"))
             return WasmToken(WasmToken::Global, begin, cur_);
         if (consume(u"grow_memory"))
-            return WasmToken(WasmToken::UnaryOpcode, Expr::GrowMemory, begin, cur_);
+            return WasmToken(WasmToken::GrowMemory, begin, cur_);
         break;
 
       case 'i':
         if (consume(u"i32")) {
             if (!consume(u"."))
                 return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
 
             switch (*cur_) {
@@ -1407,17 +1403,17 @@ WasmTokenStream::next()
         if (consume(u"mut"))
             return WasmToken(WasmToken::Mutable, begin, cur_);
         break;
 
       case 'n':
         if (consume(u"nan"))
             return nan(begin);
         if (consume(u"nop"))
-            return WasmToken(WasmToken::NullaryOpcode, Expr::Nop, begin, cur_);
+            return WasmToken(WasmToken::Nop, begin, cur_);
         break;
 
       case 'o':
         if (consume(u"offset"))
             return WasmToken(WasmToken::Offset, begin, cur_);
         break;
 
       case 'p':
@@ -1434,18 +1430,16 @@ WasmTokenStream::next()
 
       case 's':
         if (consume(u"select"))
             return WasmToken(WasmToken::TernaryOpcode, Expr::Select, begin, cur_);
         if (consume(u"set_global"))
             return WasmToken(WasmToken::SetGlobal, begin, cur_);
         if (consume(u"set_local"))
             return WasmToken(WasmToken::SetLocal, begin, cur_);
-        if (consume(u"segment"))
-            return WasmToken(WasmToken::Segment, begin, cur_);
         if (consume(u"start"))
             return WasmToken(WasmToken::Start, begin, cur_);
         break;
 
       case 't':
         if (consume(u"table"))
             return WasmToken(WasmToken::Table, begin, cur_);
         if (consume(u"tee_local"))
@@ -1656,31 +1650,29 @@ ParseArgs(WasmParseContext& c, AstExprVe
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
     }
 
     return true;
 }
 
 static AstCall*
-ParseCall(WasmParseContext& c, Expr expr, bool inParens)
+ParseCall(WasmParseContext& c, bool inParens)
 {
-    MOZ_ASSERT(expr == Expr::Call || expr == Expr::CallImport);
-
     AstRef func;
     if (!c.ts.matchRef(&func, c.error))
         return nullptr;
 
     AstExprVector args(c.lifo);
     if (inParens) {
         if (!ParseArgs(c, &args))
             return nullptr;
     }
 
-    return new(c.lifo) AstCall(expr, ExprType::Void, func, Move(args));
+    return new(c.lifo) AstCall(Expr::Call, ExprType::Void, func, Move(args));
 }
 
 static AstCallIndirect*
 ParseCallIndirect(WasmParseContext& c, bool inParens)
 {
     AstRef sig;
     if (!c.ts.matchRef(&sig, c.error))
         return nullptr;
@@ -2093,22 +2085,16 @@ ParseUnaryOperator(WasmParseContext& c, 
 {
     AstExpr* op = ParseExpr(c, inParens);
     if (!op)
         return nullptr;
 
     return new(c.lifo) AstUnaryOperator(expr, op);
 }
 
-static AstNullaryOperator*
-ParseNullaryOperator(WasmParseContext& c, Expr expr)
-{
-    return new(c.lifo) AstNullaryOperator(expr);
-}
-
 static AstBinaryOperator*
 ParseBinaryOperator(WasmParseContext& c, Expr expr, bool inParens)
 {
     AstExpr* lhs = ParseExpr(c, inParens);
     if (!lhs)
         return nullptr;
 
     AstExpr* rhs = ParseExpr(c, inParens);
@@ -2211,17 +2197,17 @@ ParseIf(WasmParseContext& c, bool inPare
             AstExpr* elseBranch = ParseExprInsideParens(c);
             if (!elseBranch || !elseExprs.append(elseBranch))
                 return nullptr;
         }
         if (inParens) {
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
-            if (!c.ts.getIf(WasmToken::End))
+            if (!c.ts.match(WasmToken::End, c.error))
                 return nullptr;
         }
     }
 
     return new(c.lifo) AstIf(type, cond, name, Move(thenExprs), Move(elseExprs));
 }
 
 static bool
@@ -2386,16 +2372,26 @@ ParseBranchTable(WasmParseContext& c, Wa
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         }
     }
 
     return new(c.lifo) AstBranchTable(*index, def, Move(table), value);
 }
 
+static AstGrowMemory*
+ParseGrowMemory(WasmParseContext& c, bool inParens)
+{
+    AstExpr* op = ParseExpr(c, inParens);
+    if (!op)
+        return nullptr;
+
+    return new(c.lifo) AstGrowMemory(op);
+}
+
 static AstExpr*
 ParseExprBody(WasmParseContext& c, WasmToken token, bool inParens)
 {
     switch (token.kind()) {
       case WasmToken::Unreachable:
         return new(c.lifo) AstUnreachable;
       case WasmToken::BinaryOpcode:
         return ParseBinaryOperator(c, token.expr(), inParens);
@@ -2403,19 +2399,17 @@ ParseExprBody(WasmParseContext& c, WasmT
         return ParseBlock(c, Expr::Block, inParens);
       case WasmToken::Br:
         return ParseBranch(c, Expr::Br, inParens);
       case WasmToken::BrIf:
         return ParseBranch(c, Expr::BrIf, inParens);
       case WasmToken::BrTable:
         return ParseBranchTable(c, token, inParens);
       case WasmToken::Call:
-        return ParseCall(c, Expr::Call, inParens);
-      case WasmToken::CallImport:
-        return ParseCall(c, Expr::Call, inParens);
+        return ParseCall(c, inParens);
       case WasmToken::CallIndirect:
         return ParseCallIndirect(c, inParens);
       case WasmToken::ComparisonOpcode:
         return ParseComparisonOperator(c, token.expr(), inParens);
       case WasmToken::Const:
         return ParseConst(c, token);
       case WasmToken::ConversionOpcode:
         return ParseConversionOperator(c, token.expr(), inParens);
@@ -2440,18 +2434,22 @@ ParseExprBody(WasmParseContext& c, WasmT
       case WasmToken::Store:
         return ParseStore(c, token.expr(), inParens);
       case WasmToken::TeeLocal:
         return ParseTeeLocal(c, inParens);
       case WasmToken::TernaryOpcode:
         return ParseTernaryOperator(c, token.expr(), inParens);
       case WasmToken::UnaryOpcode:
         return ParseUnaryOperator(c, token.expr(), inParens);
-      case WasmToken::NullaryOpcode:
-        return ParseNullaryOperator(c, token.expr());
+      case WasmToken::Nop:
+        return new(c.lifo) AstNop();
+      case WasmToken::CurrentMemory:
+        return new(c.lifo) AstCurrentMemory();
+      case WasmToken::GrowMemory:
+        return ParseGrowMemory(c, inParens);
       default:
         c.ts.generateError(token, c.error);
         return nullptr;
     }
 }
 
 static AstExpr*
 ParseExprInsideParens(WasmParseContext& c)
@@ -2839,22 +2837,24 @@ ParseStartFunc(WasmParseContext& c, Wasm
         c.ts.generateError(token, c.error);
         return false;
     }
 
     return true;
 }
 
 static bool
-ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
+ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, bool* isMutable)
 {
+    *isMutable = false;
+
     // Either (mut i32) or i32.
     if (c.ts.getIf(WasmToken::OpenParen)) {
         // Immutable by default.
-        *flags = c.ts.getIf(WasmToken::Mutable) ? 0x1 : 0x0;
+        *isMutable = c.ts.getIf(WasmToken::Mutable);
         if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
             return false;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
         return true;
     }
 
     return c.ts.match(WasmToken::ValueType, typeToken, c.error);
@@ -2914,24 +2914,24 @@ ParseImport(WasmParseContext& c, AstModu
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
                                          DefinitionKind::Table, table);
         }
         if (c.ts.getIf(WasmToken::Global)) {
             if (name.empty())
                 name = c.ts.getIfName();
 
             WasmToken typeToken;
-            uint32_t flags = 0;
-            if (!ParseGlobalType(c, &typeToken, &flags))
+            bool isMutable;
+            if (!ParseGlobalType(c, &typeToken, &isMutable))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
 
             return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
-                                         AstGlobal(AstName(), typeToken.valueType(), flags));
+                                         AstGlobal(AstName(), typeToken.valueType(), isMutable));
         }
         if (c.ts.getIf(WasmToken::Func)) {
             if (name.empty())
                 name = c.ts.getIfName();
 
             AstRef sigRef;
             if (!ParseFuncType(c, &sigRef, module))
                 return nullptr;
@@ -3151,61 +3151,62 @@ ParseElemSegment(WasmParseContext& c)
 }
 
 static bool
 ParseGlobal(WasmParseContext& c, AstModule* module)
 {
     AstName name = c.ts.getIfName();
 
     WasmToken typeToken;
-    uint32_t flags = 0;
+    bool isMutable;
 
     WasmToken openParen;
     if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
         if (c.ts.getIf(WasmToken::Import)) {
             if (module->globals().length()) {
                 c.ts.generateError(openParen, "import after global definition", c.error);
                 return false;
             }
 
             InlineImport names;
             if (!ParseInlineImport(c, &names))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
 
-            if (!ParseGlobalType(c, &typeToken, &flags))
+            if (!ParseGlobalType(c, &typeToken, &isMutable))
                 return false;
 
             auto* imp = new(c.lifo) AstImport(name, names.module.text(), names.field.text(),
-                                              AstGlobal(AstName(), typeToken.valueType(), flags));
+                                              AstGlobal(AstName(), typeToken.valueType(),
+                                                        isMutable));
             return imp && module->append(imp);
         }
 
         if (c.ts.getIf(WasmToken::Export)) {
             AstRef ref = name.empty()
                          ? AstRef(AstName(), module->globals().length())
                          : AstRef(name, AstNoIndex);
             if (!ParseInlineExport(c, DefinitionKind::Global, module, ref))
                 return false;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return false;
         } else {
             c.ts.unget(openParen);
         }
     }
 
-    if (!ParseGlobalType(c, &typeToken, &flags))
+    if (!ParseGlobalType(c, &typeToken, &isMutable))
         return false;
 
     AstExpr* init = ParseExpr(c, true);
     if (!init)
         return false;
 
-    auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
+    auto* glob = new(c.lifo) AstGlobal(name, typeToken.valueType(), isMutable, Some(init));
     return glob && module->append(glob);
 }
 
 static AstModule*
 ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
 {
     WasmParseContext c(text, lifo, error);
 
@@ -3489,27 +3490,23 @@ ResolveArgs(Resolver& r, const AstExprVe
     }
 
     return true;
 }
 
 static bool
 ResolveCall(Resolver& r, AstCall& c)
 {
+    MOZ_ASSERT(c.expr() == Expr::Call);
+
     if (!ResolveArgs(r, c.args()))
         return false;
 
-    if (c.expr() == Expr::Call) {
-        if (!r.resolveFunction(c.func()))
-            return false;
-    } else {
-        MOZ_ASSERT(c.expr() == Expr::CallImport);
-        if (!r.resolveImport(c.func()))
-            return false;
-    }
+    if (!r.resolveFunction(c.func()))
+        return false;
 
     return true;
 }
 
 static bool
 ResolveCallIndirect(Resolver& r, AstCallIndirect& c)
 {
     if (!ResolveArgs(r, c.args()))
@@ -3580,16 +3577,22 @@ ResolveTeeLocal(Resolver& r, AstTeeLocal
 
 static bool
 ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
 {
     return ResolveExpr(r, *b.op());
 }
 
 static bool
+ResolveGrowMemory(Resolver& r, AstGrowMemory& gm)
+{
+    return ResolveExpr(r, *gm.op());
+}
+
+static bool
 ResolveBinaryOperator(Resolver& r, AstBinaryOperator& b)
 {
     return ResolveExpr(r, *b.lhs()) &&
            ResolveExpr(r, *b.rhs());
 }
 
 static bool
 ResolveTernaryOperator(Resolver& r, AstTernaryOperator& b)
@@ -3672,18 +3675,18 @@ ResolveBranchTable(Resolver& r, AstBranc
 }
 
 static bool
 ResolveExpr(Resolver& r, AstExpr& expr)
 {
     switch (expr.kind()) {
       case AstExprKind::Nop:
       case AstExprKind::Pop:
-      case AstExprKind::NullaryOperator:
       case AstExprKind::Unreachable:
+      case AstExprKind::CurrentMemory:
         return true;
       case AstExprKind::Drop:
         return ResolveDropOperator(r, expr.as<AstDrop>());
       case AstExprKind::BinaryOperator:
         return ResolveBinaryOperator(r, expr.as<AstBinaryOperator>());
       case AstExprKind::Block:
         return ResolveBlock(r, expr.as<AstBlock>());
       case AstExprKind::Branch:
@@ -3719,16 +3722,18 @@ ResolveExpr(Resolver& r, AstExpr& expr)
       case AstExprKind::BranchTable:
         return ResolveBranchTable(r, expr.as<AstBranchTable>());
       case AstExprKind::TeeLocal:
         return ResolveTeeLocal(r, expr.as<AstTeeLocal>());
       case AstExprKind::TernaryOperator:
         return ResolveTernaryOperator(r, expr.as<AstTernaryOperator>());
       case AstExprKind::UnaryOperator:
         return ResolveUnaryOperator(r, expr.as<AstUnaryOperator>());
+      case AstExprKind::GrowMemory:
+        return ResolveGrowMemory(r, expr.as<AstGrowMemory>());
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 static bool
 ResolveFunc(Resolver& r, AstFunc& func)
 {
     r.beginFunc();
@@ -3855,17 +3860,17 @@ EncodeExprList(Encoder& e, const AstExpr
 }
 
 static bool
 EncodeBlock(Encoder& e, AstBlock& b)
 {
     if (!e.writeExpr(b.expr()))
         return false;
 
-    if (!e.writeExprType(b.type()))
+    if (!e.writeBlockType(b.type()))
         return false;
 
     if (!EncodeExprList(e, b.exprs()))
         return false;
 
     if (!e.writeExpr(Expr::End))
         return false;
 
@@ -3938,16 +3943,19 @@ EncodeCallIndirect(Encoder& e, AstCallIn
         return false;
 
     if (!e.writeExpr(Expr::CallIndirect))
         return false;
 
     if (!e.writeVarU32(c.sig().index()))
         return false;
 
+    if (!e.writeVarU32(uint32_t(MemoryTableFlags::Default)))
+        return false;
+
     return true;
 }
 
 static bool
 EncodeConst(Encoder& e, AstConst& c)
 {
     switch (c.val().type()) {
       case ValType::I32:
@@ -4016,22 +4024,16 @@ EncodeSetGlobal(Encoder& e, AstSetGlobal
 static bool
 EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
 {
     return EncodeExpr(e, *b.op()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EncodeNullaryOperator(Encoder& e, AstNullaryOperator& b)
-{
-    return e.writeExpr(b.expr());
-}
-
-static bool
 EncodeBinaryOperator(Encoder& e, AstBinaryOperator& b)
 {
     return EncodeExpr(e, *b.lhs()) &&
            EncodeExpr(e, *b.rhs()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
@@ -4059,17 +4061,17 @@ EncodeConversionOperator(Encoder& e, Ast
 }
 
 static bool
 EncodeIf(Encoder& e, AstIf& i)
 {
     if (!EncodeExpr(e, i.cond()) || !e.writeExpr(Expr::If))
         return false;
 
-    if (!e.writeExprType(i.type()))
+    if (!e.writeBlockType(i.type()))
         return false;
 
     if (!EncodeExprList(e, i.thenExprs()))
         return false;
 
     if (i.hasElse()) {
         if (!e.writeExpr(Expr::Else))
             return false;
@@ -4148,16 +4150,43 @@ EncodeBranchTable(Encoder& e, AstBranchT
 
     if (!e.writeVarU32(bt.def().index()))
         return false;
 
     return true;
 }
 
 static bool
+EncodeCurrentMemory(Encoder& e, AstCurrentMemory& cm)
+{
+    if (!e.writeExpr(Expr::CurrentMemory))
+        return false;
+
+    if (!e.writeVarU32(uint32_t(MemoryTableFlags::Default)))
+        return false;
+
+    return true;
+}
+
+static bool
+EncodeGrowMemory(Encoder& e, AstGrowMemory& gm)
+{
+    if (!EncodeExpr(e, *gm.op()))
+        return false;
+
+    if (!e.writeExpr(Expr::GrowMemory))
+        return false;
+
+    if (!e.writeVarU32(uint32_t(MemoryTableFlags::Default)))
+        return false;
+
+    return true;
+}
+
+static bool
 EncodeExpr(Encoder& e, AstExpr& expr)
 {
     switch (expr.kind()) {
       case AstExprKind::Pop:
         return true;
       case AstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
       case AstExprKind::Unreachable:
@@ -4201,18 +4230,20 @@ EncodeExpr(Encoder& e, AstExpr& expr)
       case AstExprKind::Store:
         return EncodeStore(e, expr.as<AstStore>());
       case AstExprKind::BranchTable:
         return EncodeBranchTable(e, expr.as<AstBranchTable>());
       case AstExprKind::TernaryOperator:
         return EncodeTernaryOperator(e, expr.as<AstTernaryOperator>());
       case AstExprKind::UnaryOperator:
         return EncodeUnaryOperator(e, expr.as<AstUnaryOperator>());
-      case AstExprKind::NullaryOperator:
-        return EncodeNullaryOperator(e, expr.as<AstNullaryOperator>());
+      case AstExprKind::CurrentMemory:
+        return EncodeCurrentMemory(e, expr.as<AstCurrentMemory>());
+      case AstExprKind::GrowMemory:
+        return EncodeGrowMemory(e, expr.as<AstGrowMemory>());
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 /*****************************************************************************/
 // wasm AST binary serialization
 
 static bool
@@ -4224,17 +4255,17 @@ EncodeTypeSection(Encoder& e, AstModule&
     size_t offset;
     if (!e.startSection(SectionId::Type, &offset))
         return false;
 
     if (!e.writeVarU32(module.sigs().length()))
         return false;
 
     for (AstSig* sig : module.sigs()) {
-        if (!e.writeVarU32(uint32_t(TypeConstructor::Function)))
+        if (!e.writeVarU32(uint32_t(TypeCode::Func)))
             return false;
 
         if (!e.writeVarU32(sig->args().length()))
             return false;
 
         for (ValType t : sig->args()) {
             if (!e.writeValType(t))
                 return false;
@@ -4299,23 +4330,30 @@ EncodeLimits(Encoder& e, const Limits& l
     }
 
     return true;
 }
 
 static bool
 EncodeTableLimits(Encoder& e, const Limits& limits)
 {
-    if (!e.writeVarU32(uint32_t(TypeConstructor::AnyFunc)))
+    if (!e.writeVarU32(uint32_t(TypeCode::AnyFunc)))
         return false;
 
     return EncodeLimits(e, limits);
 }
 
 static bool
+EncodeGlobalType(Encoder& e, const AstGlobal* global)
+{
+    return e.writeValType(global->type()) &&
+           e.writeVarU32(global->isMutable() ? uint32_t(GlobalFlags::IsMutable) : 0);
+}
+
+static bool
 EncodeImport(Encoder& e, AstImport& imp)
 {
     if (!EncodeBytes(e, imp.module()))
         return false;
 
     if (!EncodeBytes(e, imp.field()))
         return false;
 
@@ -4324,19 +4362,17 @@ EncodeImport(Encoder& e, AstImport& imp)
 
     switch (imp.kind()) {
       case DefinitionKind::Function:
         if (!e.writeVarU32(imp.funcSig().index()))
             return false;
         break;
       case DefinitionKind::Global:
         MOZ_ASSERT(!imp.global().hasInit());
-        if (!e.writeValType(imp.global().type()))
-            return false;
-        if (!e.writeVarU32(imp.global().flags()))
+        if (!EncodeGlobalType(e, &imp.global()))
             return false;
         break;
       case DefinitionKind::Table:
         if (!EncodeTableLimits(e, imp.resizable()))
             return false;
         break;
       case DefinitionKind::Memory:
         if (!EncodeLimits(e, imp.resizable()))
@@ -4401,19 +4437,17 @@ EncodeGlobalSection(Encoder& e, AstModul
 
     const AstGlobalVector& globals = module.globals();
 
     if (!e.writeVarU32(globals.length()))
         return false;
 
     for (const AstGlobal* global : globals) {
         MOZ_ASSERT(global->hasInit());
-        if (!e.writeValType(global->type()))
-            return false;
-        if (!e.writeVarU32(global->flags()))
+        if (!EncodeGlobalType(e, global))
             return false;
         if (!EncodeExpr(e, global->init()))
             return false;
         if (!e.writeExpr(Expr::End))