Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Fri, 14 Sep 2018 01:01:37 +0300
changeset 491969 e923330d5bd3
parent 491907 71736f1f0f3e (current diff)
parent 491968 2a521c242436 (diff)
child 491997 0f90d60814d8
child 492021 2633168c3fb7
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
e923330d5bd3 / 64.0a1 / 20180913222046 / files
nightly linux64
e923330d5bd3 / 64.0a1 / 20180913222046 / files
nightly mac
e923330d5bd3 / 64.0a1 / 20180913222046 / files
nightly win32
e923330d5bd3 / 64.0a1 / 20180913222046 / files
nightly win64
e923330d5bd3 / 64.0a1 / 20180913222046 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/base/content/browser.js
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
devtools/client/aboutdebugging-new/src/components/ConnectPage.css
devtools/client/aboutdebugging-new/src/components/ConnectPage.js
dom/indexedDB/ActorsParent.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/CTypes.h
js/src/ctypes/Library.cpp
modules/libpref/init/all.js
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -182,30 +182,30 @@ dependencies = [
  "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "binjs_meta"
-version = "0.3.8"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "Inflector 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "webidl 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "binsource"
 version = "0.1.0"
 dependencies = [
- "binjs_meta 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "binjs_meta 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "webidl 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -2839,17 +2839,17 @@ dependencies = [
 "checksum ascii-canvas 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b385d69402821a1c254533a011a312531cbcc0e3e24f19bbb4747a5a2daf37e2"
 "checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
 "checksum atty 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0fd4c0631f06448cc45a6bbb3b710ebb7ff8ccb96a0800c994afe23a70d5df2"
 "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
 "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9"
 "checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
 "checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
 "checksum bindgen 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eac4ed5f2de9efc3c87cb722468fa49d0763e98f999d539bfc5e452c13d85c91"
-"checksum binjs_meta 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fd7ca5635f1c6f94aaef7de76cb834c5920578355ce41dbcaf731b7ebe348518"
+"checksum binjs_meta 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cc0956bac41c458cf38340699dbb54c2220c91cdbfa33be19670fe69e0a6ac9b"
 "checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
 "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
 "checksum bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80b13e2ab064ff3aa0bdbf1eff533f9822dc37899821f5f98c67f263eab51707"
 "checksum boxfnonce 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8380105befe91099e6f69206164072c05bc92427ff6aa8a5171388317346dd75"
 "checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9"
 "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
 "checksum bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e178b8e0e239e844b083d5a0d4a156b2654e67f9f80144d48398fcd736a24fb8"
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -330,16 +330,18 @@ pref("browser.urlbar.decodeURLsOnCopy", 
 // Whether or not to move tabs into the active window when using the "Switch to
 // Tab" feature of the awesomebar.
 pref("browser.urlbar.switchTabs.adoptIntoActiveWindow", false);
 
 // Whether addresses and search results typed into the address bar
 // should be opened in new tabs by default.
 pref("browser.urlbar.openintab", false);
 
+pref("browser.urlbar.quantumbar", false);
+
 pref("browser.altClickSave", false);
 
 // Enable logging downloads operations to the Console.
 pref("browser.download.loglevel", "Error");
 
 // Number of milliseconds to wait for the http headers (and thus
 // the Content-Disposition filename) before giving up and falling back to
 // picking a filename without that info in hand so that the user sees some
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -531,16 +531,20 @@ toolbar:not(#TabsToolbar) > #personal-bo
 .searchbar-textbox {
   /* Setting a width and min-width to let the location & search bars maintain
      a constant width in case they haven't be resized manually. (bug 965772) */
   width: 1px;
   min-width: 1px;
 }
 
 #urlbar {
+  -moz-binding: url(chrome://browser/content/urlbarBindings.xml#legacy-urlbar);
+}
+
+#urlbar[quantumbar] {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 /* Display URLs left-to-right but right aligned in RTL mode. */
 html|input.urlbar-input:-moz-locale-dir(rtl) {
   direction: ltr !important;
   text-align: right !important;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -62,16 +62,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
   SiteDataManager: "resource:///modules/SiteDataManager.jsm",
   SitePermissions: "resource:///modules/SitePermissions.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
   Translation: "resource:///modules/translation/Translation.jsm",
   UITour: "resource:///modules/UITour.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
+  UrlbarInput: "resource:///modules/UrlbarInput.jsm",
   Utils: "resource://gre/modules/sessionstore/Utils.jsm",
   Weave: "resource://services-sync/main.js",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   fxAccounts: "resource://gre/modules/FxAccounts.jsm",
   webrtcUI: "resource:///modules/webrtcUI.jsm",
   ZoomUI: "resource:///modules/ZoomUI.jsm",
 });
 
@@ -246,37 +247,51 @@ var gMultiProcessBrowser =
   window.docShell
         .QueryInterface(Ci.nsILoadContext)
         .useRemoteTabs;
 
 if (AppConstants.platform != "macosx") {
   var gEditUIVisible = true;
 }
 
-/* globals gNavToolbox, gURLBar:true */
-[
-  ["gNavToolbox",         "navigator-toolbox"],
-  ["gURLBar",             "urlbar"],
-].forEach(function(elementGlobal) {
-  var [name, id] = elementGlobal;
-  Object.defineProperty(window, name, {
-    configurable: true,
-    enumerable: true,
-    get() {
-      var element = document.getElementById(id);
-      if (!element)
-        return null;
-      delete window[name];
-      return window[name] = element;
-    },
-    set(val) {
-      delete window[name];
-      return window[name] = val;
-    },
-  });
+Object.defineProperty(this, "gURLBar", {
+  configurable: true,
+  enumerable: true,
+  get() {
+    delete this.gURLBar;
+
+    let element = document.getElementById("urlbar");
+
+    // For now, always use the legacy implementation in the first window to
+    // have a usable address bar e.g. for accessing about:config.
+    if (BrowserWindowTracker.windowCount <= 1 ||
+        !Services.prefs.getBoolPref("browser.urlbar.quantumbar", false)) {
+      return this.gURLBar = element;
+    }
+
+    // Disable the legacy XBL binding.
+    element.setAttribute("quantumbar", "true");
+
+    // Re-focus the input field if it was focused before switching bindings.
+    if (element.hasAttribute("focused")) {
+      element.inputField.focus();
+    }
+
+    return this.gURLBar =
+      new UrlbarInput(element, document.getElementById("urlbar-results"));
+  },
+});
+
+Object.defineProperty(this, "gNavToolbox", {
+  configurable: true,
+  enumerable: true,
+  get() {
+    delete this.gNavToolbox;
+    return this.gNavToolbox = document.getElementById("navigator-toolbox");
+  },
 });
 
 // Smart getter for the findbar.  If you don't wish to force the creation of
 // the findbar, check gFindBarInitialized first.
 
 Object.defineProperty(this, "gFindBar", {
   configurable: true,
   enumerable: true,
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -208,16 +208,32 @@ xmlns="http://www.w3.org/1999/xhtml"
            id="PopupAutoCompleteRichResult"
            role="group"
            noautofocus="true"
            hidden="true"
            flip="none"
            level="parent"
            overflowpadding="15" />
 
+    <!-- for url bar autocomplete -->
+    <panel id="urlbar-results"
+           role="group"
+           noautofocus="true"
+           hidden="true"
+           flip="none"
+           level="parent">
+      <html:div class="urlbarView-body-outer">
+        <html:div class="urlbarView-body-inner">
+          <!-- TODO: add search suggestions notification -->
+          <html:div class="urlbarView-results"/>
+          <!-- TODO: add footer -->
+        </html:div>
+      </html:div>
+    </panel>
+
    <!-- for date/time picker. consumeoutsideclicks is set to never, so that
         clicks on the anchored input box are never consumed. -->
     <panel id="DateTimePickerPanel"
            type="arrow"
            hidden="true"
            orient="vertical"
            noautofocus="true"
            norolluponanchor="true"
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search.js
@@ -10,31 +10,35 @@ requestLongerTimeout(5);
  * the front-end. Instead of adding more reflows to the whitelist, you should
  * be modifying your code to avoid the reflow.
  *
  * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
  * for tips on how to do that.
  */
 
 /* These reflows happen only the first time the awesomebar panel opens. */
-const EXPECTED_REFLOWS_FIRST_OPEN = [
-  {
+const EXPECTED_REFLOWS_FIRST_OPEN = [];
+if (AppConstants.DEBUG ||
+    AppConstants.platform == "win" ||
+    AppConstants.platform == "macosx") {
+  EXPECTED_REFLOWS_FIRST_OPEN.push({
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "set_oneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
+      "@chrome://browser/content/urlbarBindings.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
-  },
-
+  });
+}
+EXPECTED_REFLOWS_FIRST_OPEN.push(
   {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate@chrome://global/content/bindings/autocomplete.xml",
       "invalidate@chrome://global/content/bindings/autocomplete.xml",
@@ -57,39 +61,39 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
   // Bug 1359989
   {
     stack: [
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
-  },
-];
+  }
+);
 
 // These extra reflows happen on beta/release as one of the default bookmarks in
 // bookmarks.html.in has a long URL.
 if (AppConstants.RELEASE_OR_BETA) {
   EXPECTED_REFLOWS_FIRST_OPEN.push({
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "_onUnderflow@chrome://global/content/bindings/autocomplete.xml",
       "onunderflow@chrome://browser/content/browser.xul",
     ],
     maxCount: 6,
-  });
-  EXPECTED_REFLOWS_FIRST_OPEN.push({
+  },
+  {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "_onOverflow@chrome://global/content/bindings/autocomplete.xml",
       "onoverflow@chrome://browser/content/browser.xul",
     ],
     maxCount: 6,
-  });
-  EXPECTED_REFLOWS_FIRST_OPEN.push({
+  },
+  {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "_adjustAcItem@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate@chrome://global/content/bindings/autocomplete.xml",
       "invalidate@chrome://global/content/bindings/autocomplete.xml",
     ],
     maxCount: 12,
--- a/browser/base/content/test/performance/browser_urlbar_search.js
+++ b/browser/base/content/test/performance/browser_urlbar_search.js
@@ -10,31 +10,36 @@ requestLongerTimeout(5);
  * the front-end. Instead of adding more reflows to the whitelist, you should
  * be modifying your code to avoid the reflow.
  *
  * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers
  * for tips on how to do that.
  */
 
 /* These reflows happen only the first time the awesomebar panel opens. */
-const EXPECTED_REFLOWS_FIRST_OPEN = [
-  {
+const EXPECTED_REFLOWS_FIRST_OPEN = [];
+if (AppConstants.DEBUG ||
+    AppConstants.platform == "linux" ||
+    AppConstants.platform == "macosx" ||
+    AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+  EXPECTED_REFLOWS_FIRST_OPEN.push({
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
       "set_oneOffSearchesEnabled@chrome://browser/content/urlbarBindings.xml",
       "_enableOrDisableOneOffSearches@chrome://browser/content/urlbarBindings.xml",
-      "urlbar_XBL_Constructor/<@chrome://browser/content/urlbarBindings.xml",
+      "@chrome://browser/content/urlbarBindings.xml",
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
-  },
-
+  });
+}
+EXPECTED_REFLOWS_FIRST_OPEN.push(
   {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate@chrome://global/content/bindings/autocomplete.xml",
       "invalidate@chrome://global/content/bindings/autocomplete.xml",
@@ -57,18 +62,18 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
   // Bug 1359989
   {
     stack: [
       "_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
       "openPopup@chrome://global/content/bindings/autocomplete.xml",
       "set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
     ],
-  },
-];
+  }
+);
 
 /* These reflows happen everytime the awesomebar panel opens. */
 const EXPECTED_REFLOWS_SECOND_OPEN = [
   {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
       "_reuseAcItem@chrome://global/content/bindings/autocomplete.xml",
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -16,17 +16,53 @@ file, You can obtain one at http://mozil
 %brandDTD;
 ]>
 
 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
           xmlns:html="http://www.w3.org/1999/xhtml"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+  <binding id="urlbar" extends="chrome://global/content/bindings/textbox.xml#textbox">
+    <content sizetopopup="pref">
+      <xul:hbox flex="1" class="urlbar-textbox-container" tooltip="aHTMLTooltip">
+        <children includes="image|deck|stack|box"/>
+        <xul:moz-input-box anonid="moz-input-box"
+                  class="urlbar-input-box"
+                  flex="1">
+          <children/>
+          <html:input anonid="scheme"
+                      class="urlbar-scheme textbox-input"
+                      required="required"
+                      xbl:inherits="textoverflow,focused"/>
+          <html:input anonid="input"
+                      class="urlbar-input textbox-input"
+                      allowevents="true"
+                      inputmode="mozAwesomebar"
+                      xbl:inherits="value,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey,focused,textoverflow"/>
+        </xul:moz-input-box>
+        <xul:image anonid="urlbar-go-button"
+                   class="urlbar-go-button urlbar-icon"
+                   onclick="gURLBar.handleCommand(event);"
+                   tooltiptext="&goEndCap.tooltip;"
+                   xbl:inherits="pageproxystate,parentfocused=focused,usertyping"/>
+        <xul:dropmarker anonid="historydropmarker"
+                        class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional"
+                        tooltiptext="&urlbar.openHistoryPopup.tooltip;"
+                        allowevents="true"
+                        xbl:inherits="open,parentfocused=focused,usertyping"/>
+        <children includes="hbox"/>
+      </xul:hbox>
+      <xul:popupset anonid="popupset"
+                    class="autocomplete-result-popupset"/>
+      <children includes="toolbarbutton"/>
+    </content>
+  </binding>
+
+  <binding id="legacy-urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
 
     <content sizetopopup="pref">
       <xul:hbox flex="1" class="urlbar-textbox-container" tooltip="aHTMLTooltip">
         <children includes="image|deck|stack|box"/>
         <xul:moz-input-box anonid="moz-input-box"
                   class="urlbar-input-box"
                   flex="1">
           <children/>
--- a/browser/components/urlbar/.eslintrc.js
+++ b/browser/components/urlbar/.eslintrc.js
@@ -1,20 +1,20 @@
 "use strict";
 
 module.exports = {
   rules: {
     "mozilla/var-only-at-top-level": "error",
     "require-jsdoc": ["error", {
         "require": {
-            "FunctionDeclaration": true,
-            "MethodDefinition": true,
+            "FunctionDeclaration": false,
+            "MethodDefinition": false,
             "ClassDeclaration": true,
-            "ArrowFunctionExpression": true,
-            "FunctionExpression": true
+            "ArrowFunctionExpression": false,
+            "FunctionExpression": false
         }
     }],
     "valid-jsdoc": ["error", {
       prefer: {
         return: "returns",
       },
       preferType: {
         Boolean: "boolean",
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -0,0 +1,101 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["UrlbarInput"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  UrlbarView: "resource:///modules/UrlbarView.jsm",
+});
+
+/**
+ * Represents the urlbar <textbox>.
+ * Also forwards important textbox properties and methods.
+ */
+class UrlbarInput {
+  /**
+   * @param {object} textbox
+   *   The <textbox> element.
+   * @param {object} panel
+   *   The <panel> element.
+   */
+  constructor(textbox, panel) {
+    this.textbox = textbox;
+    this.panel = panel;
+    this.view = new UrlbarView(this);
+    this.valueIsTyped = false;
+    this.userInitiatedFocus = false;
+
+    const METHODS = ["addEventListener", "removeEventListener",
+      "setAttribute", "hasAttribute", "removeAttribute", "getAttribute",
+      "focus", "blur", "select"];
+    const READ_ONLY_PROPERTIES = ["focused", "inputField", "editor"];
+    const READ_WRITE_PROPERTIES = ["value", "placeholder", "readOnly",
+      "selectionStart", "selectionEnd"];
+
+    for (let method of METHODS) {
+      this[method] = (...args) => {
+        this.textbox[method](...args);
+      };
+    }
+
+    for (let property of READ_ONLY_PROPERTIES) {
+      Object.defineProperty(this, property, {
+        enumerable: true,
+        get() {
+          return this.textbox[property];
+        },
+      });
+    }
+
+    for (let property of READ_WRITE_PROPERTIES) {
+      Object.defineProperty(this, property, {
+        enumerable: true,
+        get() {
+          return this.textbox[property];
+        },
+        set(val) {
+          return this.textbox[property] = val;
+        },
+      });
+    }
+
+    this.addEventListener("input", this);
+  }
+
+  formatValue() {
+  }
+
+  closePopup() {
+    this.view.close();
+  }
+
+  openResults() {
+    this.view.open();
+  }
+
+  /**
+   * Passes DOM events for the textbox to the _on<event type> methods.
+   * @param {Event} event
+   *   DOM event from the <textbox>.
+   */
+  handleEvent(event) {
+    let methodName = "_on" + event.type;
+    if (methodName in this) {
+      this[methodName](event);
+    } else {
+      throw "Unrecognized urlbar event: " + event.type;
+    }
+  }
+
+  // Private methods below.
+  /* eslint-disable require-jsdoc */
+
+  _oninput(event) {
+    this.openResults();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -0,0 +1,131 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["UrlbarView"];
+
+/**
+ * Receives and displays address bar autocomplete results.
+ */
+class UrlbarView {
+  /**
+   * @param {UrlbarInput} urlbar
+   *   The UrlbarInput instance belonging to this UrlbarView instance.
+   */
+  constructor(urlbar) {
+    this.urlbar = urlbar;
+    this.panel = urlbar.panel;
+    this.document = urlbar.panel.ownerDocument;
+    this.window = this.document.defaultView;
+
+    this._mainContainer = this.panel.querySelector(".urlbarView-body-inner");
+    this._rows = this.panel.querySelector(".urlbarView-results");
+
+    // For the horizontal fade-out effect, set the overflow attribute on result
+    // rows when they overflow.
+    this._rows.addEventListener("overflow", event => {
+      if (event.target.classList.contains("urlbarView-row-inner")) {
+        event.target.toggleAttribute("overflow", true);
+      }
+    });
+    this._rows.addEventListener("underflow", event => {
+      if (event.target.classList.contains("urlbarView-row-inner")) {
+        event.target.toggleAttribute("overflow", false);
+      }
+    });
+  }
+
+  /**
+   * Opens the autocomplete results popup.
+   */
+  open() {
+    this.panel.removeAttribute("hidden");
+
+    let panelDirection = this.panel.style.direction;
+    if (!panelDirection) {
+      panelDirection = this.panel.style.direction =
+        this.window.getComputedStyle(this.urlbar.textbox).direction;
+    }
+
+    // Make the panel span the width of the window.
+    let documentRect =
+      this._getBoundsWithoutFlushing(this.document.documentElement);
+    let width = documentRect.right - documentRect.left;
+    this.panel.setAttribute("width", width);
+
+    // Subtract two pixels for left and right borders on the panel.
+    this._mainContainer.style.maxWidth = (width - 2) + "px";
+
+    this.panel.openPopup(this.urlbar.textbox.closest("toolbar"), "after_end", 0, -1);
+
+    this._rows.textContent = "";
+    for (let i = 0; i < 12; i++) {
+      this._addRow();
+    }
+    this._rows.firstElementChild.toggleAttribute("selected", true);
+  }
+
+  /**
+   * Closes the autocomplete results popup.
+   */
+  close() {
+  }
+
+  // Private methods below.
+  /* eslint-disable require-jsdoc */
+
+  _getBoundsWithoutFlushing(element) {
+    return this.window.windowUtils.getBoundsWithoutFlushing(element);
+  }
+
+  _createElement(name) {
+    return this.document.createElementNS("http://www.w3.org/1999/xhtml", name);
+  }
+
+  _addRow() {
+    const SWITCH_TO_TAB = Math.random() < .3;
+
+    let item = this._createElement("div");
+    item.className = "urlbarView-row";
+    if (SWITCH_TO_TAB) {
+      item.setAttribute("action", "switch-to-tab");
+    }
+
+    let content = this._createElement("span");
+    content.className = "urlbarView-row-inner";
+    item.appendChild(content);
+
+    let actionIcon = this._createElement("span");
+    actionIcon.className = "urlbarView-action-icon";
+    content.appendChild(actionIcon);
+
+    let favicon = this._createElement("span");
+    favicon.className = "urlbarView-favicon";
+    content.appendChild(favicon);
+
+    let title = this._createElement("span");
+    title.className = "urlbarView-title";
+    do {
+      title.textContent += "foo bar ";
+    } while (Math.random() < .5);
+    content.appendChild(title);
+
+    let secondary = this._createElement("span");
+    secondary.className = "urlbarView-secondary";
+    if (SWITCH_TO_TAB) {
+      secondary.classList.add("urlbarView-action");
+      secondary.textContent = "Switch to Tab";
+    } else {
+      secondary.classList.add("urlbarView-url");
+      secondary.textContent = "http://www";
+      while (Math.random() < .95) {
+        secondary.textContent += ".xyz";
+      }
+    }
+    content.appendChild(secondary);
+
+    this._rows.appendChild(item);
+  }
+}
--- a/browser/components/urlbar/moz.build
+++ b/browser/components/urlbar/moz.build
@@ -2,12 +2,14 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Address Bar")
 
 EXTRA_JS_MODULES += [
     'UrlbarController.jsm',
+    'UrlbarInput.jsm',
     'UrlbarTokenizer.jsm',
+    'UrlbarView.jsm',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1,15 +1,14 @@
 %if 0
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 
 %include ../shared/browser.inc.css
 
 :root {
   --toolbar-non-lwt-bgcolor: -moz-dialog;
   --toolbar-non-lwt-textcolor: -moz-dialogtext;
   --toolbar-non-lwt-bgimage: linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15));
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1,16 +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/. */
 
 %include shared.inc
 %define toolbarButtonPressed :hover:active:not([disabled="true"]):not([cui-areatype="menu-panel"])
 
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 
 %include ../shared/browser.inc.css
 
 :root {
   --toolbar-non-lwt-bgcolor: #f9f9fa;
   --toolbar-non-lwt-textcolor: #0c0c0d;
   --toolbar-non-lwt-bgimage: none;
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -17,16 +17,104 @@
   --urlbar-popup-action-color: hsl(178, 100%, 28%);
 }
 
 :root[lwt-popup-brighttext] {
   --urlbar-popup-url-color: @lwtPopupBrighttextLinkColor@;
   --urlbar-popup-action-color: #30e60b;
 }
 
+#urlbar-results {
+  -moz-appearance: none;
+  background: var(--autocomplete-popup-background);
+  color: var(--autocomplete-popup-color);
+  border: 1px solid var(--autocomplete-popup-border-color);
+}
+
+.urlbarView-body-inner {
+  box-sizing: border-box;
+}
+
+.urlbarView-results {
+  padding: 5px;
+  white-space: nowrap;
+}
+
+.urlbarView-row {
+  padding: 7px 50px;
+  border-radius: 2px;
+}
+
+.urlbarView-row-inner {
+  overflow: hidden;
+  display: block;
+}
+
+.urlbarView-row-inner[overflow] {
+  mask-image: linear-gradient(to left, transparent, black 2em);
+}
+
+.urlbarView-row:hover {
+  background: var(--arrowpanel-dimmed);
+}
+
+.urlbarView-row[selected] {
+  background: var(--autocomplete-popup-highlight-background);
+  color: var(--autocomplete-popup-highlight-color);
+}
+
+.urlbarView-action-icon,
+.urlbarView-favicon {
+  display: inline-block;
+  vertical-align: middle;
+  width: 16px;
+  height: 16px;
+  margin-inline-end: .6em;
+}
+
+.urlbarView-favicon {
+  border-radius: 8px;
+  background: currentcolor;
+  opacity: 0.6;
+}
+
+.urlbarView-row[action=switch-to-tab] > .row-inner > .action-icon {
+  height: 8px;
+  border-top-left-radius: 3px;
+  border-top-right-radius: 3px;
+  background: currentcolor;
+  opacity: 0.5;
+}
+
+.urlbarView-title {
+  font-size: 1.05em;
+}
+
+.urlbarView-title::after {
+  content: "\2014";
+  color: var(--panel-disabled-color);
+  margin: 0 .4em;
+}
+
+.urlbarView-secondary {
+  color: var(--urlbar-popup-action-color);
+  font-size: 0.9em;
+}
+
+.urlbarView-url {
+  color: var(--urlbar-popup-url-color);
+}
+
+.urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-title::after,
+.urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-secondary {
+  color: inherit;
+}
+
+/* legacy URL bar styles below */
+
 #PopupAutoCompleteRichResult,
 #PopupSearchAutoComplete {
   background: var(--autocomplete-popup-background);
   color: var(--autocomplete-popup-color);
   border-color: var(--autocomplete-popup-border-color);
 }
 
 #PopupAutoCompleteRichResult .autocomplete-richlistbox {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1,13 +1,12 @@
 /* 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/. */
 
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 
 %include ../shared/browser.inc.css
 %filter substitution
 %define glassShadowColor hsla(240,5%,5%,0.3)
 
 :root {
   --toolbar-non-lwt-bgcolor: -moz-dialog;
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -42,18 +42,18 @@ AC_DEFUN([MOZ_ANDROID_CPU_ARCH],
 if test "$OS_TARGET" = "Android"; then
     case "${CPU_ARCH}" in
     arm)
         ANDROID_CPU_ARCH=armeabi-v7a
         ;;
     x86)
         ANDROID_CPU_ARCH=x86
         ;;
-    mips32) # When target_cpu is mipsel, CPU_ARCH is mips32
-        ANDROID_CPU_ARCH=mips
+    x86_64)
+        ANDROID_CPU_ARCH=x86_64
         ;;
     aarch64)
         ANDROID_CPU_ARCH=arm64-v8a
         ;;
     esac
 
     AC_SUBST(ANDROID_CPU_ARCH)
 fi
--- a/build/gyp_includes/common.gypi
+++ b/build/gyp_includes/common.gypi
@@ -1011,20 +1011,24 @@
           'variables': {
             'variables': {
               'android_ndk_root%': '<!(/bin/echo -n $ANDROID_NDK_ROOT)',
             },
             'android_ndk_root%': '<(android_ndk_root)',
             'conditions': [
               ['target_arch == "ia32"', {
                 'android_app_abi%': 'x86',
-                'android_ndk_sysroot%': '<(android_ndk_root)/platforms/android-9/arch-x86',
+                'android_ndk_sysroot%': '<(android_ndk_root)/platforms/android-16/arch-x86',
+              }],
+              ['target_arch == "x64"', {
+                'android_app_abi%': 'x86_64',
+                'android_ndk_sysroot%': '<(android_ndk_root)/platforms/android-21/arch-x86_64',
               }],
               ['target_arch=="arm"', {
-                'android_ndk_sysroot%': '<(android_ndk_root)/platforms/android-9/arch-arm',
+                'android_ndk_sysroot%': '<(android_ndk_root)/platforms/android-16/arch-arm',
                 'conditions': [
                   ['armv7==0', {
                     'android_app_abi%': 'armeabi',
                   }, {
                     'android_app_abi%': 'armeabi-v7a',
                   }],
                 ],
               }],
--- a/build/moz.configure/android-ndk.configure
+++ b/build/moz.configure/android-ndk.configure
@@ -12,17 +12,17 @@ js_option('--with-android-toolchain', na
           help='location of the Android toolchain')
 
 option('--with-android-googlevr-sdk', nargs=1,
        help='location of the Android GoogleVR SDK')
 
 
 @depends(target)
 def min_android_version(target):
-    if target.cpu in ['aarch64', 'x86_64', 'mips64']:
+    if target.cpu in ['aarch64', 'x86_64']:
         # 64-bit support was added in API 21.
         return '21'
     return '16'
 
 
 js_option('--with-android-version',
           nargs=1,
           help='android platform version',
@@ -114,19 +114,17 @@ set_config('ANDROID_NDK_MINOR_VERSION', 
 
 @depends(target, android_version, ndk, '--help')
 @checking('for android platform directory')
 @imports(_from='os.path', _import='isdir')
 def android_platform(target, android_version, ndk, _):
     if target.os != 'Android':
         return
 
-    if 'mips' in target.cpu:
-        target_dir_name = 'mips'
-    elif 'aarch64' == target.cpu:
+    if 'aarch64' == target.cpu:
         target_dir_name = 'arm64'
     else:
         target_dir_name = target.cpu
 
     # Not all Android releases have their own platform release. We use
     # the next lower platform version in these cases.
     if android_version in (11, 10):
         platform_version = 9
@@ -209,18 +207,18 @@ def android_toolchain(target, host, ndk,
         return
     if toolchain:
         return toolchain[0]
     else:
         if target.cpu == 'arm' and target.endianness == 'little':
             target_base = 'arm-linux-androideabi'
         elif target.cpu == 'x86':
             target_base = 'x86'
-        elif target.cpu == 'mips32' and target.endianness == 'little':
-            target_base = 'mipsel-linux-android'
+        elif target.cpu == 'x86_64':
+            target_base = 'x86_64'
         elif target.cpu == 'aarch64' and target.endianness == 'little':
             target_base = 'aarch64-linux-android'
         else:
             die('Target cpu is not supported.')
 
         toolchain_format = '%s/toolchains/%s-4.9/prebuilt/%s-%s'
 
         toolchain = toolchain_format % (ndk, target_base,
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -347,16 +347,18 @@ def try_preprocess(compiler, language, s
     return try_invoke_compiler(compiler, language, source, ['-E'])
 
 
 @imports(_from='mozbuild.configure.constants', _import='CompilerType')
 @imports(_from='mozbuild.configure.constants',
          _import='CPU_preprocessor_checks')
 @imports(_from='mozbuild.configure.constants',
          _import='kernel_preprocessor_checks')
+@imports(_from='mozbuild.configure.constants',
+         _import='OS_preprocessor_checks')
 @imports(_from='textwrap', _import='dedent')
 def get_compiler_info(compiler, language):
     '''Returns information about the given `compiler` (command line in the
     form of a list or tuple), in the given `language`.
 
     The returned information includes:
     - the compiler type (msvc, clang-cl, clang or gcc)
     - the compiler version
@@ -403,16 +405,17 @@ def get_compiler_info(compiler, language
     ''')
 
     # While we're doing some preprocessing, we might as well do some more
     # preprocessor-based tests at the same time, to check the toolchain
     # matches what we want.
     for name, preprocessor_checks in (
         ('CPU', CPU_preprocessor_checks),
         ('KERNEL', kernel_preprocessor_checks),
+        ('OS', OS_preprocessor_checks),
     ):
         for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
             check += dedent('''\
                 #%(if)s %(condition)s
                 %%%(name)s "%(value)s"
             ''' % {
                 'if': 'elif' if n else 'if',
                 'condition': condition,
@@ -470,22 +473,25 @@ def get_compiler_info(compiler, language
         version = Version(version)
 
     return namespace(
         type=type,
         version=version,
         cpu=data.get('CPU'),
         kernel=data.get('KERNEL'),
         endianness=data.get('ENDIANNESS'),
+        os=data.get('OS'),
         language='C++' if cplusplus else 'C',
         language_version=cplusplus if cplusplus else stdc_version,
     )
 
 
 @imports(_from='mozbuild.shellutil', _import='quote')
+@imports(_from='mozbuild.configure.constants',
+         _import='OS_preprocessor_checks')
 def check_compiler(compiler, language, target):
     info = get_compiler_info(compiler, language)
 
     flags = []
 
     def append_flag(flag):
         if flag not in flags:
             if info.type == 'clang-cl':
@@ -550,22 +556,31 @@ def check_compiler(compiler, language, t
     if not info.kernel or info.kernel != target.kernel:
         if info.type == 'clang':
             append_flag('--target=%s' % target.toolchain)
 
     if not info.endianness or info.endianness != target.endianness:
         if info.type == 'clang':
             append_flag('--target=%s' % target.toolchain)
 
+    # Add target flag when there is an OS mismatch (e.g. building for Android on
+    # Linux). However, only do this if the target OS is in our whitelist, to
+    # keep things the same on other platforms.
+    if target.os in OS_preprocessor_checks and (
+            not info.os or info.os != target.os):
+        if info.type == 'clang':
+            append_flag('--target=%s' % target.toolchain)
+
     return namespace(
         type=info.type,
         version=info.version,
         target_cpu=info.cpu,
         target_kernel=info.kernel,
         target_endianness=info.endianness,
+        target_os=info.os,
         flags=flags,
     )
 
 
 @imports(_from='__builtin__', _import='open')
 @imports('json')
 @imports('subprocess')
 @imports('sys')
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -1,17 +1,21 @@
 /* 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/. */
 
 @import "chrome://global/skin/in-content/common.css";
 @import "resource://devtools/client/themes/variables.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
-@import "resource://devtools/client/aboutdebugging-new/src/components/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSection.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionDetail.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/WorkerDetail.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/sidebar/SidebarItem.css";
 
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -25,44 +25,45 @@ const { L10nRegistry, FileSource } =
 const actions = require("./src/actions/index");
 const { configureStore } = require("./src/create-store");
 const {
   setDebugTargetCollapsibilities,
 } = require("./src/modules/debug-target-collapsibilities");
 const {
   addNetworkLocationsObserver,
   getNetworkLocations,
+  removeNetworkLocationsObserver,
 } = require("./src/modules/network-locations");
 
 const App = createFactory(require("./src/components/App"));
 
 const { PAGES } = require("./src/constants");
 
 const AboutDebugging = {
   async init() {
     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
       // If DevTools are disabled, navigate to about:devtools.
       window.location = "about:devtools?reason=AboutDebugging";
       return;
     }
 
+    this.onNetworkLocationsUpdated = this.onNetworkLocationsUpdated.bind(this);
+
     this.store = configureStore();
     this.actions = bindActionCreators(actions, this.store.dispatch);
 
     const messageContexts = await this.createMessageContexts();
 
     render(
       Provider({ store: this.store }, App({ messageContexts })),
       this.mount
     );
 
     this.actions.selectPage(PAGES.THIS_FIREFOX);
-    addNetworkLocationsObserver(() => {
-      this.actions.updateNetworkLocations(getNetworkLocations());
-    });
+    addNetworkLocationsObserver(this.onNetworkLocationsUpdated);
   },
 
   async createMessageContexts() {
     // XXX Until the strings for the updated about:debugging stabilize, we
     // locate them outside the regular directory for locale resources so that
     // they don't get picked up by localization tools.
     if (!L10nRegistry.sources.has("aboutdebugging")) {
       const temporarySource = new FileSource(
@@ -80,17 +81,26 @@ const AboutDebugging = {
     const contexts = [];
     for await (const context of generator) {
       contexts.push(context);
     }
 
     return contexts;
   },
 
-  destroy() {
+  onNetworkLocationsUpdated() {
+    this.actions.updateNetworkLocations(getNetworkLocations());
+  },
+
+  async destroy() {
+    L10nRegistry.removeSource("aboutdebugging");
+    // Call removeNetworkLocationsObserver before disconnectRuntime,
+    // follow up in Bug 1490950.
+    removeNetworkLocationsObserver(this.onNetworkLocationsUpdated);
+    await this.actions.disconnectRuntime();
     setDebugTargetCollapsibilities(this.store.getState().ui.debugTargetCollapsibilities);
     unmountComponentAtNode(this.mount);
   },
 
   get mount() {
     return document.getElementById("mount");
   },
 };
--- a/devtools/client/aboutdebugging-new/moz.build
+++ b/devtools/client/aboutdebugging-new/moz.build
@@ -5,10 +5,15 @@
 DIRS += [
     'src',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/xpcshell.ini'
 ]
 
+BROWSER_CHROME_MANIFESTS += [
+    'test/browser/browser.ini'
+]
+
+
 with Files('**'):
     BUG_COMPONENT = ('DevTools', 'about:debugging')
\ No newline at end of file
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -6,41 +6,57 @@
 
 const {
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_SELECTED,
   PAGES,
 } = require("../constants");
 
+const NetworkLocationsModule = require("../modules/network-locations");
+
 const Actions = require("./index");
 
 function selectPage(page) {
   return async (dispatch, getState) => {
     const currentPage = getState().ui.selectedPage;
     if (page === currentPage) {
       // Nothing to dispatch if the page is the same as the current page.
       return;
     }
 
-    dispatch({ type: PAGE_SELECTED, page });
+    if (page === PAGES.THIS_FIREFOX) {
+      await dispatch(Actions.connectRuntime());
+    } else {
+      await dispatch(Actions.disconnectRuntime());
+    }
 
-    if (page === PAGES.THIS_FIREFOX) {
-      dispatch(Actions.connectRuntime());
-    } else {
-      dispatch(Actions.disconnectRuntime());
-    }
+    dispatch({ type: PAGE_SELECTED, page });
   };
 }
 
 function updateDebugTargetCollapsibility(key, isCollapsed) {
   return { type: DEBUG_TARGET_COLLAPSIBILITY_UPDATED, key, isCollapsed };
 }
 
+function addNetworkLocation(location) {
+  return (dispatch, getState) => {
+    NetworkLocationsModule.addNetworkLocation(location);
+  };
+}
+
+function removeNetworkLocation(location) {
+  return (dispatch, getState) => {
+    NetworkLocationsModule.removeNetworkLocation(location);
+  };
+}
+
 function updateNetworkLocations(locations) {
   return { type: NETWORK_LOCATIONS_UPDATED, locations };
 }
 
 module.exports = {
+  addNetworkLocation,
+  removeNetworkLocation,
   selectPage,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -9,17 +9,17 @@ const { createFactory, PureComponent } =
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const LocalizationProvider = createFactory(FluentReact.LocalizationProvider);
 
 const { PAGES } = require("../constants");
 
-const ConnectPage = createFactory(require("./ConnectPage"));
+const ConnectPage = createFactory(require("./connect/ConnectPage"));
 const RuntimePage = createFactory(require("./RuntimePage"));
 const Sidebar = createFactory(require("./sidebar/Sidebar"));
 
 class App extends PureComponent {
   static get propTypes() {
     return {
       // The "dispatch" helper is forwarded to the App component via connect.
       // From that point, components are responsible for forwarding the dispatch
@@ -33,17 +33,17 @@ class App extends PureComponent {
 
   getSelectedPageComponent() {
     const { dispatch, networkLocations, selectedPage } = this.props;
 
     switch (selectedPage) {
       case PAGES.THIS_FIREFOX:
         return RuntimePage({ dispatch });
       case PAGES.CONNECT:
-        return ConnectPage({ networkLocations });
+        return ConnectPage({ dispatch, networkLocations });
       default:
         // Invalid page, blank.
         return null;
     }
   }
 
   render() {
     const {
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -49,17 +49,17 @@ class RuntimePage extends PureComponent 
       serviceWorkers,
       sharedWorkers,
       tabs,
       temporaryExtensions,
     } = this.props;
 
     return dom.article(
       {
-        className: "page",
+        className: "page js-runtime-page",
       },
       RuntimeInfo({
         icon: "chrome://branding/content/icon64.png",
         name: Services.appinfo.name,
         version: Services.appinfo.version,
       }),
       TemporaryExtensionInstaller({ dispatch }),
       Localized(
rename from devtools/client/aboutdebugging-new/src/components/ConnectPage.css
rename to devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
--- a/devtools/client/aboutdebugging-new/src/components/ConnectPage.css
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css
@@ -1,68 +1,12 @@
 /* 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/. */
 
-.connect-page__steps {
-  line-height: 1.5em;
-  list-style-type: decimal;
-  margin-block-end: 20px;
-  margin-inline-start: 40px;
-}
-
-.connect-page__step {
-  padding-inline-start: 20px;
-}
-
-.connect-page__section__title {
-  display: flex;
-}
-
-.connect-page__section__icon {
-  fill: currentColor;
-  height: 32px;
-  margin-inline-end: 5px;
-  width: 32px;
-  -moz-context-properties: fill;
-}
-
 .connect-page__network {
   max-width: 600px;
 }
 
 .connect-page__network__separator {
   margin-block-start: 20px;
   margin-block-end: 20px;
 }
-
-/*
- * Layout of a network location item
- *
- *  +-------------------------------------+---------------+
- *  | Location (eg localhost:8080)        | Remove button |
- *  +-------------------------------------+---------------+
- */
-.connect-page__network-location {
-  display: grid;
-  grid-template-columns: auto max-content;
-  line-height: 36px;
-  margin-block-start: 4px;
-  margin-block-end: 4px;
-}
-
-/*
- * Layout of a network location form
- *
- *  +-------------+--------------------+------------+
- *  | "Host:port" | Input              | Add button |
- *  +-------------+--------------------+------------+
- */
-.connect-page__network-form {
-  display: grid;
-  grid-column-gap: 10px;
-  grid-template-columns: 100px auto max-content;
-  line-height: 36px;
-}
-
-.connect-page__network-form__input {
-  box-sizing: border-box;
-}
rename from devtools/client/aboutdebugging-new/src/components/ConnectPage.js
rename to devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
--- a/devtools/client/aboutdebugging-new/src/components/ConnectPage.js
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.js
@@ -1,175 +1,79 @@
 /* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
-const {
-  addNetworkLocation,
-  removeNetworkLocation
-} = require("../modules/network-locations");
+const ConnectSection = createFactory(require("./ConnectSection"));
+const ConnectSteps = createFactory(require("./ConnectSteps"));
+const NetworkLocationsForm = createFactory(require("./NetworkLocationsForm"));
+const NetworkLocationsList = createFactory(require("./NetworkLocationsList"));
 
 const USB_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const WIFI_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-connect-icon.svg";
 const GLOBE_ICON_SRC = "chrome://devtools/skin/images/aboutdebugging-globe-icon.svg";
 
 class ConnectPage extends PureComponent {
   static get propTypes() {
     return {
+      dispatch: PropTypes.func.isRequired,
       networkLocations: PropTypes.arrayOf(PropTypes.object).isRequired,
     };
   }
 
-  constructor(props) {
-    super(props);
-    this.state = {
-      locationInputValue: ""
-    };
-  }
-
-  renderSteps(steps) {
-    return dom.ul(
+  renderWifi() {
+    return ConnectSection(
       {
-        className: "connect-page__steps"
+        icon: WIFI_ICON_SRC,
+        title: "Via WiFi (Recommended)",
       },
-      steps.map(step => dom.li(
-        {
-          className: "connect-page__step"
-        },
-        step)
-      )
-    );
-  }
-
-  renderWifi() {
-    return dom.section(
-      {},
-      dom.h2(
-        {
-          className: "connect-page__section__title"
-        },
-        dom.img(
-          {
-            className: "connect-page__section__icon",
-            src: WIFI_ICON_SRC
-          }
-        ),
-        "Via WiFi (Recommended)"
-      ),
-      this.renderSteps([
-        "Ensure that your browser and device are on the same network",
-        "Open Firefox for Android",
-        "Go to Options -> Settings -> Advanced",
-        "Enable Remote Debugging via WiFi in the Developer Tools section",
-      ])
+      ConnectSteps({
+        steps: [
+          "Ensure that your browser and device are on the same network",
+          "Open Firefox for Android",
+          "Go to Options -> Settings -> Advanced",
+          "Enable Remote Debugging via WiFi in the Developer Tools section",
+        ]
+      })
     );
   }
 
   renderUsb() {
-    return dom.section(
-      {},
-      dom.h2(
-        {
-          className: "connect-page__section__title"
-        },
-        dom.img(
-          {
-            className: "connect-page__section__icon",
-            src: USB_ICON_SRC
-          }
-        ),
-        "Via USB"
-      ),
-      this.renderSteps([
-        "Enable Developer menu on your Android device",
-        "Enable USB Debugging on the Android Developer Menu",
-        "Connect the USB Device to your computer",
-      ])
+    return ConnectSection(
+      {
+        icon: USB_ICON_SRC,
+        title: "Via USB",
+      },
+      ConnectSteps({
+        steps: [
+          "Enable Developer menu on your Android device",
+          "Enable USB Debugging on the Android Developer Menu",
+          "Connect the USB Device to your computer",
+        ]
+      })
     );
   }
 
   renderNetwork() {
-    const { networkLocations } = this.props;
-    return dom.section(
+    const { dispatch, networkLocations } = this.props;
+    return ConnectSection(
       {
-        className: "connect-page__network"
+        className: "connect-page__network",
+        icon: GLOBE_ICON_SRC,
+        title: "Via Network Location",
       },
-      dom.h2(
-        {
-          className: "connect-page__section__title"
-        },
-        dom.img(
-          {
-            className: "connect-page__section__icon",
-            src: GLOBE_ICON_SRC
-          }
-        ),
-        "Via Network Location"
-      ),
-      dom.ul(
-        {},
-        networkLocations.map(location =>
-          dom.li(
-            {
-              className: "connect-page__network-location"
-            },
-            dom.span(
-              {
-                className: "ellipsis-text"
-              },
-              location
-            ),
-            dom.button(
-              {
-                className: "aboutdebugging-button",
-                onClick: () => removeNetworkLocation(location)
-              },
-              "Remove"
-            )
-          )
-        )
-      ),
-      dom.hr(
-        {
-          className: "connect-page__network__separator"
-        }
-      ),
-      dom.form(
-        {
-          className: "connect-page__network-form",
-          onSubmit: (e) => {
-            const locationInputValue = this.state.locationInputValue;
-            if (locationInputValue) {
-              addNetworkLocation(locationInputValue);
-              this.setState({ locationInputValue: "" });
-            }
-            e.preventDefault();
-          }
-        },
-        dom.span({}, "Host:port"),
-        dom.input({
-          className: "connect-page__network-form__input",
-          placeholder: "localhost:6080",
-          type: "text",
-          value: this.state.locationInputValue,
-          onChange: (e) => {
-            const locationInputValue = e.target.value;
-            this.setState({ locationInputValue });
-          }
-        }),
-        dom.button({
-          className: "aboutdebugging-button"
-        }, "Add")
-      )
+      NetworkLocationsList({ dispatch, networkLocations }),
+      dom.hr({ className: "connect-page__network__separator" }),
+      NetworkLocationsForm({ dispatch }),
     );
   }
 
   render() {
     return dom.article(
       {
         className: "page connect-page",
       },
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectSection.css
@@ -0,0 +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/. */
+
+.connect-page__section__title {
+  display: flex;
+}
+
+.connect-page__section__icon {
+  fill: currentColor;
+  height: 32px;
+  margin-inline-end: 5px;
+  width: 32px;
+  -moz-context-properties: fill;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectSection.js
@@ -0,0 +1,43 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+class ConnectSection extends PureComponent {
+  static get propTypes() {
+    return {
+      children: PropTypes.any.isRequired,
+      className: PropTypes.string,
+      icon: PropTypes.string.isRequired,
+      title: PropTypes.string.isRequired,
+    };
+  }
+
+  render() {
+    return dom.section(
+      {
+        className: this.props.className,
+      },
+      dom.h2(
+        {
+          className: "connect-page__section__title"
+        },
+        dom.img(
+          {
+            className: "connect-page__section__icon",
+            src: this.props.icon
+          }
+        ),
+        this.props.title
+      ),
+      this.props.children
+    );
+  }
+}
+
+module.exports = ConnectSection;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+.connect-page__steps {
+  line-height: 1.5em;
+  list-style-type: decimal;
+  margin-block-end: 20px;
+  margin-inline-start: 40px;
+}
+
+.connect-page__step {
+  padding-inline-start: 20px;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.js
@@ -0,0 +1,33 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+class ConnectSteps extends PureComponent {
+  static get propTypes() {
+    return {
+      steps: PropTypes.arrayOf(PropTypes.string).isRequired,
+    };
+  }
+
+  render() {
+    return dom.ul(
+      {
+        className: "connect-page__steps"
+      },
+      this.props.steps.map(step => dom.li(
+        {
+          className: "connect-page__step"
+        },
+        step)
+      )
+    );
+  }
+}
+
+module.exports = ConnectSteps;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css
@@ -0,0 +1,21 @@
+/* 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/. */
+
+/*
+ * Layout of a network location form
+ *
+ *  +-------------+--------------------+------------+
+ *  | "Host:port" | Input              | Add button |
+ *  +-------------+--------------------+------------+
+ */
+.connect-page__network-form {
+  display: grid;
+  grid-column-gap: 10px;
+  grid-template-columns: 100px auto max-content;
+  line-height: 36px;
+}
+
+.connect-page__network-form__input {
+  box-sizing: border-box;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.js
@@ -0,0 +1,58 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const Actions = require("../../actions/index");
+
+class NetworkLocationsForm extends PureComponent {
+  static get propTypes() {
+    return {
+      dispatch: PropTypes.func.isRequired,
+    };
+  }
+
+  constructor(props) {
+    super(props);
+    this.state = {
+      value: ""
+    };
+  }
+
+  render() {
+    return dom.form(
+      {
+        className: "connect-page__network-form",
+        onSubmit: (e) => {
+          const { value } = this.state;
+          if (value) {
+            this.props.dispatch(Actions.addNetworkLocation(value));
+            this.setState({ value: "" });
+          }
+          e.preventDefault();
+        }
+      },
+      dom.span({}, "Host:port"),
+      dom.input({
+        className: "connect-page__network-form__input",
+        placeholder: "localhost:6080",
+        type: "text",
+        value: this.state.value,
+        onChange: (e) => {
+          const value = e.target.value;
+          this.setState({ value });
+        }
+      }),
+      dom.button({
+        className: "aboutdebugging-button"
+      }, "Add")
+    );
+  }
+}
+
+module.exports = NetworkLocationsForm;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.css
@@ -0,0 +1,18 @@
+/* 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/. */
+
+/*
+ * Layout of a network location item
+ *
+ *  +-------------------------------------+---------------+
+ *  | Location (eg localhost:8080)        | Remove button |
+ *  +-------------------------------------+---------------+
+ */
+.connect-page__network-location {
+  display: grid;
+  grid-template-columns: auto max-content;
+  line-height: 36px;
+  margin-block-start: 4px;
+  margin-block-end: 4px;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.js
@@ -0,0 +1,50 @@
+/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const Actions = require("../../actions/index");
+
+class NetworkLocationsList extends PureComponent {
+  static get propTypes() {
+    return {
+      dispatch: PropTypes.func.isRequired,
+      networkLocations: PropTypes.arrayOf(PropTypes.object).isRequired,
+    };
+  }
+
+  render() {
+    return dom.ul(
+      {},
+      this.props.networkLocations.map(location =>
+        dom.li(
+          {
+            className: "connect-page__network-location"
+          },
+          dom.span(
+            {
+              className: "ellipsis-text"
+            },
+            location
+          ),
+          dom.button(
+            {
+              className: "aboutdebugging-button",
+              onClick: () => {
+                this.props.dispatch(Actions.removeNetworkLocation(location));
+              }
+            },
+            "Remove"
+          )
+        )
+      )
+    );
+  }
+}
+
+module.exports = NetworkLocationsList;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/connect/moz.build
@@ -0,0 +1,16 @@
+# 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/.
+
+DevToolsModules(
+    'ConnectPage.css',
+    'ConnectPage.js',
+    'ConnectSection.css',
+    'ConnectSection.js',
+    'ConnectSteps.css',
+    'ConnectSteps.js',
+    'NetworkLocationsForm.css',
+    'NetworkLocationsForm.js',
+    'NetworkLocationsList.css',
+    'NetworkLocationsList.js',
+)
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.js
@@ -44,17 +44,17 @@ class DebugTargetPane extends PureCompon
     } = this.props;
 
     return dom.section(
       {},
       dom.h2(
         {},
         dom.a(
           {
-            className: "debug-target-pane__title" +
+            className: "debug-target-pane__title js-debug-target-pane-title" +
                        (isCollapsed ? " debug-target-pane__title--collapsed" : ""),
             href: "#",
             onClick: e => this.toggleCollapsibility(),
           },
           name,
           isCollapsed ? dom.span({}, `(${ targets.length })`) : null,
         )
       ),
--- a/devtools/client/aboutdebugging-new/src/components/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/moz.build
@@ -1,18 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
+    'connect',
     'debugtarget',
     'sidebar',
 ]
 
 DevToolsModules(
     'App.css',
     'App.js',
-    'ConnectPage.css',
-    'ConnectPage.js',
     'RuntimeInfo.css',
     'RuntimeInfo.js',
     'RuntimePage.js',
 )
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarItem.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarItem.js
@@ -30,17 +30,20 @@ class SidebarItem extends PureComponent 
   }
 
   render() {
     const { icon, isSelected, name, selectable } = this.props;
 
     return dom.li(
       {
         className: "sidebar-item" +
-                   (isSelected ? " sidebar-item--selected" : "") +
+                   (isSelected ?
+                      " sidebar-item--selected js-sidebar-item-selected" :
+                      ""
+                   ) +
                    (selectable ? " sidebar-item--selectable" : ""),
         onClick: selectable ? () => this.onItemClick() : null
       },
       dom.img({
         className: "sidebar-item__icon" +
                    (isSelected ? " sidebar-item__icon--selected" : ""),
         src: icon,
       }),
--- a/devtools/client/aboutdebugging-new/src/modules/network-locations.js
+++ b/devtools/client/aboutdebugging-new/src/modules/network-locations.js
@@ -13,16 +13,21 @@ const NETWORK_LOCATIONS_PREF = "devtools
  * locations monitored by about-debugging.
  */
 
 function addNetworkLocationsObserver(listener) {
   Services.prefs.addObserver(NETWORK_LOCATIONS_PREF, listener);
 }
 exports.addNetworkLocationsObserver = addNetworkLocationsObserver;
 
+function removeNetworkLocationsObserver(listener) {
+  Services.prefs.removeObserver(NETWORK_LOCATIONS_PREF, listener);
+}
+exports.removeNetworkLocationsObserver = removeNetworkLocationsObserver;
+
 /**
  * Read the current preference value for aboutdebugging network locations.
  * Will throw if the value cannot be parsed or is not an array.
  */
 function _parsePreferenceAsArray() {
   const pref = Services.prefs.getStringPref(NETWORK_LOCATIONS_PREF, "[]");
   const parsedValue = JSON.parse(pref);
   if (!Array.isArray(parsedValue)) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/.eslintrc.js
@@ -0,0 +1,10 @@
+/* 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";
+
+module.exports = {
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../../.eslintrc.mochitests.js"
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+  head.js
+  !/devtools/client/shared/test/shared-head.js
+  !/devtools/client/shared/test/telemetry-test-helpers.js
+
+[browser_aboutdebugging_thisfirefox.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that the This Firefox page is displayed by default when opening the new
+ * about:debugging and that it contains the expected categories.
+ */
+
+const EXPECTED_TARGET_PANES = [
+  "Temporary Extensions",
+  "Extensions",
+  "Tabs",
+  "Service Workers",
+  "Shared Workers",
+  "Other Workers",
+];
+
+add_task(async function() {
+  const { document, tab } = await openAboutDebugging();
+
+  // Wait until the client connection was established.
+  await waitUntil(() => document.querySelector(".js-runtime-page"));
+
+  // Check that the selected sidebar item is "This Firefox"
+  const selectedSidebarItem = document.querySelector(".js-sidebar-item-selected");
+  ok(selectedSidebarItem, "An item is selected in the sidebar");
+  is(selectedSidebarItem.textContent, "This Firefox",
+    "The selected sidebar item is This Firefox");
+
+  const paneTitlesEls = document.querySelectorAll(".js-debug-target-pane-title");
+  is(paneTitlesEls.length, EXPECTED_TARGET_PANES.length,
+    "This Firefox has the expecte number of debug target categories");
+
+  const paneTitles = [...paneTitlesEls].map(el => el.textContent);
+
+  EXPECTED_TARGET_PANES.forEach(expectedPane => {
+    ok(paneTitles.includes(expectedPane),
+      "Expected debug target category found: " + expectedPane);
+  });
+
+  await removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env browser */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from ../../../shared/test/shared-head.js */
+
+"use strict";
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+  this);
+
+/**
+ * Enable the new about:debugging panel.
+ */
+async function enableNewAboutDebugging() {
+  await pushPref("devtools.aboutdebugging.new-enabled", true);
+}
+
+async function openAboutDebugging(page, win) {
+  await enableNewAboutDebugging();
+
+  info("opening about:debugging");
+  const tab = await addTab("about:debugging", { window: win });
+  const browser = tab.linkedBrowser;
+  const document = browser.contentDocument;
+  const window = browser.contentWindow;
+
+  info("Wait until the main about debugging container is available");
+  await waitUntil(() => document.querySelector(".app"));
+
+  return { tab, document, window };
+}
--- a/devtools/client/framework/components/ToolboxTabs.js
+++ b/devtools/client/framework/components/ToolboxTabs.js
@@ -46,17 +46,18 @@ class ToolboxTabs extends Component {
     // Map with tool Id and its width size. This lifecycle is out of React's
     // lifecycle. If a tool is registered, ToolboxTabs will add target tool id
     // to this map. ToolboxTabs will never remove tool id from this cache.
     this._cachedToolTabsWidthMap = new Map();
 
     this._resizeTimerId = null;
     this.resizeHandler = this.resizeHandler.bind(this);
 
-    this._tabsOrderManager = new ToolboxTabsOrderManager(props.onTabsOrderUpdated);
+    this._tabsOrderManager =
+      new ToolboxTabsOrderManager(props.onTabsOrderUpdated, props.panelDefinitions);
   }
 
   componentDidMount() {
     window.addEventListener("resize", this.resizeHandler);
     this.updateCachedToolTabsWidthMap();
     this.updateOverflowedTabs();
   }
 
@@ -67,16 +68,17 @@ class ToolboxTabs extends Component {
       nextState.overflowedTabIds = [];
     }
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (this.shouldUpdateToolboxTabs(prevProps, this.props)) {
       this.updateCachedToolTabsWidthMap();
       this.updateOverflowedTabs();
+      this._tabsOrderManager.setCurrentPanelDefinitions(this.props.panelDefinitions);
     }
   }
 
   componentWillUnmount() {
     window.removeEventListener("resize", this.resizeHandler);
     window.cancelIdleCallback(this._resizeTimerId);
     this._tabsOrderManager.destroy();
   }
@@ -243,18 +245,16 @@ class ToolboxTabs extends Component {
       currentToolId,
       focusButton,
       focusedButton,
       highlightedTools,
       panelDefinitions,
       selectTool,
     } = this.props;
 
-    this._tabsOrderManager.setCurrentPanelDefinitions(panelDefinitions);
-
     const tabs = panelDefinitions.map(panelDefinition => {
       // Don't display overflowed tab.
       if (!this.state.overflowedTabIds.includes(panelDefinition.id)) {
         return ToolboxTab({
           key: panelDefinition.id,
           currentToolId,
           focusButton,
           focusedButton,
--- a/devtools/client/framework/moz.build
+++ b/devtools/client/framework/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 DIRS += [
     'components',
 ]
 
 DevToolsModules(
     'attach-thread.js',
     'browser-menus.js',
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -121,16 +121,18 @@ run-if = e10s
 [browser_toolbox_tool_ready.js]
 [browser_toolbox_tool_remote_reopen.js]
 [browser_toolbox_toolbar_minimum_width.js]
 [browser_toolbox_toolbar_overflow.js]
 [browser_toolbox_toolbar_overflow_button_visibility.js]
 [browser_toolbox_toolbar_reorder_by_dnd.js]
 [browser_toolbox_toolbar_reorder_by_width.js]
 [browser_toolbox_toolbar_reorder_with_extension.js]
+[browser_toolbox_toolbar_reorder_with_hidden_extension.js]
+[browser_toolbox_toolbar_reorder_with_secondary_toolbox.js]
 [browser_toolbox_tools_per_toolbox_registration.js]
 [browser_toolbox_view_source_01.js]
 [browser_toolbox_view_source_02.js]
 [browser_toolbox_view_source_03.js]
 [browser_toolbox_view_source_04.js]
 [browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
 [browser_toolbox_window_title_changes.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_toolbar_reorder_with_hidden_extension.js
@@ -0,0 +1,129 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for reordering with an hidden extension installed.
+
+const { Toolbox } = require("devtools/client/framework/toolbox");
+
+const EXTENSION = "@reorder.test";
+
+const TEST_DATA = [
+  {
+    description: "Test that drags a tab to left beyond the extension's tab",
+    startingOrder: ["inspector", EXTENSION, "webconsole", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+    dragTarget: "webconsole",
+    dropTarget: "inspector",
+    expectedOrder: ["webconsole", "inspector", EXTENSION, "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+  },
+  {
+    description: "Test that drags a tab to right beyond the extension's tab",
+    startingOrder: ["inspector", EXTENSION, "webconsole", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+    dragTarget: "inspector",
+    dropTarget: "webconsole",
+    expectedOrder: [EXTENSION, "webconsole", "inspector", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+  },
+  {
+    description: "Test that drags a tab to left end, but hidden tab is left end",
+    startingOrder: [EXTENSION, "inspector", "webconsole", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+    dragTarget: "webconsole",
+    dropTarget: "inspector",
+    expectedOrder: [EXTENSION, "webconsole", "inspector", "jsdebugger", "styleeditor",
+                    "performance", "memory", "netmonitor", "storage", "accessibility"],
+  },
+  {
+    description: "Test that drags a tab to right end, but hidden tab is right end",
+    startingOrder: ["inspector", "webconsole", "jsdebugger", "styleeditor", "performance",
+                    "memory", "netmonitor", "storage", "accessibility", EXTENSION],
+    dragTarget: "webconsole",
+    dropTarget: "accessibility",
+    expectedOrder: ["inspector", "jsdebugger", "styleeditor", "performance", "memory",
+                    "netmonitor", "storage", "accessibility", EXTENSION, "webconsole"],
+  },
+];
+
+add_task(async function() {
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("devtools.toolbox.tabsOrder");
+  });
+
+  const extension = ExtensionTestUtils.loadExtension({
+    useAddonManager: "temporary",
+    manifest: {
+      devtools_page: "extension.html",
+      applications: {
+        gecko: { id: EXTENSION },
+      },
+    },
+    files: {
+      "extension.html": `<!DOCTYPE html>
+                         <html>
+                           <head>
+                             <meta charset="utf-8">
+                           </head>
+                           <body>
+                             <script src="extension.js"></script>
+                           </body>
+                         </html>`,
+      "extension.js": async () => {
+        // Don't call browser.devtools.panels.create since this need to be as hidden.
+        // eslint-disable-next-line
+        browser.test.sendMessage("devtools-page-ready");
+      },
+    },
+  });
+
+  await extension.startup();
+
+  let tab = await addTab("about:blank");
+  const toolbox = await openToolboxForTab(tab, "webconsole", Toolbox.HostType.BOTTOM);
+  await extension.awaitMessage("devtools-page-ready");
+
+  for (const { description, startingOrder,
+               dragTarget, dropTarget, expectedOrder } of TEST_DATA) {
+    info(description);
+    prepareTestWithHiddenExtension(toolbox, startingOrder);
+    await dndToolTab(toolbox, dragTarget, dropTarget);
+    assertToolTabPreferenceOrder(expectedOrder);
+  }
+
+  info("Check ordering preference after destroying toolbox");
+  let target = gDevTools.getTargetForTab(tab);
+  await gDevTools.closeToolbox(target);
+  await target.destroy();
+  assertExtensionExistence(true);
+
+  info("Check ordering preference after uninstalling hidden addon");
+  await extension.unload();
+  tab = await addTab("about:blank");
+  await openToolboxForTab(tab, "webconsole", Toolbox.HostType.BOTTOM);
+  target = gDevTools.getTargetForTab(tab);
+  await gDevTools.closeToolbox(target);
+  assertExtensionExistence(false);
+});
+
+function assertExtensionExistence(shouldExist) {
+  const ids = Services.prefs.getCharPref("devtools.toolbox.tabsOrder").split(",");
+  is(ids.includes(EXTENSION), shouldExist,
+     `Hidden extension id should ${shouldExist ? "" : "not "}exist`);
+}
+
+function prepareTestWithHiddenExtension(toolbox, startingOrder) {
+  Services.prefs.setCharPref("devtools.toolbox.tabsOrder", startingOrder.join(","));
+
+  for (const id of startingOrder) {
+    if (id === EXTENSION) {
+      ok(!getElementByToolId(toolbox, id), "Hidden extension tab should not exist");
+    } else {
+      ok(getElementByToolId(toolbox, id), `Tab element should exist for ${ id }`);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_toolbar_reorder_with_secondary_toolbox.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test for reordering with secondary toolbox such as Browser Content Toolbox.
+// We test whether the ordering preference will not change when the secondary toolbox
+// was closed without reordering.
+
+const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+
+add_task(async function() {
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("devtools.toolbox.selectedTool");
+  });
+
+  // Keep initial tabs order preference of devtools so as to compare after re-ordering
+  // tabs on browser content toolbox.
+  const initialTabsOrderOnDevTools = Services.prefs.getCharPref("devtools.toolbox.tabsOrder");
+
+  info("Prepare the toolbox on browser content toolbox");
+  await addTab(`${ URL_ROOT }doc_empty-tab-01.html`);
+  // Select "memory" tool from first, because the webconsole might connect to the content.
+  Services.prefs.setCharPref("devtools.toolbox.selectedTool", "memory");
+  const toolbox = await gDevToolsBrowser.openContentProcessToolbox(gBrowser);
+
+  info("Check whether the value of devtools.toolbox.tabsOrder was not affected after closed");
+  const onToolboxDestroyed = toolbox.once("destroyed");
+  toolbox.win.top.close();
+  await onToolboxDestroyed;
+  is(Services.prefs.getCharPref("devtools.toolbox.tabsOrder"), initialTabsOrderOnDevTools,
+     "The preference of devtools.toolbox.tabsOrder should not be affected");
+});
--- a/devtools/client/framework/test/head.js
+++ b/devtools/client/framework/test/head.js
@@ -272,18 +272,17 @@ async function closeChevronMenu(toolbox)
 }
 
 function prepareToolTabReorderTest(toolbox, startingOrder) {
   Services.prefs.setCharPref("devtools.toolbox.tabsOrder", startingOrder.join(","));
   ok(!toolbox.doc.getElementById("tools-chevron-menu-button"),
      "The size of the screen being too small");
 
   for (const id of startingOrder) {
-    ok(getElementByToolIdOrExtensionIdOrSelector(toolbox, id),
-       `Tab element should exist for ${ id }`);
+    ok(getElementByToolId(toolbox, id), `Tab element should exist for ${ id }`);
   }
 }
 
 async function dndToolTab(toolbox, dragTarget, dropTarget, passedTargets = []) {
   info(`Drag ${ dragTarget } to ${ dropTarget }`);
   const dragTargetEl = getElementByToolIdOrExtensionIdOrSelector(toolbox, dragTarget);
 
   const onReady = dragTargetEl.classList.contains("selected")
@@ -336,25 +335,30 @@ function assertToolTabSelected(toolbox, 
 }
 
 function assertToolTabPreferenceOrder(expectedOrder) {
   info("Check the order in DevTools preference for tabs order");
   is(Services.prefs.getCharPref("devtools.toolbox.tabsOrder"), expectedOrder.join(","),
      "The preference should be correct");
 }
 
-function getElementByToolIdOrExtensionIdOrSelector(toolbox, idOrSelector) {
+function getElementByToolId(toolbox, id) {
   for (const tabEl of toolbox.doc.querySelectorAll(".devtools-tab")) {
-    if (tabEl.dataset.id === idOrSelector ||
-        tabEl.dataset.extensionId === idOrSelector) {
+    if (tabEl.dataset.id === id ||
+        tabEl.dataset.extensionId === id) {
       return tabEl;
     }
   }
 
-  return toolbox.doc.querySelector(idOrSelector);
+  return null;
+}
+
+function getElementByToolIdOrExtensionIdOrSelector(toolbox, idOrSelector) {
+  const tabEl = getElementByToolId(toolbox, idOrSelector);
+  return tabEl ? tabEl : toolbox.doc.querySelector(idOrSelector);
 }
 
 function getWindow(toolbox) {
   return toolbox.win.parent;
 }
 
 async function resizeWindow(toolbox, width, height) {
   const hostWindow = toolbox.win.parent;
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/unit/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../../.eslintrc.xpcshell.js"
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/unit/test_tabs_absolute_order.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+
+const TEST_DATA = [
+  {
+    description: "Test for no order in preference",
+    preferenceOrder: [],
+    currentTabsOrder: ["T1", "T2", "T3", "T4", "T5"],
+    dragTarget: "T1",
+    expectedOrder: ["T1", "T2", "T3", "T4", "T5"],
+  },
+  {
+    description: "Test for drag a tab to left with hidden tab",
+    preferenceOrder: ["T1", "T2", "T3", "E1", "T4", "T5"],
+    currentTabsOrder: ["T1", "T2", "T4", "T3", "T5"],
+    dragTarget: "T4",
+    expectedOrder: ["T1", "T2", "T4", "T3", "E1", "T5"],
+  },
+  {
+    description: "Test for drag a tab to right with hidden tab",
+    preferenceOrder: ["T1", "T2", "T3", "E1", "T4", "T5"],
+    currentTabsOrder: ["T1", "T3", "T4", "T2", "T5"],
+    dragTarget: "T2",
+    expectedOrder: ["T1", "T3", "E1", "T4", "T2", "T5"],
+  },
+  {
+    description: "Test for drag a tab to left end in case hidden tab was left end",
+    preferenceOrder: ["E1", "T1", "T2", "T3", "T4", "T5"],
+    currentTabsOrder: ["T4", "T1", "T2", "T3", "T5"],
+    dragTarget: "T4",
+    expectedOrder: ["E1", "T4", "T1", "T2", "T3", "T5"],
+  },
+  {
+    description: "Test for drag a tab to right end in case hidden tab was right end",
+    preferenceOrder: ["T1", "T2", "T3", "T4", "T5", "E1"],
+    currentTabsOrder: ["T2", "T3", "T4", "T5", "T1"],
+    dragTarget: "T1",
+    expectedOrder: ["T2", "T3", "T4", "T5", "E1", "T1"],
+  },
+  {
+    description: "Test for multiple hidden tabs",
+    preferenceOrder: ["T1", "T2", "E1", "E2", "E3", "E4"],
+    currentTabsOrder: ["T2", "T1"],
+    dragTarget: "T1",
+    expectedOrder: ["T2", "E1", "E2", "E3", "E4", "T1"],
+  },
+];
+
+function run_test() {
+  const { toAbsoluteOrder } =
+    require("devtools/client/framework/toolbox-tabs-order-manager");
+
+  for (const { description, preferenceOrder,
+               currentTabsOrder, dragTarget, expectedOrder} of TEST_DATA) {
+    info(description);
+    const resultOrder = toAbsoluteOrder(preferenceOrder, currentTabsOrder, dragTarget);
+    equal(resultOrder.join(","), expectedOrder.join(","), "Result should be correct");
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/unit/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+tags = devtools
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+
+[test_tabs_absolute_order.js]
--- a/devtools/client/framework/toolbox-tabs-order-manager.js
+++ b/devtools/client/framework/toolbox-tabs-order-manager.js
@@ -1,46 +1,52 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
+const { gDevTools } = require("devtools/client/framework/devtools");
 const Services = require("Services");
 const Telemetry = require("devtools/client/shared/telemetry");
 const TABS_REORDERED_SCALAR = "devtools.toolbox.tabs_reordered";
 const PREFERENCE_NAME = "devtools.toolbox.tabsOrder";
 
 /**
  * Manage the order of devtools tabs.
  */
 class ToolboxTabsOrderManager {
-  constructor(onOrderUpdated) {
+  constructor(onOrderUpdated, panelDefinitions) {
     this.onOrderUpdated = onOrderUpdated;
-    this.currentPanelDefinitions = [];
+    this.currentPanelDefinitions = panelDefinitions || [];
 
     this.onMouseDown = this.onMouseDown.bind(this);
     this.onMouseMove = this.onMouseMove.bind(this);
     this.onMouseUp = this.onMouseUp.bind(this);
 
     Services.prefs.addObserver(PREFERENCE_NAME, this.onOrderUpdated);
 
     this.telemetry = new Telemetry();
   }
 
-  destroy() {
+  async destroy() {
     Services.prefs.removeObserver(PREFERENCE_NAME, this.onOrderUpdated);
 
-    // Save the reordering preference, because some tools might be removed.
-    const ids =
-      this.currentPanelDefinitions.map(definition => definition.extensionId || definition.id);
-    const pref = ids.join(",");
-    Services.prefs.setCharPref(PREFERENCE_NAME, pref);
+    // Call mouseUp() to clear the state to prepare for in case a dragging was in progress
+    // when the destroy() was called.
+    this.onMouseUp();
 
-    this.onMouseUp();
+    // Remove panel id which is not in panel definitions and addons list.
+    let prefIds = Services.prefs.getCharPref(PREFERENCE_NAME, "").split(",");
+    const extensions = await AddonManager.getAllAddons();
+    const definitions = gDevTools.getToolDefinitionArray();
+    prefIds = prefIds.filter(id => definitions.find(d => id === (d.extensionId || d.id)) ||
+                                   extensions.find(e => id === e.id));
+    Services.prefs.setCharPref(PREFERENCE_NAME, prefIds.join(","));
   }
 
   insertBefore(target) {
     const xBefore = this.dragTarget.offsetLeft;
     this.toolboxTabsElement.insertBefore(this.dragTarget, target);
     const xAfter = this.dragTarget.offsetLeft;
     this.dragStartX += xAfter - xBefore;
     this.isOrderUpdated = true;
@@ -50,16 +56,35 @@ class ToolboxTabsOrderManager {
     return !tabElement.previousSibling;
   }
 
   isLastTab(tabElement) {
     return !tabElement.nextSibling ||
            tabElement.nextSibling.id === "tools-chevron-menu-button";
   }
 
+  saveOrderPreference() {
+    const tabs = [...this.toolboxTabsElement.querySelectorAll(".devtools-tab")];
+    const tabIds = tabs.map(tab => tab.dataset.extensionId || tab.dataset.id);
+    // Concat the overflowed tabs id since they are not contained in visible tabs.
+    // The overflowed tabs cannot be reordered so we just append the id from current
+    // panel definitions on their order.
+    const overflowedTabIds =
+      this.currentPanelDefinitions
+          .filter(definition => !tabs.some(tab => tab.dataset.id === definition.id))
+          .map(definition => definition.extensionId || definition.id);
+    const currentTabIds = tabIds.concat(overflowedTabIds);
+    const dragTargetId =
+      this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
+    const prefIds = getTabsOrderFromPreference();
+
+    const result = toAbsoluteOrder(prefIds, currentTabIds, dragTargetId);
+    Services.prefs.setCharPref(PREFERENCE_NAME, result.join(","));
+  }
+
   setCurrentPanelDefinitions(currentPanelDefinitions) {
     this.currentPanelDefinitions = currentPanelDefinitions;
   }
 
   onMouseDown(e) {
     if (!e.target.classList.contains("devtools-tab")) {
       return;
     }
@@ -126,27 +151,17 @@ class ToolboxTabsOrderManager {
     if (!this.dragTarget) {
       // The case in here has two type:
       // 1. Although destroy method was called, it was not during reordering.
       // 2. Although mouse event occur, destroy method was called during reordering.
       return;
     }
 
     if (this.isOrderUpdated) {
-      const tabs = [...this.toolboxTabsElement.querySelectorAll(".devtools-tab")];
-      const tabIds = tabs.map(tab => tab.dataset.extensionId || tab.dataset.id);
-      // Concat the overflowed tabs id since they are not contained in visible tabs.
-      // The overflowed tabs cannot be reordered so we just append the id from current
-      // panel definitions on their order.
-      const overflowedTabIds =
-        this.currentPanelDefinitions
-            .filter(definition => !tabs.some(tab => tab.dataset.id === definition.id))
-            .map(definition => definition.extensionId || definition.id);
-      const pref = tabIds.concat(overflowedTabIds).join(",");
-      Services.prefs.setCharPref(PREFERENCE_NAME, pref);
+      this.saveOrderPreference();
 
       // Log which tabs reordered. The question we want to answer is:
       // "How frequently are the tabs re-ordered, also which tabs get re-ordered?"
       const toolId = this.dragTarget.dataset.extensionId || this.dragTarget.dataset.id;
       this.telemetry.keyedScalarAdd(TABS_REORDERED_SCALAR, toolId, 1);
     }
 
     this.eventTarget.removeEventListener("mousemove", this.onMouseMove);
@@ -156,23 +171,83 @@ class ToolboxTabsOrderManager {
     this.dragTarget.style.left = null;
     this.dragTarget = null;
     this.toolboxContainerElement = null;
     this.toolboxTabsElement = null;
     this.eventTarget = null;
   }
 }
 
-function sortPanelDefinitions(definitions) {
+function getTabsOrderFromPreference() {
   const pref = Services.prefs.getCharPref(PREFERENCE_NAME, "");
-  const toolIds = pref.split(",");
+  return pref ? pref.split(",") : [];
+}
+
+function sortPanelDefinitions(definitions) {
+  const toolIds = getTabsOrderFromPreference();
 
   return definitions.sort((a, b) => {
     let orderA = toolIds.indexOf(a.extensionId || a.id);
     let orderB = toolIds.indexOf(b.extensionId || b.id);
     orderA = orderA < 0 ? Number.MAX_VALUE : orderA;
     orderB = orderB < 0 ? Number.MAX_VALUE : orderB;
     return orderA - orderB;
   });
 }
 
+/*
+ * This function returns absolute tab ids that were merged the both ids that are in
+ * preference and tabs.
+ * Some tabs added with add-ons etc show/hide depending on conditions.
+ * However, all of tabs that include hidden tab always keep the relationship with
+ * left side tab, except in case the left tab was target of dragging. If the left
+ * tab has been moved, it keeps its relationship with the tab next to it.
+ *
+ * Case 1: Drag a tab to left
+ *   currentTabIds: [T1, T2, T3, T4, T5]
+ *   prefIds      : [T1, T2, T3, E1(hidden), T4, T5]
+ *   drag T4      : [T1, T2, T4, T3, T5]
+ *   result       : [T1, T2, T4, T3, E1, T5]
+ *
+ * Case 2: Drag a tab to right
+ *   currentTabIds: [T1, T2, T3, T4, T5]
+ *   prefIds      : [T1, T2, T3, E1(hidden), T4, T5]
+ *   drag T2      : [T1, T3, T4, T2, T5]
+ *   result       : [T1, T3, E1, T4, T2, T5]
+ *
+ * Case 3: Hidden tab was left end and drag a tab to left end
+ *   currentTabIds: [T1, T2, T3, T4, T5]
+ *   prefIds      : [E1(hidden), T1, T2, T3, T4, T5]
+ *   drag T4      : [T4, T1, T2, T3, T5]
+ *   result       : [E1, T4, T1, T2, T3, T5]
+ *
+ * Case 4: Hidden tab was right end and drag a tab to right end
+ *   currentTabIds: [T1, T2, T3, T4, T5]
+ *   prefIds      : [T1, T2, T3, T4, T5, E1(hidden)]
+ *   drag T1      : [T2, T3, T4, T5, T1]
+ *   result       : [T2, T3, T4, T5, E1, T1]
+ *
+ * @param Array - prefIds: id array of preference
+ * @param Array - currentTabIds: id array of appearanced tabs
+ * @param String - dragTargetId: id of dragged target
+ * @return Array
+ */
+function toAbsoluteOrder(prefIds, currentTabIds, dragTargetId) {
+  currentTabIds = [...currentTabIds];
+  let indexAtCurrentTabs = 0;
+
+  for (const prefId of prefIds) {
+    if (prefId === dragTargetId) {
+      // do nothing
+    } else if (currentTabIds.includes(prefId)) {
+      indexAtCurrentTabs = currentTabIds.indexOf(prefId) + 1;
+    } else {
+      currentTabIds.splice(indexAtCurrentTabs, 0, prefId);
+      indexAtCurrentTabs += 1;
+    }
+  }
+
+  return currentTabIds;
+}
+
 module.exports.ToolboxTabsOrderManager = ToolboxTabsOrderManager;
 module.exports.sortPanelDefinitions = sortPanelDefinitions;
+module.exports.toAbsoluteOrder = toAbsoluteOrder;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5302,17 +5302,20 @@ AssertContentPrivilegedAboutPageHasCSP(n
     "about: page must contain a CSP including default-src");
 }
 #endif
 
 void
 nsDocument::EndLoad()
 {
 #if defined(DEBUG) && !defined(ANDROID)
-  AssertContentPrivilegedAboutPageHasCSP(mDocumentURI, NodePrincipal());
+  // only assert if nothing stopped the load on purpose
+  if (!mParserAborted) {
+    AssertContentPrivilegedAboutPageHasCSP(mDocumentURI, NodePrincipal());
+  }
 #endif
 
   // EndLoad may have been called without a matching call to BeginLoad, in the
   // case of a failed parse (for example, due to timeout). In such a case, we
   // still want to execute part of this code to do appropriate cleanup, but we
   // gate part of it because it is intended to match 1-for-1 with calls to
   // BeginLoad. We have an explicit flag bit for this purpose, since it's
   // complicated and error prone to derive this condition from other related
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -17,16 +17,17 @@
 #include "IndexedDatabaseManager.h"
 #include "js/StructuredClone.h"
 #include "js/Value.h"
 #include "jsapi.h"
 #include "KeyPath.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Casting.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/SnappyCompressOutputStream.h"
@@ -783,39 +784,35 @@ MakeCompressedIndexDataValues(
     const IndexDataValue& info = aIndexValues[arrayIndex];
     const nsCString& keyBuffer = info.mKey.GetBuffer();
     const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
     const uint32_t keyBufferLength = keyBuffer.Length();
     const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
 
     MOZ_ASSERT(!keyBuffer.IsEmpty());
 
-    // Don't let |infoLength| overflow.
-    if (NS_WARN_IF(UINT32_MAX - keyBuffer.Length() <
-                   CompressedByteCountForIndexId(info.mIndexId) +
-                   CompressedByteCountForNumber(keyBufferLength) +
-                   CompressedByteCountForNumber(sortKeyBufferLength))) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-
-    const uint32_t infoLength =
-      CompressedByteCountForIndexId(info.mIndexId) +
+    const CheckedUint32 infoLength =
+      CheckedUint32(CompressedByteCountForIndexId(info.mIndexId)) +
       CompressedByteCountForNumber(keyBufferLength) +
       CompressedByteCountForNumber(sortKeyBufferLength) +
       keyBufferLength +
       sortKeyBufferLength;
+    // Don't let |infoLength| overflow.
+    if (NS_WARN_IF(!infoLength.isValid())) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
 
     // Don't let |blobDataLength| overflow.
-    if (NS_WARN_IF(UINT32_MAX - infoLength < blobDataLength)) {
+    if (NS_WARN_IF(UINT32_MAX - infoLength.value() < blobDataLength)) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    blobDataLength += infoLength;
+    blobDataLength += infoLength.value();
   }
 
   UniqueFreePtr<uint8_t> blobData(
     static_cast<uint8_t*>(malloc(blobDataLength)));
   if (NS_WARN_IF(!blobData)) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_OUT_OF_MEMORY;
   }
new file mode 100644
--- /dev/null
+++ b/dom/media/ImageToI420.cpp
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageToI420.h"
+
+#include "ImageContainer.h"
+#include "libyuv/convert.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/dom/ImageUtils.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+
+using mozilla::ImageFormat;
+using mozilla::dom::ImageBitmapFormat;
+using mozilla::dom::ImageUtils;
+using mozilla::gfx::DataSourceSurface;
+using mozilla::gfx::SourceSurface;
+using mozilla::gfx::SurfaceFormat;
+using mozilla::layers::Image;
+using mozilla::layers::PlanarYCbCrData;
+using mozilla::layers::PlanarYCbCrImage;
+
+static const PlanarYCbCrData* GetPlanarYCbCrData(Image* aImage)
+{
+  switch(aImage->GetFormat()) {
+    case ImageFormat::PLANAR_YCBCR:
+      return aImage->AsPlanarYCbCrImage()->GetData();
+    case ImageFormat::NV_IMAGE:
+      return aImage->AsNVImage()->GetData();
+    default:
+      return nullptr;
+  }
+}
+
+static already_AddRefed<SourceSurface> GetSourceSurface(Image* aImage)
+{
+  if (!aImage->AsGLImage() || NS_IsMainThread()) {
+    return aImage->GetAsSourceSurface();
+  }
+
+  // GLImage::GetAsSourceSurface() only supports main thread
+  RefPtr<SourceSurface> surf;
+  NS_DispatchToMainThread(
+    NS_NewRunnableFunction(
+      "ImageToI420::GLImage::GetSourceSurface",
+      [&aImage, &surf]() { surf = aImage->GetAsSourceSurface(); }),
+    NS_DISPATCH_SYNC);
+
+  return surf.forget();
+}
+
+static nsresult MapRv(int aRv)
+{
+  // Docs for libyuv::ConvertToI420 say:
+  // Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
+  switch(aRv) {
+    case 0:
+      return NS_OK;
+    case -1:
+      return NS_ERROR_INVALID_ARG;
+    default:
+      return NS_ERROR_FAILURE;
+  }
+}
+
+namespace mozilla {
+
+nsresult ConvertToI420(
+  Image* aImage,
+  uint8_t* aDestY,
+  int aDestStrideY,
+  uint8_t* aDestU,
+  int aDestStrideU,
+  uint8_t* aDestV,
+  int aDestStrideV)
+{
+  if (!aImage->IsValid()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (const PlanarYCbCrData* data = GetPlanarYCbCrData(aImage)) {
+    const ImageUtils imageUtils(aImage);
+    switch(imageUtils.GetFormat()) {
+      case ImageBitmapFormat::YUV420P:
+        return MapRv(libyuv::I420ToI420(
+          data->mYChannel,
+          data->mYStride,
+          data->mCbChannel,
+          data->mCbCrStride,
+          data->mCrChannel,
+          data->mCbCrStride,
+          aDestY,
+          aDestStrideY,
+          aDestU,
+          aDestStrideU,
+          aDestV,
+          aDestStrideV,
+          aImage->GetSize().width,
+          aImage->GetSize().height));
+      case ImageBitmapFormat::YUV422P:
+        return MapRv(libyuv::I422ToI420(
+          data->mYChannel,
+          data->mYStride,
+          data->mCbChannel,
+          data->mCbCrStride,
+          data->mCrChannel,
+          data->mCbCrStride,
+          aDestY,
+          aDestStrideY,
+          aDestU,
+          aDestStrideU,
+          aDestV,
+          aDestStrideV,
+          aImage->GetSize().width,
+          aImage->GetSize().height));
+      case ImageBitmapFormat::YUV444P:
+        return MapRv(libyuv::I444ToI420(
+          data->mYChannel,
+          data->mYStride,
+          data->mCbChannel,
+          data->mCbCrStride,
+          data->mCrChannel,
+          data->mCbCrStride,
+          aDestY,
+          aDestStrideY,
+          aDestU,
+          aDestStrideU,
+          aDestV,
+          aDestStrideV,
+          aImage->GetSize().width,
+          aImage->GetSize().height));
+      case ImageBitmapFormat::YUV420SP_NV12:
+        return MapRv(libyuv::NV12ToI420(
+          data->mYChannel,
+          data->mYStride,
+          data->mCbChannel,
+          data->mCbCrStride,
+          aDestY,
+          aDestStrideY,
+          aDestU,
+          aDestStrideU,
+          aDestV,
+          aDestStrideV,
+          aImage->GetSize().width,
+          aImage->GetSize().height));
+      case ImageBitmapFormat::YUV420SP_NV21:
+        return MapRv(libyuv::NV21ToI420(
+          data->mYChannel,
+          data->mYStride,
+          data->mCrChannel,
+          data->mCbCrStride,
+          aDestY,
+          aDestStrideY,
+          aDestU,
+          aDestStrideU,
+          aDestV,
+          aDestStrideV,
+          aImage->GetSize().width,
+          aImage->GetSize().height));
+      default:
+        MOZ_ASSERT_UNREACHABLE("YUV format conversion not implemented");
+        return NS_ERROR_NOT_IMPLEMENTED;
+    }
+  }
+
+  RefPtr<SourceSurface> surf = GetSourceSurface(aImage);
+  if (!surf) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<DataSourceSurface> data = surf->GetDataSurface();
+  if (!data) {
+    return NS_ERROR_FAILURE;
+  }
+
+  DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
+  if (!map.IsMapped()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  switch (surf->GetFormat()) {
+    case SurfaceFormat::B8G8R8A8:
+    case SurfaceFormat::B8G8R8X8:
+      return MapRv(libyuv::ARGBToI420(
+        static_cast<uint8_t*>(map.GetData()),
+        map.GetStride(),
+        aDestY,
+        aDestStrideY,
+        aDestU,
+        aDestStrideU,
+        aDestV,
+        aDestStrideV,
+        aImage->GetSize().width,
+        aImage->GetSize().height));
+    case SurfaceFormat::R5G6B5_UINT16:
+      return MapRv(libyuv::RGB565ToI420(
+        static_cast<uint8_t*>(map.GetData()),
+        map.GetStride(),
+        aDestY,
+        aDestStrideY,
+        aDestU,
+        aDestStrideU,
+        aDestV,
+        aDestStrideV,
+        aImage->GetSize().width,
+        aImage->GetSize().height));
+    default:
+      MOZ_ASSERT_UNREACHABLE("Surface format conversion not implemented");
+      return NS_ERROR_NOT_IMPLEMENTED;
+  }
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/media/ImageToI420.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ImageToI420Converter_h
+#define ImageToI420Converter_h
+
+namespace mozilla {
+
+namespace layers {
+class Image;
+} // namespace layers
+
+/**
+ * Converts aImage to an I420 image and writes it to the given buffers.
+ */
+nsresult ConvertToI420(
+  layers::Image* aImage,
+  uint8_t* aDestY,
+  int aDestStrideY,
+  uint8_t* aDestU,
+  int aDestStrideU,
+  uint8_t* aDestV,
+  int aDestStrideV);
+
+} // namespace mozilla
+
+#endif /* ImageToI420Converter_h */
--- a/dom/media/encoder/VP8TrackEncoder.cpp
+++ b/dom/media/encoder/VP8TrackEncoder.cpp
@@ -1,17 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VP8TrackEncoder.h"
+
 #include "GeckoProfiler.h"
+#include "ImageToI420.h"
 #include "LayersLogging.h"
-#include "libyuv.h"
 #include "mozilla/gfx/2D.h"
 #include "prsystem.h"
 #include "VideoSegment.h"
 #include "VideoUtils.h"
 #include "vpx/vp8cx.h"
 #include "vpx/vpx_encoder.h"
 #include "WebMWriter.h"
 #include "mozilla/media/MediaUtils.h"
@@ -28,45 +29,19 @@ LazyLogModule gVP8TrackEncoderLog("VP8Tr
 #define DEFAULT_BITRATE_BPS 2500000
 #define MAX_KEYFRAME_INTERVAL 600
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::media;
 using namespace mozilla::dom;
 
-static already_AddRefed<SourceSurface>
-GetSourceSurface(already_AddRefed<Image> aImg)
-{
-  RefPtr<Image> img = aImg;
-  if (!img) {
-    return nullptr;
-  }
-
-  if (!img->AsGLImage() || NS_IsMainThread()) {
-    RefPtr<SourceSurface> surf = img->GetAsSourceSurface();
-    return surf.forget();
-  }
-
-  // GLImage::GetAsSourceSurface() only supports main thread
-  RefPtr<SourceSurface> surf;
-  RefPtr<Runnable> runnable = NewRunnableFrom([img, &surf]() -> nsresult {
-    surf = img->GetAsSourceSurface();
-    return NS_OK;
-  });
-
-  NS_DispatchToMainThread(runnable, NS_DISPATCH_SYNC);
-  return surf.forget();
-}
-
 VP8TrackEncoder::VP8TrackEncoder(TrackRate aTrackRate,
                                  FrameDroppingMode aFrameDroppingMode)
   : VideoTrackEncoder(aTrackRate, aFrameDroppingMode)
-  , mEncodedTimestamp(0)
-  , mDurationSinceLastKeyframe(0)
   , mVPXContext(new vpx_codec_ctx_t())
   , mVPXImageWrapper(new vpx_image_t())
 {
   MOZ_COUNT_CTOR(VP8TrackEncoder);
 }
 
 VP8TrackEncoder::~VP8TrackEncoder()
 {
@@ -314,16 +289,28 @@ VP8TrackEncoder::GetEncodedPartitions(En
            videoData->GetTimeStamp(), videoData->GetDuration(),
            videoData->GetFrameType());
     aData.AppendEncodedFrame(videoData);
   }
 
   return pkt ? NS_OK : NS_ERROR_NOT_AVAILABLE;
 }
 
+
+template<int N>
+static int Aligned(int aValue)
+{
+  if (aValue < N) {
+    return N;
+  }
+
+  // The `- 1` avoids overreaching when `aValue % N == 0`.
+  return (((aValue - 1) / N) + 1) * N;
+}
+
 nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
 {
   RefPtr<Image> img;
   if (aChunk.mFrame.GetForceBlack() || aChunk.IsNull()) {
     if (!mMuteFrame) {
       mMuteFrame = VideoFrame::CreateBlackImage(gfx::IntSize(mFrameWidth, mFrameHeight));
     }
     if (!mMuteFrame) {
@@ -359,214 +346,70 @@ nsresult VP8TrackEncoder::PrepareRawFram
                          imgSize.height,
                          intrinsicSize.width,
                          intrinsicSize.height);
       VP8LOG(LogLevel::Info, "Recreated VP8 encoder.");
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  ImageFormat format = img->GetFormat();
-  if (format == ImageFormat::PLANAR_YCBCR) {
-    PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
-
-    MOZ_RELEASE_ASSERT(yuv);
-    if (!yuv->IsValid()) {
-      NS_WARNING("PlanarYCbCrImage is not valid");
-      return NS_ERROR_FAILURE;
-    }
-
-    // The ImageUtils API may change depending on our support for ImageBitmap
-    // extensions. Should this happen in a breaking way we should abstract out
-    // the format detection for use here.
-    const ImageUtils imageUtils(img);
-    const ImageBitmapFormat imageBitmapFormat = imageUtils.GetFormat();
+  // Clear image state from last frame
+  mVPXImageWrapper->planes[VPX_PLANE_Y] = nullptr;
+  mVPXImageWrapper->stride[VPX_PLANE_Y] = 0;
+  mVPXImageWrapper->planes[VPX_PLANE_U] = nullptr;
+  mVPXImageWrapper->stride[VPX_PLANE_U] = 0;
+  mVPXImageWrapper->planes[VPX_PLANE_V] = nullptr;
+  mVPXImageWrapper->stride[VPX_PLANE_V] = 0;
 
-    if (imageBitmapFormat == ImageBitmapFormat::YUV420P) {
-      // 420 planar, no need for conversions
-      const PlanarYCbCrImage::Data* data = yuv->GetData();
-      mVPXImageWrapper->planes[VPX_PLANE_Y] = data->mYChannel;
-      mVPXImageWrapper->planes[VPX_PLANE_U] = data->mCbChannel;
-      mVPXImageWrapper->planes[VPX_PLANE_V] = data->mCrChannel;
-      mVPXImageWrapper->stride[VPX_PLANE_Y] = data->mYStride;
-      mVPXImageWrapper->stride[VPX_PLANE_U] = data->mCbCrStride;
-      mVPXImageWrapper->stride[VPX_PLANE_V] = data->mCbCrStride;
+  int yStride = Aligned<16>(mFrameWidth);
+  int yHeight = mFrameHeight;
+  size_t yPlaneSize = yStride * yHeight;
 
-      return NS_OK;
-    }
-  }
+  int uvStride = Aligned<16>((mFrameWidth + 1) / 2);
+  int uvHeight = (mFrameHeight + 1) / 2;
+  size_t uvPlaneSize = uvStride * uvHeight;
 
-  // Not 420 planar, have to convert
-  uint32_t yPlaneSize = mFrameWidth * mFrameHeight;
-  uint32_t halfWidth = (mFrameWidth + 1) / 2;
-  uint32_t halfHeight = (mFrameHeight + 1) / 2;
-  uint32_t uvPlaneSize = halfWidth * halfHeight;
+  size_t neededSize = yPlaneSize + uvPlaneSize * 2;
 
-  if (mI420Frame.Length() != yPlaneSize + uvPlaneSize * 2) {
-    mI420Frame.SetLength(yPlaneSize + uvPlaneSize * 2);
+  if (neededSize > mI420FrameSize) {
+    mI420Frame.reset(new (fallible) uint8_t[neededSize]);
   }
 
-  uint8_t *y = mI420Frame.Elements();
-  uint8_t *cb = mI420Frame.Elements() + yPlaneSize;
-  uint8_t *cr = mI420Frame.Elements() + yPlaneSize + uvPlaneSize;
-
-  if (format == ImageFormat::PLANAR_YCBCR) {
-    PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
+  if (!mI420Frame) {
+    VP8LOG(LogLevel::Warning,
+           "Allocating I420 frame of size %zu failed",
+           neededSize);
+    return NS_ERROR_FAILURE;
+  }
+  mI420FrameSize = neededSize;
 
-    MOZ_RELEASE_ASSERT(yuv);
-    if (!yuv->IsValid()) {
-      NS_WARNING("PlanarYCbCrImage is not valid");
-      return NS_ERROR_FAILURE;
-    }
-
-    const ImageUtils imageUtils(img);
-    const ImageBitmapFormat imageBitmapFormat = imageUtils.GetFormat();
-    const PlanarYCbCrImage::Data *data = yuv->GetData();
+  uint8_t* yChannel = &mI420Frame[0];
+  uint8_t* uChannel = &mI420Frame[yPlaneSize];
+  uint8_t* vChannel = &mI420Frame[yPlaneSize + uvPlaneSize];
 
-    int rv;
-    std::string yuvFormat;
-    if (imageBitmapFormat == ImageBitmapFormat::YUV420SP_NV12) {
-      rv = libyuv::NV12ToI420(data->mYChannel,
-                              data->mYStride,
-                              data->mCbChannel,
-                              data->mCbCrStride,
-                              y,
-                              mFrameWidth,
-                              cb,
-                              halfWidth,
-                              cr,
-                              halfWidth,
-                              mFrameWidth,
-                              mFrameHeight);
-      yuvFormat = "NV12";
-    } else if (imageBitmapFormat == ImageBitmapFormat::YUV420SP_NV21) {
-      rv = libyuv::NV21ToI420(data->mYChannel,
-                              data->mYStride,
-                              data->mCrChannel,
-                              data->mCbCrStride,
-                              y,
-                              mFrameWidth,
-                              cb,
-                              halfWidth,
-                              cr,
-                              halfWidth,
-                              mFrameWidth,
-                              mFrameHeight);
-      yuvFormat = "NV21";
-    } else if (imageBitmapFormat == ImageBitmapFormat::YUV444P) {
-      rv = libyuv::I444ToI420(data->mYChannel,
-                              data->mYStride,
-                              data->mCbChannel,
-                              data->mCbCrStride,
-                              data->mCrChannel,
-                              data->mCbCrStride,
-                              y,
-                              mFrameWidth,
-                              cb,
-                              halfWidth,
-                              cr,
-                              halfWidth,
-                              mFrameWidth,
-                              mFrameHeight);
-      yuvFormat = "I444";
-    } else if (imageBitmapFormat == ImageBitmapFormat::YUV422P) {
-      rv = libyuv::I422ToI420(data->mYChannel,
-                              data->mYStride,
-                              data->mCbChannel,
-                              data->mCbCrStride,
-                              data->mCrChannel,
-                              data->mCbCrStride,
-                              y,
-                              mFrameWidth,
-                              cb,
-                              halfWidth,
-                              cr,
-                              halfWidth,
-                              mFrameWidth,
-                              mFrameHeight);
-      yuvFormat = "I422";
-    } else {
-      VP8LOG(LogLevel::Error, "Unsupported planar format");
-      NS_ASSERTION(false, "Unsupported planar format");
-      return NS_ERROR_NOT_IMPLEMENTED;
-    }
+  nsresult rv = ConvertToI420(
+    img,
+    yChannel,
+    yStride,
+    uChannel,
+    uvStride,
+    vChannel,
+    uvStride);
 
-    if (rv != 0) {
-      VP8LOG(LogLevel::Error, "Converting an %s frame to I420 failed", yuvFormat.c_str());
-      return NS_ERROR_FAILURE;
-    }
-
-    VP8LOG(LogLevel::Verbose, "Converted an %s frame to I420", yuvFormat.c_str());
-  } else {
-    // Not YCbCr at all. Try to get access to the raw data and convert.
-
-    RefPtr<SourceSurface> surf = GetSourceSurface(img.forget());
-    if (!surf) {
-      VP8LOG(LogLevel::Error, "Getting surface from %s image failed", Stringify(format).c_str());
-      return NS_ERROR_FAILURE;
-    }
-
-    RefPtr<DataSourceSurface> data = surf->GetDataSurface();
-    if (!data) {
-      VP8LOG(LogLevel::Error, "Getting data surface from %s image with %s (%s) surface failed",
-             Stringify(format).c_str(), Stringify(surf->GetType()).c_str(),
-             Stringify(surf->GetFormat()).c_str());
-      return NS_ERROR_FAILURE;
-    }
-
-    DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
-    if (!map.IsMapped()) {
-      VP8LOG(LogLevel::Error, "Reading DataSourceSurface from %s image with %s (%s) surface failed",
-             Stringify(format).c_str(), Stringify(surf->GetType()).c_str(),
-             Stringify(surf->GetFormat()).c_str());
-      return NS_ERROR_FAILURE;
-    }
-
-    int rv;
-    switch (surf->GetFormat()) {
-      case SurfaceFormat::B8G8R8A8:
-      case SurfaceFormat::B8G8R8X8:
-        rv = libyuv::ARGBToI420(static_cast<uint8*>(map.GetData()),
-                                map.GetStride(),
-                                y, mFrameWidth,
-                                cb, halfWidth,
-                                cr, halfWidth,
-                                mFrameWidth, mFrameHeight);
-        break;
-      case SurfaceFormat::R5G6B5_UINT16:
-        rv = libyuv::RGB565ToI420(static_cast<uint8*>(map.GetData()),
-                                  map.GetStride(),
-                                  y, mFrameWidth,
-                                  cb, halfWidth,
-                                  cr, halfWidth,
-                                  mFrameWidth, mFrameHeight);
-        break;
-      default:
-        VP8LOG(LogLevel::Error, "Unsupported SourceSurface format %s",
-               Stringify(surf->GetFormat()).c_str());
-        NS_ASSERTION(false, "Unsupported SourceSurface format");
-        return NS_ERROR_NOT_IMPLEMENTED;
-    }
-
-    if (rv != 0) {
-      VP8LOG(LogLevel::Error, "%s to I420 conversion failed",
-             Stringify(surf->GetFormat()).c_str());
-      return NS_ERROR_FAILURE;
-    }
-
-    VP8LOG(LogLevel::Verbose, "Converted a %s frame to I420",
-             Stringify(surf->GetFormat()).c_str());
+  if (NS_FAILED(rv)) {
+    VP8LOG(LogLevel::Error, "Converting to I420 failed");
+    return rv;
   }
 
-  mVPXImageWrapper->planes[VPX_PLANE_Y] = y;
-  mVPXImageWrapper->planes[VPX_PLANE_U] = cb;
-  mVPXImageWrapper->planes[VPX_PLANE_V] = cr;
-  mVPXImageWrapper->stride[VPX_PLANE_Y] = mFrameWidth;
-  mVPXImageWrapper->stride[VPX_PLANE_U] = halfWidth;
-  mVPXImageWrapper->stride[VPX_PLANE_V] = halfWidth;
+  mVPXImageWrapper->planes[VPX_PLANE_Y] = yChannel;
+  mVPXImageWrapper->stride[VPX_PLANE_Y] = yStride;
+  mVPXImageWrapper->planes[VPX_PLANE_U] = uChannel;
+  mVPXImageWrapper->stride[VPX_PLANE_U] = uvStride;
+  mVPXImageWrapper->planes[VPX_PLANE_V] = vChannel;
+  mVPXImageWrapper->stride[VPX_PLANE_V] = uvStride;
 
   return NS_OK;
 }
 
 // These two define value used in GetNextEncodeOperation to determine the
 // EncodeOperation for next target frame.
 #define I_FRAME_RATIO (0.5)
 #define SKIP_FRAME_RATIO (0.75)
--- a/dom/media/encoder/VP8TrackEncoder.h
+++ b/dom/media/encoder/VP8TrackEncoder.h
@@ -60,34 +60,35 @@ private:
   // Destroys the context and image wrapper. Does not de-allocate the structs.
   void Destroy();
 
   // Helper method to set the values on a VPX configuration.
   nsresult SetConfigurationValues(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth,
                                   int32_t aDisplayHeight, vpx_codec_enc_cfg_t& config);
 
   // Encoded timestamp.
-  StreamTime mEncodedTimestamp;
+  StreamTime mEncodedTimestamp = 0;
 
   // Total duration in mTrackRate extracted by GetEncodedPartitions().
   CheckedInt64 mExtractedDuration;
 
   // Total duration in microseconds extracted by GetEncodedPartitions().
   CheckedInt64 mExtractedDurationUs;
 
   // Muted frame, we only create it once.
   RefPtr<layers::Image> mMuteFrame;
 
   // I420 frame, for converting to I420.
-  nsTArray<uint8_t> mI420Frame;
+  UniquePtr<uint8_t[]> mI420Frame;
+  size_t mI420FrameSize = 0;
 
   /**
    * A duration of non-key frames in milliseconds.
   */
-  StreamTime mDurationSinceLastKeyframe;
+  StreamTime mDurationSinceLastKeyframe = 0;
 
   /**
    * A local segment queue which takes the raw data out from mRawSegment in the
    * call of GetEncodedTrack().
    */
   VideoSegment mSourceSegment;
 
   // VP8 relative members.
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -111,16 +111,17 @@ EXPORTS += [
     'BufferReader.h',
     'ByteWriter.h',
     'ChannelMediaDecoder.h',
     'CubebUtils.h',
     'DecoderTraits.h',
     'DOMMediaStream.h',
     'FileBlockCache.h',
     'FrameStatistics.h',
+    'ImageToI420.h',
     'Intervals.h',
     'Latency.h',
     'MediaCache.h',
     'MediaContainerType.h',
     'MediaData.h',
     'MediaDataDemuxer.h',
     'MediaDecoder.h',
     'MediaDecoderOwner.h',
@@ -231,16 +232,17 @@ UNIFIED_SOURCES += [
     'ChannelMediaDecoder.cpp',
     'ChannelMediaResource.cpp',
     'CloneableWithRangeMediaResource.cpp',
     'DOMMediaStream.cpp',
     'FileBlockCache.cpp',
     'FileMediaResource.cpp',
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
+    'ImageToI420.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaContainerType.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaDeviceInfo.cpp',
     'MediaDevices.cpp',
@@ -318,16 +320,17 @@ EXTRA_JS_MODULES.media += [
 ]
 
 LOCAL_INCLUDES += [
     '/caps',
     '/docshell/base',
     '/dom/base',
     '/layout/generic',
     '/layout/xul',
+    '/media/libyuv/libyuv/include',
     '/netwerk/base',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     LOCAL_INCLUDES += [
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/trunk',
     ]
new file mode 100644
--- /dev/null
+++ b/dom/payments/MerchantValidationEvent.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MerchantValidationEvent.h"
+#include "nsNetCID.h"
+#include "mozilla/dom/PaymentRequest.h"
+#include "mozilla/dom/Location.h"
+#include "mozilla/dom/URL.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(MerchantValidationEvent, Event, mRequest)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MerchantValidationEvent, Event)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MerchantValidationEvent)
+NS_INTERFACE_MAP_END_INHERITING(Event)
+
+NS_IMPL_ADDREF_INHERITED(MerchantValidationEvent, Event)
+NS_IMPL_RELEASE_INHERITED(MerchantValidationEvent, Event)
+
+// User-land code constructor
+already_AddRefed<MerchantValidationEvent>
+MerchantValidationEvent::Constructor(
+  const GlobalObject& aGlobal,
+  const nsAString& aType,
+  const MerchantValidationEventInit& aEventInitDict,
+  ErrorResult& aRv)
+{
+  // validate passed URL
+  nsCOMPtr<mozilla::dom::EventTarget> owner =
+    do_QueryInterface(aGlobal.GetAsSupports());
+  return Constructor(owner, aType, aEventInitDict, aRv);
+}
+
+// Internal JS object constructor
+already_AddRefed<MerchantValidationEvent>
+MerchantValidationEvent::Constructor(
+  EventTarget* aOwner,
+  const nsAString& aType,
+  const MerchantValidationEventInit& aEventInitDict,
+  ErrorResult& aRv)
+{
+  RefPtr<MerchantValidationEvent> e = new MerchantValidationEvent(aOwner);
+  bool trusted = e->Init(aOwner);
+  e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
+  if (!e->init(aEventInitDict, aRv)) {
+    return nullptr;
+  }
+  e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
+  return e.forget();
+}
+
+bool
+MerchantValidationEvent::init(const MerchantValidationEventInit& aEventInitDict,
+                              ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
+  auto doc = window->GetExtantDoc();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return false;
+  }
+  auto principal = doc->NodePrincipal();
+
+  nsCOMPtr<nsIURI> baseURI;
+  principal->GetURI(getter_AddRefs(baseURI));
+
+  nsresult rv;
+  nsCOMPtr<nsIURI> validationUri;
+  rv = NS_NewURI(getter_AddRefs(validationUri),
+                 aEventInitDict.mValidationURL,
+                 nullptr,
+                 baseURI,
+                 nsContentUtils::GetIOService());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.ThrowTypeError<MSG_INVALID_URL>(aEventInitDict.mValidationURL);
+    return false;
+  }
+  nsAutoCString utf8href;
+  rv = validationUri->GetSpec(utf8href);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_BAD_URI);
+    return false;
+  }
+  CopyUTF8toUTF16(utf8href, mValidationURL);
+  return true;
+}
+
+MerchantValidationEvent::MerchantValidationEvent(EventTarget* aOwner)
+  : Event(aOwner, nullptr, nullptr)
+  , mWaitForUpdate(false)
+{
+  MOZ_ASSERT(aOwner);
+}
+
+void
+MerchantValidationEvent::ResolvedCallback(JSContext* aCx,
+                                          JS::Handle<JS::Value> aValue)
+{
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(mRequest);
+
+  if (!mWaitForUpdate) {
+    return;
+  }
+  mWaitForUpdate = false;
+
+  // If we eventually end up supporting merchant validation
+  // we would validate `aValue` here, as per:
+  // https://w3c.github.io/payment-request/#validate-merchant-s-details-algorithm
+  //
+  // Right now, MerchantValidationEvent is only implemented for standards
+  // conformance, which is why at this point we throw a NS_ERROR_DOM_NOT_SUPPORTED_ERR.
+
+  mRequest->AbortUpdate(NS_ERROR_DOM_NOT_SUPPORTED_ERR, false);
+  mRequest->SetUpdating(false);
+}
+
+void
+MerchantValidationEvent::RejectedCallback(JSContext* aCx,
+                                          JS::Handle<JS::Value> aValue)
+{
+  MOZ_ASSERT(mRequest);
+  if (!mWaitForUpdate) {
+    return;
+  }
+  mWaitForUpdate = false;
+  mRequest->AbortUpdate(NS_ERROR_DOM_ABORT_ERR, false);
+  mRequest->SetUpdating(false);
+}
+
+void
+MerchantValidationEvent::Complete(Promise& aPromise, ErrorResult& aRv)
+{
+  if (!IsTrusted()) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  MOZ_ASSERT(mRequest);
+
+  if (mWaitForUpdate || !mRequest->ReadyForUpdate()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  aPromise.AppendNativeHandler(this);
+
+  StopPropagation();
+  StopImmediatePropagation();
+  mWaitForUpdate = true;
+  mRequest->SetUpdating(true);
+}
+
+void
+MerchantValidationEvent::SetRequest(PaymentRequest* aRequest)
+{
+  MOZ_ASSERT(IsTrusted());
+  MOZ_ASSERT(!mRequest);
+  MOZ_ASSERT(aRequest);
+
+  mRequest = aRequest;
+}
+
+void
+MerchantValidationEvent::GetValidationURL(nsAString& aValidationURL)
+{
+  aValidationURL.Assign(mValidationURL);
+}
+
+void
+MerchantValidationEvent::SetValidationURL(nsAString& aValidationURL)
+{
+  mValidationURL.Assign(aValidationURL);
+}
+
+MerchantValidationEvent::~MerchantValidationEvent() {}
+
+JSObject*
+MerchantValidationEvent::WrapObjectInternal(JSContext* aCx,
+                                            JS::Handle<JSObject*> aGivenProto)
+{
+  return MerchantValidationEvent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/payments/MerchantValidationEvent.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MerchantValidationEvent_h
+#define mozilla_dom_MerchantValidationEvent_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/MerchantValidationEventBinding.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class PaymentRequest;
+class MerchantValidationEvent
+  : public Event
+  , public PromiseNativeHandler
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
+    MerchantValidationEvent,
+    Event)
+
+  explicit MerchantValidationEvent(EventTarget* aOwner);
+
+  virtual JSObject* WrapObjectInternal(
+    JSContext* aCx,
+    JS::Handle<JSObject*> aGivenProto) override;
+
+  virtual void ResolvedCallback(JSContext* aCx,
+                                JS::Handle<JS::Value> aValue) override;
+  virtual void RejectedCallback(JSContext* aCx,
+                                JS::Handle<JS::Value> aValue) override;
+
+  static already_AddRefed<MerchantValidationEvent> Constructor(
+    EventTarget* aOwner,
+    const nsAString& aType,
+    const MerchantValidationEventInit& aEventInitDict,
+    ErrorResult& aRv);
+
+  // Called by WebIDL constructor
+  static already_AddRefed<MerchantValidationEvent> Constructor(
+    const GlobalObject& aGlobal,
+    const nsAString& aType,
+    const MerchantValidationEventInit& aEventInitDict,
+    ErrorResult& aRv);
+
+  void Complete(Promise& aPromise, ErrorResult& aRv);
+
+  void SetRequest(PaymentRequest* aRequest);
+
+  void GetValidationURL(nsAString& aValidationURL);
+
+  void SetValidationURL(nsAString& aValidationURL);
+
+protected:
+  bool init(const MerchantValidationEventInit& aEventInitDict, ErrorResult& aRv);
+  ~MerchantValidationEvent();
+
+private:
+  // Indicating whether an Complete()-initiated update is currently in progress.
+  bool mWaitForUpdate;
+  nsString mValidationURL;
+  RefPtr<PaymentRequest> mRequest;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MerchantValidationEvent_h
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/PaymentRequest.h"
 #include "mozilla/dom/PaymentRequestChild.h"
 #include "mozilla/dom/PaymentResponse.h"
 #include "mozilla/EventStateManager.h"
 #include "nsContentUtils.h"
 #include "nsIURLParser.h"
 #include "nsNetCID.h"
 #include "PaymentRequestManager.h"
+#include "mozilla/dom/MerchantValidationEvent.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequest,
                                                DOMEventTargetHelper)
@@ -978,16 +979,39 @@ PaymentRequest::DispatchUpdateEvent(cons
   event->SetTrusted(true);
   event->SetRequest(this);
 
   ErrorResult rv;
   DispatchEvent(*event, rv);
   return rv.StealNSResult();
 }
 
+nsresult
+PaymentRequest::DispatchMerchantValidationEvent(const nsAString& aType)
+{
+  MOZ_ASSERT(ReadyForUpdate());
+
+  MerchantValidationEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mValidationURL = EmptyString();
+
+  ErrorResult rv;
+  RefPtr<MerchantValidationEvent> event =
+    MerchantValidationEvent::Constructor(this, aType, init, rv);
+  if (rv.Failed()) {
+    return rv.StealNSResult();
+  }
+  event->SetTrusted(true);
+  event->SetRequest(this);
+
+  DispatchEvent(*event, rv);
+  return rv.StealNSResult();
+}
+
 already_AddRefed<PaymentAddress>
 PaymentRequest::GetShippingAddress() const
 {
   RefPtr<PaymentAddress> address = mShippingAddress;
   return address.forget();
 }
 
 nsresult
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -151,16 +151,17 @@ public:
     mRequestShipping = true;
   }
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
   void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
+  IMPL_EVENT_HANDLER(merchantvalidation);
   IMPL_EVENT_HANDLER(shippingaddresschange);
   IMPL_EVENT_HANDLER(shippingoptionchange);
   IMPL_EVENT_HANDLER(paymentmethodchange);
 
   void SetIPC(PaymentRequestChild* aChild)
   {
     mIPC = aChild;
   }
@@ -173,16 +174,18 @@ public:
 protected:
   ~PaymentRequest();
 
   void RegisterActivityObserver();
   void UnregisterActivityObserver();
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
+  nsresult DispatchMerchantValidationEvent(const nsAString& aType);
+
   PaymentRequest(nsPIDOMWindowInner* aWindow, const nsAString& aInternalId);
 
   // Id for internal identification
   nsString mInternalId;
   // Id for communicating with merchant side
   // mId is initialized to details.id if it exists
   // otherwise, mId has the same value as mInternalId.
   nsString mId;
--- a/dom/payments/moz.build
+++ b/dom/payments/moz.build
@@ -9,26 +9,28 @@ DIRS += [
 ]
 
 EXPORTS += [
     'PaymentRequestData.h',
     'PaymentRequestService.h',
 ]
 
 EXPORTS.mozilla.dom += [
+    'MerchantValidationEvent.h',
     'PaymentAddress.h',
     'PaymentMethodChangeEvent.h',
     'PaymentRequest.h',
     'PaymentRequestManager.h',
     'PaymentRequestUpdateEvent.h',
     'PaymentResponse.h',
 ]
 
 UNIFIED_SOURCES += [
     'BasicCardPayment.cpp',
+    'MerchantValidationEvent.cpp',
     'PaymentActionResponse.cpp',
     'PaymentAddress.cpp',
     'PaymentMethodChangeEvent.cpp',
     'PaymentRequest.cpp',
     'PaymentRequestData.cpp',
     'PaymentRequestManager.cpp',
     'PaymentRequestModule.cpp',
     'PaymentRequestService.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MerchantValidationEvent.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this WebIDL file is
+ *   https://w3c.github.io/payment-request/#merchantvalidationevent-interface
+ *   https://w3c.github.io/payment-request/#merchantvalidationeventinit-dictionary
+ */
+
+[Constructor(DOMString type, optional MerchantValidationEventInit eventInitDict),
+SecureContext,
+Exposed=Window,
+Func="mozilla::dom::PaymentRequest::PrefEnabled"]
+interface MerchantValidationEvent : Event {
+  readonly attribute USVString validationURL;
+  [Throws]
+  void complete(Promise<any> merchantSessionPromise);
+};
+
+dictionary MerchantValidationEventInit : EventInit {
+  USVString validationURL = "";
+};
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -115,12 +115,13 @@ interface PaymentRequest : EventTarget {
   [NewObject]
   Promise<boolean>         canMakePayment();
 
   readonly attribute DOMString            id;
   readonly attribute PaymentAddress?      shippingAddress;
   readonly attribute DOMString?           shippingOption;
   readonly attribute PaymentShippingType? shippingType;
 
+           attribute EventHandler         onmerchantvalidation;
            attribute EventHandler         onshippingaddresschange;
            attribute EventHandler         onshippingoptionchange;
            attribute EventHandler         onpaymentmethodchange;
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -661,16 +661,17 @@ WEBIDL_FILES = [
     'MediaStreamAudioDestinationNode.webidl',
     'MediaStreamAudioSourceNode.webidl',
     'MediaStreamError.webidl',
     'MediaStreamTrack.webidl',
     'MediaTrackConstraintSet.webidl',
     'MediaTrackSettings.webidl',
     'MediaTrackSupportedConstraints.webidl',
     'MenuBoxObject.webidl',
+    'MerchantValidationEvent.webidl',
     'MessageChannel.webidl',
     'MessageEvent.webidl',
     'MessagePort.webidl',
     'MIDIAccess.webidl',
     'MIDIInput.webidl',
     'MIDIInputMap.webidl',
     'MIDIMessageEvent.webidl',
     'MIDIOptions.webidl',
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -363,9 +363,28 @@ JS_EncodeStringToLatin1(JSContext* cx, J
  * expect null-terminated strings).
  *
  * Avoid using this function if possible, because we'll remove it once we can
  * devise a better API for the task.
  */
 extern JS_PUBLIC_API(JS::UniqueChars)
 JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
 
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeStringToLatin1(), but encode into an ASCII string.
+ *
+ * This function asserts in debug mode that the input string contains only
+ * ASCII characters.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API(JS::UniqueChars)
+JS_EncodeStringToASCII(JSContext* cx, JSString* str);
+
 #endif /* js_CharacterEncoding_h */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -971,44 +971,77 @@ GCPreserveCode(JSContext* cx, unsigned a
 
     args.rval().setUndefined();
     return true;
 }
 
 #ifdef JS_GC_ZEAL
 
 static bool
+ParseGCZealMode(JSContext* cx, const CallArgs& args, uint8_t* zeal)
+{
+    uint32_t value;
+    if (!ToUint32(cx, args.get(0), &value)) {
+        return false;
+    }
+
+    if (value > uint32_t(gc::ZealMode::Limit)) {
+        JS_ReportErrorASCII(cx, "gczeal argument out of range");
+        return false;
+    }
+
+    *zeal = static_cast<uint8_t>(value);
+    return true;
+}
+
+static bool
 GCZeal(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() > 2) {
         RootedObject callee(cx, &args.callee());
         ReportUsageErrorASCII(cx, callee, "Too many arguments");
         return false;
     }
 
-    uint32_t zeal;
-    if (!ToUint32(cx, args.get(0), &zeal)) {
-        return false;
-    }
-
-    if (zeal > uint32_t(gc::ZealMode::Limit)) {
-        JS_ReportErrorASCII(cx, "gczeal argument out of range");
+    uint8_t zeal;
+    if (!ParseGCZealMode(cx, args, &zeal)) {
         return false;
     }
 
     uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
     if (args.length() >= 2) {
         if (!ToUint32(cx, args.get(1), &frequency)) {
             return false;
         }
     }
 
-    JS_SetGCZeal(cx, (uint8_t)zeal, frequency);
+    JS_SetGCZeal(cx, zeal, frequency);
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() > 1) {
+        RootedObject callee(cx, &args.callee());
+        ReportUsageErrorASCII(cx, callee, "Too many arguments");
+        return false;
+    }
+
+    uint8_t zeal;
+    if (!ParseGCZealMode(cx, args, &zeal)) {
+        return false;
+    }
+
+    JS_UnsetGCZeal(cx, zeal);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 ScheduleGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -5011,17 +5044,27 @@ SetTimeZone(JSContext* cx, unsigned argc
 #if defined(_WIN32)
         return _putenv_s("TZ", "") == 0;
 #else
         return unsetenv("TZ") == 0;
 #endif /* _WIN32 */
     };
 
     if (args[0].isString() && !args[0].toString()->empty()) {
-        UniqueChars timeZone = JS_EncodeStringToLatin1(cx, args[0].toString());
+        RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
+        if (!str) {
+            return false;
+        }
+
+        if (!StringIsAscii(str)) {
+            ReportUsageErrorASCII(cx, callee, "First argument contains non-ASCII characters");
+            return false;
+        }
+
+        UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
         if (!timeZone) {
             return false;
         }
 
         if (!setTimeZone(timeZone.get())) {
             JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
             return false;
         }
@@ -5076,45 +5119,38 @@ SetDefaultLocale(JSContext* cx, unsigned
     }
 
     if (!args[0].isString() && !args[0].isUndefined()) {
         ReportUsageErrorASCII(cx, callee, "First argument should be a string or undefined");
         return false;
     }
 
     if (args[0].isString() && !args[0].toString()->empty()) {
-        auto containsOnlyValidBCP47Characters = [](auto* chars, size_t length) {
-            return mozilla::IsAsciiAlpha(chars[0]) &&
-                   std::all_of(chars, chars + length, [](auto c) {
-                       return mozilla::IsAsciiAlphanumeric(c) || c == '-';
-                   });
-        };
-
         RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
         if (!str) {
             return false;
         }
 
-        bool hasValidChars;
-        {
-            JS::AutoCheckCannotGC nogc;
-
-            size_t length = str->length();
-            hasValidChars = str->hasLatin1Chars()
-                            ? containsOnlyValidBCP47Characters(str->latin1Chars(nogc), length)
-                            : containsOnlyValidBCP47Characters(str->twoByteChars(nogc), length);
-        }
-
-        if (!hasValidChars) {
-            ReportUsageErrorASCII(cx, callee, "First argument should be BCP47 language tag");
+        if (!StringIsAscii(str)) {
+            ReportUsageErrorASCII(cx, callee, "First argument contains non-ASCII characters");
+            return false;
+        }
+
+        UniqueChars locale = JS_EncodeStringToASCII(cx, str);
+        if (!locale) {
             return false;
         }
 
-        UniqueChars locale = JS_EncodeStringToLatin1(cx, str);
-        if (!locale) {
+        bool containsOnlyValidBCP47Characters = mozilla::IsAsciiAlpha(locale[0]) &&
+            std::all_of(locale.get(), locale.get() + str->length(), [](auto c) {
+                return mozilla::IsAsciiAlphanumeric(c) || c == '-';
+            });
+
+        if (!containsOnlyValidBCP47Characters) {
+            ReportUsageErrorASCII(cx, callee, "First argument should be a BCP47 language tag");
             return false;
         }
 
         if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
             ReportOutOfMemory(cx);
             return false;
         }
     } else {
@@ -5672,19 +5708,24 @@ JS_FN_HELP("streamsAreEnabled", StreamsA
 "  Reset the value returned by finalizeCount()."),
 
     JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
 "gcPreserveCode()",
 "  Preserve JIT code during garbage collections."),
 
 #ifdef JS_GC_ZEAL
     JS_FN_HELP("gczeal", GCZeal, 2, 0,
-"gczeal(level, [N])",
+"gczeal(mode, [frequency])",
 gc::ZealModeHelpText),
 
+    JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
+"unsetgczeal(mode)",
+"  Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
+"  collection that may be happening."),
+
     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
 "schedulegc([num | obj | string])",
 "  If num is given, schedule a GC after num allocations.\n"
 "  If obj is given, schedule a GC of obj's zone.\n"
 "  If string is given, schedule a GC of the string's zone if possible.\n"
 "  Returns the number of allocations before the next trigger."),
 
     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
--- a/js/src/builtin/intl/CommonFunctions.cpp
+++ b/js/src/builtin/intl/CommonFunctions.cpp
@@ -86,37 +86,32 @@ void
 js::intl::ReportInternalError(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
 }
 
 js::UniqueChars
 js::intl::EncodeLocale(JSContext* cx, JSString* locale)
 {
-#ifdef DEBUG
-    auto containsOnlyValidBCP47Chars = [](auto* chars, size_t length) {
-        return length > 0 &&
-               mozilla::IsAsciiAlpha(chars[0]) &&
-               std::all_of(chars, chars + length, [](auto c) {
-                   return mozilla::IsAsciiAlphanumeric(c) || c == '-';
-               });
-    };
+    MOZ_ASSERT(locale->length() > 0);
+
+    js::UniqueChars chars = EncodeAscii(cx, locale);
 
-    if (JSLinearString* linear = locale->ensureLinear(cx)) {
-        JS::AutoCheckCannotGC nogc;
-        MOZ_ASSERT(linear->hasLatin1Chars()
-                   ? containsOnlyValidBCP47Chars(linear->latin1Chars(nogc), linear->length())
-                   : containsOnlyValidBCP47Chars(linear->twoByteChars(nogc), linear->length()));
-    } else {
-        // Ignore OOM when only performing a debug assertion.
-        cx->recoverFromOutOfMemory();
+#ifdef DEBUG
+    // Ensure the returned value contains only valid BCP 47 characters.
+    // (Lambdas can't be placed inside MOZ_ASSERT, so move the checks in an
+    // #ifdef block.)
+    if (chars) {
+        auto alnumOrDash = [](char c) { return mozilla::IsAsciiAlphanumeric(c) || c == '-'; };
+        MOZ_ASSERT(mozilla::IsAsciiAlpha(chars[0]));
+        MOZ_ASSERT(std::all_of(chars.get(), chars.get() + locale->length(), alnumOrDash));
     }
 #endif
 
-    return EncodeLatin1(cx, locale);
+    return chars;
 }
 
 bool
 js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable,
                               GetAvailable getAvailable, JS::MutableHandle<JS::Value> result)
 {
     RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
     if (!locales) {
--- a/js/src/frontend/BinSource-auto.cpp
+++ b/js/src/frontend/BinSource-auto.cpp
@@ -37,18 +37,20 @@ template<typename Tok, size_t N>
 bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
     return Tok::equals(left, right);
 }
 
 
 // ----- Sums of interfaces (autogenerated, by lexicographical order)
 // Sums of sums are flattened.
 /*
-ArrowExpression ::= EagerArrowExpression
-    SkippableArrowExpression
+ArrowExpression ::= EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseArrowExpression()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -61,29 +63,84 @@ BinASTParser<Tok>::parseArrowExpression(
     return result;
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumArrowExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("ArrowExpression", kind);
     }
     return result;
 }
 
 /*
+AssertedMaybePositionalParameterName ::= AssertedParameterName
+    AssertedPositionalParameterName
+    AssertedRestParameterName
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedMaybePositionalParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+    const auto start = tokenizer_->offset();
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+
+    BINJS_MOZ_TRY_DECL(result, parseSumAssertedMaybePositionalParameterName(start, kind, fields,
+        scopeKind, positionalParams));
+
+    MOZ_TRY(guard.done());
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseSumAssertedMaybePositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    Ok result;
+    switch (kind) {
+      case BinKind::AssertedParameterName:
+        MOZ_TRY_VAR(result, parseInterfaceAssertedParameterName(start, kind, fields,
+            scopeKind, positionalParams));
+        break;
+      case BinKind::AssertedPositionalParameterName:
+        MOZ_TRY_VAR(result, parseInterfaceAssertedPositionalParameterName(start, kind, fields,
+            scopeKind, positionalParams));
+        break;
+      case BinKind::AssertedRestParameterName:
+        MOZ_TRY_VAR(result, parseInterfaceAssertedRestParameterName(start, kind, fields,
+            scopeKind, positionalParams));
+        break;
+      default:
+        return raiseInvalidKind("AssertedMaybePositionalParameterName", kind);
+    }
+    return result;
+}
+
+/*
 AssignmentTarget ::= ArrayAssignmentTarget
     AssignmentTargetIdentifier
     ComputedMemberAssignmentTarget
     ObjectAssignmentTarget
     StaticMemberAssignmentTarget
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseAssignmentTarget()
@@ -468,30 +525,32 @@ Expression ::= ArrayExpression
     AssignmentExpression
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
     StaticMemberExpression
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
 */
@@ -538,25 +597,37 @@ BinASTParser<Tok>::parseSumExpression(co
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -574,22 +645,16 @@ BinASTParser<Tok>::parseSumExpression(co
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
         break;
       case BinKind::ThisExpression:
         MOZ_TRY_VAR(result, parseInterfaceThisExpression(start, kind, fields));
@@ -617,30 +682,32 @@ ExpressionOrSuper ::= ArrayExpression
     AssignmentExpression
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
     StaticMemberExpression
     Super
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
@@ -688,25 +755,37 @@ BinASTParser<Tok>::parseSumExpressionOrS
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -724,22 +803,16 @@ BinASTParser<Tok>::parseSumExpressionOrS
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::Super:
         MOZ_TRY_VAR(result, parseInterfaceSuper(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
@@ -770,30 +843,32 @@ ExpressionOrTemplateElement ::= ArrayExp
     AssignmentExpression
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
     StaticMemberExpression
     TemplateElement
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
@@ -841,25 +916,37 @@ BinASTParser<Tok>::parseSumExpressionOrT
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -877,22 +964,16 @@ BinASTParser<Tok>::parseSumExpressionOrT
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::TemplateElement:
         MOZ_TRY_VAR(result, parseInterfaceTemplateElement(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
@@ -967,171 +1048,18 @@ BinASTParser<Tok>::parseSumForInOfBindin
         break;
       default:
         return raiseInvalidKind("ForInOfBindingOrAssignmentTarget", kind);
     }
     return result;
 }
 
 /*
-FunctionBodyOrExpression ::= ArrayExpression
-    AssignmentExpression
-    AwaitExpression
-    BinaryExpression
-    CallExpression
-    ClassExpression
-    CompoundAssignmentExpression
-    ComputedMemberExpression
-    ConditionalExpression
-    EagerArrowExpression
-    EagerFunctionExpression
-    FunctionBody
-    IdentifierExpression
-    LiteralBooleanExpression
-    LiteralInfinityExpression
-    LiteralNullExpression
-    LiteralNumericExpression
-    LiteralRegExpExpression
-    LiteralStringExpression
-    NewExpression
-    NewTargetExpression
-    ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
-    StaticMemberExpression
-    TemplateExpression
-    ThisExpression
-    UnaryExpression
-    UpdateExpression
-    YieldExpression
-    YieldStarExpression
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseFunctionBodyOrExpression()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-    const auto start = tokenizer_->offset();
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-
-    BINJS_MOZ_TRY_DECL(result, parseSumFunctionBodyOrExpression(start, kind, fields));
-
-    MOZ_TRY(guard.done());
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSumFunctionBodyOrExpression(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    ParseNode* result;
-    switch (kind) {
-      case BinKind::ArrayExpression:
-        MOZ_TRY_VAR(result, parseInterfaceArrayExpression(start, kind, fields));
-        break;
-      case BinKind::AssignmentExpression:
-        MOZ_TRY_VAR(result, parseInterfaceAssignmentExpression(start, kind, fields));
-        break;
-      case BinKind::AwaitExpression:
-        MOZ_TRY_VAR(result, parseInterfaceAwaitExpression(start, kind, fields));
-        break;
-      case BinKind::BinaryExpression:
-        MOZ_TRY_VAR(result, parseInterfaceBinaryExpression(start, kind, fields));
-        break;
-      case BinKind::CallExpression:
-        MOZ_TRY_VAR(result, parseInterfaceCallExpression(start, kind, fields));
-        break;
-      case BinKind::ClassExpression:
-        MOZ_TRY_VAR(result, parseInterfaceClassExpression(start, kind, fields));
-        break;
-      case BinKind::CompoundAssignmentExpression:
-        MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
-        break;
-      case BinKind::ComputedMemberExpression:
-        MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
-        break;
-      case BinKind::ConditionalExpression:
-        MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
-        break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
-        break;
-      case BinKind::EagerFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
-        break;
-      case BinKind::FunctionBody:
-        MOZ_TRY_VAR(result, parseInterfaceFunctionBody(start, kind, fields));
-        break;
-      case BinKind::IdentifierExpression:
-        MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralBooleanExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralInfinityExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralNullExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralNumericExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralNumericExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralRegExpExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralRegExpExpression(start, kind, fields));
-        break;
-      case BinKind::LiteralStringExpression:
-        MOZ_TRY_VAR(result, parseInterfaceLiteralStringExpression(start, kind, fields));
-        break;
-      case BinKind::NewExpression:
-        MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
-        break;
-      case BinKind::NewTargetExpression:
-        MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
-        break;
-      case BinKind::ObjectExpression:
-        MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
-      case BinKind::StaticMemberExpression:
-        MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
-        break;
-      case BinKind::TemplateExpression:
-        MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
-        break;
-      case BinKind::ThisExpression:
-        MOZ_TRY_VAR(result, parseInterfaceThisExpression(start, kind, fields));
-        break;
-      case BinKind::UnaryExpression:
-        MOZ_TRY_VAR(result, parseInterfaceUnaryExpression(start, kind, fields));
-        break;
-      case BinKind::UpdateExpression:
-        MOZ_TRY_VAR(result, parseInterfaceUpdateExpression(start, kind, fields));
-        break;
-      case BinKind::YieldExpression:
-        MOZ_TRY_VAR(result, parseInterfaceYieldExpression(start, kind, fields));
-        break;
-      case BinKind::YieldStarExpression:
-        MOZ_TRY_VAR(result, parseInterfaceYieldStarExpression(start, kind, fields));
-        break;
-      default:
-        return raiseInvalidKind("FunctionBodyOrExpression", kind);
-    }
-    return result;
-}
-
-/*
 FunctionDeclaration ::= EagerFunctionDeclaration
-    SkippableFunctionDeclaration
+    LazyFunctionDeclaration
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseFunctionDeclaration()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1147,18 +1075,18 @@ BinASTParser<Tok>::parseFunctionDeclarat
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
       case BinKind::EagerFunctionDeclaration:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionDeclaration(start, kind, fields));
         break;
-      case BinKind::SkippableFunctionDeclaration:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
+      case BinKind::LazyFunctionDeclaration:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("FunctionDeclaration", kind);
     }
     return result;
 }
 
 /*
@@ -1167,32 +1095,34 @@ FunctionDeclarationOrClassDeclarationOrE
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassDeclaration
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionDeclaration
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionDeclaration
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionDeclaration
-    SkippableFunctionExpression
     StaticMemberExpression
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
 */
@@ -1242,28 +1172,43 @@ BinASTParser<Tok>::parseSumFunctionDecla
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionDeclaration:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionDeclaration(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionDeclaration:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -1281,25 +1226,16 @@ BinASTParser<Tok>::parseSumFunctionDecla
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionDeclaration:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
         break;
       case BinKind::ThisExpression:
         MOZ_TRY_VAR(result, parseInterfaceThisExpression(start, kind, fields));
@@ -1320,17 +1256,17 @@ BinASTParser<Tok>::parseSumFunctionDecla
         return raiseInvalidKind("FunctionDeclarationOrClassDeclarationOrExpression", kind);
     }
     return result;
 }
 
 /*
 FunctionDeclarationOrClassDeclarationOrVariableDeclaration ::= ClassDeclaration
     EagerFunctionDeclaration
-    SkippableFunctionDeclaration
+    LazyFunctionDeclaration
     VariableDeclaration
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseFunctionDeclarationOrClassDeclarationOrVariableDeclaration()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -1350,31 +1286,31 @@ BinASTParser<Tok>::parseSumFunctionDecla
     ParseNode* result;
     switch (kind) {
       case BinKind::ClassDeclaration:
         MOZ_TRY_VAR(result, parseInterfaceClassDeclaration(start, kind, fields));
         break;
       case BinKind::EagerFunctionDeclaration:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionDeclaration(start, kind, fields));
         break;
-      case BinKind::SkippableFunctionDeclaration:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
+      case BinKind::LazyFunctionDeclaration:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
         break;
       case BinKind::VariableDeclaration:
         MOZ_TRY_VAR(result, parseInterfaceVariableDeclaration(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("FunctionDeclarationOrClassDeclarationOrVariableDeclaration", kind);
     }
     return result;
 }
 
 /*
 FunctionExpression ::= EagerFunctionExpression
-    SkippableFunctionExpression
+    LazyFunctionExpression
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseFunctionExpression()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1390,28 +1326,28 @@ BinASTParser<Tok>::parseFunctionExpressi
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("FunctionExpression", kind);
     }
     return result;
 }
 
 /*
 Getter ::= EagerGetter
-    SkippableGetter
+    LazyGetter
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseGetter()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1427,18 +1363,18 @@ BinASTParser<Tok>::parseGetter()
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumGetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
       case BinKind::EagerGetter:
         MOZ_TRY_VAR(result, parseInterfaceEagerGetter(start, kind, fields));
         break;
-      case BinKind::SkippableGetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableGetter(start, kind, fields));
+      case BinKind::LazyGetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazyGetter(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("Getter", kind);
     }
     return result;
 }
 
 /*
@@ -1495,18 +1431,18 @@ ImportDeclarationOrExportDeclarationOrSt
     ExpressionStatement
     ForInStatement
     ForOfStatement
     ForStatement
     IfStatement
     Import
     ImportNamespace
     LabelledStatement
+    LazyFunctionDeclaration
     ReturnStatement
-    SkippableFunctionDeclaration
     SwitchStatement
     SwitchStatementWithDefault
     ThrowStatement
     TryCatchStatement
     TryFinallyStatement
     VariableDeclaration
     WhileStatement
     WithStatement
@@ -1590,22 +1526,22 @@ BinASTParser<Tok>::parseSumImportDeclara
         MOZ_TRY_VAR(result, parseInterfaceImport(start, kind, fields));
         break;
       case BinKind::ImportNamespace:
         MOZ_TRY_VAR(result, parseInterfaceImportNamespace(start, kind, fields));
         break;
       case BinKind::LabelledStatement:
         MOZ_TRY_VAR(result, parseInterfaceLabelledStatement(start, kind, fields));
         break;
+      case BinKind::LazyFunctionDeclaration:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
+        break;
       case BinKind::ReturnStatement:
         MOZ_TRY_VAR(result, parseInterfaceReturnStatement(start, kind, fields));
         break;
-      case BinKind::SkippableFunctionDeclaration:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
-        break;
       case BinKind::SwitchStatement:
         MOZ_TRY_VAR(result, parseInterfaceSwitchStatement(start, kind, fields));
         break;
       case BinKind::SwitchStatementWithDefault:
         MOZ_TRY_VAR(result, parseInterfaceSwitchStatementWithDefault(start, kind, fields));
         break;
       case BinKind::ThrowStatement:
         MOZ_TRY_VAR(result, parseInterfaceThrowStatement(start, kind, fields));
@@ -1726,17 +1662,17 @@ BinASTParser<Tok>::parseSumLiteral(const
       default:
         return raiseInvalidKind("Literal", kind);
     }
     return result;
 }
 
 /*
 Method ::= EagerMethod
-    SkippableMethod
+    LazyMethod
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseMethod()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1752,32 +1688,32 @@ BinASTParser<Tok>::parseMethod()
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumMethod(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
       case BinKind::EagerMethod:
         MOZ_TRY_VAR(result, parseInterfaceEagerMethod(start, kind, fields));
         break;
-      case BinKind::SkippableMethod:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableMethod(start, kind, fields));
+      case BinKind::LazyMethod:
+        MOZ_TRY_VAR(result, parseInterfaceLazyMethod(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("Method", kind);
     }
     return result;
 }
 
 /*
 MethodDefinition ::= EagerGetter
     EagerMethod
     EagerSetter
-    SkippableGetter
-    SkippableMethod
-    SkippableSetter
+    LazyGetter
+    LazyMethod
+    LazySetter
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseMethodDefinition()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1799,40 +1735,40 @@ BinASTParser<Tok>::parseSumMethodDefinit
         MOZ_TRY_VAR(result, parseInterfaceEagerGetter(start, kind, fields));
         break;
       case BinKind::EagerMethod:
         MOZ_TRY_VAR(result, parseInterfaceEagerMethod(start, kind, fields));
         break;
       case BinKind::EagerSetter:
         MOZ_TRY_VAR(result, parseInterfaceEagerSetter(start, kind, fields));
         break;
-      case BinKind::SkippableGetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableGetter(start, kind, fields));
-        break;
-      case BinKind::SkippableMethod:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableMethod(start, kind, fields));
-        break;
-      case BinKind::SkippableSetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableSetter(start, kind, fields));
+      case BinKind::LazyGetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazyGetter(start, kind, fields));
+        break;
+      case BinKind::LazyMethod:
+        MOZ_TRY_VAR(result, parseInterfaceLazyMethod(start, kind, fields));
+        break;
+      case BinKind::LazySetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazySetter(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("MethodDefinition", kind);
     }
     return result;
 }
 
 /*
 ObjectProperty ::= DataProperty
     EagerGetter
     EagerMethod
     EagerSetter
+    LazyGetter
+    LazyMethod
+    LazySetter
     ShorthandProperty
-    SkippableGetter
-    SkippableMethod
-    SkippableSetter
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseObjectProperty()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -1857,28 +1793,28 @@ BinASTParser<Tok>::parseSumObjectPropert
         MOZ_TRY_VAR(result, parseInterfaceEagerGetter(start, kind, fields));
         break;
       case BinKind::EagerMethod:
         MOZ_TRY_VAR(result, parseInterfaceEagerMethod(start, kind, fields));
         break;
       case BinKind::EagerSetter:
         MOZ_TRY_VAR(result, parseInterfaceEagerSetter(start, kind, fields));
         break;
+      case BinKind::LazyGetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazyGetter(start, kind, fields));
+        break;
+      case BinKind::LazyMethod:
+        MOZ_TRY_VAR(result, parseInterfaceLazyMethod(start, kind, fields));
+        break;
+      case BinKind::LazySetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazySetter(start, kind, fields));
+        break;
       case BinKind::ShorthandProperty:
         MOZ_TRY_VAR(result, parseInterfaceShorthandProperty(start, kind, fields));
         break;
-      case BinKind::SkippableGetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableGetter(start, kind, fields));
-        break;
-      case BinKind::SkippableMethod:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableMethod(start, kind, fields));
-        break;
-      case BinKind::SkippableSetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableSetter(start, kind, fields));
-        break;
       default:
         return raiseInvalidKind("ObjectProperty", kind);
     }
     return result;
 }
 
 /*
 Parameter ::= ArrayBinding
@@ -2000,17 +1936,17 @@ BinASTParser<Tok>::parseSumPropertyName(
       default:
         return raiseInvalidKind("PropertyName", kind);
     }
     return result;
 }
 
 /*
 Setter ::= EagerSetter
-    SkippableSetter
+    LazySetter
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSetter()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
     const auto start = tokenizer_->offset();
@@ -2026,18 +1962,18 @@ BinASTParser<Tok>::parseSetter()
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSumSetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     ParseNode* result;
     switch (kind) {
       case BinKind::EagerSetter:
         MOZ_TRY_VAR(result, parseInterfaceEagerSetter(start, kind, fields));
         break;
-      case BinKind::SkippableSetter:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableSetter(start, kind, fields));
+      case BinKind::LazySetter:
+        MOZ_TRY_VAR(result, parseInterfaceLazySetter(start, kind, fields));
         break;
       default:
         return raiseInvalidKind("Setter", kind);
     }
     return result;
 }
 
 /*
@@ -2086,30 +2022,32 @@ SpreadElementOrExpression ::= ArrayExpre
     AssignmentExpression
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
     SpreadElement
     StaticMemberExpression
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     YieldExpression
     YieldStarExpression
@@ -2157,25 +2095,37 @@ BinASTParser<Tok>::parseSumSpreadElement
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -2193,22 +2143,16 @@ BinASTParser<Tok>::parseSumSpreadElement
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::SpreadElement:
         MOZ_TRY_VAR(result, parseInterfaceSpreadElement(start, kind, fields));
         break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
@@ -2244,18 +2188,18 @@ Statement ::= Block
     EagerFunctionDeclaration
     EmptyStatement
     ExpressionStatement
     ForInStatement
     ForOfStatement
     ForStatement
     IfStatement
     LabelledStatement
+    LazyFunctionDeclaration
     ReturnStatement
-    SkippableFunctionDeclaration
     SwitchStatement
     SwitchStatementWithDefault
     ThrowStatement
     TryCatchStatement
     TryFinallyStatement
     VariableDeclaration
     WhileStatement
     WithStatement
@@ -2318,22 +2262,22 @@ BinASTParser<Tok>::parseSumStatement(con
         MOZ_TRY_VAR(result, parseInterfaceForStatement(start, kind, fields));
         break;
       case BinKind::IfStatement:
         MOZ_TRY_VAR(result, parseInterfaceIfStatement(start, kind, fields));
         break;
       case BinKind::LabelledStatement:
         MOZ_TRY_VAR(result, parseInterfaceLabelledStatement(start, kind, fields));
         break;
+      case BinKind::LazyFunctionDeclaration:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
+        break;
       case BinKind::ReturnStatement:
         MOZ_TRY_VAR(result, parseInterfaceReturnStatement(start, kind, fields));
         break;
-      case BinKind::SkippableFunctionDeclaration:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
-        break;
       case BinKind::SwitchStatement:
         MOZ_TRY_VAR(result, parseInterfaceSwitchStatement(start, kind, fields));
         break;
       case BinKind::SwitchStatementWithDefault:
         MOZ_TRY_VAR(result, parseInterfaceSwitchStatementWithDefault(start, kind, fields));
         break;
       case BinKind::ThrowStatement:
         MOZ_TRY_VAR(result, parseInterfaceThrowStatement(start, kind, fields));
@@ -2364,30 +2308,32 @@ VariableDeclarationOrExpression ::= Arra
     AssignmentExpression
     AwaitExpression
     BinaryExpression
     CallExpression
     ClassExpression
     CompoundAssignmentExpression
     ComputedMemberExpression
     ConditionalExpression
-    EagerArrowExpression
+    EagerArrowExpressionWithExpression
+    EagerArrowExpressionWithFunctionBody
     EagerFunctionExpression
     IdentifierExpression
+    LazyArrowExpressionWithExpression
+    LazyArrowExpressionWithFunctionBody
+    LazyFunctionExpression
     LiteralBooleanExpression
     LiteralInfinityExpression
     LiteralNullExpression
     LiteralNumericExpression
     LiteralRegExpExpression
     LiteralStringExpression
     NewExpression
     NewTargetExpression
     ObjectExpression
-    SkippableArrowExpression
-    SkippableFunctionExpression
     StaticMemberExpression
     TemplateExpression
     ThisExpression
     UnaryExpression
     UpdateExpression
     VariableDeclaration
     YieldExpression
     YieldStarExpression
@@ -2435,25 +2381,37 @@ BinASTParser<Tok>::parseSumVariableDecla
         MOZ_TRY_VAR(result, parseInterfaceCompoundAssignmentExpression(start, kind, fields));
         break;
       case BinKind::ComputedMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceComputedMemberExpression(start, kind, fields));
         break;
       case BinKind::ConditionalExpression:
         MOZ_TRY_VAR(result, parseInterfaceConditionalExpression(start, kind, fields));
         break;
-      case BinKind::EagerArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpression(start, kind, fields));
+      case BinKind::EagerArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::EagerArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
         break;
       case BinKind::EagerFunctionExpression:
         MOZ_TRY_VAR(result, parseInterfaceEagerFunctionExpression(start, kind, fields));
         break;
       case BinKind::IdentifierExpression:
         MOZ_TRY_VAR(result, parseInterfaceIdentifierExpression(start, kind, fields));
         break;
+      case BinKind::LazyArrowExpressionWithExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+        break;
+      case BinKind::LazyArrowExpressionWithFunctionBody:
+        MOZ_TRY_VAR(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+        break;
+      case BinKind::LazyFunctionExpression:
+        MOZ_TRY_VAR(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+        break;
       case BinKind::LiteralBooleanExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralBooleanExpression(start, kind, fields));
         break;
       case BinKind::LiteralInfinityExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralInfinityExpression(start, kind, fields));
         break;
       case BinKind::LiteralNullExpression:
         MOZ_TRY_VAR(result, parseInterfaceLiteralNullExpression(start, kind, fields));
@@ -2471,22 +2429,16 @@ BinASTParser<Tok>::parseSumVariableDecla
         MOZ_TRY_VAR(result, parseInterfaceNewExpression(start, kind, fields));
         break;
       case BinKind::NewTargetExpression:
         MOZ_TRY_VAR(result, parseInterfaceNewTargetExpression(start, kind, fields));
         break;
       case BinKind::ObjectExpression:
         MOZ_TRY_VAR(result, parseInterfaceObjectExpression(start, kind, fields));
         break;
-      case BinKind::SkippableArrowExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-        break;
-      case BinKind::SkippableFunctionExpression:
-        MOZ_TRY_VAR(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-        break;
       case BinKind::StaticMemberExpression:
         MOZ_TRY_VAR(result, parseInterfaceStaticMemberExpression(start, kind, fields));
         break;
       case BinKind::TemplateExpression:
         MOZ_TRY_VAR(result, parseInterfaceTemplateExpression(start, kind, fields));
         break;
       case BinKind::ThisExpression:
         MOZ_TRY_VAR(result, parseInterfaceThisExpression(start, kind, fields));
@@ -2645,19 +2597,84 @@ BinASTParser<Tok>::parseInterfaceArrayEx
     BINJS_MOZ_TRY_DECL(elements, parseListOfOptionalSpreadElementOrExpression());
 
     auto result = elements;
     return result;
 }
 
 
 /*
+ interface ArrowExpressionContentsWithExpression : Node {
+    AssertedParameterScope parameterScope;
+    FormalParameters params;
+    AssertedVarScope bodyScope;
+    Expression body;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseArrowExpressionContentsWithExpression()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::ArrowExpressionContentsWithExpression) {
+        return raiseInvalidKind("ArrowExpressionContentsWithExpression", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceArrowExpressionContentsWithExpression(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceArrowExpressionContentsWithExpression(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (ArrowExpressionContentsWithExpression)");
+}
+
+
+/*
+ interface ArrowExpressionContentsWithFunctionBody : Node {
+    AssertedParameterScope parameterScope;
+    FormalParameters params;
+    AssertedVarScope bodyScope;
+    FunctionBody body;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseArrowExpressionContentsWithFunctionBody()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::ArrowExpressionContentsWithFunctionBody) {
+        return raiseInvalidKind("ArrowExpressionContentsWithFunctionBody", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceArrowExpressionContentsWithFunctionBody(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceArrowExpressionContentsWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (ArrowExpressionContentsWithFunctionBody)");
+}
+
+
+/*
  interface AssertedBlockScope : Node {
-    FrozenArray<IdentifierName> lexicallyDeclaredNames;
-    FrozenArray<IdentifierName> capturedNames;
+    FrozenArray<AssertedDeclaredName> declaredNames;
     bool hasDirectEval;
  }
 */
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseAssertedBlockScope()
 {
     BinKind kind;
     BinFields fields(cx_);
@@ -2676,21 +2693,128 @@ BinASTParser<Tok>::parseAssertedBlockSco
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseInterfaceAssertedBlockScope(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssertedBlockScope);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[3] = { BinField::LexicallyDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval };
+    const BinField expected_fields[2] = { BinField::DeclaredNames, BinField::HasDirectEval };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
-    MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));
-    MOZ_TRY(parseAndUpdateCapturedNames(kind));
+    const auto scopeKind = AssertedScopeKind::Block;
+
+    MOZ_TRY(parseListOfAssertedDeclaredName(
+        scopeKind));
+
+    BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
+    if (hasDirectEval) {
+        parseContext_->sc()->setHasDirectEval();
+        parseContext_->sc()->setBindingsAccessedDynamically();
+    }
+    if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
+        // In non-strict mode code, direct calls to eval can
+        // add variables to the call object.
+        parseContext_->functionBox()->setHasExtensibleScope();
+    }
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedBoundName : Node {
+    IdentifierName name;
+    bool isCaptured;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedBoundName(
+        AssertedScopeKind scopeKind)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedBoundName) {
+        return raiseInvalidKind("AssertedBoundName", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedBoundName(start, kind, fields,
+        scopeKind));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedBoundName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedBoundName);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Name, BinField::IsCaptured };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    RootedAtom name(cx_);
+    MOZ_TRY_VAR(name, tokenizer_->readAtom());
+
+    BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool());
+    ParseContext::Scope* scope;
+    DeclarationKind declKind;
+    MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
+    MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedBoundNamesScope : Node {
+    FrozenArray<AssertedBoundName> boundNames;
+    bool hasDirectEval;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedBoundNamesScope()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedBoundNamesScope) {
+        return raiseInvalidKind("AssertedBoundNamesScope", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedBoundNamesScope(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedBoundNamesScope(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedBoundNamesScope);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::BoundNames, BinField::HasDirectEval };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+    const auto scopeKind = AssertedScopeKind::Catch;
+
+    MOZ_TRY(parseListOfAssertedBoundName(
+        scopeKind));
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
         parseContext_->sc()->setHasDirectEval();
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
@@ -2698,60 +2822,336 @@ BinASTParser<Tok>::parseInterfaceAsserte
         parseContext_->functionBox()->setHasExtensibleScope();
     }
     auto result = Ok();
     return result;
 }
 
 
 /*
- interface AssertedParameterScope : Node {
-    FrozenArray<IdentifierName> parameterNames;
-    FrozenArray<IdentifierName> capturedNames;
-    bool hasDirectEval;
+ interface AssertedDeclaredName : Node {
+    IdentifierName name;
+    AssertedDeclaredKind kind;
+    bool isCaptured;
  }
 */
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseAssertedParameterScope()
+BinASTParser<Tok>::parseAssertedDeclaredName(
+        AssertedScopeKind scopeKind)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedDeclaredName) {
+        return raiseInvalidKind("AssertedDeclaredName", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedDeclaredName(start, kind, fields,
+        scopeKind));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedDeclaredName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedDeclaredName);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::Name, BinField::Kind, BinField::IsCaptured };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    RootedAtom name(cx_);
+    MOZ_TRY_VAR(name, tokenizer_->readAtom());
+
+    BINJS_MOZ_TRY_DECL(kind_, parseAssertedDeclaredKind());
+
+    BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool());
+    ParseContext::Scope* scope;
+    DeclarationKind declKind;
+    MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));
+    MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedParameterName : Node {
+    IdentifierName name;
+    bool isCaptured;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedParameterName) {
+        return raiseInvalidKind("AssertedParameterName", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedParameterName(start, kind, fields,
+        scopeKind, positionalParams));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedParameterName);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Name, BinField::IsCaptured };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    RootedAtom name(cx_);
+    MOZ_TRY_VAR(name, tokenizer_->readAtom());
+
+    BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool());
+    ParseContext::Scope* scope;
+    DeclarationKind declKind;
+    MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
+    MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedParameterScope : Node {
+    FrozenArray<AssertedMaybePositionalParameterName> paramNames;
+    bool hasDirectEval;
+    bool isSimpleParameterList;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedParameterScope(
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
 
     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
     if (kind != BinKind::AssertedParameterScope) {
         return raiseInvalidKind("AssertedParameterScope", kind);
     }
     const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedParameterScope(start, kind, fields));
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedParameterScope(start, kind, fields,
+        positionalParams));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedParameterScope);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::ParamNames, BinField::HasDirectEval, BinField::IsSimpleParameterList };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+    const auto scopeKind = AssertedScopeKind::Parameter;
+
+    MOZ_TRY(parseListOfAssertedMaybePositionalParameterName(
+        scopeKind, positionalParams));
+
+    BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
+    if (hasDirectEval) {
+        parseContext_->sc()->setHasDirectEval();
+        parseContext_->sc()->setBindingsAccessedDynamically();
+    }
+    BINJS_MOZ_TRY_DECL(isSimpleParameterList, tokenizer_->readBool());
+    (void) isSimpleParameterList;
+    if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
+        // In non-strict mode code, direct calls to eval can
+        // add variables to the call object.
+        parseContext_->functionBox()->setHasExtensibleScope();
+    }
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedPositionalParameterName : Node {
+    unsigned long index;
+    IdentifierName name;
+    bool isCaptured;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedPositionalParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedPositionalParameterName) {
+        return raiseInvalidKind("AssertedPositionalParameterName", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedPositionalParameterName(start, kind, fields,
+        scopeKind, positionalParams));
     MOZ_TRY(guard.done());
 
     return result;
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    MOZ_ASSERT(kind == BinKind::AssertedParameterScope);
+BinASTParser<Tok>::parseInterfaceAssertedPositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedPositionalParameterName);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[3] = { BinField::ParameterNames, BinField::CapturedNames, BinField::HasDirectEval };
+    const BinField expected_fields[3] = { BinField::Index, BinField::Name, BinField::IsCaptured };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
-    ParseContext::Statement* inStatement = parseContext_->innermostStatement();
-
-    // If we are in a `CatchClause`, the binding is a implicit CatchParameter
-    // and it goes into the innermost scope. Otherwise, we're in a function,
-    // so it goes in the function scope.
-    if (inStatement && inStatement->kind() == StatementKind::Catch)
-        MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::CatchParameter));
-    else
-        MOZ_TRY(parseAndUpdateScopeNames(parseContext_->functionScope(), DeclarationKind::PositionalFormalParameter));
-    MOZ_TRY(parseAndUpdateCapturedNames(kind));
+
+    BINJS_MOZ_TRY_DECL(index, tokenizer_->readUnsignedLong());
+
+    RootedAtom name(cx_);
+    MOZ_TRY_VAR(name, tokenizer_->readAtom());
+    // FIXME: The following checks should be performed inside
+    // checkPositionalParameterIndices to match the spec's order
+    // (bug 1490976).
+    if (index >= positionalParams.get().length())
+        return raiseError("AssertedPositionalParameterName.length out of range");
+    if (positionalParams.get()[index])
+        return raiseError("AssertedPositionalParameterName has duplicate entry for the same index");
+    positionalParams.get()[index] = name;
+    BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool());
+    ParseContext::Scope* scope;
+    DeclarationKind declKind;
+    MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
+    MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedRestParameterName : Node {
+    IdentifierName name;
+    bool isCaptured;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedRestParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedRestParameterName) {
+        return raiseInvalidKind("AssertedRestParameterName", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedRestParameterName(start, kind, fields,
+        scopeKind, positionalParams));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedRestParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedRestParameterName);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::Name, BinField::IsCaptured };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    RootedAtom name(cx_);
+    MOZ_TRY_VAR(name, tokenizer_->readAtom());
+
+    BINJS_MOZ_TRY_DECL(isCaptured, tokenizer_->readBool());
+    ParseContext::Scope* scope;
+    DeclarationKind declKind;
+    MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
+    MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface AssertedScriptGlobalScope : Node {
+    FrozenArray<AssertedDeclaredName> declaredNames;
+    bool hasDirectEval;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseAssertedScriptGlobalScope()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::AssertedScriptGlobalScope) {
+        return raiseInvalidKind("AssertedScriptGlobalScope", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceAssertedScriptGlobalScope(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceAssertedScriptGlobalScope(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    MOZ_ASSERT(kind == BinKind::AssertedScriptGlobalScope);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[2] = { BinField::DeclaredNames, BinField::HasDirectEval };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+    const auto scopeKind = AssertedScopeKind::Global;
+
+    MOZ_TRY(parseListOfAssertedDeclaredName(
+        scopeKind));
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
         parseContext_->sc()->setHasDirectEval();
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
@@ -2760,19 +3160,17 @@ BinASTParser<Tok>::parseInterfaceAsserte
     }
     auto result = Ok();
     return result;
 }
 
 
 /*
  interface AssertedVarScope : Node {
-    FrozenArray<IdentifierName> lexicallyDeclaredNames;
-    FrozenArray<IdentifierName> varDeclaredNames;
-    FrozenArray<IdentifierName> capturedNames;
+    FrozenArray<AssertedDeclaredName> declaredNames;
     bool hasDirectEval;
  }
 */
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseAssertedVarScope()
 {
     BinKind kind;
     BinFields fields(cx_);
@@ -2791,22 +3189,23 @@ BinASTParser<Tok>::parseAssertedVarScope
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::parseInterfaceAssertedVarScope(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::AssertedVarScope);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[4] = { BinField::LexicallyDeclaredNames, BinField::VarDeclaredNames, BinField::CapturedNames, BinField::HasDirectEval };
+    const BinField expected_fields[2] = { BinField::DeclaredNames, BinField::HasDirectEval };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
-    MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));
-    MOZ_TRY(parseAndUpdateScopeNames(parseContext_->varScope(), DeclarationKind::Var));
-    MOZ_TRY(parseAndUpdateCapturedNames(kind));
+    const auto scopeKind = AssertedScopeKind::Var;
+
+    MOZ_TRY(parseListOfAssertedDeclaredName(
+        scopeKind));
 
     BINJS_MOZ_TRY_DECL(hasDirectEval, tokenizer_->readBool());
     if (hasDirectEval) {
         parseContext_->sc()->setHasDirectEval();
         parseContext_->sc()->setBindingsAccessedDynamically();
     }
     if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
         // In non-strict mode code, direct calls to eval can
@@ -3303,17 +3702,17 @@ template<typename Tok> JS::Result<ParseN
 BinASTParser<Tok>::parseInterfaceBindingWithInitializer(const size_t start, const BinKind kind, const BinFields& fields)
 {
     return raiseError("FIXME: Not implemented yet (BindingWithInitializer)");
 }
 
 
 /*
  interface Block : Node {
-    AssertedBlockScope? scope;
+    AssertedBlockScope scope;
     FrozenArray<Statement> statements;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseBlock()
 {
     BinKind kind;
     BinFields fields(cx_);
@@ -3339,17 +3738,17 @@ BinASTParser<Tok>::parseInterfaceBlock(c
 #if defined(DEBUG)
     const BinField expected_fields[2] = { BinField::Scope, BinField::Statements };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
     ParseContext::Statement stmt(parseContext_, StatementKind::Block);
     ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(currentScope.init(parseContext_));
 
-    MOZ_TRY(parseOptionalAssertedBlockScope());
+    MOZ_TRY(parseAssertedBlockScope());
 
     BINJS_MOZ_TRY_DECL(statements, parseListOfStatement());
 
     MOZ_TRY(checkClosedVars(currentScope));
     BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
     BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, statements));
     return result;
 }
@@ -3466,17 +3865,17 @@ BinASTParser<Tok>::parseInterfaceCallExp
     BINJS_TRY_DECL(result, factory_.newCall(callee, arguments));
     result->setOp(op);
     return result;
 }
 
 
 /*
  interface CatchClause : Node {
-    AssertedParameterScope? bindingScope;
+    AssertedBoundNamesScope bindingScope;
     Binding binding;
     Block body;
  }
 */
 template<typename Tok> JS::Result<LexicalScopeNode*>
 BinASTParser<Tok>::parseCatchClause()
 {
     BinKind kind;
@@ -3503,17 +3902,17 @@ BinASTParser<Tok>::parseInterfaceCatchCl
 #if defined(DEBUG)
     const BinField expected_fields[3] = { BinField::BindingScope, BinField::Binding, BinField::Body };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
     ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
     ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(currentScope.init(parseContext_));
 
-    MOZ_TRY(parseOptionalAssertedParameterScope());
+    MOZ_TRY(parseAssertedBoundNamesScope());
 
     BINJS_MOZ_TRY_DECL(binding, parseBinding());
 
     BINJS_MOZ_TRY_DECL(body, parseBlock());
 
     BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
     BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
     BINJS_TRY(factory_.setupCatchScope(result, binding, body));
@@ -4083,58 +4482,88 @@ BinASTParser<Tok>::parseInterfaceDoWhile
     BINJS_MOZ_TRY_DECL(body, parseStatement());
 
     BINJS_TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
     return result;
 }
 
 
 /*
- interface EagerArrowExpression : Node {
+ interface EagerArrowExpressionWithExpression : Node {
     bool isAsync;
-    AssertedParameterScope? parameterScope;
-    AssertedVarScope? bodyScope;
-    FormalParameters params;
-    (FunctionBody or Expression) body;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseEagerArrowExpression()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::EagerArrowExpression) {
-        return raiseInvalidKind("EagerArrowExpression", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceEagerArrowExpression(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceEagerArrowExpression(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (EagerArrowExpression)");
+    unsigned long length;
+    ArrowExpressionContentsWithExpression contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseEagerArrowExpressionWithExpression()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::EagerArrowExpressionWithExpression) {
+        return raiseInvalidKind("EagerArrowExpressionWithExpression", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceEagerArrowExpressionWithExpression(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (EagerArrowExpressionWithExpression)");
+}
+
+
+/*
+ interface EagerArrowExpressionWithFunctionBody : Node {
+    bool isAsync;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    ArrowExpressionContentsWithFunctionBody contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseEagerArrowExpressionWithFunctionBody()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::EagerArrowExpressionWithFunctionBody) {
+        return raiseInvalidKind("EagerArrowExpressionWithFunctionBody", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceEagerArrowExpressionWithFunctionBody(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceEagerArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (EagerArrowExpressionWithFunctionBody)");
 }
 
 
 /*
  interface EagerFunctionDeclaration : Node {
     bool isAsync;
     bool isGenerator;
     BindingIdentifier name;
-    AssertedParameterScope? parameterScope;
-    AssertedVarScope? bodyScope;
-    FormalParameters params;
-    FunctionBody body;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionOrMethodContents contents;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseEagerFunctionDeclaration()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -4152,66 +4581,68 @@ BinASTParser<Tok>::parseEagerFunctionDec
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerFunctionDeclaration);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::ParameterScope, BinField::BodyScope, BinField::Params, BinField::Body };
+    const BinField expected_fields[6] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::Length, BinField::Directives, BinField::Contents };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
     const auto syntax = FunctionSyntaxKind::Statement;
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(name, parseBindingIdentifier());
 
+    BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong());
+
+    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
+
     BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
         isGenerator ? GeneratorKind::Generator
                     : GeneratorKind::NotGenerator,
         isAsync ? FunctionAsyncKind::AsyncFunction
                 : FunctionAsyncKind::SyncFunction,
-        syntax, name));
+        syntax,
+        (syntax != FunctionSyntaxKind::Setter &&
+         syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
     // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
-    MOZ_TRY(parseOptionalAssertedParameterScope());
-
-    MOZ_TRY(parseOptionalAssertedVarScope());
-
-    BINJS_MOZ_TRY_DECL(params, parseFormalParameters());
-
-    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
-
+    ListNode* params;
+    ListNode* tmpBody;
+    MOZ_TRY(parseFunctionOrMethodContents(
+        length, &params, &tmpBody));
+    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
     BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
     BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
     return result;
 }
 
 
 /*
  interface EagerFunctionExpression : Node {
     bool isAsync;
     bool isGenerator;
     BindingIdentifier? name;
-    AssertedParameterScope? parameterScope;
-    AssertedVarScope? bodyScope;
-    FormalParameters params;
-    FunctionBody body;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionExpressionContents contents;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseEagerFunctionExpression()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -4229,62 +4660,65 @@ BinASTParser<Tok>::parseEagerFunctionExp
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerFunctionExpression);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::ParameterScope, BinField::BodyScope, BinField::Params, BinField::Body };
+    const BinField expected_fields[6] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::Length, BinField::Directives, BinField::Contents };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
     const auto syntax = FunctionSyntaxKind::Expression;
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(name, parseOptionalBindingIdentifier());
 
+    BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong());
+
+    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
+
     BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
         isGenerator ? GeneratorKind::Generator
                     : GeneratorKind::NotGenerator,
         isAsync ? FunctionAsyncKind::AsyncFunction
                 : FunctionAsyncKind::SyncFunction,
-        syntax, name));
+        syntax,
+        (syntax != FunctionSyntaxKind::Setter &&
+         syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
     // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
-    MOZ_TRY(parseOptionalAssertedParameterScope());
-
-    MOZ_TRY(parseOptionalAssertedVarScope());
-
-    BINJS_MOZ_TRY_DECL(params, parseFormalParameters());
-
-    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
-
+    ListNode* params;
+    ListNode* tmpBody;
+    MOZ_TRY(parseFunctionExpressionContents(
+        length, &params, &tmpBody));
+    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
     BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
     BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
     return result;
 }
 
 
 /*
  interface EagerGetter : Node {
     PropertyName name;
-    AssertedVarScope? bodyScope;
-    FunctionBody body;
+    FrozenArray<Directive> directives;
+    GetterContents contents;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseEagerGetter()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -4302,54 +4736,65 @@ BinASTParser<Tok>::parseEagerGetter()
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerGetter);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[3] = { BinField::Name, BinField::BodyScope, BinField::Body };
+    const BinField expected_fields[3] = { BinField::Name, BinField::Directives, BinField::Contents };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    const auto syntax = FunctionSyntaxKind::Setter;
+    const bool isGenerator = false;
+    const bool isAsync = false;
+    const auto accessorType = AccessorType::Getter;
+    const uint32_t length = 0;
 
     BINJS_MOZ_TRY_DECL(name, parsePropertyName());
+
+    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
+
     BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
-        GeneratorKind::NotGenerator,
-        FunctionAsyncKind::SyncFunction,
-        FunctionSyntaxKind::Getter, /* name = */ nullptr));
+        isGenerator ? GeneratorKind::Generator
+                    : GeneratorKind::NotGenerator,
+        isAsync ? FunctionAsyncKind::AsyncFunction
+                : FunctionAsyncKind::SyncFunction,
+        syntax,
+        (syntax != FunctionSyntaxKind::Setter &&
+         syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
     // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
-    MOZ_TRY(parseOptionalAssertedVarScope());
-
-    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
-
-    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+    ListNode* params;
+    ListNode* tmpBody;
+    MOZ_TRY(parseGetterContents(
+        length, &params, &tmpBody));
+    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Getter));
+    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 
 /*
  interface EagerMethod : Node {
     bool isAsync;
     bool isGenerator;
     PropertyName name;
-    AssertedParameterScope? parameterScope;
-    AssertedVarScope? bodyScope;
-    FormalParameters params;
-    FunctionBody body;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionOrMethodContents contents;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseEagerMethod()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -4367,63 +4812,66 @@ BinASTParser<Tok>::parseEagerMethod()
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerMethod);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[7] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::ParameterScope, BinField::BodyScope, BinField::Params, BinField::Body };
+    const BinField expected_fields[6] = { BinField::IsAsync, BinField::IsGenerator, BinField::Name, BinField::Length, BinField::Directives, BinField::Contents };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
     const auto syntax = FunctionSyntaxKind::Method;
+    const auto accessorType = AccessorType::None;
 
     BINJS_MOZ_TRY_DECL(isAsync, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(isGenerator, tokenizer_->readBool());
 
     BINJS_MOZ_TRY_DECL(name, parsePropertyName());
 
+    BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong());
+
+    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
+
     BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
         isGenerator ? GeneratorKind::Generator
                     : GeneratorKind::NotGenerator,
         isAsync ? FunctionAsyncKind::AsyncFunction
                 : FunctionAsyncKind::SyncFunction,
-        syntax, name));
+        syntax,
+        (syntax != FunctionSyntaxKind::Setter &&
+         syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
     // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
-    MOZ_TRY(parseOptionalAssertedParameterScope());
-
-    MOZ_TRY(parseOptionalAssertedVarScope());
-
-    BINJS_MOZ_TRY_DECL(params, parseFormalParameters());
-
-    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
-
+    ListNode* params;
+    ListNode* tmpBody;
+    MOZ_TRY(parseFunctionOrMethodContents(
+        length, &params, &tmpBody));
+    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));
+    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 
 /*
  interface EagerSetter : Node {
     PropertyName name;
-    AssertedParameterScope? parameterScope;
-    AssertedVarScope? bodyScope;
-    Parameter param;
-    FunctionBody body;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    SetterContents contents;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseEagerSetter()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
@@ -4441,46 +4889,54 @@ BinASTParser<Tok>::parseEagerSetter()
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields)
 {
     MOZ_ASSERT(kind == BinKind::EagerSetter);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[5] = { BinField::Name, BinField::ParameterScope, BinField::BodyScope, BinField::Param, BinField::Body };
+    const BinField expected_fields[4] = { BinField::Name, BinField::Length, BinField::Directives, BinField::Contents };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
+    const auto syntax = FunctionSyntaxKind::Setter;
+    const bool isGenerator = false;
+    const bool isAsync = false;
+    const auto accessorType = AccessorType::Setter;
 
     BINJS_MOZ_TRY_DECL(name, parsePropertyName());
+
+    BINJS_MOZ_TRY_DECL(length, tokenizer_->readUnsignedLong());
+
+    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
+
     BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
-        GeneratorKind::NotGenerator,
-        FunctionAsyncKind::SyncFunction,
-        FunctionSyntaxKind::Setter, /* name = */ nullptr));
+        isGenerator ? GeneratorKind::Generator
+                    : GeneratorKind::NotGenerator,
+        isAsync ? FunctionAsyncKind::AsyncFunction
+                : FunctionAsyncKind::SyncFunction,
+        syntax,
+        (syntax != FunctionSyntaxKind::Setter &&
+         syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
     // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
     BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
     BINJS_TRY(funpc.init());
     parseContext_->functionScope().useAsVarScope(parseContext_);
     MOZ_ASSERT(parseContext_->isFunctionBox());
 
     ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
     BINJS_TRY(lexicalScope.init(parseContext_));
-    MOZ_TRY(parseOptionalAssertedParameterScope());
-
-    MOZ_TRY(parseOptionalAssertedVarScope());
-
-    BINJS_MOZ_TRY_DECL(param, parseParameter());
-
-    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
-
-    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
-    factory_.addList(params, param);
+    ListNode* params;
+    ListNode* tmpBody;
+    MOZ_TRY(parseSetterContents(
+        length, &params, &tmpBody));
+    BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
+    BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
     return result;
 }
 
 
 /*
  interface EmptyStatement : Node {
  }
 */
@@ -5022,55 +5478,211 @@ BinASTParser<Tok>::parseInterfaceFormalP
         BINJS_TRY_DECL(spread, factory_.newSpread(start, rest));
         factory_.addList(result, spread);
     }
     return result;
 }
 
 
 /*
- interface FunctionBody : Node {
-    FrozenArray<Directive> directives;
-    FrozenArray<Statement> statements;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseFunctionBody()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::FunctionBody) {
-        return raiseInvalidKind("FunctionBody", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceFunctionBody(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceFunctionBody(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    MOZ_ASSERT(kind == BinKind::FunctionBody);
+ interface FunctionExpressionContents : Node {
+    bool isFunctionNameCaptured;
+    bool isThisCaptured;
+    AssertedParameterScope parameterScope;
+    FormalParameters params;
+    AssertedVarScope bodyScope;
+    FunctionBody body;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseFunctionExpressionContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::FunctionExpressionContents) {
+        return raiseInvalidKind("FunctionExpressionContents", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceFunctionExpressionContents(start, kind, fields,
+        funLength, paramsOut, bodyOut));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceFunctionExpressionContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    MOZ_ASSERT(kind == BinKind::FunctionExpressionContents);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
-    const BinField expected_fields[2] = { BinField::Directives, BinField::Statements };
+    const BinField expected_fields[6] = { BinField::IsFunctionNameCaptured, BinField::IsThisCaptured, BinField::ParameterScope, BinField::Params, BinField::BodyScope, BinField::Body };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
-    BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
-
-    BINJS_MOZ_TRY_DECL(statements, parseListOfStatement());
-
-    BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
+    BINJS_MOZ_TRY_DECL(isFunctionNameCaptured, tokenizer_->readBool());
+    // Per spec, isFunctionNameCaptured can be true for anonymous
+    // function.  Check isFunctionNameCaptured only for named
+    // function.
+    if (parseContext_->functionBox()->function()->isNamedLambda() &&
+        isFunctionNameCaptured)
+    {
+        captureFunctionName();
+    }
+    BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
+    // TODO: Use this in BinASTParser::buildFunction.
+    (void) isThisCaptured;
+    Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
+    MOZ_TRY(parseAssertedParameterScope(
+        &positionalParams));
+
+    BINJS_MOZ_TRY_DECL(params, parseFormalParameters());
+    MOZ_TRY(checkFunctionLength(funLength));
+    MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
+    MOZ_TRY(parseAssertedVarScope());
+
+    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
+
+    *paramsOut = params;
+    *bodyOut = body;
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface FunctionOrMethodContents : Node {
+    bool isThisCaptured;
+    AssertedParameterScope parameterScope;
+    FormalParameters params;
+    AssertedVarScope bodyScope;
+    FunctionBody body;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseFunctionOrMethodContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::FunctionOrMethodContents) {
+        return raiseInvalidKind("FunctionOrMethodContents", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceFunctionOrMethodContents(start, kind, fields,
+        funLength, paramsOut, bodyOut));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceFunctionOrMethodContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    MOZ_ASSERT(kind == BinKind::FunctionOrMethodContents);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[5] = { BinField::IsThisCaptured, BinField::ParameterScope, BinField::Params, BinField::BodyScope, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
+    // TODO: Use this in BinASTParser::buildFunction.
+    (void) isThisCaptured;
+    Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
+    MOZ_TRY(parseAssertedParameterScope(
+        &positionalParams));
+
+    BINJS_MOZ_TRY_DECL(params, parseFormalParameters());
+    MOZ_TRY(checkFunctionLength(funLength));
+    MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
+    MOZ_TRY(parseAssertedVarScope());
+
+    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
+
+    *paramsOut = params;
+    *bodyOut = body;
+    auto result = Ok();
+    return result;
+}
+
+
+/*
+ interface GetterContents : Node {
+    bool isThisCaptured;
+    AssertedVarScope bodyScope;
+    FunctionBody body;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseGetterContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::GetterContents) {
+        return raiseInvalidKind("GetterContents", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceGetterContents(start, kind, fields,
+        funLength, paramsOut, bodyOut));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceGetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    MOZ_ASSERT(kind == BinKind::GetterContents);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[3] = { BinField::IsThisCaptured, BinField::BodyScope, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
+    // TODO: Use this in BinASTParser::buildFunction.
+    (void) isThisCaptured;
+    MOZ_TRY(parseAssertedVarScope());
+
+    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
+
+    *paramsOut = params;
+    *bodyOut = body;
+    auto result = Ok();
     return result;
 }
 
 
 /*
  interface IdentifierExpression : Node {
     Identifier name;
  }
@@ -5300,16 +5912,251 @@ BinASTParser<Tok>::parseInterfaceLabelle
     BINJS_MOZ_TRY_DECL(body, parseStatement());
 
     BINJS_TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
     return result;
 }
 
 
 /*
+ interface LazyArrowExpressionWithExpression : Node {
+    bool isAsync;
+    unsigned long length;
+    ArrowExpressionContentsWithExpression contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyArrowExpressionWithExpression()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyArrowExpressionWithExpression) {
+        return raiseInvalidKind("LazyArrowExpressionWithExpression", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyArrowExpressionWithExpression(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyArrowExpressionWithExpression)");
+}
+
+
+/*
+ interface LazyArrowExpressionWithFunctionBody : Node {
+    bool isAsync;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    ArrowExpressionContentsWithFunctionBody contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyArrowExpressionWithFunctionBody()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyArrowExpressionWithFunctionBody) {
+        return raiseInvalidKind("LazyArrowExpressionWithFunctionBody", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyArrowExpressionWithFunctionBody(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyArrowExpressionWithFunctionBody)");
+}
+
+
+/*
+ interface LazyFunctionDeclaration : Node {
+    bool isAsync;
+    bool isGenerator;
+    BindingIdentifier name;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionOrMethodContents contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyFunctionDeclaration()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyFunctionDeclaration) {
+        return raiseInvalidKind("LazyFunctionDeclaration", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyFunctionDeclaration(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyFunctionDeclaration)");
+}
+
+
+/*
+ interface LazyFunctionExpression : Node {
+    bool isAsync;
+    bool isGenerator;
+    BindingIdentifier? name;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionExpressionContents contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyFunctionExpression()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyFunctionExpression) {
+        return raiseInvalidKind("LazyFunctionExpression", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyFunctionExpression(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyFunctionExpression)");
+}
+
+
+/*
+ interface LazyGetter : Node {
+    PropertyName name;
+    FrozenArray<Directive> directives;
+    GetterContents contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyGetter()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyGetter) {
+        return raiseInvalidKind("LazyGetter", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyGetter(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyGetter(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyGetter)");
+}
+
+
+/*
+ interface LazyMethod : Node {
+    bool isAsync;
+    bool isGenerator;
+    PropertyName name;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    FunctionOrMethodContents contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazyMethod()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazyMethod) {
+        return raiseInvalidKind("LazyMethod", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazyMethod(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazyMethod(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazyMethod)");
+}
+
+
+/*
+ interface LazySetter : Node {
+    PropertyName name;
+    unsigned long length;
+    FrozenArray<Directive> directives;
+    SetterContents contents;
+ }
+*/
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseLazySetter()
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::LazySetter) {
+        return raiseInvalidKind("LazySetter", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceLazySetter(start, kind, fields));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<ParseNode*>
+BinASTParser<Tok>::parseInterfaceLazySetter(const size_t start, const BinKind kind, const BinFields& fields)
+{
+    return raiseError("FIXME: Not implemented yet (LazySetter)");
+}
+
+
+/*
  interface LiteralBooleanExpression : Node {
     bool value;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseLiteralBooleanExpression()
 {
     BinKind kind;
@@ -5604,17 +6451,17 @@ BinASTParser<Tok>::parseInterfaceLiteral
 
     BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
     return result;
 }
 
 
 /*
  interface Module : Node {
-    AssertedVarScope? scope;
+    AssertedVarScope scope;
     FrozenArray<Directive> directives;
     FrozenArray<(ImportDeclaration or ExportDeclaration or Statement)> items;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseModule()
 {
     BinKind kind;
@@ -5857,17 +6704,17 @@ BinASTParser<Tok>::parseInterfaceReturnS
 
     BINJS_TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));
     return result;
 }
 
 
 /*
  interface Script : Node {
-    AssertedVarScope? scope;
+    AssertedScriptGlobalScope scope;
     FrozenArray<Directive> directives;
     FrozenArray<Statement> statements;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseScript()
 {
     BinKind kind;
@@ -5891,28 +6738,95 @@ BinASTParser<Tok>::parseInterfaceScript(
     MOZ_ASSERT(kind == BinKind::Script);
     BINJS_TRY(CheckRecursionLimit(cx_));
 
 #if defined(DEBUG)
     const BinField expected_fields[3] = { BinField::Scope, BinField::Directives, BinField::Statements };
     MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif // defined(DEBUG)
 
-    MOZ_TRY(parseOptionalAssertedVarScope());
+    MOZ_TRY(parseAssertedScriptGlobalScope());
 
     BINJS_MOZ_TRY_DECL(directives, parseListOfDirective());
 
     BINJS_MOZ_TRY_DECL(statements, parseListOfStatement());
 
     MOZ_TRY(checkClosedVars(parseContext_->varScope())); BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
     return result;
 }
 
 
 /*
+ interface SetterContents : Node {
+    bool isThisCaptured;
+    AssertedParameterScope parameterScope;
+    Parameter param;
+    AssertedVarScope bodyScope;
+    FunctionBody body;
+ }
+*/
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseSetterContents(
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    BinKind kind;
+    BinFields fields(cx_);
+    AutoTaggedTuple guard(*tokenizer_);
+
+    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
+    if (kind != BinKind::SetterContents) {
+        return raiseInvalidKind("SetterContents", kind);
+    }
+    const auto start = tokenizer_->offset();
+    BINJS_MOZ_TRY_DECL(result, parseInterfaceSetterContents(start, kind, fields,
+        funLength, paramsOut, bodyOut));
+    MOZ_TRY(guard.done());
+
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseInterfaceSetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut)
+{
+    MOZ_ASSERT(kind == BinKind::SetterContents);
+    BINJS_TRY(CheckRecursionLimit(cx_));
+
+#if defined(DEBUG)
+    const BinField expected_fields[5] = { BinField::IsThisCaptured, BinField::ParameterScope, BinField::Param, BinField::BodyScope, BinField::Body };
+    MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
+#endif // defined(DEBUG)
+
+    BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool());
+    // TODO: Use this in BinASTParser::buildFunction.
+    (void) isThisCaptured;
+    Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
+    MOZ_TRY(parseAssertedParameterScope(
+        &positionalParams));
+
+    BINJS_MOZ_TRY_DECL(param, parseParameter());
+    BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
+    factory_.addList(params, param);
+    MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
+    MOZ_TRY(parseAssertedVarScope());
+
+    BINJS_MOZ_TRY_DECL(body, parseFunctionBody());
+
+    *paramsOut = params;
+    *bodyOut = body;
+    auto result = Ok();
+    return result;
+}
+
+
+/*
  interface ShorthandProperty : Node {
     IdentifierExpression name;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseShorthandProperty()
 {
     BinKind kind;
@@ -5947,196 +6861,16 @@ BinASTParser<Tok>::parseInterfaceShortha
         BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
 
     BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
     return result;
 }
 
 
 /*
- interface SkippableArrowExpression : Node {
-    EagerArrowExpression skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableArrowExpression()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableArrowExpression) {
-        return raiseInvalidKind("SkippableArrowExpression", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableArrowExpression(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableArrowExpression(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableArrowExpression)");
-}
-
-
-/*
- interface SkippableFunctionDeclaration : Node {
-    EagerFunctionDeclaration skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableFunctionDeclaration()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableFunctionDeclaration) {
-        return raiseInvalidKind("SkippableFunctionDeclaration", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableFunctionDeclaration(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableFunctionDeclaration)");
-}
-
-
-/*
- interface SkippableFunctionExpression : Node {
-    EagerFunctionExpression skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableFunctionExpression()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableFunctionExpression) {
-        return raiseInvalidKind("SkippableFunctionExpression", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableFunctionExpression(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableFunctionExpression)");
-}
-
-
-/*
- interface SkippableGetter : Node {
-    EagerGetter skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableGetter()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableGetter) {
-        return raiseInvalidKind("SkippableGetter", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableGetter(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableGetter(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableGetter)");
-}
-
-
-/*
- interface SkippableMethod : Node {
-    EagerMethod skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableMethod()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableMethod) {
-        return raiseInvalidKind("SkippableMethod", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableMethod(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableMethod(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableMethod)");
-}
-
-
-/*
- interface SkippableSetter : Node {
-    EagerSetter skipped;
- }
-*/
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseSkippableSetter()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    if (kind != BinKind::SkippableSetter) {
-        return raiseInvalidKind("SkippableSetter", kind);
-    }
-    const auto start = tokenizer_->offset();
-    BINJS_MOZ_TRY_DECL(result, parseInterfaceSkippableSetter(start, kind, fields));
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseInterfaceSkippableSetter(const size_t start, const BinKind kind, const BinFields& fields)
-{
-    return raiseError("FIXME: Not implemented yet (SkippableSetter)");
-}
-
-
-/*
  interface SpreadElement : Node {
     Expression expression;
  }
 */
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseSpreadElement()
 {
     BinKind kind;
@@ -7151,16 +7885,40 @@ BinASTParser<Tok>::parseInterfaceYieldSt
 {
     return raiseError("FIXME: Not implemented yet (YieldStarExpression)");
 }
 
 
 
 // ----- String enums (autogenerated, by lexicographical order)
 /*
+enum AssertedDeclaredKind {
+    "var",
+    "non-const lexical",
+    "const lexical"
+};
+*/
+template<typename Tok> JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind>
+BinASTParser<Tok>::parseAssertedDeclaredKind()
+{
+    BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant());
+
+    switch (variant) {
+    case BinVariant::AssertedDeclaredKindOrVariableDeclarationKindVar:
+        return AssertedDeclaredKind::Var;
+    case BinVariant::AssertedDeclaredKindNonConstLexical:
+        return AssertedDeclaredKind::NonConstLexical;
+    case BinVariant::AssertedDeclaredKindConstLexical:
+        return AssertedDeclaredKind::ConstLexical;
+      default:
+        return raiseInvalidVariant("AssertedDeclaredKind", variant);
+    }
+}
+
+/*
 enum BinaryOperator {
     ",",
     "||",
     "&&",
     "|",
     "^",
     "&",
     "==",
@@ -7361,17 +8119,17 @@ enum VariableDeclarationKind {
 };
 */
 template<typename Tok> JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind>
 BinASTParser<Tok>::parseVariableDeclarationKind()
 {
     BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant());
 
     switch (variant) {
-    case BinVariant::VariableDeclarationKindVar:
+    case BinVariant::AssertedDeclaredKindOrVariableDeclarationKindVar:
         return VariableDeclarationKind::Var;
     case BinVariant::VariableDeclarationKindLet:
         return VariableDeclarationKind::Let;
     case BinVariant::VariableDeclarationKindConst:
         return VariableDeclarationKind::Const;
       default:
         return raiseInvalidVariant("VariableDeclarationKind", variant);
     }
@@ -7395,16 +8153,105 @@ BinASTParser<Tok>::parseArguments()
         BINJS_MOZ_TRY_DECL(item, parseSpreadElementOrExpression());
         factory_.addList(/* list = */ result, /* kid = */ item);
     }
 
     MOZ_TRY(guard.done());
     return result;
 }
 
+template<typename Tok> JS::Result<ListNode*>
+BinASTParser<Tok>::parseFunctionBody()
+{
+    uint32_t length;
+    AutoList guard(*tokenizer_);
+
+    const auto start = tokenizer_->offset();
+    MOZ_TRY(tokenizer_->enterList(length, guard));
+    BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
+
+    for (uint32_t i = 0; i < length; ++i) {
+        BINJS_MOZ_TRY_DECL(item, parseStatement());
+        factory_.addStatementToList(result, item);
+    }
+
+    MOZ_TRY(guard.done());
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseListOfAssertedBoundName(
+        AssertedScopeKind scopeKind)
+{
+    uint32_t length;
+    AutoList guard(*tokenizer_);
+
+    const auto start = tokenizer_->offset();
+    MOZ_TRY(tokenizer_->enterList(length, guard));
+    (void) start;
+    auto result = Ok();
+
+    for (uint32_t i = 0; i < length; ++i) {
+        MOZ_TRY(parseAssertedBoundName(
+            scopeKind));
+        // Nothing to do here.
+    }
+
+    MOZ_TRY(guard.done());
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseListOfAssertedDeclaredName(
+        AssertedScopeKind scopeKind)
+{
+    uint32_t length;
+    AutoList guard(*tokenizer_);
+
+    const auto start = tokenizer_->offset();
+    MOZ_TRY(tokenizer_->enterList(length, guard));
+    (void) start;
+    auto result = Ok();
+
+    for (uint32_t i = 0; i < length; ++i) {
+        MOZ_TRY(parseAssertedDeclaredName(
+            scopeKind));
+        // Nothing to do here.
+    }
+
+    MOZ_TRY(guard.done());
+    return result;
+}
+
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::parseListOfAssertedMaybePositionalParameterName(
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams)
+{
+    uint32_t length;
+    AutoList guard(*tokenizer_);
+
+    const auto start = tokenizer_->offset();
+    MOZ_TRY(tokenizer_->enterList(length, guard));
+    (void) start;
+    auto result = Ok();
+    BINJS_TRY(positionalParams.get().resize(length));
+    for (uint32_t i = 0; i < length; i++)
+        positionalParams.get()[i] = nullptr;
+
+    for (uint32_t i = 0; i < length; ++i) {
+        MOZ_TRY(parseAssertedMaybePositionalParameterName(
+            scopeKind, positionalParams));
+        // Nothing to do here.
+    }
+
+    MOZ_TRY(guard.done());
+    return result;
+}
+
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseListOfAssignmentTargetOrAssignmentTargetWithInitializer()
 {
     return raiseError("FIXME: Not implemented yet (ListOfAssignmentTargetOrAssignmentTargetWithInitializer)");
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseListOfAssignmentTargetProperty()
@@ -7457,22 +8304,16 @@ BinASTParser<Tok>::parseListOfExportLoca
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseListOfExpressionOrTemplateElement()
 {
     return raiseError("FIXME: Not implemented yet (ListOfExpressionOrTemplateElement)");
 }
 
 template<typename Tok> JS::Result<ParseNode*>
-BinASTParser<Tok>::parseListOfIdentifierName()
-{
-    return raiseError("FIXME: Not implemented yet (ListOfIdentifierName)");
-}
-
-template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseListOfImportDeclarationOrExportDeclarationOrStatement()
 {
     return raiseError("FIXME: Not implemented yet (ListOfImportDeclarationOrExportDeclarationOrStatement)");
 }
 
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseListOfImportSpecifier()
 {
@@ -7600,82 +8441,16 @@ BinASTParser<Tok>::parseListOfVariableDe
     }
 
     MOZ_TRY(guard.done());
     return result;
 }
 
 
     // ----- Default values (by lexicographical order)
-template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseOptionalAssertedBlockScope()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    Ok result;
-    if (kind == BinKind::_Null) {
-        result = Ok();
-    } else if (kind == BinKind::AssertedBlockScope) {
-        const auto start = tokenizer_->offset();
-        MOZ_TRY_VAR(result, parseInterfaceAssertedBlockScope(start, kind, fields));
-    } else {
-        return raiseInvalidKind("AssertedBlockScope", kind);
-    }
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseOptionalAssertedParameterScope()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    Ok result;
-    if (kind == BinKind::_Null) {
-        result = Ok();
-    } else if (kind == BinKind::AssertedParameterScope) {
-        const auto start = tokenizer_->offset();
-        MOZ_TRY_VAR(result, parseInterfaceAssertedParameterScope(start, kind, fields));
-    } else {
-        return raiseInvalidKind("AssertedParameterScope", kind);
-    }
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
-template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseOptionalAssertedVarScope()
-{
-    BinKind kind;
-    BinFields fields(cx_);
-    AutoTaggedTuple guard(*tokenizer_);
-
-    MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
-    Ok result;
-    if (kind == BinKind::_Null) {
-        result = Ok();
-    } else if (kind == BinKind::AssertedVarScope) {
-        const auto start = tokenizer_->offset();
-        MOZ_TRY_VAR(result, parseInterfaceAssertedVarScope(start, kind, fields));
-    } else {
-        return raiseInvalidKind("AssertedVarScope", kind);
-    }
-    MOZ_TRY(guard.done());
-
-    return result;
-}
-
 template<typename Tok> JS::Result<ParseNode*>
 BinASTParser<Tok>::parseOptionalAssignmentTarget()
 {
     BinKind kind;
     BinFields fields(cx_);
     AutoTaggedTuple guard(*tokenizer_);
 
     MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
--- a/js/src/frontend/BinSource-auto.h
+++ b/js/src/frontend/BinSource-auto.h
@@ -9,16 +9,22 @@
 // To generate this file, see the documentation in
 // js/src/frontend/binsource/README.md.
 
 // This file is meant to be included from the declaration
 // of class `BinASTParser`. The include may be public or private.
 
 
 // ----- Declaring string enums (by lexicographical order)
+enum class AssertedDeclaredKind {
+    Var                       /* "var" */,
+    NonConstLexical           /* "non-const lexical" */,
+    ConstLexical              /* "const lexical" */
+};
+
 enum class BinaryOperator {
     Comma                     /* "," */,
     LogicalOr                 /* "||" */,
     LogicalAnd                /* "&&" */,
     BitOr                     /* "|" */,
     BitXor                    /* "^" */,
     BitAnd                    /* "&" */,
     Eq                        /* "==" */,
@@ -79,30 +85,32 @@ enum class VariableDeclarationKind {
 };
 
 
 
 // ----- Sums of interfaces (by lexicographical order)
 // Implementations are autogenerated
 // `ParseNode*` may never be nullptr
 JS::Result<ParseNode*> parseArrowExpression();
+JS::Result<Ok> parseAssertedMaybePositionalParameterName(
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
 JS::Result<ParseNode*> parseAssignmentTarget();
 JS::Result<ParseNode*> parseAssignmentTargetOrAssignmentTargetWithInitializer();
 JS::Result<ParseNode*> parseAssignmentTargetPattern();
 JS::Result<ParseNode*> parseAssignmentTargetProperty();
 JS::Result<ParseNode*> parseBinding();
 JS::Result<ParseNode*> parseBindingOrBindingWithInitializer();
 JS::Result<ParseNode*> parseBindingPattern();
 JS::Result<ParseNode*> parseBindingProperty();
 JS::Result<ParseNode*> parseExportDeclaration();
 JS::Result<ParseNode*> parseExpression();
 JS::Result<ParseNode*> parseExpressionOrSuper();
 JS::Result<ParseNode*> parseExpressionOrTemplateElement();
 JS::Result<ParseNode*> parseForInOfBindingOrAssignmentTarget();
-JS::Result<ParseNode*> parseFunctionBodyOrExpression();
 JS::Result<ParseNode*> parseFunctionDeclaration();
 JS::Result<ParseNode*> parseFunctionDeclarationOrClassDeclarationOrExpression();
 JS::Result<ParseNode*> parseFunctionDeclarationOrClassDeclarationOrVariableDeclaration();
 JS::Result<ParseNode*> parseFunctionExpression();
 JS::Result<ParseNode*> parseGetter();
 JS::Result<ParseNode*> parseImportDeclaration();
 JS::Result<ParseNode*> parseImportDeclarationOrExportDeclarationOrStatement();
 JS::Result<ParseNode*> parseIterationStatement();
@@ -114,30 +122,32 @@ JS::Result<ParseNode*> parseParameter();
 JS::Result<ParseNode*> parseProgram();
 JS::Result<ParseNode*> parsePropertyName();
 JS::Result<ParseNode*> parseSetter();
 JS::Result<ParseNode*> parseSimpleAssignmentTarget();
 JS::Result<ParseNode*> parseSpreadElementOrExpression();
 JS::Result<ParseNode*> parseStatement();
 JS::Result<ParseNode*> parseVariableDeclarationOrExpression();
 JS::Result<ParseNode*> parseSumArrowExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<Ok> parseSumAssertedMaybePositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
 JS::Result<ParseNode*> parseSumAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumAssignmentTargetOrAssignmentTargetWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumAssignmentTargetPattern(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumAssignmentTargetProperty(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumBinding(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumBindingOrBindingWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumBindingPattern(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumBindingProperty(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumExportDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumExpressionOrSuper(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumExpressionOrTemplateElement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumForInOfBindingOrAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseSumFunctionBodyOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumFunctionDeclarationOrClassDeclarationOrExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumFunctionDeclarationOrClassDeclarationOrVariableDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumGetter(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumImportDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumImportDeclarationOrExportDeclarationOrStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseSumIterationStatement(const size_t start, const BinKind kind, const BinFields& fields);
@@ -157,18 +167,36 @@ JS::Result<ParseNode*> parseSumVariableD
 
 // ----- Interfaces (by lexicographical order)
 // Implementations are autogenerated
 // `ParseNode*` may never be nullptr
 JS::Result<ParseNode*> parseNull();
 JS::Result<ParseNode*> parseArrayAssignmentTarget();
 JS::Result<ParseNode*> parseArrayBinding();
 JS::Result<ParseNode*> parseArrayExpression();
+JS::Result<ParseNode*> parseArrowExpressionContentsWithExpression();
+JS::Result<ParseNode*> parseArrowExpressionContentsWithFunctionBody();
 JS::Result<Ok> parseAssertedBlockScope();
-JS::Result<Ok> parseAssertedParameterScope();
+JS::Result<Ok> parseAssertedBoundName(
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseAssertedBoundNamesScope();
+JS::Result<Ok> parseAssertedDeclaredName(
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseAssertedParameterName(
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseAssertedParameterScope(
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseAssertedPositionalParameterName(
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseAssertedRestParameterName(
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseAssertedScriptGlobalScope();
 JS::Result<Ok> parseAssertedVarScope();
 JS::Result<ParseNode*> parseAssignmentExpression();
 JS::Result<ParseNode*> parseAssignmentTargetIdentifier();
 JS::Result<ParseNode*> parseAssignmentTargetPropertyIdentifier();
 JS::Result<ParseNode*> parseAssignmentTargetPropertyProperty();
 JS::Result<ParseNode*> parseAssignmentTargetWithInitializer();
 JS::Result<ParseNode*> parseAwaitExpression();
 JS::Result<ParseNode*> parseBinaryExpression();
@@ -188,17 +216,18 @@ JS::Result<ParseNode*> parseComputedMemb
 JS::Result<ParseNode*> parseComputedMemberExpression();
 JS::Result<ParseNode*> parseComputedPropertyName();
 JS::Result<ParseNode*> parseConditionalExpression();
 JS::Result<ParseNode*> parseContinueStatement();
 JS::Result<ParseNode*> parseDataProperty();
 JS::Result<ParseNode*> parseDebuggerStatement();
 JS::Result<ParseNode*> parseDirective();
 JS::Result<ParseNode*> parseDoWhileStatement();
-JS::Result<ParseNode*> parseEagerArrowExpression();
+JS::Result<ParseNode*> parseEagerArrowExpressionWithExpression();
+JS::Result<ParseNode*> parseEagerArrowExpressionWithFunctionBody();
 JS::Result<ParseNode*> parseEagerFunctionDeclaration();
 JS::Result<ParseNode*> parseEagerFunctionExpression();
 JS::Result<ParseNode*> parseEagerGetter();
 JS::Result<ParseNode*> parseEagerMethod();
 JS::Result<ParseNode*> parseEagerSetter();
 JS::Result<ParseNode*> parseEmptyStatement();
 JS::Result<ParseNode*> parseExport();
 JS::Result<ParseNode*> parseExportAllFrom();
@@ -208,45 +237,61 @@ JS::Result<ParseNode*> parseExportFromSp
 JS::Result<ParseNode*> parseExportLocalSpecifier();
 JS::Result<ParseNode*> parseExportLocals();
 JS::Result<ParseNode*> parseExpressionStatement();
 JS::Result<ParseNode*> parseForInOfBinding();
 JS::Result<ParseNode*> parseForInStatement();
 JS::Result<ParseNode*> parseForOfStatement();
 JS::Result<ParseNode*> parseForStatement();
 JS::Result<ListNode*> parseFormalParameters();
-JS::Result<ParseNode*> parseFunctionBody();
+JS::Result<Ok> parseFunctionExpressionContents(
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
+JS::Result<Ok> parseFunctionOrMethodContents(
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
+JS::Result<Ok> parseGetterContents(
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
 JS::Result<ParseNode*> parseIdentifierExpression();
 JS::Result<ParseNode*> parseIfStatement();
 JS::Result<ParseNode*> parseImport();
 JS::Result<ParseNode*> parseImportNamespace();
 JS::Result<ParseNode*> parseImportSpecifier();
 JS::Result<ParseNode*> parseLabelledStatement();
+JS::Result<ParseNode*> parseLazyArrowExpressionWithExpression();
+JS::Result<ParseNode*> parseLazyArrowExpressionWithFunctionBody();
+JS::Result<ParseNode*> parseLazyFunctionDeclaration();
+JS::Result<ParseNode*> parseLazyFunctionExpression();
+JS::Result<ParseNode*> parseLazyGetter();
+JS::Result<ParseNode*> parseLazyMethod();
+JS::Result<ParseNode*> parseLazySetter();
 JS::Result<ParseNode*> parseLiteralBooleanExpression();
 JS::Result<ParseNode*> parseLiteralInfinityExpression();
 JS::Result<ParseNode*> parseLiteralNullExpression();
 JS::Result<ParseNode*> parseLiteralNumericExpression();
 JS::Result<ParseNode*> parseLiteralPropertyName();
 JS::Result<ParseNode*> parseLiteralRegExpExpression();
 JS::Result<ParseNode*> parseLiteralStringExpression();
 JS::Result<ParseNode*> parseModule();
 JS::Result<ParseNode*> parseNewExpression();
 JS::Result<ParseNode*> parseNewTargetExpression();
 JS::Result<ParseNode*> parseObjectAssignmentTarget();
 JS::Result<ParseNode*> parseObjectBinding();
 JS::Result<ParseNode*> parseObjectExpression();
 JS::Result<ParseNode*> parseReturnStatement();
 JS::Result<ParseNode*> parseScript();
+JS::Result<Ok> parseSetterContents(
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
 JS::Result<ParseNode*> parseShorthandProperty();
-JS::Result<ParseNode*> parseSkippableArrowExpression();
-JS::Result<ParseNode*> parseSkippableFunctionDeclaration();
-JS::Result<ParseNode*> parseSkippableFunctionExpression();
-JS::Result<ParseNode*> parseSkippableGetter();
-JS::Result<ParseNode*> parseSkippableMethod();
-JS::Result<ParseNode*> parseSkippableSetter();
 JS::Result<ParseNode*> parseSpreadElement();
 JS::Result<ParseNode*> parseStaticMemberAssignmentTarget();
 JS::Result<ParseNode*> parseStaticMemberExpression();
 JS::Result<ParseNode*> parseSuper();
 JS::Result<CaseClause*> parseSwitchCase();
 JS::Result<ParseNode*> parseSwitchDefault();
 JS::Result<ParseNode*> parseSwitchStatement();
 JS::Result<ParseNode*> parseSwitchStatementWithDefault();
@@ -263,18 +308,36 @@ JS::Result<ParseNode*> parseVariableDecl
 JS::Result<ParseNode*> parseWhileStatement();
 JS::Result<ParseNode*> parseWithStatement();
 JS::Result<ParseNode*> parseYieldExpression();
 JS::Result<ParseNode*> parseYieldStarExpression();
 JS::Result<ParseNode*> parseInterfaceNull(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceArrayAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceArrayBinding(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceArrayExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceArrowExpressionContentsWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceArrowExpressionContentsWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<Ok> parseInterfaceAssertedBlockScope(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<Ok> parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<Ok> parseInterfaceAssertedBoundName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseInterfaceAssertedBoundNamesScope(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<Ok> parseInterfaceAssertedDeclaredName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseInterfaceAssertedParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseInterfaceAssertedParameterScope(const size_t start, const BinKind kind, const BinFields& fields,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseInterfaceAssertedPositionalParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseInterfaceAssertedRestParameterName(const size_t start, const BinKind kind, const BinFields& fields,
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
+JS::Result<Ok> parseInterfaceAssertedScriptGlobalScope(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<Ok> parseInterfaceAssertedVarScope(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAssignmentExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAssignmentTargetIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAssignmentTargetPropertyIdentifier(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAssignmentTargetPropertyProperty(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAssignmentTargetWithInitializer(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceAwaitExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceBinaryExpression(const size_t start, const BinKind kind, const BinFields& fields);
@@ -294,17 +357,18 @@ JS::Result<ParseNode*> parseInterfaceCom
 JS::Result<ParseNode*> parseInterfaceComputedMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceComputedPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceConditionalExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceContinueStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceDataProperty(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceDebuggerStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceDirective(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceDoWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceEagerArrowExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceEagerArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEagerFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEagerFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEagerGetter(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEagerMethod(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEagerSetter(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceEmptyStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceExport(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceExportAllFrom(const size_t start, const BinKind kind, const BinFields& fields);
@@ -314,45 +378,61 @@ JS::Result<ParseNode*> parseInterfaceExp
 JS::Result<ParseNode*> parseInterfaceExportLocalSpecifier(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceExportLocals(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceExpressionStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceForInOfBinding(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceForInStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceForOfStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceForStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ListNode*> parseInterfaceFormalParameters(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<Ok> parseInterfaceFunctionExpressionContents(const size_t start, const BinKind kind, const BinFields& fields,
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
+JS::Result<Ok> parseInterfaceFunctionOrMethodContents(const size_t start, const BinKind kind, const BinFields& fields,
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
+JS::Result<Ok> parseInterfaceGetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
 JS::Result<ParseNode*> parseInterfaceIdentifierExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceIfStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceImport(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceImportNamespace(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceImportSpecifier(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLabelledStatement(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyArrowExpressionWithFunctionBody(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyGetter(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazyMethod(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<ParseNode*> parseInterfaceLazySetter(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralBooleanExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralInfinityExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralNullExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralNumericExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralPropertyName(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralRegExpExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceLiteralStringExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceModule(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceNewExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceNewTargetExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceObjectAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceObjectBinding(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceObjectExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceReturnStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceScript(const size_t start, const BinKind kind, const BinFields& fields);
+JS::Result<Ok> parseInterfaceSetterContents(const size_t start, const BinKind kind, const BinFields& fields,
+    uint32_t funLength,
+    ListNode** paramsOut,
+    ListNode** bodyOut);
 JS::Result<ParseNode*> parseInterfaceShorthandProperty(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableArrowExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableFunctionDeclaration(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableFunctionExpression(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableGetter(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableMethod(const size_t start, const BinKind kind, const BinFields& fields);
-JS::Result<ParseNode*> parseInterfaceSkippableSetter(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceSpreadElement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceStaticMemberAssignmentTarget(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceStaticMemberExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceSuper(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<CaseClause*> parseInterfaceSwitchCase(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceSwitchDefault(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceSwitchStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceSwitchStatementWithDefault(const size_t start, const BinKind kind, const BinFields& fields);
@@ -369,51 +449,56 @@ JS::Result<ParseNode*> parseInterfaceVar
 JS::Result<ParseNode*> parseInterfaceWhileStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceWithStatement(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceYieldExpression(const size_t start, const BinKind kind, const BinFields& fields);
 JS::Result<ParseNode*> parseInterfaceYieldStarExpression(const size_t start, const BinKind kind, const BinFields& fields);
 
 
 // ----- String enums (by lexicographical order)
 // Implementations are autogenerated
+JS::Result<typename BinASTParser<Tok>::AssertedDeclaredKind> parseAssertedDeclaredKind();
 JS::Result<typename BinASTParser<Tok>::BinaryOperator> parseBinaryOperator();
 JS::Result<typename BinASTParser<Tok>::CompoundAssignmentOperator> parseCompoundAssignmentOperator();
 JS::Result<typename BinASTParser<Tok>::UnaryOperator> parseUnaryOperator();
 JS::Result<typename BinASTParser<Tok>::UpdateOperator> parseUpdateOperator();
 JS::Result<typename BinASTParser<Tok>::VariableDeclarationKind> parseVariableDeclarationKind();
 
 
 // ----- Lists (by lexicographical order)
 // Implementations are autogenerated
 JS::Result<ParseNode*> parseArguments();
+JS::Result<ListNode*> parseFunctionBody();
+JS::Result<Ok> parseListOfAssertedBoundName(
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseListOfAssertedDeclaredName(
+    AssertedScopeKind scopeKind);
+JS::Result<Ok> parseListOfAssertedMaybePositionalParameterName(
+    AssertedScopeKind scopeKind,
+    MutableHandle<GCVector<JSAtom*>> positionalParams);
 JS::Result<ParseNode*> parseListOfAssignmentTargetOrAssignmentTargetWithInitializer();
 JS::Result<ParseNode*> parseListOfAssignmentTargetProperty();
 JS::Result<ParseNode*> parseListOfBindingProperty();
 JS::Result<ParseNode*> parseListOfClassElement();
 JS::Result<ListNode*> parseListOfDirective();
 JS::Result<ParseNode*> parseListOfExportFromSpecifier();
 JS::Result<ParseNode*> parseListOfExportLocalSpecifier();
 JS::Result<ParseNode*> parseListOfExpressionOrTemplateElement();
-JS::Result<ParseNode*> parseListOfIdentifierName();
 JS::Result<ParseNode*> parseListOfImportDeclarationOrExportDeclarationOrStatement();
 JS::Result<ParseNode*> parseListOfImportSpecifier();
 JS::Result<ListNode*> parseListOfObjectProperty();
 JS::Result<ParseNode*> parseListOfOptionalBindingOrBindingWithInitializer();
 JS::Result<ListNode*> parseListOfOptionalSpreadElementOrExpression();
 JS::Result<ListNode*> parseListOfParameter();
 JS::Result<ListNode*> parseListOfStatement();
 JS::Result<ListNode*> parseListOfSwitchCase();
 JS::Result<ListNode*> parseListOfVariableDeclarator();
 
 
 // ----- Default values (by lexicographical order)
 // Implementations are autogenerated
-JS::Result<Ok> parseOptionalAssertedBlockScope();
-JS::Result<Ok> parseOptionalAssertedParameterScope();
-JS::Result<Ok> parseOptionalAssertedVarScope();
 JS::Result<ParseNode*> parseOptionalAssignmentTarget();
 JS::Result<ParseNode*> parseOptionalBinding();
 JS::Result<ParseNode*> parseOptionalBindingIdentifier();
 JS::Result<ParseNode*> parseOptionalBindingOrBindingWithInitializer();
 JS::Result<LexicalScopeNode*> parseOptionalCatchClause();
 JS::Result<ParseNode*> parseOptionalExpression();
 JS::Result<ParseNode*> parseOptionalIdentifierName();
 JS::Result<ParseNode*> parseOptionalLabel();
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -191,17 +191,17 @@ BinASTParser<Tok>::buildFunction(const s
     // Set the argument count for building argument packets. Function.length is handled
     // by setting the appropriate funbox field during argument parsing.
     funbox->function()->setArgCount(params ? uint16_t(params->count()) : 0);
 
     // ParseNode represents the body as concatenated after the params.
     params->appendWithoutOrderAssumption(body);
 
     bool isStatement = kind == BinKind::EagerFunctionDeclaration ||
-                       kind == BinKind::SkippableFunctionDeclaration;
+                       kind == BinKind::LazyFunctionDeclaration;
 
     BINJS_TRY_DECL(result, isStatement
                      ? factory_.newFunctionStatement(pos)
                      : factory_.newFunctionExpression(pos));
 
     factory_.setFunctionBox(result, funbox);
     factory_.setFunctionFormalParametersAndBody(result, params);
 
@@ -235,90 +235,101 @@ BinASTParser<Tok>::buildFunction(const s
                  NewLexicalScopeData(cx_, parseContext_->namedLambdaScope(), alloc_, parseContext_));
 
         funbox->namedLambdaBindings().set(*recursiveBinding);
     }
 
     return result;
 }
 
-// Try to mark the capture in the given scope, if the variable exists.
-// Return whether it was found in this scope and marked successfully.
-static bool TryMarkCaptureInScope(ParseContext::Scope& scope, HandleAtom atom)
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::addScopeName(AssertedScopeKind scopeKind, HandleAtom name,
+                                ParseContext::Scope* scope, DeclarationKind declKind,
+                                bool isCaptured)
 {
-    auto name = scope.lookupDeclaredName(atom);
-    if (!name) {
-        return false;
+    auto ptr = scope->lookupDeclaredNameForAdd(name);
+    if (ptr) {
+        return raiseError("Variable redeclaration");
+    }
+
+    BINJS_TRY(scope->addDeclaredName(parseContext_, ptr, name.get(), declKind,
+                                     tokenizer_->offset()));
+
+    if (isCaptured) {
+        auto declaredPtr = scope->lookupDeclaredName(name);
+        MOZ_ASSERT(declaredPtr);
+        declaredPtr->value()->setClosedOver();
     }
-    name->value()->setClosedOver();
-    return true;
+
+    return Ok();
+}
+
+template<typename Tok> void
+BinASTParser<Tok>::captureFunctionName()
+{
+    MOZ_ASSERT(parseContext_->isFunctionBox());
+    MOZ_ASSERT(parseContext_->functionBox()->function()->isNamedLambda());
+
+    RootedAtom funName(cx_, parseContext_->functionBox()->function()->explicitName());
+    MOZ_ASSERT(funName);
+
+    auto ptr = parseContext_->namedLambdaScope().lookupDeclaredName(funName);
+    MOZ_ASSERT(ptr);
+    ptr->value()->setClosedOver();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseAndUpdateCapturedNames(const BinKind kind)
+BinASTParser<Tok>::getDeclaredScope(AssertedScopeKind scopeKind, AssertedDeclaredKind kind,
+                                    ParseContext::Scope*& scope, DeclarationKind& declKind)
 {
-    // For the moment, we do not attempt to validate the list of captured names.
-    AutoList guard(*tokenizer_);
-    uint32_t length = 0;
-
-    MOZ_TRY(tokenizer_->enterList(length, guard));
-    RootedAtom name(cx_);
-    for (uint32_t i = 0; i < length; ++i) {
-        name = nullptr;
-
-        MOZ_TRY_VAR(name, tokenizer_->readAtom());
-        if (kind == BinKind::AssertedParameterScope) {
-            MOZ_ASSERT(parseContext_->isFunctionBox());
+    MOZ_ASSERT(scopeKind == AssertedScopeKind::Block ||
+               scopeKind == AssertedScopeKind::Global ||
+               scopeKind == AssertedScopeKind::Var);
+    switch (kind) {
+      case AssertedDeclaredKind::Var:
+        if (scopeKind == AssertedScopeKind::Block) {
+            return raiseError("AssertedBlockScope cannot contain 'var' binding");
+        }
+        declKind = DeclarationKind::Var;
+        scope = &parseContext_->varScope();
+        break;
+      case AssertedDeclaredKind::NonConstLexical:
+        declKind = DeclarationKind::Let;
+        scope = parseContext_->innermostScope();
+        break;
+      case AssertedDeclaredKind::ConstLexical:
+        declKind = DeclarationKind::Const;
+        scope = parseContext_->innermostScope();
+        break;
+    }
 
-            if (parseContext_->functionBox()->function()->isNamedLambda()) {
-                if (TryMarkCaptureInScope(parseContext_->namedLambdaScope(), name)) {
-                    continue;
-                }
-            }
-
-            if (!TryMarkCaptureInScope(parseContext_->functionScope(), name)) {
-                return raiseUndeclaredCapture(name);
-            }
-            continue;
-        }
-
-        if (kind == BinKind::AssertedVarScope) {
-            if (TryMarkCaptureInScope(parseContext_->varScope(), name)) {
-                continue;
-            }
-        }
-
-        if (!TryMarkCaptureInScope(*parseContext_->innermostScope(), name)) {
-            return raiseUndeclaredCapture(name);
-        }
-    }
-    MOZ_TRY(guard.done());
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
-BinASTParser<Tok>::parseAndUpdateScopeNames(ParseContext::Scope& scope, DeclarationKind kind)
+BinASTParser<Tok>::getBoundScope(AssertedScopeKind scopeKind,
+                                 ParseContext::Scope*& scope, DeclarationKind& declKind)
 {
-    AutoList guard(*tokenizer_);
-    uint32_t length = 0;
-
-    MOZ_TRY(tokenizer_->enterList(length, guard));
-    RootedAtom name(cx_);
-    for (uint32_t i = 0; i < length; ++i) {
-        name = nullptr;
+    MOZ_ASSERT(scopeKind == AssertedScopeKind::Catch ||
+               scopeKind == AssertedScopeKind::Parameter);
+    switch (scopeKind) {
+      case AssertedScopeKind::Catch:
+        declKind = DeclarationKind::CatchParameter;
+        scope = parseContext_->innermostScope();
+        break;
+      case AssertedScopeKind::Parameter:
+        MOZ_ASSERT(parseContext_->isFunctionBox());
+        declKind = DeclarationKind::PositionalFormalParameter;
+        scope = &parseContext_->functionScope();
+        break;
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unexpected AssertedScopeKind");
+        break;
+    }
 
-        MOZ_TRY_VAR(name, tokenizer_->readAtom());
-        auto ptr = scope.lookupDeclaredNameForAdd(name);
-        if (ptr) {
-            return raiseError("Variable redeclaration");
-        }
-
-        BINJS_TRY(scope.addDeclaredName(parseContext_, ptr, name.get(), kind, tokenizer_->offset()));
-    }
-    MOZ_TRY(guard.done());
     return Ok();
 }
 
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::checkBinding(JSAtom* name)
 {
     // Check that the variable appears in the corresponding scope.
     ParseContext::Scope& scope =
@@ -329,16 +340,70 @@ BinASTParser<Tok>::checkBinding(JSAtom* 
     auto ptr = scope.lookupDeclaredName(name->asPropertyName());
     if (!ptr) {
         return raiseMissingVariableInAssertedScope(name);
     }
 
     return Ok();
 }
 
+// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
+// 3.1.5 CheckPositionalParameterIndices.
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> positionalParams,
+                                                   ListNode* params)
+{
+    MOZ_ASSERT(positionalParams.get().length() == params->count());
+
+    uint32_t i = 0;
+    for (ParseNode* param : params->contents()) {
+        if (param->isKind(ParseNodeKind::Assign)) {
+            param = param->as<AssignmentNode>().left();
+        }
+        MOZ_ASSERT(param->isKind(ParseNodeKind::Name) ||
+                   param->isKind(ParseNodeKind::Object) ||
+                   param->isKind(ParseNodeKind::Array) ||
+                   param->isKind(ParseNodeKind::Spread));
+
+        if (JSAtom* name = positionalParams.get()[i]) {
+            // Simple or default parameter.
+            if (param->isKind(ParseNodeKind::Object) || param->isKind(ParseNodeKind::Array)) {
+                return raiseError("AssertedPositionalParameterName: expected positional parameter, got destructuring parameter");
+            }
+            if (param->isKind(ParseNodeKind::Spread)) {
+                return raiseError("AssertedPositionalParameterName: expected positional parameter, got rest parameter");
+            }
+
+            if (param->name() != name) {
+                return raiseError("AssertedPositionalParameterName: name mismatch");
+            }
+        } else {
+            // Destructuring or rest parameter.
+            if (param->isKind(ParseNodeKind::Name)) {
+                return raiseError("AssertedParameterName/AssertedRestParameterName: expected destructuring/rest parameter, got positional parameter");
+            }
+        }
+
+        i++;
+    }
+
+    return Ok();
+}
+
+// Binary AST (revision 8eab67e0c434929a66ff6abe99ff790bca087dda)
+// 3.1.13 CheckFunctionLength.
+template<typename Tok> JS::Result<Ok>
+BinASTParser<Tok>::checkFunctionLength(uint32_t expectedLength)
+{
+    if (parseContext_->functionBox()->length != expectedLength) {
+        return raiseError("Function length does't match");
+    }
+    return Ok();
+}
+
 template<typename Tok> JS::Result<Ok>
 BinASTParser<Tok>::checkClosedVars(ParseContext::Scope& scope)
 {
     for (ParseContext::Scope::BindingIter bi = scope.bindings(parseContext_); bi; bi++) {
         if (UsedNamePtr p = usedNames_.lookup(bi.name())) {
             bool closedOver;
             p->value().noteBoundInScope(parseContext_->scriptId(), scope.id(), &closedOver);
             if (closedOver && !bi.closedOver()) {
@@ -398,23 +463,16 @@ BinASTParser<Tok>::appendDirectivesToBod
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
 BinASTParser<Tok>::raiseInvalidClosedVar(JSAtom* name)
 {
     return raiseError("Captured variable was not declared as captured");
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
-BinASTParser<Tok>::raiseUndeclaredCapture(JSAtom* name)
-{
-    // As below, don't put the name in a message.
-    return raiseError("Captured variable undeclared in scope");
-}
-
-template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
 BinASTParser<Tok>::raiseMissingVariableInAssertedScope(JSAtom* name)
 {
     // For the moment, we don't trust inputs sufficiently to put the name
     // in an error message.
     return raiseError("Missing variable in AssertedScope");
 }
 
 template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
--- a/js/src/frontend/BinSource.h
+++ b/js/src/frontend/BinSource.h
@@ -131,17 +131,16 @@ class BinASTParser : public BinASTParser
   private:
     MOZ_MUST_USE JS::Result<ParseNode*> parseAux(GlobalSharedContext* globalsc,
                                                  const uint8_t* start, const size_t length);
 
     // --- Raise errors.
     //
     // These methods return a (failed) JS::Result for convenience.
 
-    MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseUndeclaredCapture(JSAtom* name);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidClosedVar(JSAtom* name);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingVariableInAssertedScope(JSAtom* name);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingDirectEvalInAssertedScope();
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(const char* superKind,
         const BinKind kind);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidVariant(const char* kind,
         const BinVariant value);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(const char* kind,
@@ -151,37 +150,63 @@ class BinASTParser : public BinASTParser
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(const char* description);
     MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(BinKind kind,
         const char* description);
 
 
     // Ensure that this parser will never be used again.
     void poison();
 
+    // The owner or the target of Asserted*Scope.
+    enum class AssertedScopeKind {
+        Block,
+        Catch,
+        Global,
+        Parameter,
+        Var,
+    };
+
     // Auto-generated methods
 #include "frontend/BinSource-auto.h"
 
     // --- Auxiliary parsing functions
 
     // Build a function object for a function-producing production. Called AFTER creating the scope.
     JS::Result<ParseNode*>
     buildFunction(const size_t start, const BinKind kind, ParseNode* name, ListNode* params,
         ParseNode* body, FunctionBox* funbox);
     JS::Result<FunctionBox*>
     buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind, FunctionSyntaxKind syntax, ParseNode* name);
 
-    // Parse full scope information to a specific var scope / let scope combination.
-    MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScope(ParseContext::Scope& varScope,
-        ParseContext::Scope& letScope);
-    // Parse a list of names and add it to a given scope.
-    MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScopeNames(ParseContext::Scope& scope,
-        DeclarationKind kind);
-    MOZ_MUST_USE JS::Result<Ok> parseAndUpdateCapturedNames(const BinKind kind);
+    // Add name to a given scope.
+    MOZ_MUST_USE JS::Result<Ok> addScopeName(AssertedScopeKind scopeKind, HandleAtom name,
+                                             ParseContext::Scope* scope,
+                                             DeclarationKind declKind,
+                                             bool isCaptured);
+
+    void captureFunctionName();
+
+    // Map AssertedScopeKind and AssertedDeclaredKind for single binding to
+    // corresponding ParseContext::Scope to store the binding, and
+    // DeclarationKind for the binding.
+    MOZ_MUST_USE JS::Result<Ok> getDeclaredScope(AssertedScopeKind scopeKind,
+                                                 AssertedDeclaredKind kind,
+                                                 ParseContext::Scope*& scope,
+                                                 DeclarationKind& declKind);
+    MOZ_MUST_USE JS::Result<Ok> getBoundScope(AssertedScopeKind scopeKind,
+                                              ParseContext::Scope*& scope,
+                                              DeclarationKind& declKind);
+
     MOZ_MUST_USE JS::Result<Ok> checkBinding(JSAtom* name);
 
+    MOZ_MUST_USE JS::Result<Ok> checkPositionalParameterIndices(Handle<GCVector<JSAtom*>> positionalParams,
+                                                                ListNode* params);
+
+    MOZ_MUST_USE JS::Result<Ok> checkFunctionLength(uint32_t expectedLength);
+
     // When leaving a scope, check that none of its bindings are known closed over and un-marked.
     MOZ_MUST_USE JS::Result<Ok> checkClosedVars(ParseContext::Scope& scope);
 
     // As a convenience, a helper that checks the body, parameter, and recursive binding scopes.
     MOZ_MUST_USE JS::Result<Ok> checkFunctionClosedVars();
 
     // --- Utilities.
 
--- a/js/src/frontend/BinSource.webidl_
+++ b/js/src/frontend/BinSource.webidl_
@@ -65,44 +65,79 @@ enum UnaryOperator {
   "delete"
 };
 
 enum UpdateOperator {
   "++",
   "--"
 };
 
+enum AssertedDeclaredKind {
+  "var",
+  "non-const lexical",
+  "const lexical"
+};
 
 // deferred assertions
 
+interface AssertedDeclaredName {
+  attribute IdentifierName name;
+  attribute AssertedDeclaredKind kind;
+  attribute boolean isCaptured;
+};
+
+interface AssertedPositionalParameterName {
+  attribute unsigned long index;
+  attribute IdentifierName name;
+  attribute boolean isCaptured;
+};
+
+interface AssertedRestParameterName {
+  attribute IdentifierName name;
+  attribute boolean isCaptured;
+};
+
+interface AssertedParameterName {
+  attribute IdentifierName name;
+  attribute boolean isCaptured;
+};
+
+typedef (AssertedPositionalParameterName or
+         AssertedRestParameterName or
+         AssertedParameterName)
+        AssertedMaybePositionalParameterName;
+
+interface AssertedBoundName {
+  attribute IdentifierName name;
+  attribute boolean isCaptured;
+};
+
 interface AssertedBlockScope {
-  // checked eagerly during transformation
-  attribute FrozenArray<IdentifierName> lexicallyDeclaredNames;
+  attribute FrozenArray<AssertedDeclaredName> declaredNames;
+  attribute boolean hasDirectEval;
+};
 
-  // checked lazily as inner functions are invoked
-  attribute FrozenArray<IdentifierName> capturedNames;
+interface AssertedScriptGlobalScope {
+  attribute FrozenArray<AssertedDeclaredName> declaredNames;
   attribute boolean hasDirectEval;
 };
 
 interface AssertedVarScope {
-  // checked eagerly during transformation
-  attribute FrozenArray<IdentifierName> lexicallyDeclaredNames;
-  attribute FrozenArray<IdentifierName> varDeclaredNames;
-
-  // checked lazily as inner functions are invoked
-  attribute FrozenArray<IdentifierName> capturedNames;
+  attribute FrozenArray<AssertedDeclaredName> declaredNames;
   attribute boolean hasDirectEval;
 };
 
 interface AssertedParameterScope {
-  // checked eagerly during transformation
-  attribute FrozenArray<IdentifierName> parameterNames;
+  attribute FrozenArray<AssertedMaybePositionalParameterName> paramNames;
+  attribute boolean hasDirectEval;
+  attribute boolean isSimpleParameterList;
+};
 
-  // checked lazily as inner functions are invoked
-  attribute FrozenArray<IdentifierName> capturedNames;
+interface AssertedBoundNamesScope {
+  attribute FrozenArray<AssertedBoundName> boundNames;
   attribute boolean hasDirectEval;
 };
 
 // nodes
 
 interface Node {
   [TypeIndicator] readonly attribute Type type;
 };
@@ -185,27 +220,32 @@ typedef (ExportAllFrom or
          ExportFrom or
          ExportLocals or
          ExportDefault or
          Export)
         ExportDeclaration;
 
 typedef (ImportNamespace or Import) ImportDeclaration;
 
-typedef (EagerFunctionDeclaration or SkippableFunctionDeclaration) FunctionDeclaration;
+typedef (EagerFunctionDeclaration or
+         LazyFunctionDeclaration) FunctionDeclaration;
 
-typedef (EagerFunctionExpression or SkippableFunctionExpression) FunctionExpression;
+typedef (EagerFunctionExpression or
+         LazyFunctionExpression) FunctionExpression;
 
-typedef (EagerMethod or SkippableMethod) Method;
+typedef (EagerMethod or LazyMethod) Method;
 
-typedef (EagerGetter or SkippableGetter) Getter;
+typedef (EagerGetter or LazyGetter) Getter;
+
+typedef (EagerSetter or LazySetter) Setter;
 
-typedef (EagerSetter or SkippableSetter) Setter;
-
-typedef (EagerArrowExpression or SkippableArrowExpression) ArrowExpression;
+typedef (EagerArrowExpressionWithFunctionBody or
+         LazyArrowExpressionWithFunctionBody or
+         EagerArrowExpressionWithExpression or
+         LazyArrowExpressionWithExpression) ArrowExpression;
 
 // bindings
 
 interface BindingIdentifier : Node {
   attribute Identifier name;
 };
 
 typedef (ObjectBinding or
@@ -337,17 +377,17 @@ interface ClassElement : Node {
   attribute boolean isStatic;
   attribute MethodDefinition method;
 };
 
 
 // modules
 
 interface Module : Node {
-  attribute AssertedVarScope? scope;
+  attribute AssertedVarScope scope;
   attribute FrozenArray<Directive> directives;
   attribute FrozenArray<(ImportDeclaration or ExportDeclaration or Statement)> items;
 };
 
 // An `ImportDeclaration` not including a namespace import.
 interface Import : Node {
   attribute string moduleSpecifier;
   // `ImportedDefaultBinding`, if present.
@@ -419,55 +459,70 @@ interface ExportLocalSpecifier : Node {
 
 
 // property definition
 
 // `MethodDefinition :: PropertyName ( UniqueFormalParameters ) { FunctionBody }`,
 // `GeneratorMethod :: * PropertyName ( UniqueFormalParameters ) { GeneratorBody }`,
 // `AsyncMethod :: async PropertyName ( UniqueFormalParameters ) { AsyncFunctionBody }`
 interface EagerMethod : Node {
-  // True for `AsyncMethod`, false otherwise.
   attribute boolean isAsync;
-  // True for `GeneratorMethod`, false otherwise.
   attribute boolean isGenerator;
   attribute PropertyName name;
-  attribute AssertedParameterScope? parameterScope;
-  attribute AssertedVarScope? bodyScope;
-  // The `UniqueFormalParameters`.
-  attribute FormalParameters params;
-  attribute FunctionBody body;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  attribute FunctionOrMethodContents contents;
 };
-
-[Skippable] interface SkippableMethod : Node {
-  attribute EagerMethod skipped;
+interface LazyMethod : Node {
+  attribute boolean isAsync;
+  attribute boolean isGenerator;
+  attribute PropertyName name;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute FunctionOrMethodContents contents;
 };
 
 // `get PropertyName ( ) { FunctionBody }`
 interface EagerGetter : Node {
   attribute PropertyName name;
-  attribute AssertedVarScope? bodyScope;
-  attribute FunctionBody body;
+  attribute FrozenArray<Directive> directives;
+  attribute GetterContents contents;
+};
+interface LazyGetter : Node {
+  attribute PropertyName name;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute GetterContents contents;
 };
 
-[Skippable] interface SkippableGetter : Node {
-  attribute EagerGetter skipped;
+interface GetterContents : Node {
+  attribute boolean isThisCaptured;
+  attribute AssertedVarScope bodyScope;
+  attribute FunctionBody body;
 };
 
 // `set PropertyName ( PropertySetParameterList ) { FunctionBody }`
 interface EagerSetter : Node {
   attribute PropertyName name;
-  attribute AssertedParameterScope? parameterScope;
-  attribute AssertedVarScope? bodyScope;
-  // The `PropertySetParameterList`.
-  attribute Parameter param;
-  attribute FunctionBody body;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  attribute SetterContents contents;
+};
+interface LazySetter : Node {
+  attribute PropertyName name;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute SetterContents contents;
 };
 
-[Skippable] interface SkippableSetter : Node {
-  attribute EagerSetter skipped;
+interface SetterContents : Node {
+  attribute boolean isThisCaptured;
+  attribute AssertedParameterScope parameterScope;
+  attribute Parameter param;
+  attribute AssertedVarScope bodyScope;
+  attribute FunctionBody body;
 };
 
 // `PropertyDefinition :: PropertyName : AssignmentExpression`
 interface DataProperty : Node {
   attribute PropertyName name;
   // The `AssignmentExpression`.
   attribute Expression expression;
 };
@@ -523,27 +578,55 @@ interface LiteralStringExpression : Node
 // `ArrayLiteral`
 interface ArrayExpression : Node {
   // The elements of the array literal; a null value represents an elision.
   attribute FrozenArray<(SpreadElement or Expression)?> elements;
 };
 
 // `ArrowFunction`,
 // `AsyncArrowFunction`
-interface EagerArrowExpression : Node {
+interface EagerArrowExpressionWithFunctionBody : Node {
+  // True for `AsyncArrowFunction`, false otherwise.
+  attribute boolean isAsync;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  attribute ArrowExpressionContentsWithFunctionBody contents;
+};
+interface LazyArrowExpressionWithFunctionBody : Node {
+  // True for `AsyncArrowFunction`, false otherwise.
+  attribute boolean isAsync;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute ArrowExpressionContentsWithFunctionBody contents;
+};
+interface EagerArrowExpressionWithExpression : Node {
   // True for `AsyncArrowFunction`, false otherwise.
   attribute boolean isAsync;
-  attribute AssertedParameterScope? parameterScope;
-  attribute AssertedVarScope? bodyScope;
-  attribute FormalParameters params;
-  attribute (FunctionBody or Expression) body;
+  attribute unsigned long length;
+  attribute ArrowExpressionContentsWithExpression contents;
+};
+interface LazyArrowExpressionWithExpression : Node {
+  // True for `AsyncArrowFunction`, false otherwise.
+  attribute boolean isAsync;
+  attribute unsigned long length;
+  [Lazy] attribute ArrowExpressionContentsWithExpression contents;
 };
 
-[Skippable] interface SkippableArrowExpression : Node {
-  attribute EagerArrowExpression skipped;
+interface ArrowExpressionContentsWithFunctionBody : Node {
+  attribute AssertedParameterScope parameterScope;
+  attribute FormalParameters params;
+  attribute AssertedVarScope bodyScope;
+  attribute FunctionBody body;
+};
+
+interface ArrowExpressionContentsWithExpression : Node {
+  attribute AssertedParameterScope parameterScope;
+  attribute FormalParameters params;
+  attribute AssertedVarScope bodyScope;
+  attribute Expression body;
 };
 
 // `AssignmentExpression :: LeftHandSideExpression = AssignmentExpression`
 interface AssignmentExpression : Node {
   // The `LeftHandSideExpression`.
   attribute AssignmentTarget binding;
   // The `AssignmentExpression` following the `=`.
   attribute Expression expression;
@@ -601,24 +684,36 @@ interface ConditionalExpression : Node {
 
 // `FunctionExpression`,
 // `GeneratorExpression`,
 // `AsyncFunctionExpression`,
 interface EagerFunctionExpression : Node {
   attribute boolean isAsync;
   attribute boolean isGenerator;
   attribute BindingIdentifier? name;
-  attribute AssertedParameterScope? parameterScope;
-  attribute AssertedVarScope? bodyScope;
-  attribute FormalParameters params;
-  attribute FunctionBody body;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  attribute FunctionExpressionContents contents;
+};
+interface LazyFunctionExpression : Node {
+  attribute boolean isAsync;
+  attribute boolean isGenerator;
+  attribute BindingIdentifier? name;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute FunctionExpressionContents contents;
 };
 
-[Skippable] interface SkippableFunctionExpression : Node {
-  attribute EagerFunctionExpression skipped;
+interface FunctionExpressionContents : Node {
+  attribute boolean isFunctionNameCaptured;
+  attribute boolean isThisCaptured;
+  attribute AssertedParameterScope parameterScope;
+  attribute FormalParameters params;
+  attribute AssertedVarScope bodyScope;
+  attribute FunctionBody body;
 };
 
 // `IdentifierReference`
 interface IdentifierExpression : Node {
   attribute Identifier name;
 };
 
 interface NewExpression : Node {
@@ -819,65 +914,73 @@ interface WithStatement : Node {
   attribute Expression _object;
   attribute Statement body;
 };
 
 
 // other nodes
 
 interface Block : Node {
-  attribute AssertedBlockScope? scope;
+  attribute AssertedBlockScope scope;
   attribute FrozenArray<Statement> statements;
 };
 
 // `Catch`
 interface CatchClause : Node {
-  // `AssertedParameterScope` is used for catch bindings so the declared names
-  // are checked using BoundNames.
-  attribute AssertedParameterScope? bindingScope;
+  attribute AssertedBoundNamesScope bindingScope;
   attribute Binding binding;
   attribute Block body;
 };
 
 // An item in a `DirectivePrologue`
 interface Directive : Node {
   attribute string rawValue;
 };
 
 interface FormalParameters : Node {
   attribute FrozenArray<Parameter> items;
   attribute Binding? rest;
 };
 
-interface FunctionBody : Node {
-  attribute FrozenArray<Directive> directives;
-  attribute FrozenArray<Statement> statements;
-};
+typedef FrozenArray<Statement> FunctionBody;
 
 
 
 // `FunctionDeclaration`,
 // `GeneratorDeclaration`,
 // `AsyncFunctionDeclaration`
 interface EagerFunctionDeclaration : Node {
   attribute boolean isAsync;
   attribute boolean isGenerator;
   attribute BindingIdentifier name;
-  attribute AssertedParameterScope? parameterScope;
-  attribute AssertedVarScope? bodyScope;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  attribute FunctionOrMethodContents contents;
+};
+
+interface LazyFunctionDeclaration : Node {
+  attribute boolean isAsync;
+  attribute boolean isGenerator;
+  attribute BindingIdentifier name;
+  attribute unsigned long length;
+  attribute FrozenArray<Directive> directives;
+  [Lazy] attribute FunctionOrMethodContents contents;
+};
+
+interface FunctionOrMethodContents : Node {
+  attribute boolean isThisCaptured;
+  attribute AssertedParameterScope parameterScope;
   attribute FormalParameters params;
+  attribute AssertedVarScope bodyScope;
   attribute FunctionBody body;
 };
 
-[Skippable] interface SkippableFunctionDeclaration : Node {
-  attribute EagerFunctionDeclaration skipped;
-};
 
 interface Script : Node {
-  attribute AssertedVarScope? scope;
+  attribute AssertedScriptGlobalScope scope;
   attribute FrozenArray<Directive> directives;
   attribute FrozenArray<Statement> statements;
 };
 
 interface SpreadElement : Node {
   attribute Expression expression;
 };
 
--- a/js/src/frontend/BinSource.yaml
+++ b/js/src/frontend/BinSource.yaml
@@ -213,62 +213,119 @@ Arguments:
 
 ArrayExpression:
     build:
         auto result = elements;
 
 AssertedBlockScope:
     type-ok:
         Ok
+    init: |
+        const auto scopeKind = AssertedScopeKind::Block;
+    fields:
+        declaredNames:
+            extra-args: scopeKind
+        hasDirectEval:
+            after: |
+                if (hasDirectEval) {
+                    parseContext_->sc()->setHasDirectEval();
+                    parseContext_->sc()->setBindingsAccessedDynamically();
+                }
     build: |
         if (hasDirectEval && parseContext_->isFunctionBox() && !parseContext_->sc()->strict()) {
             // In non-strict mode code, direct calls to eval can
             // add variables to the call object.
             parseContext_->functionBox()->setHasExtensibleScope();
         }
         auto result = Ok();
+
+AssertedBoundName:
+    type-ok:
+        Ok
+    extra-params: AssertedScopeKind scopeKind
+    extra-args: scopeKind
     fields:
-        capturedNames:
-            block:
-                replace: |
-                    MOZ_TRY(parseAndUpdateCapturedNames(kind));
-        hasDirectEval:
+        isCaptured:
+             after: |
+                ParseContext::Scope* scope;
+                DeclarationKind declKind;
+                MOZ_TRY(getBoundScope(scopeKind, scope, declKind));
+    build: |
+        MOZ_TRY(addScopeName(scopeKind, name, scope, declKind, isCaptured));
+        auto result = Ok();
+
+AssertedDeclaredName:
+    inherits: AssertedBoundName
+    fields:
+        isCaptured:
             after: |
-                if (hasDirectEval) {
-                    parseContext_->sc()->setHasDirectEval();
-                    parseContext_->sc()->setBindingsAccessedDynamically();
-                }
-        lexicallyDeclaredNames:
-            block:
-                replace:
-                    MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::Let));
+                ParseContext::Scope* scope;
+                DeclarationKind declKind;
+                MOZ_TRY(getDeclaredScope(scopeKind, kind_, scope, declKind));
+
+AssertedMaybePositionalParameterName:
+    inherits: AssertedBoundName
+    extra-params: |
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams
+    extra-args: |
+        scopeKind, positionalParams
+
+AssertedPositionalParameterName:
+    inherits: AssertedMaybePositionalParameterName
+    fields:
+        name:
+            after: |
+                // FIXME: The following checks should be performed inside
+                // checkPositionalParameterIndices to match the spec's order
+                // (bug 1490976).
+                if (index >= positionalParams.get().length())
+                    return raiseError("AssertedPositionalParameterName.length out of range");
+                if (positionalParams.get()[index])
+                    return raiseError("AssertedPositionalParameterName has duplicate entry for the same index");
+                positionalParams.get()[index] = name;
+
+AssertedRestParameterName:
+    inherits: AssertedMaybePositionalParameterName
+
+AssertedParameterName:
+    inherits: AssertedMaybePositionalParameterName
+
+AssertedBoundNamesScope:
+    inherits: AssertedBlockScope
+    init: |
+        const auto scopeKind = AssertedScopeKind::Catch;
+    fields:
+        boundNames:
+            extra-args: scopeKind
 
 AssertedParameterScope:
     inherits: AssertedBlockScope
+    extra-params: |
+        MutableHandle<GCVector<JSAtom*>> positionalParams
+    extra-args: |
+        positionalParams
+    init: |
+        const auto scopeKind = AssertedScopeKind::Parameter;
     fields:
-        parameterNames:
-            block:
-                replace: |
-                    ParseContext::Statement* inStatement = parseContext_->innermostStatement();
+        isSimpleParameterList:
+            after: |
+                (void) isSimpleParameterList;
+        paramNames:
+            extra-args: scopeKind, positionalParams
 
-                    // If we are in a `CatchClause`, the binding is a implicit CatchParameter
-                    // and it goes into the innermost scope. Otherwise, we're in a function,
-                    // so it goes in the function scope.
-                    if (inStatement && inStatement->kind() == StatementKind::Catch)
-                        MOZ_TRY(parseAndUpdateScopeNames(*parseContext_->innermostScope(), DeclarationKind::CatchParameter));
-                    else
-                        MOZ_TRY(parseAndUpdateScopeNames(parseContext_->functionScope(), DeclarationKind::PositionalFormalParameter));
+AssertedScriptGlobalScope:
+    inherits: AssertedBlockScope
+    init: |
+        const auto scopeKind = AssertedScopeKind::Global;
 
 AssertedVarScope:
     inherits: AssertedBlockScope
-    fields:
-        varDeclaredNames:
-            block:
-                replace:
-                    MOZ_TRY(parseAndUpdateScopeNames(parseContext_->varScope(), DeclarationKind::Var));
+    init: |
+        const auto scopeKind = AssertedScopeKind::Var;
 
 AssignmentExpression:
     build: |
         BINJS_TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));
 
 AssignmentTargetIdentifier:
     build: |
         if (!IsIdentifier(name))
@@ -544,94 +601,132 @@ DoWhileStatement:
     build:
         BINJS_TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
 
 EagerFunctionDeclaration:
     init: |
         const auto syntax = FunctionSyntaxKind::Statement;
     inherits: EagerFunctionExpression
 
+FunctionExpressionContents:
+    type-ok:
+        Ok
+    extra-params: |
+        uint32_t funLength,
+        ListNode** paramsOut,
+        ListNode** bodyOut
+    extra-args: |
+        funLength, paramsOut, bodyOut
+    fields:
+        isThisCaptured:
+            after: |
+                // TODO: Use this in BinASTParser::buildFunction.
+                (void) isThisCaptured;
+        isFunctionNameCaptured:
+            after: |
+                // Per spec, isFunctionNameCaptured can be true for anonymous
+                // function.  Check isFunctionNameCaptured only for named
+                // function.
+                if (parseContext_->functionBox()->function()->isNamedLambda() &&
+                    isFunctionNameCaptured)
+                {
+                    captureFunctionName();
+                }
+        parameterScope:
+            before: |
+                Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
+            extra-args: |
+                &positionalParams
+        params:
+            after: |
+                MOZ_TRY(checkFunctionLength(funLength));
+                MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
+    build: |
+        *paramsOut = params;
+        *bodyOut = body;
+        auto result = Ok();
+
+FunctionOrMethodContents:
+    inherits: FunctionExpressionContents
+
+GetterContents:
+    inherits: FunctionExpressionContents
+    fields:
+        body:
+            before: |
+                BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
+
+SetterContents:
+    inherits: FunctionExpressionContents
+    fields:
+        param:
+            after: |
+              BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
+              factory_.addList(params, param);
+              MOZ_TRY(checkPositionalParameterIndices(positionalParams, params));
+
 EagerFunctionExpression:
     init: |
         const auto syntax = FunctionSyntaxKind::Expression;
     fields:
-        parameterScope:
+        contents:
             before: |
                 BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
                     isGenerator ? GeneratorKind::Generator
                                 : GeneratorKind::NotGenerator,
                     isAsync ? FunctionAsyncKind::AsyncFunction
                             : FunctionAsyncKind::SyncFunction,
-                    syntax, name));
+                    syntax,
+                    (syntax != FunctionSyntaxKind::Setter &&
+                     syntax != FunctionSyntaxKind::Getter) ? name : nullptr));
 
                 // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
                 BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
                 BINJS_TRY(funpc.init());
                 parseContext_->functionScope().useAsVarScope(parseContext_);
                 MOZ_ASSERT(parseContext_->isFunctionBox());
 
                 ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
                 BINJS_TRY(lexicalScope.init(parseContext_));
+                ListNode* params;
+                ListNode* tmpBody;
+            extra-args: |
+                length, &params, &tmpBody
+            after: |
+                BINJS_MOZ_TRY_DECL(body, appendDirectivesToBody(tmpBody, directives));
     build: |
         BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
         BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
         BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
 
 EagerGetter:
-    fields:
-        name:
-            after: |
-                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
-                    GeneratorKind::NotGenerator,
-                    FunctionAsyncKind::SyncFunction,
-                    FunctionSyntaxKind::Getter, /* name = */ nullptr));
-
-                // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
-                BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
-                BINJS_TRY(funpc.init());
-                parseContext_->functionScope().useAsVarScope(parseContext_);
-                MOZ_ASSERT(parseContext_->isFunctionBox());
-
-                ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
-                BINJS_TRY(lexicalScope.init(parseContext_));
-    build: |
-        BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
-        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Getter));
+    init: |
+        const auto syntax = FunctionSyntaxKind::Setter;
+        const bool isGenerator = false;
+        const bool isAsync = false;
+        const auto accessorType = AccessorType::Getter;
+        const uint32_t length = 0;
+    inherits: EagerMethod
 
 EagerMethod:
     init: |
         const auto syntax = FunctionSyntaxKind::Method;
+        const auto accessorType = AccessorType::None;
     inherits: EagerFunctionExpression
     build: |
         BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));
+        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, accessorType));
 
 EagerSetter:
-    fields:
-        name:
-            after: |
-                BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
-                    GeneratorKind::NotGenerator,
-                    FunctionAsyncKind::SyncFunction,
-                    FunctionSyntaxKind::Setter, /* name = */ nullptr));
-
-                // Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
-                BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
-                BINJS_TRY(funpc.init());
-                parseContext_->functionScope().useAsVarScope(parseContext_);
-                MOZ_ASSERT(parseContext_->isFunctionBox());
-
-                ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
-                BINJS_TRY(lexicalScope.init(parseContext_));
-    build: |
-        BINJS_TRY_DECL(params, new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos));
-        factory_.addList(params, param);
-        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
+    init: |
+        const auto syntax = FunctionSyntaxKind::Setter;
+        const bool isGenerator = false;
+        const bool isAsync = false;
+        const auto accessorType = AccessorType::Setter;
+    inherits: EagerMethod
 
 EmptyStatement:
     build:
         BINJS_TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));
 
 ExpressionStatement:
     build:
         BINJS_TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
@@ -696,19 +791,17 @@ ForStatement:
         BINJS_TRY_VAR(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
 
         if (!scope.isEmpty()) {
             BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
             BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
         }
 
 FunctionBody:
-    build: |
-        BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
-
+    inherits: ListOfStatement
 
 IdentifierExpression:
     build: |
         if (!IsIdentifier(name))
             return raiseError("Invalid identifier");
         BINJS_TRY(usedNames_.noteUse(cx_, name, parseContext_->scriptId(), parseContext_->innermostScope()->id()));
         BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
 
@@ -721,16 +814,44 @@ LabelledStatement:
         label:
             after: |
                 if (!IsIdentifier(label))
                     return raiseError("Invalid identifier");
                 ParseContext::LabelStatement stmt(parseContext_, label);
     build:
         BINJS_TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
 
+ListOfAssertedBoundName:
+    extra-params: AssertedScopeKind scopeKind
+    extra-args: scopeKind
+    type-ok:
+        Ok
+    init: |
+        (void) start;
+        auto result = Ok();
+    append: |
+        // Nothing to do here.
+
+ListOfAssertedMaybePositionalParameterName:
+    inherits: ListOfAssertedBoundName
+    extra-params: |
+        AssertedScopeKind scopeKind,
+        MutableHandle<GCVector<JSAtom*>> positionalParams
+    extra-args: |
+        scopeKind, positionalParams
+    init: |
+        (void) start;
+        auto result = Ok();
+        BINJS_TRY(positionalParams.get().resize(length));
+        for (uint32_t i = 0; i < length; i++)
+            positionalParams.get()[i] = nullptr;
+
+ListOfAssertedDeclaredName:
+    inherits: ListOfAssertedBoundName
+
 ListOfDirective:
     type-ok:
         ListNode*
     init:
         BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
     append:
         factory_.addStatementToList(result, item);
 
@@ -853,28 +974,16 @@ LiteralStringExpression:
 NewExpression:
     build: |
         BINJS_TRY_DECL(result, factory_.newNewExpression(tokenizer_->pos(start).begin, callee, arguments));
 
 ObjectExpression:
     build:
         auto result = properties;
 
-OptionalAssertedBlockScope:
-    type-ok:
-        Ok
-
-OptionalAssertedVarScope:
-    type-ok:
-        Ok
-
-OptionalAssertedParameterScope:
-    type-ok:
-        Ok
-
 OptionalCatchClause:
     type-ok:
         LexicalScopeNode*
 
 Parameter:
     sum-arms:
         BindingIdentifier:
             after: |
@@ -894,26 +1003,16 @@ ReturnStatement:
     build:
         BINJS_TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));
 
 Script:
     build:
         MOZ_TRY(checkClosedVars(parseContext_->varScope()));
         BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
 
-Setter:
-    inherits: Method
-    init: |
-        const auto isAsync = false;
-        const auto isGenerator = false;
-    build: |
-        BINJS_TRY_DECL(params, factory_.newList(ParseNodeKind::ParamsBody, param));
-        BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
-        BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
-
 ShorthandProperty:
     build: |
         if (!factory_.isUsableAsObjectPropertyName(name))
             BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
 
         BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
 
 SwitchCase:
--- a/js/src/frontend/BinToken.h
+++ b/js/src/frontend/BinToken.h
@@ -62,18 +62,29 @@ namespace frontend {
  */
 #define FOR_EACH_BIN_KIND(F) \
     F(_Null, "") \
     F(Arguments, "Arguments") \
     F(ArrayAssignmentTarget, "ArrayAssignmentTarget") \
     F(ArrayBinding, "ArrayBinding") \
     F(ArrayExpression, "ArrayExpression") \
     F(ArrowExpression, "ArrowExpression") \
+    F(ArrowExpressionContentsWithExpression, "ArrowExpressionContentsWithExpression") \
+    F(ArrowExpressionContentsWithFunctionBody, "ArrowExpressionContentsWithFunctionBody") \
     F(AssertedBlockScope, "AssertedBlockScope") \
+    F(AssertedBoundName, "AssertedBoundName") \
+    F(AssertedBoundNamesScope, "AssertedBoundNamesScope") \
+    F(AssertedDeclaredKind, "AssertedDeclaredKind") \
+    F(AssertedDeclaredName, "AssertedDeclaredName") \
+    F(AssertedMaybePositionalParameterName, "AssertedMaybePositionalParameterName") \
+    F(AssertedParameterName, "AssertedParameterName") \
     F(AssertedParameterScope, "AssertedParameterScope") \
+    F(AssertedPositionalParameterName, "AssertedPositionalParameterName") \
+    F(AssertedRestParameterName, "AssertedRestParameterName") \
+    F(AssertedScriptGlobalScope, "AssertedScriptGlobalScope") \
     F(AssertedVarScope, "AssertedVarScope") \
     F(AssignmentExpression, "AssignmentExpression") \
     F(AssignmentTarget, "AssignmentTarget") \
     F(AssignmentTargetIdentifier, "AssignmentTargetIdentifier") \
     F(AssignmentTargetOrAssignmentTargetWithInitializer, "AssignmentTargetOrAssignmentTargetWithInitializer") \
     F(AssignmentTargetPattern, "AssignmentTargetPattern") \
     F(AssignmentTargetProperty, "AssignmentTargetProperty") \
     F(AssignmentTargetPropertyIdentifier, "AssignmentTargetPropertyIdentifier") \
@@ -103,17 +114,18 @@ namespace frontend {
     F(ComputedMemberExpression, "ComputedMemberExpression") \
     F(ComputedPropertyName, "ComputedPropertyName") \
     F(ConditionalExpression, "ConditionalExpression") \
     F(ContinueStatement, "ContinueStatement") \
     F(DataProperty, "DataProperty") \
     F(DebuggerStatement, "DebuggerStatement") \
     F(Directive, "Directive") \
     F(DoWhileStatement, "DoWhileStatement") \
-    F(EagerArrowExpression, "EagerArrowExpression") \
+    F(EagerArrowExpressionWithExpression, "EagerArrowExpressionWithExpression") \
+    F(EagerArrowExpressionWithFunctionBody, "EagerArrowExpressionWithFunctionBody") \
     F(EagerFunctionDeclaration, "EagerFunctionDeclaration") \
     F(EagerFunctionExpression, "EagerFunctionExpression") \
     F(EagerGetter, "EagerGetter") \
     F(EagerMethod, "EagerMethod") \
     F(EagerSetter, "EagerSetter") \
     F(EmptyStatement, "EmptyStatement") \
     F(Export, "Export") \
     F(ExportAllFrom, "ExportAllFrom") \
@@ -129,43 +141,54 @@ namespace frontend {
     F(ExpressionStatement, "ExpressionStatement") \
     F(ForInOfBinding, "ForInOfBinding") \
     F(ForInOfBindingOrAssignmentTarget, "ForInOfBindingOrAssignmentTarget") \
     F(ForInStatement, "ForInStatement") \
     F(ForOfStatement, "ForOfStatement") \
     F(ForStatement, "ForStatement") \
     F(FormalParameters, "FormalParameters") \
     F(FunctionBody, "FunctionBody") \
-    F(FunctionBodyOrExpression, "FunctionBodyOrExpression") \
     F(FunctionDeclaration, "FunctionDeclaration") \
     F(FunctionDeclarationOrClassDeclarationOrExpression, "FunctionDeclarationOrClassDeclarationOrExpression") \
     F(FunctionDeclarationOrClassDeclarationOrVariableDeclaration, "FunctionDeclarationOrClassDeclarationOrVariableDeclaration") \
     F(FunctionExpression, "FunctionExpression") \
+    F(FunctionExpressionContents, "FunctionExpressionContents") \
+    F(FunctionOrMethodContents, "FunctionOrMethodContents") \
     F(Getter, "Getter") \
+    F(GetterContents, "GetterContents") \
     F(Identifier, "Identifier") \
     F(IdentifierExpression, "IdentifierExpression") \
     F(IdentifierName, "IdentifierName") \
     F(IfStatement, "IfStatement") \
     F(Import, "Import") \
     F(ImportDeclaration, "ImportDeclaration") \
     F(ImportDeclarationOrExportDeclarationOrStatement, "ImportDeclarationOrExportDeclarationOrStatement") \
     F(ImportNamespace, "ImportNamespace") \
     F(ImportSpecifier, "ImportSpecifier") \
     F(IterationStatement, "IterationStatement") \
     F(Label, "Label") \
     F(LabelledStatement, "LabelledStatement") \
+    F(LazyArrowExpressionWithExpression, "LazyArrowExpressionWithExpression") \
+    F(LazyArrowExpressionWithFunctionBody, "LazyArrowExpressionWithFunctionBody") \
+    F(LazyFunctionDeclaration, "LazyFunctionDeclaration") \
+    F(LazyFunctionExpression, "LazyFunctionExpression") \
+    F(LazyGetter, "LazyGetter") \
+    F(LazyMethod, "LazyMethod") \
+    F(LazySetter, "LazySetter") \
+    F(ListOfAssertedBoundName, "ListOfAssertedBoundName") \
+    F(ListOfAssertedDeclaredName, "ListOfAssertedDeclaredName") \
+    F(ListOfAssertedMaybePositionalParameterName, "ListOfAssertedMaybePositionalParameterName") \
     F(ListOfAssignmentTargetOrAssignmentTargetWithInitializer, "ListOfAssignmentTargetOrAssignmentTargetWithInitializer") \
     F(ListOfAssignmentTargetProperty, "ListOfAssignmentTargetProperty") \
     F(ListOfBindingProperty, "ListOfBindingProperty") \
     F(ListOfClassElement, "ListOfClassElement") \
     F(ListOfDirective, "ListOfDirective") \
     F(ListOfExportFromSpecifier, "ListOfExportFromSpecifier") \
     F(ListOfExportLocalSpecifier, "ListOfExportLocalSpecifier") \
     F(ListOfExpressionOrTemplateElement, "ListOfExpressionOrTemplateElement") \
-    F(ListOfIdentifierName, "ListOfIdentifierName") \
     F(ListOfImportDeclarationOrExportDeclarationOrStatement, "ListOfImportDeclarationOrExportDeclarationOrStatement") \
     F(ListOfImportSpecifier, "ListOfImportSpecifier") \
     F(ListOfObjectProperty, "ListOfObjectProperty") \
     F(ListOfOptionalBindingOrBindingWithInitializer, "ListOfOptionalBindingOrBindingWithInitializer") \
     F(ListOfOptionalSpreadElementOrExpression, "ListOfOptionalSpreadElementOrExpression") \
     F(ListOfParameter, "ListOfParameter") \
     F(ListOfStatement, "ListOfStatement") \
     F(ListOfSwitchCase, "ListOfSwitchCase") \
@@ -182,19 +205,16 @@ namespace frontend {
     F(MethodDefinition, "MethodDefinition") \
     F(Module, "Module") \
     F(NewExpression, "NewExpression") \
     F(NewTargetExpression, "NewTargetExpression") \
     F(ObjectAssignmentTarget, "ObjectAssignmentTarget") \
     F(ObjectBinding, "ObjectBinding") \
     F(ObjectExpression, "ObjectExpression") \
     F(ObjectProperty, "ObjectProperty") \
-    F(OptionalAssertedBlockScope, "OptionalAssertedBlockScope") \
-    F(OptionalAssertedParameterScope, "OptionalAssertedParameterScope") \
-    F(OptionalAssertedVarScope, "OptionalAssertedVarScope") \
     F(OptionalAssignmentTarget, "OptionalAssignmentTarget") \
     F(OptionalBinding, "OptionalBinding") \
     F(OptionalBindingIdentifier, "OptionalBindingIdentifier") \
     F(OptionalBindingOrBindingWithInitializer, "OptionalBindingOrBindingWithInitializer") \
     F(OptionalCatchClause, "OptionalCatchClause") \
     F(OptionalExpression, "OptionalExpression") \
     F(OptionalIdentifierName, "OptionalIdentifierName") \
     F(OptionalLabel, "OptionalLabel") \
@@ -202,24 +222,19 @@ namespace frontend {
     F(OptionalStatement, "OptionalStatement") \
     F(OptionalVariableDeclarationOrExpression, "OptionalVariableDeclarationOrExpression") \
     F(Parameter, "Parameter") \
     F(Program, "Program") \
     F(PropertyName, "PropertyName") \
     F(ReturnStatement, "ReturnStatement") \
     F(Script, "Script") \
     F(Setter, "Setter") \
+    F(SetterContents, "SetterContents") \
     F(ShorthandProperty, "ShorthandProperty") \
     F(SimpleAssignmentTarget, "SimpleAssignmentTarget") \
-    F(SkippableArrowExpression, "SkippableArrowExpression") \
-    F(SkippableFunctionDeclaration, "SkippableFunctionDeclaration") \
-    F(SkippableFunctionExpression, "SkippableFunctionExpression") \
-    F(SkippableGetter, "SkippableGetter") \
-    F(SkippableMethod, "SkippableMethod") \
-    F(SkippableSetter, "SkippableSetter") \
     F(SpreadElement, "SpreadElement") \
     F(SpreadElementOrExpression, "SpreadElementOrExpression") \
     F(Statement, "Statement") \
     F(StaticMemberAssignmentTarget, "StaticMemberAssignmentTarget") \
     F(StaticMemberExpression, "StaticMemberExpression") \
     F(Super, "Super") \
     F(SwitchCase, "SwitchCase") \
     F(SwitchDefault, "SwitchDefault") \
@@ -247,17 +262,17 @@ namespace frontend {
 
 enum class BinKind {
 #define EMIT_ENUM(name, _) name,
     FOR_EACH_BIN_KIND(EMIT_ENUM)
 #undef EMIT_ENUM
 };
 
 // The number of distinct values of BinKind.
-const size_t BINKIND_LIMIT = 183;
+const size_t BINKIND_LIMIT = 198;
 
 
 
 
 /**
  * The different variants of Binary AST string enums, as per
  * the specifications of Binary AST, as a single macro and
  * `enum class`.
@@ -269,93 +284,101 @@ const size_t BINKIND_LIMIT = 183;
  * ```c++
  * #define WITH_VARIANT(CPP_NAME, SPEC_NAME) ...
  * FOR_EACH_BIN_VARIANT(WITH_VARIANT)
  * ```
  *
  * (sorted by alphabetical order)
  */
 #define FOR_EACH_BIN_FIELD(F) \
-    F(Skip, "_skip") \
     F(Alternate, "alternate") \
     F(Arguments, "arguments") \
     F(Binding, "binding") \
     F(BindingScope, "bindingScope") \
     F(Body, "body") \
     F(BodyScope, "bodyScope") \
+    F(BoundNames, "boundNames") \
     F(Callee, "callee") \
-    F(CapturedNames, "capturedNames") \
     F(Cases, "cases") \
     F(CatchClause, "catchClause") \
     F(Consequent, "consequent") \
+    F(Contents, "contents") \
+    F(ContentsSkip, "contents_skip") \
     F(Declaration, "declaration") \
     F(Declarators, "declarators") \
+    F(DeclaredNames, "declaredNames") \
     F(DefaultBinding, "defaultBinding") \
     F(DefaultCase, "defaultCase") \
     F(Directives, "directives") \
     F(Discriminant, "discriminant") \
     F(Elements, "elements") \
     F(ExportedName, "exportedName") \
     F(Expression, "expression") \
     F(Finalizer, "finalizer") \
     F(Flags, "flags") \
     F(HasDirectEval, "hasDirectEval") \
+    F(Index, "index") \
     F(Init, "init") \
     F(IsAsync, "isAsync") \
+    F(IsCaptured, "isCaptured") \
+    F(IsFunctionNameCaptured, "isFunctionNameCaptured") \
     F(IsGenerator, "isGenerator") \
     F(IsPrefix, "isPrefix") \
+    F(IsSimpleParameterList, "isSimpleParameterList") \
     F(IsStatic, "isStatic") \
+    F(IsThisCaptured, "isThisCaptured") \
     F(Items, "items") \
     F(Kind, "kind") \
     F(Label, "label") \
     F(Left, "left") \
-    F(LexicallyDeclaredNames, "lexicallyDeclaredNames") \
+    F(Length, "length") \
     F(Method, "method") \
     F(ModuleSpecifier, "moduleSpecifier") \
     F(Name, "name") \
     F(NamedExports, "namedExports") \
     F(NamedImports, "namedImports") \
     F(NamespaceBinding, "namespaceBinding") \
     F(Object, "object") \
     F(Operand, "operand") \
     F(Operator, "operator") \
     F(Param, "param") \
-    F(ParameterNames, "parameterNames") \
+    F(ParamNames, "paramNames") \
     F(ParameterScope, "parameterScope") \
     F(Params, "params") \
     F(Pattern, "pattern") \
     F(PostDefaultCases, "postDefaultCases") \
     F(PreDefaultCases, "preDefaultCases") \
     F(Properties, "properties") \
     F(Property, "property") \
     F(RawValue, "rawValue") \
     F(Rest, "rest") \
     F(Right, "right") \
     F(Scope, "scope") \
-    F(Skipped, "skipped") \
     F(Statements, "statements") \
     F(Super, "super") \
     F(Tag, "tag") \
     F(Test, "test") \
     F(Update, "update") \
-    F(Value, "value") \
-    F(VarDeclaredNames, "varDeclaredNames")
+    F(Value, "value")
 
 enum class BinField {
 #define EMIT_ENUM(name, _) name,
     FOR_EACH_BIN_FIELD(EMIT_ENUM)
 #undef EMIT_ENUM
 };
 
 // The number of distinct values of BinField.
-const size_t BINFIELD_LIMIT = 64;
+const size_t BINFIELD_LIMIT = 69;
 
 
 
 #define FOR_EACH_BIN_VARIANT(F) \
+    F(AssertedDeclaredKindConstLexical, "const lexical") \
+    F(AssertedDeclaredKindNonConstLexical, "non-const lexical") \
+    F(AssertedDeclaredKindOrVariableDeclarationKindVar, "var") \
     F(BinaryOperatorBitAnd, "&") \
     F(BinaryOperatorBitOr, "|") \
     F(BinaryOperatorBitXor, "^") \
     F(BinaryOperatorComma, ",") \
     F(BinaryOperatorDiv, "/") \
     F(BinaryOperatorEq, "==") \
     F(BinaryOperatorGeqThan, ">=") \
     F(BinaryOperatorGreaterThan, ">") \
@@ -391,27 +414,26 @@ const size_t BINFIELD_LIMIT = 64;
     F(UnaryOperatorBitNot, "~") \
     F(UnaryOperatorDelete, "delete") \
     F(UnaryOperatorNot, "!") \
     F(UnaryOperatorTypeof, "typeof") \
     F(UnaryOperatorVoid, "void") \
     F(UpdateOperatorDecr, "--") \
     F(UpdateOperatorIncr, "++") \
     F(VariableDeclarationKindConst, "const") \
-    F(VariableDeclarationKindLet, "let") \
-    F(VariableDeclarationKindVar, "var")
+    F(VariableDeclarationKindLet, "let")
 
 enum class BinVariant {
 #define EMIT_ENUM(name, _) name,
     FOR_EACH_BIN_VARIANT(EMIT_ENUM)
 #undef EMIT_ENUM
 };
 
 // The number of distinct values of BinVariant.
-const size_t BINVARIANT_LIMIT = 47;
+const size_t BINVARIANT_LIMIT = 49;
 
 
 
 /**
  * Return a string describing a `BinKind`.
  */
 const char* describeBinKind(const BinKind& kind);
 
--- a/js/src/frontend/BinTokenReaderMultipart.cpp
+++ b/js/src/frontend/BinTokenReaderMultipart.cpp
@@ -19,17 +19,17 @@
 #include "js/Result.h"
 
 namespace js {
 namespace frontend {
 
 // The magic header, at the start of every binjs file.
 const char MAGIC_HEADER[] = "BINJS";
 // The latest format version understood by this tokenizer.
-const uint32_t MAGIC_FORMAT_VERSION = 0;
+const uint32_t MAGIC_FORMAT_VERSION = 1;
 
 // The headers at the start of each section of the binjs file.
 const char SECTION_HEADER_GRAMMAR[] = "[GRAMMAR]";
 const char SECTION_HEADER_STRINGS[] = "[STRINGS]";
 const char SECTION_HEADER_TREE[] = "[TREE]";
 
 // The (only) internal compression mechanism understood by this parser.
 const char COMPRESSION_IDENTITY[] = "identity;";
--- a/js/src/frontend/BinTokenReaderMultipart.h
+++ b/js/src/frontend/BinTokenReaderMultipart.h
@@ -173,16 +173,23 @@ class MOZ_STACK_CLASS BinTokenReaderMult
      * The `guard` is dedicated to ensuring that reading the list has consumed
      * exactly all the bytes from that tuple. The `guard` MUST therefore be
      * destroyed at the point where the caller has reached the end of the tuple.
      * If the caller has consumed too few/too many bytes, this will be reported
      * in the call go `guard.done()`.
      */
     MOZ_MUST_USE JS::Result<Ok> enterUntaggedTuple(AutoTuple& guard);
 
+    /**
+     * Read a single unsigned long.
+     */
+    MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong() {
+        return readInternalUint32();
+    }
+
   private:
     /**
      * Read a single uint32_t.
      */
     MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
 
   private:
     // A mapping grammar index => BinKind, as defined by the [GRAMMAR]
--- a/js/src/frontend/BinTokenReaderTester.h
+++ b/js/src/frontend/BinTokenReaderTester.h
@@ -197,16 +197,23 @@ class MOZ_STACK_CLASS BinTokenReaderTest
      * If the caller has consumed too few/too many bytes, this will be reported
      * in the call go `guard.done()`.
      *
      * @return out If the header of the tuple is invalid.
      */
     MOZ_MUST_USE JS::Result<Ok> enterUntaggedTuple(AutoTuple& guard);
 
     /**
+     * Read a single unsigned long.
+     */
+    MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong() {
+        return readInternalUint32();
+    }
+
+    /**
      * Read a single uint32_t.
      */
     MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
 
   public:
     // The following classes are used whenever we encounter a tuple/tagged tuple/list
     // to make sure that:
     //
--- a/js/src/frontend/binsource/Cargo.toml
+++ b/js/src/frontend/binsource/Cargo.toml
@@ -1,13 +1,13 @@
 [package]
 name = "binsource"
 version = "0.1.0"
 authors = ["David Teller <D.O.Teller@gmail.com>"]
 
 [dependencies]
-binjs_meta = "^0.3.8"
+binjs_meta = "^0.3.10"
 clap = "^2"
 env_logger = "^0.5.6"
 itertools = "^0.7.6"
 log = "0.4.1"
 yaml-rust = "^0.4"
 webidl = "^0.6.0"
--- a/js/src/frontend/binsource/src/main.rs
+++ b/js/src/frontend/binsource/src/main.rs
@@ -71,16 +71,18 @@ struct NodeRules {
 
     /// Default value for the optional field.
     default_value: Option<Rc<String>>,
 
     /// Extra parameters for the method.
     extra_params: Option<Rc<String>>,
 
     /// Extra arguments passed to the method when parsing this interface.
+    /// For ListOf* interfaces, this arguments are passed to each item.
+    /// For sum interface, this arguments are passed to each interface.
     extra_args: Option<Rc<String>>,
 
     /// Things to add before calling the method when the optional field has
     /// value.
     some_before: Option<Rc<String>>,
 
     /// Things to add after calling the method when the optional field has
     /// value.
@@ -1368,16 +1370,25 @@ impl CPPExporter {
                     if needs_block {
                         (Some(format!("double {var_name};", var_name = var_name)),
                             Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readDouble());", var_name = var_name)))
                     } else {
                         (None,
                             Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readDouble());", var_name = var_name)))
                     }
                 }
+                Some(IsNullable { is_nullable: false, content: Primitive::UnsignedLong }) => {
+                    if needs_block {
+                        (Some(format!("uint32_t {var_name};", var_name = var_name)),
+                            Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readUnsignedLong());", var_name = var_name)))
+                    } else {
+                        (None,
+                            Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readUnsignedLong());", var_name = var_name)))
+                    }
+                }
                 Some(IsNullable { is_nullable: false, content: Primitive::Boolean }) => {
                     if needs_block {
                         (Some(format!("bool {var_name};", var_name = var_name)),
                         Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readBool());", var_name = var_name)))
                     } else {
                         (None,
                         Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readBool());", var_name = var_name)))
                     }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1145,16 +1145,51 @@ GCRuntime::setZeal(uint8_t zeal, uint32_
     } else {
         zealModeBits = 0;
     }
     zealFrequency = frequency;
     nextScheduled = schedule ? frequency : 0;
 }
 
 void
+GCRuntime::unsetZeal(uint8_t zeal)
+{
+    MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
+    ZealMode zealMode = ZealMode(zeal);
+
+    if (temporaryAbortIfWasmGc(rt->mainContextFromOwnThread())) {
+        return;
+    }
+
+    if (!hasZealMode(zealMode)) {
+        return;
+    }
+
+    if (verifyPreData) {
+        VerifyBarriers(rt, PreBarrierVerifier);
+    }
+
+    if (zealMode == ZealMode::GenerationalGC) {
+        evictNursery(JS::gcreason::DEBUG_GC);
+        nursery().leaveZealMode();
+    }
+
+    clearZealMode(zealMode);
+
+    if (zealModeBits == 0) {
+        if (isIncrementalGCInProgress()) {
+            finishGC(JS::gcreason::DEBUG_GC);
+        }
+
+        zealFrequency = 0;
+        nextScheduled = 0;
+    }
+}
+
+void
 GCRuntime::setNextScheduled(uint32_t count)
 {
     nextScheduled = count;
 }
 
 using CharRange = mozilla::Range<const char>;
 using CharRangeVector = Vector<CharRange, 0, SystemAllocPolicy>;
 
@@ -2611,20 +2646,20 @@ Zone::prepareForCompacting()
 void
 GCRuntime::sweepTypesAfterCompacting(Zone* zone)
 {
     zone->beginSweepTypes(releaseObservedTypes && !zone->isPreservingCode());
 
     AutoClearTypeInferenceStateOnOOM oom(zone);
 
     for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
-        AutoSweepTypeScript sweep(script, &oom);
+        AutoSweepTypeScript sweep(script, oom);
     }
     for (auto group = zone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
-        AutoSweepObjectGroup sweep(group, &oom);
+        AutoSweepObjectGroup sweep(group, oom);
     }
 
     zone->types.endSweep(rt);
 }
 
 void
 GCRuntime::sweepZoneAfterCompacting(Zone* zone)
 {
@@ -6234,34 +6269,34 @@ static void
 SweepThing(Shape* shape)
 {
     if (!shape->isMarkedAny()) {
         shape->sweep();
     }
 }
 
 static void
-SweepThing(JSScript* script, AutoClearTypeInferenceStateOnOOM* oom)
+SweepThing(JSScript* script, AutoClearTypeInferenceStateOnOOM& oom)
 {
     AutoSweepTypeScript sweep(script, oom);
 }
 
 static void
-SweepThing(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM* oom)
+SweepThing(ObjectGroup* group, AutoClearTypeInferenceStateOnOOM& oom)
 {
     AutoSweepObjectGroup sweep(group, oom);
 }
 
 template <typename T, typename... Args>
 static bool
-SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args... args)
+SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args&&... args)
 {
     while (Arena* arena = *arenasToSweep) {
         for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
-            SweepThing(i.get<T>(), args...);
+            SweepThing(i.get<T>(), std::forward<Args>(args)...);
         }
 
         *arenasToSweep = (*arenasToSweep)->next;
         AllocKind kind = MapTypeToFinalizeKind<T>::kind;
         sliceBudget.step(Arena::thingsPerArena(kind));
         if (sliceBudget.isOverBudget()) {
             return false;
         }
@@ -6283,21 +6318,21 @@ GCRuntime::sweepTypeInformation(FreeOp* 
 
     gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::SWEEP_COMPARTMENTS);
     gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_TYPES);