Merge inbound to mozilla-central r=merge a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 12 Jan 2018 23:59:06 +0200
changeset 450918 f5b4481c9fd50becb35cef02b599198b766fb1bb
parent 450917 bec7964d4dd08ba2204c37137c38b2e4f277b093 (current diff)
parent 450872 d16fe1a6931e5dadaeddafd626ef5bc0bb8bf8ea (diff)
child 450919 9da066f5cf32327b5bdfeb66e3e58382428b2bc8
child 450970 946879222ce01e45f259a751e3b11fd7468d0d97
push id8543
push userryanvm@gmail.com
push dateTue, 16 Jan 2018 14:33:22 +0000
treeherdermozilla-beta@a6525ed16a32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
f5b4481c9fd5 / 59.0a1 / 20180112220334 / files
nightly linux64
f5b4481c9fd5 / 59.0a1 / 20180112220334 / files
nightly mac
f5b4481c9fd5 / 59.0a1 / 20180112220334 / files
nightly win32
f5b4481c9fd5 / 59.0a1 / 20180112220334 / files
nightly win64
f5b4481c9fd5 / 59.0a1 / 20180112220334 / 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 r=merge a=merge
modules/libpref/init/all.js
toolkit/components/filepicker/content/filepicker.js
toolkit/components/filepicker/content/filepicker.xul
toolkit/components/filepicker/jar.mn
toolkit/components/filepicker/moz.build
toolkit/components/filepicker/nsFilePicker.js
toolkit/components/filepicker/nsFilePicker.manifest
toolkit/components/filepicker/nsFileView.cpp
toolkit/components/filepicker/nsIFileView.idl
toolkit/components/filepicker/test/unit/.eslintrc.js
toolkit/components/filepicker/test/unit/test_filecomplete.js
toolkit/components/filepicker/test/unit/xpcshell.ini
toolkit/locales/en-US/chrome/global/filepicker.dtd
toolkit/themes/linux/global/filepicker.css
toolkit/themes/linux/global/filepicker/Filepicker.png
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3144,34 +3144,17 @@ var BrowserOnClick = {
       // There is no button for reporting errors since Google doesn't currently
       // provide a URL endpoint for these reports.
     } else if (reason === "harmful") {
       title = gNavigatorBundle.getString("safebrowsing.reportedHarmfulSite");
       // There is no button for reporting errors since Google doesn't currently
       // provide a URL endpoint for these reports.
     }
 
-    let notificationBox = gBrowser.getNotificationBox();
-    let value = "blocked-badware-page";
-
-    let previousNotification = notificationBox.getNotificationWithValue(value);
-    if (previousNotification) {
-      notificationBox.removeNotification(previousNotification);
-    }
-
-    let notification = notificationBox.appendNotification(
-      title,
-      value,
-      "chrome://global/skin/icons/blacklist_favicon.png",
-      notificationBox.PRIORITY_CRITICAL_HIGH,
-      buttons
-    );
-    // Persist the notification until the user removes so it
-    // doesn't get removed on redirects.
-    notification.persistence = -1;
+    SafeBrowsingNotificationBox.show(title, buttons);
   },
 };
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
@@ -4583,16 +4566,18 @@ var XULBrowserWindow = {
       URLBarSetURI(aLocationURI);
 
       BookmarkingUI.onLocationChange();
 
       gIdentityHandler.onLocationChange();
 
       BrowserPageActions.onLocationChange();
 
+      SafeBrowsingNotificationBox.onLocationChange(aLocationURI);
+
       gTabletModePageCounter.inc();
 
       // Utility functions for disabling find
       var shouldDisableFind = function(aDocument) {
         let docElt = aDocument.documentElement;
         return docElt && docElt.getAttribute("disablefastfind") == "true";
       };
 
@@ -8903,16 +8888,63 @@ var AboutPrivateBrowsingListener = {
     window.messageManager.addMessageListener(
       "AboutPrivateBrowsing:DontShowIntroPanelAgain",
       msg => {
         TrackingProtection.dontShowIntroPanelAgain();
     });
   }
 };
 
+const SafeBrowsingNotificationBox = {
+  _currentURIBaseDomain: null,
+  show(title, buttons) {
+    let uri = gBrowser.currentURI;
+
+    // start tracking host so that we know when we leave the domain
+    this._currentURIBaseDomain = Services.eTLD.getBaseDomain(uri);
+
+    let notificationBox = gBrowser.getNotificationBox();
+    let value = "blocked-badware-page";
+
+    let previousNotification = notificationBox.getNotificationWithValue(value);
+    if (previousNotification) {
+      notificationBox.removeNotification(previousNotification);
+    }
+
+    let notification = notificationBox.appendNotification(
+      title,
+      value,
+      "chrome://global/skin/icons/blacklist_favicon.png",
+      notificationBox.PRIORITY_CRITICAL_HIGH,
+      buttons
+    );
+    // Persist the notification until the user removes so it
+    // doesn't get removed on redirects.
+    notification.persistence = -1;
+  },
+  onLocationChange(aLocationURI) {
+    // take this to represent that you haven't visited a bad place
+    if (!this._currentURIBaseDomain) {
+      return;
+    }
+
+    let newURIBaseDomain = Services.eTLD.getBaseDomain(aLocationURI);
+
+    if (newURIBaseDomain !== this._currentURIBaseDomain) {
+      let notificationBox = gBrowser.getNotificationBox();
+      let notification = notificationBox.getNotificationWithValue("blocked-badware-page");
+      if (notification) {
+        notificationBox.removeNotification(notification, false);
+      }
+
+      this._currentURIBaseDomain = null;
+    }
+  }
+};
+
 function TabModalPromptBox(browser) {
   this._weakBrowserRef = Cu.getWeakReference(browser);
 }
 
 TabModalPromptBox.prototype = {
   _promptCloseCallback(onCloseCallback, principalToAllowFocusFor, allowFocusCheckbox, ...args) {
     if (principalToAllowFocusFor && allowFocusCheckbox &&
         allowFocusCheckbox.checked) {
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -154,22 +154,29 @@ var gUIStateBeforeReset = {
   uiCustomizationState: null,
   drawInTitlebar: null,
   extraDragSpace: null,
   currentTheme: null,
   uiDensity: null,
   autoTouchMode: null,
 };
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "gDebuggingEnabled", kPrefCustomizationDebug, false,
+  (pref, oldVal, newVal) => {
+    if (typeof log != "undefined") {
+      log.maxLogLevel = newVal ? "all" : "log";
+    }
+  }
+);
+
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   Cu.import("resource://gre/modules/Console.jsm", scope);
-  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
-    maxLogLevel: debug ? "all" : "log",
+    maxLogLevel: gDebuggingEnabled ? "all" : "log",
     prefix: "CustomizableUI",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
 var CustomizableUIInternal = {
   initialize() {
     log.debug("Initializing");
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -2312,20 +2312,16 @@ CustomizeMode.prototype = {
     return aElement.closest(areas.map(a => "#" + CSS.escape(a)).join(","));
   },
 
   _getDragOverNode(aEvent, aAreaElement, aAreaType, aDraggedItemId) {
     let expectedParent = aAreaElement.customizationTarget || aAreaElement;
     if (!expectedParent.contains(aEvent.target)) {
       return expectedParent;
     }
-    // Our tests are stupid. Cope:
-    if (!aEvent.clientX && !aEvent.clientY) {
-      return aEvent.target;
-    }
     // Offset the drag event's position with the offset to the center of
     // the thing we're dragging
     let dragX = aEvent.clientX - this._dragOffset.x;
     let dragY = aEvent.clientY - this._dragOffset.y;
 
     // Ensure this is within the container
     let boundsContainer = expectedParent;
     let bounds = this._dwu.getBoundsWithoutFlushing(boundsContainer);
--- a/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
+++ b/browser/components/customizableui/test/browser_876926_customize_mode_wrapping.js
@@ -9,53 +9,60 @@ const kAPIWidgetId = "feed-button";
 const kPanel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL;
 const kToolbar = CustomizableUI.AREA_NAVBAR;
 const kVisiblePalette = "customization-palette";
 
 function checkWrapper(id) {
   is(document.querySelectorAll("#wrapper-" + id).length, 1, "There should be exactly 1 wrapper for " + id + " in the customizing window.");
 }
 
-async function ensureVisibleIfInPalette(node) {
-    if (node.parentNode.parentNode == gNavToolbox.palette) {
-      node.scrollIntoView();
-      window.QueryInterface(Ci.nsIInterfaceRequestor);
-      let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
-      await BrowserTestUtils.waitForCondition(() => {
-        let nodeBounds = dwu.getBoundsWithoutFlushing(node);
-        let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette);
-        return nodeBounds.top >= paletteBounds.top && nodeBounds.bottom <= paletteBounds.bottom;
-      });
+async function ensureVisible(node) {
+  let isInPalette = node.parentNode.parentNode == gNavToolbox.palette;
+  if (isInPalette) {
+    node.scrollIntoView();
+  }
+  window.QueryInterface(Ci.nsIInterfaceRequestor);
+  let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
+  await BrowserTestUtils.waitForCondition(() => {
+    let nodeBounds = dwu.getBoundsWithoutFlushing(node);
+    if (isInPalette) {
+      let paletteBounds = dwu.getBoundsWithoutFlushing(gNavToolbox.palette);
+      if (!(nodeBounds.top >= paletteBounds.top && nodeBounds.bottom <= paletteBounds.bottom)) {
+        return false;
+      }
     }
+    return nodeBounds.height && nodeBounds.width;
+  });
 }
 
 var move = {
   "drag": async function(id, target) {
     let targetNode = document.getElementById(target);
     if (targetNode.customizationTarget) {
       targetNode = targetNode.customizationTarget;
     }
     let nodeToMove = document.getElementById(id);
-    await ensureVisibleIfInPalette(nodeToMove);
-    simulateItemDrag(nodeToMove, targetNode);
+    await ensureVisible(nodeToMove);
+
+    simulateItemDrag(nodeToMove, targetNode, "end");
   },
   "dragToItem": async function(id, target) {
     let targetNode = document.getElementById(target);
     if (targetNode.customizationTarget) {
       targetNode = targetNode.customizationTarget;
     }
     let items = targetNode.querySelectorAll("toolbarpaletteitem");
     if (target == kPanel) {
       targetNode = items[items.length - 1];
     } else {
       targetNode = items[0];
     }
     let nodeToMove = document.getElementById(id);
-    await ensureVisibleIfInPalette(nodeToMove);
-    simulateItemDrag(nodeToMove, targetNode);
+    await ensureVisible(nodeToMove);
+    simulateItemDrag(nodeToMove, targetNode, "start");
   },
   "API": function(id, target) {
     if (target == kVisiblePalette) {
       return CustomizableUI.removeWidgetFromArea(id);
     }
     return CustomizableUI.addWidgetToArea(id, target, null);
   }
 };
@@ -145,16 +152,21 @@ async function checkPanel(id, method) {
 }
 
 async function checkPalette(id, method) {
   // Move back to palette:
   await move[method](id, kVisiblePalette);
   ok(CustomizableUI.inDefaultState, "Should end in default state");
   let visibleChildren = gCustomizeMode.visiblePalette.children;
   let expectedChild = method == "dragToItem" ? visibleChildren[0] : visibleChildren[visibleChildren.length - 1];
+  // Items dragged to the end of the palette should be the final item. That they're the penultimate
+  // item when dragged is tracked in bug 1395950. Once that's fixed, this hack can be removed.
+  if (method == "drag") {
+    expectedChild = expectedChild.previousElementSibling;
+  }
   is(expectedChild.firstChild.id, id, "Widget " + id + " was moved using " + method + " and should now be wrapped in palette in customizing window.");
   if (id == kXULWidgetId) {
     ok(otherWin.gNavToolbox.palette.querySelector("#" + id), "Widget " + id + " should be in invisible palette in other window.");
   }
   checkWrapper(id);
 }
 
 // This test needs a XUL button that's in the palette by default. No such
--- a/browser/components/customizableui/test/browser_878452_drag_to_panel.js
+++ b/browser/components/customizableui/test/browser_878452_drag_to_panel.js
@@ -10,17 +10,17 @@ add_task(async function() {
   await startCustomizing();
   let btn = document.getElementById("feed-button");
   let placements = getAreaWidgetIds(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
 
   let lastButtonIndex = placements.length - 1;
   let lastButton = placements[lastButtonIndex];
   let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["feed-button", lastButton]);
   let lastButtonNode = document.getElementById(lastButton);
-  simulateItemDrag(btn, lastButtonNode);
+  simulateItemDrag(btn, lastButtonNode, "start");
   assertAreaPlacements(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, placementsAfterInsert);
   ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
   let palette = document.getElementById("customization-palette");
   simulateItemDrag(btn, palette);
   CustomizableUI.removeWidgetFromArea("cui-panel-item-to-drag-to");
   ok(CustomizableUI.inDefaultState, "Should be in default state again.");
 });
 
@@ -58,12 +58,13 @@ add_task(async function() {
   simulateItemDrag(btn, panel);
   assertAreaPlacements(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, placementsAfterAppend);
   ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
   let palette = document.getElementById("customization-palette");
   simulateItemDrag(btn, palette);
   assertAreaPlacements(panel.id, []);
 });
 
-add_task(async function asyncCleanup() {
+registerCleanupFunction(async function asyncCleanup() {
+  CustomizableUI.destroyWidget("cui-panel-item-to-drag-to");
   await endCustomizing();
   await resetCustomization();
 });
--- a/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
+++ b/browser/components/customizableui/test/browser_918049_skipintoolbarset_dnd.js
@@ -13,23 +13,24 @@ add_task(async function() {
   skippedItem = document.createElement("toolbarbutton");
   skippedItem.id = "test-skipintoolbarset-item";
   skippedItem.setAttribute("label", "Test");
   skippedItem.setAttribute("skipintoolbarset", "true");
   skippedItem.setAttribute("removable", "true");
   navbar.customizationTarget.appendChild(skippedItem);
   let libraryButton = document.getElementById("library-button");
   await startCustomizing();
+  await waitForElementShown(skippedItem);
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
-  simulateItemDrag(skippedItem, libraryButton);
+  simulateItemDrag(skippedItem, libraryButton, "start");
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
   let skippedItemWrapper = skippedItem.parentNode;
   is(skippedItemWrapper.nextSibling && skippedItemWrapper.nextSibling.id,
      libraryButton.parentNode.id, "Should be next to library button");
-  simulateItemDrag(libraryButton, skippedItem);
+  simulateItemDrag(libraryButton, skippedItem, "start");
   let libraryWrapper = libraryButton.parentNode;
   is(libraryWrapper.nextSibling && libraryWrapper.nextSibling.id,
      skippedItem.parentNode.id, "Should be next to skipintoolbarset item");
   ok(CustomizableUI.inDefaultState, "Should still be in default state");
 });
 
 add_task(async function asyncCleanup() {
   await endCustomizing();
--- a/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
+++ b/browser/components/customizableui/test/browser_968565_insert_before_hidden_items.js
@@ -40,17 +40,17 @@ add_task(async function() {
   // Make sure we have some hidden items at the end of the nav-bar.
   navbar.insertItem(hidden1.id);
   navbar.insertItem(hidden2.id);
 
   // Drag an item and drop it onto the nav-bar customization target, but
   // not over a particular item.
   await startCustomizing();
   let homeButton = document.getElementById("home-button");
-  simulateItemDrag(homeButton, navbar.customizationTarget);
+  simulateItemDrag(homeButton, navbar.customizationTarget, "end");
 
   await endCustomizing();
 
   is(homeButton.previousSibling.id, lastVisible.id,
      "The downloads button should be placed after the last visible item.");
 
   await resetCustomization();
 });
--- a/browser/components/customizableui/test/browser_newtab_button_customizemode.js
+++ b/browser/components/customizableui/test/browser_newtab_button_customizemode.js
@@ -27,46 +27,49 @@ function assertNewTabButton(which) {
   }
 }
 
 /**
  * Add and remove items *after* the new tab button in customize mode.
  */
 add_task(async function addremove_after_newtab_customizemode() {
   await startCustomizing();
-  simulateItemDrag(document.getElementById("home-button"),
-                   kGlobalNewTabButton.parentNode.nextElementSibling);
+  await waitForElementShown(kGlobalNewTabButton);
+  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "end");
   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should have the adjacent newtab attribute");
   await endCustomizing();
   assertNewTabButton("inner");
 
   await startCustomizing();
-  simulateItemDrag(document.getElementById("home-button"),
-    document.getElementById("stop-reload-button").parentNode.nextElementSibling);
+  let dropTarget = document.getElementById("stop-reload-button");
+  await waitForElementShown(dropTarget);
+  simulateItemDrag(document.getElementById("home-button"), dropTarget, "end");
   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should still have the adjacent newtab attribute");
   await endCustomizing();
   assertNewTabButton("inner");
   ok(CustomizableUI.inDefaultState, "Should be in default state");
 });
 
 /**
  * Add and remove items *before* the new tab button in customize mode.
  */
 add_task(async function addremove_before_newtab_customizemode() {
   await startCustomizing();
-  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton);
+  await waitForElementShown(kGlobalNewTabButton);
+  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "start");
   ok(!gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should no longer have the adjacent newtab attribute");
   await endCustomizing();
   assertNewTabButton("global");
   await startCustomizing();
-  simulateItemDrag(document.getElementById("home-button"),
-    document.getElementById("stop-reload-button").parentNode.nextElementSibling);
+  let dropTarget = document.getElementById("stop-reload-button");
+  await waitForElementShown(dropTarget);
+  simulateItemDrag(document.getElementById("home-button"), dropTarget, "end");
   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should have the adjacent newtab attribute again");
   await endCustomizing();
   assertNewTabButton("inner");
   ok(CustomizableUI.inDefaultState, "Should be in default state");
 });
 
 /**
@@ -105,17 +108,18 @@ add_task(async function addremove_before
   ok(CustomizableUI.inDefaultState, "Should be in default state");
 });
 
 /**
   * Reset to defaults in customize mode to see if that doesn't break things.
   */
 add_task(async function reset_before_newtab_customizemode() {
   await startCustomizing();
-  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton);
+  await waitForElementShown(kGlobalNewTabButton);
+  simulateItemDrag(document.getElementById("home-button"), kGlobalNewTabButton, "start");
   ok(!gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should no longer have the adjacent newtab attribute");
   await endCustomizing();
   assertNewTabButton("global");
   await startCustomizing();
   await gCustomizeMode.reset();
   ok(gBrowser.tabContainer.hasAttribute("hasadjacentnewtabbutton"),
     "tabs should have the adjacent newtab attribute again");
--- a/browser/components/customizableui/test/browser_remove_customized_specials.js
+++ b/browser/components/customizableui/test/browser_remove_customized_specials.js
@@ -10,19 +10,20 @@ add_task(async function() {
   await startCustomizing();
   CustomizableUI.addWidgetToArea("spring", "nav-bar", 5);
   await gCustomizeMode.reset();
   let springs = document.querySelectorAll("#nav-bar toolbarspring");
   let lastSpring = springs[springs.length - 1];
   let expectedPlacements = CustomizableUI.getWidgetIdsInArea("nav-bar");
   info("Placements before drag: " + expectedPlacements.join(","));
   let lastItem = document.getElementById(expectedPlacements[expectedPlacements.length - 1]);
-  simulateItemDrag(lastSpring, lastItem);
+  await waitForElementShown(lastItem);
+  simulateItemDrag(lastSpring, lastItem, "end");
   expectedPlacements.splice(expectedPlacements.indexOf(lastSpring.id), 1);
-  expectedPlacements.splice(expectedPlacements.length - 1, 0, lastSpring.id);
+  expectedPlacements.push(lastSpring.id);
   let actualPlacements = CustomizableUI.getWidgetIdsInArea("nav-bar");
   // Log these separately because Assert.deepEqual truncates the stringified versions...
   info("Actual placements: " + actualPlacements.join(","));
   info("Expected placements: " + expectedPlacements.join(","));
   Assert.deepEqual(expectedPlacements, actualPlacements, "Should be able to move spring");
   await gCustomizeMode.reset();
   await endCustomizing();
 });
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -164,18 +164,32 @@ function todoAssertAreaPlacements(areaId
   todo(isPassing, "The area placements for " + areaId +
                   " should equal the expected placements.");
 }
 
 function getAreaWidgetIds(areaId) {
   return CustomizableUI.getWidgetIdsInArea(areaId);
 }
 
-function simulateItemDrag(aToDrag, aTarget) {
-  synthesizeDrop(aToDrag.parentNode, aTarget);
+function simulateItemDrag(aToDrag, aTarget, aEvent = {}) {
+  let ev = aEvent;
+  if (ev == "end" || ev == "start") {
+    let win = aTarget.ownerGlobal;
+    win.QueryInterface(Ci.nsIInterfaceRequestor);
+    const dwu = win.getInterface(Ci.nsIDOMWindowUtils);
+    let bounds = dwu.getBoundsWithoutFlushing(aTarget);
+    if (ev == "end") {
+      ev = {clientX: bounds.right - 2, clientY: bounds.bottom - 2};
+    } else {
+      ev = {clientX: bounds.left + 2, clientY: bounds.top + 2};
+    }
+  }
+  ev._domDispatchOnly = true;
+  synthesizeDrop(aToDrag.parentNode, aTarget, null, null,
+                 aToDrag.ownerGlobal, aTarget.ownerGlobal, ev);
 }
 
 function endCustomizing(aWindow = window) {
   if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
     return true;
   }
   return new Promise(resolve => {
     function onCustomizationEnds() {
@@ -492,17 +506,21 @@ function checkContextMenu(aContextMenu, 
       is(menuItemDisabled, !aExpectedEntries[i][1], "disabled state for " + selector);
     } catch (e) {
       ok(false, "Exception when checking context menu: " + e);
     }
   }
 }
 
 function waitForOverflowButtonShown(win = window) {
+  let ov = win.document.getElementById("nav-bar-overflow-button");
+  let icon = win.document.getAnonymousElementByAttribute(ov, "class", "toolbarbutton-icon");
+  return waitForElementShown(icon);
+}
+function waitForElementShown(element) {
+  let win = element.ownerGlobal;
   let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
   return BrowserTestUtils.waitForCondition(() => {
     info("Waiting for overflow button to have non-0 size");
-    let ov = win.document.getElementById("nav-bar-overflow-button");
-    let icon = win.document.getAnonymousElementByAttribute(ov, "class", "toolbarbutton-icon");
-    let bounds = dwu.getBoundsWithoutFlushing(icon);
+    let bounds = dwu.getBoundsWithoutFlushing(element);
     return bounds.width > 0 && bounds.height > 0;
   });
 }
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -225,19 +225,16 @@
 @RESPATH@/components/dom_xul.xpt
 @RESPATH@/components/dom_presentation.xpt
 @RESPATH@/components/downloads.xpt
 @RESPATH@/components/editor.xpt
 @RESPATH@/components/extensions.xpt
 @RESPATH@/components/exthandler.xpt
 @RESPATH@/components/fastfind.xpt
 @RESPATH@/components/feeds.xpt
-#ifdef MOZ_GTK
-@RESPATH@/components/filepicker.xpt
-#endif
 @RESPATH@/components/gfx.xpt
 @RESPATH@/components/html5.xpt
 @RESPATH@/components/htmlparser.xpt
 @RESPATH@/components/imglib2.xpt
 @RESPATH@/components/inspector.xpt
 @RESPATH@/components/intl.xpt
 @RESPATH@/components/jar.xpt
 @RESPATH@/components/jsdebugger.xpt
@@ -399,20 +396,16 @@
 @RESPATH@/components/nsLoginManagerPrompter.js
 @RESPATH@/components/storage-json.js
 @RESPATH@/components/crypto-SDR.js
 @RESPATH@/components/TooltipTextProvider.js
 @RESPATH@/components/TooltipTextProvider.manifest
 @RESPATH@/components/webvtt.xpt
 @RESPATH@/components/WebVTT.manifest
 @RESPATH@/components/WebVTTParserWrapper.js
-#ifdef MOZ_GTK
-@RESPATH@/components/nsFilePicker.manifest
-@RESPATH@/components/nsFilePicker.js
-#endif
 @RESPATH@/components/nsHelperAppDlg.manifest
 @RESPATH@/components/nsHelperAppDlg.js
 @RESPATH@/components/NetworkGeolocationProvider.manifest
 @RESPATH@/components/NetworkGeolocationProvider.js
 @RESPATH@/components/extensions.manifest
 @RESPATH@/components/EditorUtils.manifest
 @RESPATH@/components/EditorUtils.js
 @RESPATH@/components/addonManager.js
--- a/build/unix/build-gcc/build-gcc.sh
+++ b/build/unix/build-gcc/build-gcc.sh
@@ -107,22 +107,44 @@ build_binutils() {
   make install $make_flags DESTDIR=$root_dir
   export PATH=$root_dir/${prefix-/tools/gcc}/bin:$PATH
   popd
 }
 
 build_gcc() {
   mkdir $root_dir/gcc-objdir
   pushd $root_dir/gcc-objdir
-  ../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --enable-languages=c,c++  --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --disable-initfini-array --with-sysroot=/
+  ../gcc-$gcc_version/configure --prefix=${prefix-/tools/gcc} --enable-languages=c,c++  --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro --with-sysroot=/
   make $make_flags
   make $make_flags install DESTDIR=$root_dir
 
   cd $root_dir/tools
   ln -s gcc gcc/bin/cc
+
+  if [ -f /etc/debian_version ]; then
+    cp -v /usr/lib32/crt*.o /usr/lib32/lib*_nonshared.a gcc/lib32
+    cp -v /usr/lib/x86_64-linux-gnu/crt*.o /usr/lib/x86_64-linux-gnu/lib*_nonshared.a gcc/lib64
+    cat > gcc/lib32/libc.so <<-EOF
+	OUTPUT_FORMAT(elf32-i386)
+	GROUP ( libc.so.6 libc_nonshared.a  AS_NEEDED ( ld-linux.so.2 ) )
+	EOF
+    cat > gcc/lib32/libpthread.so <<-EOF
+	OUTPUT_FORMAT(elf32-i386)
+	GROUP ( libpthread.so.0 libpthread_nonshared.a )
+	EOF
+    cat > gcc/lib64/libc.so <<-EOF
+	OUTPUT_FORMAT(elf64-x86-64)
+	GROUP ( libc.so.6 libc_nonshared.a  AS_NEEDED ( ld-linux-x86-64.so.2 ) )
+	EOF
+    cat > gcc/lib64/libpthread.so <<-EOF
+	OUTPUT_FORMAT(elf64-x86-64)
+	GROUP ( libpthread.so.0 libpthread_nonshared.a )
+	EOF
+  fi
+
   tar caf $root_dir/gcc.tar.xz gcc/
   popd
 }
 
 build_gcc_and_mingw() {
   mkdir gcc-objdir
   pushd gcc-objdir
   ../gcc-$gcc_version/configure --prefix=$install_dir --target=i686-w64-mingw32 --with-gnu-ld --with-gnu-as --disable-multilib --enable-threads=posix
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -592,21 +592,17 @@ Selection::GetTableCellLocationFromRange
   // Don't fail if range does not point to a single table cell,
   //  let aSelectionType tell user if we don't have a cell
   if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
     return NS_OK;
 
   // Get the child content (the cell) pointed to by starting node of range
   // We do minimal checking since GetTableSelectionType assures
   //   us that this really is a table cell
-  nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartContainer());
-  if (!content)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIContent> child = content->GetChildAt_Deprecated(aRange->StartOffset());
+  nsCOMPtr<nsIContent> child = aRange->GetChildAtStartOffset();
   if (!child)
     return NS_ERROR_FAILURE;
 
   // GetCellLayout depends on current frame, we need flush frame to get
   // nsITableCellLayout
   nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
   if (presShell) {
     presShell->FlushPendingNotifications(FlushType::Frames);
@@ -683,40 +679,36 @@ Selection::GetTableSelectionType(nsIDOMR
   if (!startNode) return NS_ERROR_FAILURE;
 
   nsINode* endNode = range->GetEndContainer();
   if (!endNode) return NS_ERROR_FAILURE;
 
   // Not a single selected node
   if (startNode != endNode) return NS_OK;
 
-  int32_t startOffset = range->StartOffset();
-  int32_t endOffset = range->EndOffset();
+  nsIContent* child = range->GetChildAtStartOffset();
 
   // Not a single selected node
-  if ((endOffset - startOffset) != 1)
+  if (!child || child != range->GetChildAtEndOffset()) {
     return NS_OK;
+  }
 
   nsIContent* startContent = static_cast<nsIContent*>(startNode);
   if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
     // Implies a check for being an element; if we ever make this work
     // for non-HTML, need to keep checking for elements.
     return NS_OK;
   }
 
   if (startContent->IsHTMLElement(nsGkAtoms::tr))
   {
     *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
   }
   else //check to see if we are selecting a table or row (column and all cells not done yet)
   {
-    nsIContent *child = startNode->GetChildAt_Deprecated(startOffset);
-    if (!child)
-      return NS_ERROR_FAILURE;
-
     if (child->IsHTMLElement(nsGkAtoms::table))
       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
     else if (child->IsHTMLElement(nsGkAtoms::tr))
       *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
   }
 
   return NS_OK;
 }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5050,34 +5050,29 @@ nsDocument::SetScriptGlobalObject(nsIScr
     // Also make sure to remove our onload blocker now if we haven't done it yet
     if (mOnloadBlockCount != 0) {
       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
       if (loadGroup) {
         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
       }
     }
 
-    using mozilla::dom::workers::ServiceWorkerManager;
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (swm) {
-      ErrorResult error;
-      if (GetController().isSome()) {
-        imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
-        if (loader) {
-          loader->ClearCacheForControlledDocument(this);
-        }
-
-        // We may become controlled again if this document comes back out
-        // of bfcache.  Clear our state to allow that to happen.  Only
-        // clear this flag if we are actually controlled, though, so pages
-        // that were force reloaded don't become controlled when they
-        // come out of bfcache.
-        mMaybeServiceWorkerControlled = false;
-      }
-      swm->MaybeStopControlling(this);
+    ErrorResult error;
+    if (GetController().isSome()) {
+      imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
+      if (loader) {
+        loader->ClearCacheForControlledDocument(this);
+      }
+
+      // We may become controlled again if this document comes back out
+      // of bfcache.  Clear our state to allow that to happen.  Only
+      // clear this flag if we are actually controlled, though, so pages
+      // that were force reloaded don't become controlled when they
+      // come out of bfcache.
+      mMaybeServiceWorkerControlled = false;
     }
   }
 
   // BlockOnload() might be called before mScriptGlobalObject is set.
   // We may need to add the blocker once mScriptGlobalObject is set.
   bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
 
   mScriptGlobalObject = aScriptGlobalObject;
@@ -5187,21 +5182,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
     docShell->GetLoadType(&loadType);
 
     // If we are shift-reloaded, don't associate with a ServiceWorker.
     if (IsForceReloadType(loadType)) {
       NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
       return;
     }
 
-    nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
-    if (swm) {
-      swm->MaybeStartControlling(this);
-      mMaybeServiceWorkerControlled = true;
-    }
+    mMaybeServiceWorkerControlled = true;
   }
 }
 
 nsIScriptGlobalObject*
 nsDocument::GetScriptHandlingObjectInternal() const
 {
   MOZ_ASSERT(!mScriptGlobalObject,
              "Do not call this when mScriptGlobalObject is set!");
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -3760,25 +3760,27 @@ ElementIsVisibleNoFlush(Element* aElemen
     return false;
   }
   RefPtr<nsStyleContext> sc =
     nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
   return sc && sc->StyleVisibility()->IsVisible();
 }
 
 static void
-AppendTransformedText(InnerTextAccumulator& aResult,
-                      nsGenericDOMDataNode* aTextNode,
-                      uint32_t aStart, uint32_t aEnd)
+AppendTransformedText(InnerTextAccumulator& aResult, nsIContent* aContainer)
 {
-  nsIFrame* frame = aTextNode->GetPrimaryFrame();
+  auto textNode = static_cast<nsGenericDOMDataNode*>(aContainer);
+
+  nsIFrame* frame = textNode->GetPrimaryFrame();
   if (!IsVisibleAndNotInReplacedElement(frame)) {
     return;
   }
-  nsIFrame::RenderedText text = frame->GetRenderedText(aStart, aEnd);
+
+  nsIFrame::RenderedText text =
+    frame->GetRenderedText(0, aContainer->GetChildCount());
   aResult.Append(text.mString);
 }
 
 /**
  * States for tree traversal. AT_NODE means that we are about to enter
  * the current DOM node. AFTER_NODE means that we have just finished traversing
  * the children of the current DOM node and are about to apply any
  * "after processing the node's children" steps before we finish visiting
@@ -3846,45 +3848,35 @@ IsLastNonemptyRowGroupOfTable(nsIFrame* 
       }
     }
   }
   return true;
 }
 
 void
 nsRange::GetInnerTextNoFlush(DOMString& aValue, ErrorResult& aError,
-                             nsIContent* aStartContainer, uint32_t aStartOffset,
-                             nsIContent* aEndContainer, uint32_t aEndOffset)
+                             nsIContent* aContainer)
 {
   InnerTextAccumulator result(aValue);
-  nsIContent* currentNode = aStartContainer;
+
+  if (aContainer->IsNodeOfType(nsINode::eTEXT)) {
+    AppendTransformedText(result, aContainer);
+    return;
+  }
+
+  nsIContent* currentNode = aContainer;
   TreeTraversalState currentState = AFTER_NODE;
-  if (aStartContainer->IsNodeOfType(nsINode::eTEXT)) {
-    auto t = static_cast<nsGenericDOMDataNode*>(aStartContainer);
-    if (aStartContainer == aEndContainer) {
-      AppendTransformedText(result, t, aStartOffset, aEndOffset);
-      return;
-    }
-    AppendTransformedText(result, t, aStartOffset, t->TextLength());
-  } else {
-    if (uint32_t(aStartOffset) < aStartContainer->GetChildCount()) {
-      currentNode = aStartContainer->GetChildAt_Deprecated(aStartOffset);
-      currentState = AT_NODE;
-    }
-  }
-
-  nsIContent* endNode = aEndContainer;
+
+  nsIContent* endNode = aContainer;
   TreeTraversalState endState = AFTER_NODE;
-  if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
-    endState = AT_NODE;
-  } else {
-    if (aEndOffset < aEndContainer->GetChildCount()) {
-      endNode = aEndContainer->GetChildAt_Deprecated(aEndOffset);
-      endState = AT_NODE;
-    }
+
+  nsIContent* firstChild = aContainer->GetFirstChild();
+  if (firstChild) {
+    currentNode = firstChild;
+    currentState = AT_NODE;
   }
 
   while (currentNode != endNode || currentState != endState) {
     nsIFrame* f = currentNode->GetPrimaryFrame();
     bool isVisibleAndNotReplaced = IsVisibleAndNotInReplacedElement(f);
     if (currentState == AT_NODE) {
       bool isText = currentNode->IsNodeOfType(nsINode::eTEXT);
       if (isText && currentNode->GetParent()->IsHTMLElement(nsGkAtoms::rp) &&
@@ -3934,15 +3926,11 @@ nsRange::GetInnerTextNoFlush(DOMString& 
     if (next) {
       currentNode = next;
       currentState = AT_NODE;
     } else {
       currentNode = currentNode->GetParent();
     }
   }
 
-  if (aEndContainer->IsNodeOfType(nsINode::eTEXT)) {
-    nsGenericDOMDataNode* t = static_cast<nsGenericDOMDataNode*>(aEndContainer);
-    AppendTransformedText(result, t, 0, aEndOffset);
-  }
   // Do not flush trailing line breaks! Required breaks at the end of the text
   // are suppressed.
 }
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -360,20 +360,17 @@ public:
   void SetEndBefore(nsINode& aNode, ErrorResult& aErr);
   void SetStart(nsINode& aNode, uint32_t aOffset, ErrorResult& aErr);
   void SetStart(const RawRangeBoundary& aPoint, ErrorResult& aErr);
   void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
   void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
 
   static void GetInnerTextNoFlush(mozilla::dom::DOMString& aValue,
                                   mozilla::ErrorResult& aError,
-                                  nsIContent* aStartContainer,
-                                  uint32_t aStartOffset,
-                                  nsIContent* aEndContainer,
-                                  uint32_t aEndOffset);
+                                  nsIContent* aContainer);
 
   nsINode* GetParentObject() const { return mOwner; }
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override final;
   DocGroup* GetDocGroup() const;
 
 private:
   // no copy's or assigns
   nsRange(const nsRange&);
--- a/dom/console/ConsoleReportCollector.cpp
+++ b/dom/console/ConsoleReportCollector.cpp
@@ -1,22 +1,25 @@
 /* -*- 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/ConsoleReportCollector.h"
 
+#include "ConsoleUtils.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 
+using mozilla::dom::ConsoleUtils;
+
 NS_IMPL_ISUPPORTS(ConsoleReportCollector, nsIConsoleReportCollector)
 
 ConsoleReportCollector::ConsoleReportCollector()
   : mMutex("mozilla::ConsoleReportCollector")
 {
 }
 
 void
@@ -90,16 +93,73 @@ ConsoleReportCollector::FlushReportsToCo
                                               uri,
                                               EmptyString(),
                                               report.mLineNumber,
                                               report.mColumnNumber);
   }
 }
 
 void
+ConsoleReportCollector::FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
+                                                                   ReportAction aAction)
+{
+  nsTArray<PendingReport> reports;
+
+  {
+    MutexAutoLock lock(mMutex);
+    if (aAction == ReportAction::Forget) {
+      mPendingReports.SwapElements(reports);
+    } else {
+      reports = mPendingReports;
+    }
+  }
+
+  for (uint32_t i = 0; i < reports.Length(); ++i) {
+    PendingReport& report = reports[i];
+
+    nsAutoString errorText;
+    nsresult rv;
+    if (!report.mStringParams.IsEmpty()) {
+      rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
+                                                 report.mMessageName.get(),
+                                                 report.mStringParams,
+                                                 errorText);
+    } else {
+      rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
+                                              report.mMessageName.get(),
+                                              errorText);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    ConsoleUtils::Level level = ConsoleUtils::eLog;
+    switch (report.mErrorFlags) {
+      case nsIScriptError::errorFlag:
+      case nsIScriptError::exceptionFlag:
+        level = ConsoleUtils::eError;
+        break;
+      case nsIScriptError::warningFlag:
+        level = ConsoleUtils::eWarning;
+        break;
+      default:
+        // default to log otherwise
+        break;
+    }
+
+    ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
+                                              errorText,
+                                              NS_ConvertUTF8toUTF16(report.mSourceFileURI),
+                                              report.mLineNumber,
+                                              report.mColumnNumber,
+                                              level);
+  }
+}
+
+void
 ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
                                             ReportAction aAction)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   FlushReportsToConsole(aDocument ? aDocument->InnerWindowID() : 0, aAction);
 }
 
--- a/dom/console/ConsoleReportCollector.h
+++ b/dom/console/ConsoleReportCollector.h
@@ -26,16 +26,20 @@ public:
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
   FlushReportsToConsole(uint64_t aInnerWindowID,
                         ReportAction aAction = ReportAction::Forget) override;
 
   void
+  FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
+                                             ReportAction aAction = ReportAction::Forget) override;
+
+  void
   FlushConsoleReports(nsIDocument* aDocument,
                       ReportAction aAction = ReportAction::Forget) override;
 
   void
   FlushConsoleReports(nsILoadGroup* aLoadGroup,
                       ReportAction aAction = ReportAction::Forget) override;
 
   void
--- a/dom/console/nsIConsoleReportCollector.h
+++ b/dom/console/nsIConsoleReportCollector.h
@@ -77,16 +77,20 @@ public:
   //
   // aInnerWindowID A inner window ID representing where to flush the reports.
   // aAction        An action to determine whether to reserve the pending
   //                reports. Defalut action is to forget the report.
   virtual void
   FlushReportsToConsole(uint64_t aInnerWindowID,
                         ReportAction aAction = ReportAction::Forget) = 0;
 
+  virtual void
+  FlushReportsToConsoleForServiceWorkerScope(const nsACString& aScope,
+                                             ReportAction aAction = ReportAction::Forget) = 0;
+
   // Flush all pending reports to the console.  Main thread only.
   //
   // aDocument      An optional document representing where to flush the
   //                reports.  If provided, then the corresponding window's
   //                web console will get the reports.  Otherwise the reports
   //                go to the browser console.
   // aAction        An action to determine whether to reserve the pending
   //                reports. Defalut action is to forget the report.
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -33,17 +33,16 @@
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/MutableBlobStreamListener.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/Request.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/URLSearchParams.h"
-#include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/Telemetry.h"
 
 #include "BodyExtractor.h"
 #include "FetchObserver.h"
 #include "InternalRequest.h"
 #include "InternalResponse.h"
 
 #include "WorkerPrivate.h"
@@ -286,17 +285,16 @@ public:
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     mPromiseProxy->CleanUp();
 
     mFetchObserver = nullptr;
 
     if (mSignalProxy) {
       mSignalProxy->Shutdown();
-      mSignalProxy = nullptr;
     }
 
     mWorkerHolder = nullptr;
   }
 
 private:
    WorkerFetchResolver(PromiseWorkerProxy* aProxy,
                        AbortSignalProxy* aSignalProxy,
@@ -862,23 +860,17 @@ WorkerFetchResolver::FlushConsoleReport(
   workers::WorkerPrivate* worker = mPromiseProxy->GetWorkerPrivate();
   if (!worker) {
     mReporter->FlushReportsToConsole(0);
     return;
   }
 
   if (worker->IsServiceWorker()) {
     // Flush to service worker
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (!swm) {
-      mReporter->FlushReportsToConsole(0);
-      return;
-    }
-
-    swm->FlushReportsToAllClients(worker->ServiceWorkerScope(), mReporter);
+    mReporter->FlushReportsToConsoleForServiceWorkerScope(worker->ServiceWorkerScope());
     return;
   }
 
   if (worker->IsSharedWorker()) {
     // Flush to shared worker
     worker->FlushReportsToSharedWorkers(mReporter);
     return;
   }
--- a/dom/fetch/FetchConsumer.cpp
+++ b/dom/fetch/FetchConsumer.cpp
@@ -2,16 +2,17 @@
 /* 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 "Fetch.h"
 #include "FetchConsumer.h"
 
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsIInputStreamPump.h"
 #include "nsProxyRelease.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 #include "Workers.h"
 
 namespace mozilla {
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1429783.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <script>
+      try { o2 = document.createElement('textarea') } catch(e) { }
+      try { o3 = document.getSelection() } catch(e) { }
+      try { document.documentElement.appendChild(o2) } catch(e) { }
+      try { o2.select() } catch(e) { }
+      try { o4 = o3.getRangeAt(0) } catch(e) { }
+      try { o5 = o4.commonAncestorContainer } catch(e) { }
+      try { o6 = o4.commonAncestorContainer } catch(e) { }
+      try { document.documentElement.after("", o5, "") } catch(e) { }
+      try { o4 = document.createElement('t') } catch(e) { }
+      try { document.appendChild(o4); } catch(e) { }
+      try { document.write(atob("PHNjcmlwdCBzcmM9Jyc+PC9zY3JpcHQ+Cg==")) } catch(e) { }
+      try { document.replaceChild(o6, document.documentElement); } catch(e) { }
+    </script>
+  </head>
+</html>
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -3081,17 +3081,17 @@ nsGenericHTMLElement::GetInnerText(mozil
     // ensure the document is styled.
     if (!presShell || !presShell->DidInitialize() ||
         IsOrHasAncestorWithDisplayNone(this, presShell)) {
       GetTextContentInternal(aValue, aError);
       return;
     }
   }
 
-  nsRange::GetInnerTextNoFlush(aValue, aError, this, 0, this, GetChildCount());
+  nsRange::GetInnerTextNoFlush(aValue, aError, this);
 }
 
 void
 nsGenericHTMLElement::SetInnerText(const nsAString& aValue)
 {
   // Batch possible DOMSubtreeModified events.
   mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
   FireNodeRemovedForChildren();
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -148,34 +148,19 @@ interface nsIServiceWorkerManager : nsIS
   nsISupports getReadyPromise(in mozIDOMWindow aWindow);
 
   // Remove ready pending Promise
   void removeReadyPromise(in mozIDOMWindow aWindow);
 
   nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal,
                                                               in DOMString aScope);
 
-  /**
-   * Call this to request that document `aDoc` be controlled by a ServiceWorker
-   * if a registration exists for it's scope.
-   *
-   * This MUST only be called once per document!
-   */
-  [notxpcom,nostdcall] void MaybeStartControlling(in nsIDocument aDoc);
-
   [notxpcom, nostdcall] bool StartControlling(in const_ClientInfoRef aClientInfo,
                                               in const_ServiceWorkerDescriptorRef aServiceWorker);
 
-  /**
-   * Documents that have called MaybeStartControlling() should call this when
-   * they are destroyed. This function may be called multiple times, and is
-   * idempotent.
-   */
-  [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
-
   /*
    * Returns a ServiceWorker.
    * window is the window of the caller. scope is the registration's scope and must be
    * a valid entry that window is allowed to load, otherwise this will return nullptr.
    * These are only meant to be called from ServiceWorkerRegistration instances.
    */
   [noscript] nsISupports GetInstalling(in nsPIDOMWindowInner aWindow, in DOMString aScope);
   [noscript] nsISupports GetWaiting(in nsPIDOMWindowInner aWindow, in DOMString aScope);
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1889,20 +1889,18 @@ nsNPObjWrapper::OnDestroy(NPObject *npob
 
     return;
   }
 
   auto entry =
     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
 
   if (entry && entry->mJSObj) {
-    // Found a live NPObject wrapper, null out its JSObjects' private
-    // data.
-
-    js::SetProxyPrivate(entry->mJSObj, JS::PrivateValue(nullptr));
+    // Found an NPObject wrapper, null out its JSObjects' private data.
+    js::SetProxyPrivate(entry->mJSObj.unbarrieredGetPtr(), JS::PrivateValue(nullptr));
 
     // Remove the npobj from the hash now that it went away.
     sNPObjWrappers->RawRemove(entry);
 
     // The finalize hook will call OnWrapperDestroyed().
   }
 }
 
@@ -1948,17 +1946,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
   if (entry->mJSObj) {
     // Found a NPObject wrapper. First check it is still alive.
-    JSObject* obj = entry->mJSObj;
+    JSObject* obj = entry->mJSObj.unbarrieredGetPtr();
     if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
       // The object is dead (finalization will happen at a later time). By the
       // time we leave this function, this entry will either be updated with a
       // new wrapper or removed if that fails. Clear it anyway to make sure
       // nothing touches the dead object.
       entry->mJSObj = nullptr;
     } else {
       // It may not be in the same compartment as cx, so we need to wrap it
@@ -2068,17 +2066,18 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
         // Force deallocation of plugin objects since the plugin they came
         // from is being torn down.
         if (npobj->_class && npobj->_class->deallocate) {
           npobj->_class->deallocate(npobj);
         } else {
           free(npobj);
         }
 
-        js::SetProxyPrivate(entry->mJSObj, JS::PrivateValue(nullptr));
+        js::SetProxyPrivate(entry->mJSObj.unbarrieredGetPtr(),
+                            JS::PrivateValue(nullptr));
 
         sNPObjWrappers = tmp;
 
         if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
           OnWrapperDestroyed();
         }
 
         i.Remove();
--- a/dom/push/test/test_error_reporting.html
+++ b/dom/push/test/test_error_reporting.html
@@ -74,20 +74,22 @@ http://creativecommons.org/licenses/publ
     reason = await waitForDeliveryError({ type: "rejection" });
     is(reason, SpecialPowers.Ci.nsIPushErrorReporter.DELIVERY_UNHANDLED_REJECTION,
       "Should report unhandled rejections");
   });
 
   add_task(async function reportDecryptionError() {
     var message = await new Promise(resolve => {
       SpecialPowers.registerConsoleListener(message => {
-        if (!message.isScriptError) {
+        if (!message.isScriptError && !message.isConsoleEvent) {
           return;
         }
-        if (message.innerWindowID == controlledFrame.innerWindowId()) {
+        const scope = "http://mochi.test:8888/tests/dom/push/test/";
+        if (message.innerWindowID === "ServiceWorker" &&
+            message.windowID === scope) {
           SpecialPowers.postConsoleSentinel();
           resolve(message);
         }
       });
 
       var principal = SpecialPowers.wrap(document).nodePrincipal;
       pushNotifier.notifyError(registration.scope, principal, "Push error",
         SpecialPowers.Ci.nsIScriptError.errorFlag);
--- a/dom/script/nsIScriptElement.h
+++ b/dom/script/nsIScriptElement.h
@@ -157,21 +157,27 @@ public:
 
   void PreventExecution()
   {
     mAlreadyStarted = true;
   }
 
   void LoseParserInsertedness()
   {
-    mFrozen = false;
     mUri = nullptr;
     mCreatorParser = nullptr;
     mParserCreated = mozilla::dom::NOT_FROM_PARSER;
     mForceAsync = !GetAsyncState();
+
+    // Reset state set by FreezeExecutionAttrs().
+    mFrozen = false;
+    mIsModule = false;
+    mExternal = false;
+    mAsync = false;
+    mDefer = false;
   }
 
   void SetCreatorParser(nsIParser* aParser)
   {
     mCreatorParser = do_GetWeakReference(aParser);
   }
 
   /**
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -34,16 +34,17 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ClientHandle.h"
 #include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ConsoleUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/InternalHeaders.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/NotificationEvent.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
@@ -904,17 +905,16 @@ ServiceWorkerManager::Register(mozIDOMWi
 
   nsAutoCString scopeKey;
   rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   window->NoteCalledRegisterForServiceWorkerScope(cleanedScope);
-  AddRegisteringDocument(cleanedScope, doc);
 
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
                                                             cleanedScope);
 
   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
     new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
 
   nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
@@ -1721,158 +1721,22 @@ void
 ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
                                          const nsString& aMessage,
                                          const nsString& aFilename,
                                          const nsString& aLine,
                                          uint32_t aLineNumber,
                                          uint32_t aColumnNumber,
                                          uint32_t aFlags)
 {
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv;
-
-  if (!aFilename.IsEmpty()) {
-    rv = NS_NewURI(getter_AddRefs(uri), aFilename);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-  }
-
-  AutoTArray<uint64_t, 16> windows;
-
-  // Report errors to every controlled document.
-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
-    MOZ_ASSERT(reg);
-    if (!reg->mScope.Equals(aScope)) {
-      continue;
-    }
-
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
-    if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
-      continue;
-    }
-
-    windows.AppendElement(doc->InnerWindowID());
-
-    nsContentUtils::ReportToConsoleNonLocalized(aMessage,
-                                                aFlags,
-                                                NS_LITERAL_CSTRING("Service Workers"),
-                                                doc,
-                                                uri,
-                                                aLine,
-                                                aLineNumber,
-                                                aColumnNumber,
-                                                nsContentUtils::eOMIT_LOCATION);
-  }
-
-  // Report to any documents that have called .register() for this scope.  They
-  // may not be controlled, but will still want to see error reports.
-  WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
-  if (regList) {
-    for (int32_t i = regList->Length() - 1; i >= 0; --i) {
-      nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
-      if (!doc) {
-        regList->RemoveElementAt(i);
-        continue;
-      }
-
-      if (!doc->IsCurrentActiveDocument()) {
-        continue;
-      }
-
-      uint64_t innerWindowId = doc->InnerWindowID();
-      if (windows.Contains(innerWindowId)) {
-        continue;
-      }
-
-      windows.AppendElement(innerWindowId);
-
-      nsContentUtils::ReportToConsoleNonLocalized(aMessage,
-                                                  aFlags,
-                                                  NS_LITERAL_CSTRING("Service Workers"),
-                                                  doc,
-                                                  uri,
-                                                  aLine,
-                                                  aLineNumber,
-                                                  aColumnNumber,
-                                                  nsContentUtils::eOMIT_LOCATION);
-    }
-
-    if (regList->IsEmpty()) {
-      regList = nullptr;
-      mRegisteringDocuments.Remove(aScope);
-    }
-  }
-
-  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
-  if (intList) {
-    nsIConsoleService* consoleService = nullptr;
-    for (uint32_t i = 0; i < intList->Length(); ++i) {
-      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
-
-      nsCOMPtr<nsIChannel> inner;
-      rv = channel->GetChannel(getter_AddRefs(inner));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
-      }
-
-      uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
-      if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
-        continue;
-      }
-
-      windows.AppendElement(innerWindowId);
-
-      // Unfortunately the nsContentUtils helpers don't provide a convenient
-      // way to log to a window ID without a document.  Use console service
-      // directly.
-      nsCOMPtr<nsIScriptError> errorObject =
-        do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-      }
-
-      rv = errorObject->InitWithWindowID(aMessage,
-                                         aFilename,
-                                         aLine,
-                                         aLineNumber,
-                                         aColumnNumber,
-                                         aFlags,
-                                         NS_LITERAL_CSTRING("Service Workers"),
-                                         innerWindowId);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return;
-      }
-
-      if (!consoleService) {
-        rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &consoleService);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return;
-        }
-      }
-
-      consoleService->LogMessage(errorObject);
-    }
-  }
-
-  // If there are no documents to report to, at least report something to the
-  // browser console.
-  if (windows.IsEmpty()) {
-    nsContentUtils::ReportToConsoleNonLocalized(aMessage,
-                                                aFlags,
-                                                NS_LITERAL_CSTRING("Service Workers"),
-                                                nullptr,  // document
-                                                uri,
-                                                aLine,
-                                                aLineNumber,
-                                                aColumnNumber,
-                                                nsContentUtils::eOMIT_LOCATION);
-    return;
-  }
+  ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
+                                            aMessage,
+                                            aFilename,
+                                            aLineNumber,
+                                            aColumnNumber,
+                                            ConsoleUtils::eError);
 }
 
 /* static */
 void
 ServiceWorkerManager::LocalizeAndReportToAllClients(
                                           const nsCString& aScope,
                                           const char* aStringKey,
                                           const nsTArray<nsString>& aParamArray,
@@ -1896,108 +1760,16 @@ ServiceWorkerManager::LocalizeAndReportT
                             aFilename, aLine, aLineNumber, aColumnNumber,
                             aFlags);
   } else {
     NS_WARNING("Failed to format and therefore report localized error.");
   }
 }
 
 void
-ServiceWorkerManager::FlushReportsToAllClients(const nsACString& aScope,
-                                               nsIConsoleReportCollector* aReporter)
-{
-  AutoTArray<uint64_t, 16> windows;
-
-  // Report errors to every controlled document.
-  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
-    MOZ_ASSERT(reg);
-    if (!reg->mScope.Equals(aScope)) {
-      continue;
-    }
-
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
-    if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
-      continue;
-    }
-
-    uint64_t innerWindowId = doc->InnerWindowID();
-    windows.AppendElement(innerWindowId);
-
-    aReporter->FlushReportsToConsole(
-      innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
-  }
-
-  // Report to any documents that have called .register() for this scope.  They
-  // may not be controlled, but will still want to see error reports.
-  WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
-  if (regList) {
-    for (int32_t i = regList->Length() - 1; i >= 0; --i) {
-      nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
-      if (!doc) {
-        regList->RemoveElementAt(i);
-        continue;
-      }
-
-      if (!doc->IsCurrentActiveDocument()) {
-        continue;
-      }
-
-      uint64_t innerWindowId = doc->InnerWindowID();
-      if (windows.Contains(innerWindowId)) {
-        continue;
-      }
-
-      windows.AppendElement(innerWindowId);
-
-      aReporter->FlushReportsToConsole(
-        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
-    }
-
-    if (regList->IsEmpty()) {
-      regList = nullptr;
-      mRegisteringDocuments.Remove(aScope);
-    }
-  }
-
-  nsresult rv;
-  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
-  if (intList) {
-    for (uint32_t i = 0; i < intList->Length(); ++i) {
-      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
-
-      nsCOMPtr<nsIChannel> inner;
-      rv = channel->GetChannel(getter_AddRefs(inner));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
-      }
-
-      uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
-      if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
-        continue;
-      }
-
-      windows.AppendElement(innerWindowId);
-
-      aReporter->FlushReportsToConsole(
-        innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
-    }
-  }
-
-  // If there are no documents to report to, at least report something to the
-  // browser console.
-  if (windows.IsEmpty()) {
-    aReporter->FlushReportsToConsole(0);
-    return;
-  }
-
-  aReporter->ClearConsoleReports();
-}
-
-void
 ServiceWorkerManager::HandleError(JSContext* aCx,
                                   nsIPrincipal* aPrincipal,
                                   const nsCString& aScope,
                                   const nsString& aWorkerURL,
                                   const nsString& aMessage,
                                   const nsString& aFilename,
                                   const nsString& aLine,
                                   uint32_t aLineNumber,
@@ -2351,28 +2123,16 @@ ServiceWorkerManager::RemoveScopeAndRegi
         reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
       MOZ_DIAGNOSTIC_ASSERT(false,
                             "controlled client when removing registration");
       iter.Remove();
       break;
     }
   }
 
-  // Registration lifecycle is managed via mControlledClients now.  Do not
-  // assert on on mControlledDocuments as races may cause this to still be
-  // set when the registration is destroyed.
-  for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
-    ServiceWorkerRegistrationInfo* reg = iter.UserData();
-    if (reg->mScope.Equals(aRegistration->mScope) &&
-        reg->mPrincipal->Equals(aRegistration->mPrincipal)) {
-      iter.Remove();
-      break;
-    }
-  }
-
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
   swm->MaybeRemoveRegistrationInfo(scopeKey);
   swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
 }
@@ -2383,30 +2143,16 @@ ServiceWorkerManager::MaybeRemoveRegistr
   if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
     if (entry.Data()->mOrderedScopes.IsEmpty() &&
         entry.Data()->mJobQueues.Count() == 0) {
       entry.Remove();
     }
   }
 }
 
-void
-ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aDoc);
-  RefPtr<ServiceWorkerRegistrationInfo> registration =
-    GetServiceWorkerRegistrationInfo(aDoc);
-  if (registration && registration->GetActive() &&
-      aDoc->GetSandboxFlags() == 0) {
-    MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
-    StartControllingADocument(registration, aDoc);
-  }
-}
-
 bool
 ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
                                        const ServiceWorkerDescriptor& aServiceWorker)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
@@ -2422,24 +2168,16 @@ ServiceWorkerManager::StartControlling(c
   NS_ENSURE_TRUE(registration, false);
 
   StartControllingClient(aClientInfo, registration);
 
   return true;
 }
 
 void
-ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aDoc);
-  mControlledDocuments.Remove(aDoc);
-}
-
-void
 ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
 {
   AssertIsOnMainThread();
   // We perform these success path navigation update steps when the
   // document tells us its more or less done loading.  This avoids
   // slowing down page load and also lets pages consistently get
   // updatefound events when they fire.
   //
@@ -2449,26 +2187,16 @@ ServiceWorkerManager::MaybeCheckNavigati
   //    algorithm.
   ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
   if (data && data->mRegistrationInfo) {
     data->mRegistrationInfo->MaybeScheduleUpdate();
   }
 }
 
 void
-ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
-                                                nsIDocument* aDoc)
-{
-  MOZ_ASSERT(aRegistration);
-  MOZ_ASSERT(aDoc);
-
-  mControlledDocuments.Put(aDoc, aRegistration);
-}
-
-void
 ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
   aRegistration->StopControllingClient();
   if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
     return;
   }
 
   if (aRegistration->mPendingUninstall) {
@@ -2801,18 +2529,16 @@ ServiceWorkerManager::DispatchFetchEvent
       }
 
       // But we also note the reserved state on the LoadInfo.  This allows the
       // ClientSource to be updated immediately after the nsIChannel starts.
       // This is necessary to have the correct controller in place for immediate
       // follow-on requests.
       loadInfo->SetController(serviceWorker->Descriptor());
     }
-
-    AddNavigationInterception(serviceWorker->Scope(), aChannel);
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
 
@@ -3263,17 +2989,16 @@ ServiceWorkerManager::MaybeClaimClient(n
                         getter_AddRefs(controllingRegistration));
 
   if (aWorkerRegistration != matchingRegistration ||
       aWorkerRegistration == controllingRegistration) {
     ref = GenericPromise::CreateAndResolve(true, __func__);
     return ref.forget();
   }
 
-  StartControllingADocument(aWorkerRegistration, aDocument);
   ref = StartControllingClient(clientInfo.ref(), aWorkerRegistration);
   return ref.forget();
 }
 
 already_AddRefed<GenericPromise>
 ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDoc,
                                        const ServiceWorkerDescriptor& aServiceWorker)
 {
@@ -3779,112 +3504,16 @@ ServiceWorkerManager::NotifyListenersOnU
                                         nsIServiceWorkerRegistrationInfo* aInfo)
 {
   nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnUnregister(aInfo);
   }
 }
 
-void
-ServiceWorkerManager::AddRegisteringDocument(const nsACString& aScope,
-                                             nsIDocument* aDoc)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(!aScope.IsEmpty());
-  MOZ_ASSERT(aDoc);
-
-  WeakDocumentList* list = mRegisteringDocuments.LookupOrAdd(aScope);
-  MOZ_ASSERT(list);
-
-  for (int32_t i = list->Length() - 1; i >= 0; --i) {
-    nsCOMPtr<nsIDocument> existing = do_QueryReferent(list->ElementAt(i));
-    if (!existing) {
-      list->RemoveElementAt(i);
-      continue;
-    }
-    if (existing == aDoc) {
-      return;
-    }
-  }
-
-  list->AppendElement(do_GetWeakReference(aDoc));
-}
-
-class ServiceWorkerManager::InterceptionReleaseHandle final : public nsISupports
-{
-  const nsCString mScope;
-
-  // Weak reference to channel is safe, because the channel holds a
-  // reference to this object.  Also, the pointer is only used for
-  // comparison purposes.
-  nsIInterceptedChannel* mChannel;
-
-  ~InterceptionReleaseHandle()
-  {
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (swm) {
-      swm->RemoveNavigationInterception(mScope, mChannel);
-    }
-  }
-
-public:
-  InterceptionReleaseHandle(const nsACString& aScope,
-                            nsIInterceptedChannel* aChannel)
-    : mScope(aScope)
-    , mChannel(aChannel)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(!aScope.IsEmpty());
-    MOZ_ASSERT(mChannel);
-  }
-
-  NS_DECL_ISUPPORTS
-};
-
-NS_IMPL_ISUPPORTS0(ServiceWorkerManager::InterceptionReleaseHandle);
-
-void
-ServiceWorkerManager::AddNavigationInterception(const nsACString& aScope,
-                                                nsIInterceptedChannel* aChannel)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(!aScope.IsEmpty());
-  MOZ_ASSERT(aChannel);
-
-  InterceptionList* list =
-    mNavigationInterceptions.LookupOrAdd(aScope);
-  MOZ_ASSERT(list);
-  MOZ_ASSERT(!list->Contains(aChannel));
-
-  nsCOMPtr<nsISupports> releaseHandle =
-    new InterceptionReleaseHandle(aScope, aChannel);
-  aChannel->SetReleaseHandle(releaseHandle);
-
-  list->AppendElement(aChannel);
-}
-
-void
-ServiceWorkerManager::RemoveNavigationInterception(const nsACString& aScope,
-                                                   nsIInterceptedChannel* aChannel)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aChannel);
-  InterceptionList* list =
-    mNavigationInterceptions.Get(aScope);
-  if (list) {
-    MOZ_ALWAYS_TRUE(list->RemoveElement(aChannel));
-    MOZ_ASSERT(!list->Contains(aChannel));
-    if (list->IsEmpty()) {
-      list = nullptr;
-      mNavigationInterceptions.Remove(aScope);
-    }
-  }
-}
-
 class UpdateTimerCallback final : public nsITimerCallback
                                 , public nsINamed
 {
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsCString mScope;
 
   ~UpdateTimerCallback()
   {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -99,50 +99,31 @@ public:
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECL_NSIOBSERVER
 
   struct RegistrationDataPerPrincipal;
   nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
 
   nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
 
-  nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
-
   struct ControlledClientData
   {
     RefPtr<ClientHandle> mClientHandle;
     RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
 
     ControlledClientData(ClientHandle* aClientHandle,
                          ServiceWorkerRegistrationInfo* aRegistrationInfo)
       : mClientHandle(aClientHandle)
       , mRegistrationInfo(aRegistrationInfo)
     {
     }
   };
 
   nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
 
-  // Track all documents that have attempted to register a service worker for a
-  // given scope.
-  typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
-  nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
-
-  // Track all intercepted navigation channels for a given scope.  Channels are
-  // placed in the appropriate list before dispatch the FetchEvent to the worker
-  // thread and removed once FetchEvent processing dispatches back to the main
-  // thread.
-  //
-  // Note: Its safe to use weak references here because a RAII-style callback
-  //       is registered with the channel before its added to this list.  We
-  //       are guaranteed the callback will fire before and remove the ref
-  //       from this list before the channel is destroyed.
-  typedef nsTArray<nsIInterceptedChannel*> InterceptionList;
-  nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
-
   bool
   IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
 
   // Return true if the given content process could potentially be executing
   // service worker code with the given principal.  At the current time, this
   // just means that we have any registration for the origin, regardless of
   // scope.  This is a very weak guarantee but is the best we can do when push
   // notifications can currently spin up a service worker in content processes
@@ -260,20 +241,16 @@ public:
                                 const char* aStringKey,
                                 const nsTArray<nsString>& aParamArray,
                                 uint32_t aFlags = 0x0,
                                 const nsString& aFilename = EmptyString(),
                                 const nsString& aLine = EmptyString(),
                                 uint32_t aLineNumber = 0,
                                 uint32_t aColumnNumber = 0);
 
-  void
-  FlushReportsToAllClients(const nsACString& aScope,
-                           nsIConsoleReportCollector* aReporter);
-
   // Always consumes the error by reporting to consoles of all controlled
   // documents.
   void
   HandleError(JSContext* aCx,
               nsIPrincipal* aPrincipal,
               const nsCString& aScope,
               const nsString& aWorkerURL,
               const nsString& aMessage,
@@ -394,20 +371,16 @@ private:
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
-  StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
-                            nsIDocument* aDoc);
-
-  void
   StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
 
@@ -484,29 +457,16 @@ private:
 
   void
   NotifyListenersOnRegister(nsIServiceWorkerRegistrationInfo* aRegistration);
 
   void
   NotifyListenersOnUnregister(nsIServiceWorkerRegistrationInfo* aRegistration);
 
   void
-  AddRegisteringDocument(const nsACString& aScope, nsIDocument* aDoc);
-
-  class InterceptionReleaseHandle;
-
-  void
-  AddNavigationInterception(const nsACString& aScope,
-                            nsIInterceptedChannel* aChannel);
-
-  void
-  RemoveNavigationInterception(const nsACString& aScope,
-                               nsIInterceptedChannel* aChannel);
-
-  void
   ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
   void
   UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
   void
   MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
 
--- a/dom/workers/test/serviceworkers/test_fetch_integrity.html
+++ b/dom/workers/test/serviceworkers/test_fetch_integrity.html
@@ -15,25 +15,23 @@
 "use strict";
 
 let security_localizer =
   stringBundleService.createBundle("chrome://global/locale/security/security.properties");
 
 function expect_security_console_message(/* msgId, args, ... */) {
   let expectations = [];
   // process repeated paired arguments of: msgId, args
-  for (let i = 0; i < arguments.length; i += 4) {
+  for (let i = 0; i < arguments.length; i += 3) {
     let msgId = arguments[i];
     let args = arguments[i + 1];
     let filename = arguments[i + 2];
-    let windowId = arguments[i + 3];
     expectations.push({
       errorMessage: security_localizer.formatStringFromName(msgId, args, args.length),
       sourceName: filename,
-      windowID: windowId
     });
   }
   return new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, expectations);
   });
 }
 
 // (This doesn't really need to be its own task, but it allows the actual test
@@ -56,38 +54,31 @@ add_task(async function test_integrity_s
   await waitForState(worker, 'activated');
   worker.postMessage('claim');
   await waitForControlled(window);
 
   info("Test for mNavigationInterceptions.")
   // The client_win will reload to another URL after opening filename2.
   let client_win = window.open(filename2);
 
-  // XXX windowID should be innerWindowID
-  let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
-  let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
   let expectedMessage = expect_security_console_message(
     "MalformedIntegrityHash",
     ["abc"],
     filename,
-    mainWindowID,
     "NoValidMetadata",
     [""],
     filename,
-    mainWindowID
   );
   let expectedMessage2 = expect_security_console_message(
     "MalformedIntegrityHash",
     ["abc"],
     filename,
-    clientWindowID,
     "NoValidMetadata",
     [""],
     filename,
-    clientWindowID
   );
 
   info("Test for mControlledDocuments and report error message to console.");
   // The fetch will succeed because the integrity value is invalid and we are
   // looking for the console message regarding the bad integrity value.
   await fetch("fail.html");
 
   await wait_for_expected_message(expectedMessage);
@@ -123,38 +114,31 @@ add_task(async function test_integrity_s
         resolve();
       } else {
         reject();
       }
     }
   });
   await waitForBothConnected;
 
-  // XXX windowID should be innerWindowID
-  let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
-  let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
   let expectedMessage = expect_security_console_message(
     "MalformedIntegrityHash",
     ["abc"],
     filename,
-    mainWindowID,
     "NoValidMetadata",
     [""],
     filename,
-    mainWindowID
   );
   let expectedMessage2 = expect_security_console_message(
     "MalformedIntegrityHash",
     ["abc"],
     filename,
-    clientWindowID,
     "NoValidMetadata",
     [""],
     filename,
-    clientWindowID
   );
 
   info("Start to fetch a URL with wrong integrity.")
   sharedWorker.port.start();
   sharedWorker.port.postMessage("StartFetchWithWrongIntegrity");
 
   let waitForSRIFailed = new Promise((resolve) => {
     sharedWorker.port.onmessage = function (e) {
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1711,17 +1711,19 @@ CompositorBridgeParent::AllocPWebRenderB
     mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     mWrBridge.get()->AddRef(); // IPDL reference
     *aIdNamespace = mWrBridge->GetIdNamespace();
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return mWrBridge;
   }
   mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
   RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
-  api->SetRootPipeline(aPipelineId);
+  wr::TransactionBuilder txn;
+  txn.SetRootPipeline(aPipelineId);
+  api->SendTransaction(txn);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(asyncMgr), Move(animStorage));
   mWrBridge.get()->AddRef(); // IPDL reference
 
   *aIdNamespace = mWrBridge->GetIdNamespace();
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -17,16 +17,17 @@
 #include "mozilla/layers/AsyncDragMetrics.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FocusTarget.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/KeyboardMap.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/RefCountedShmem.h"
 #include "mozilla/Move.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -47,23 +47,23 @@ parent:
   // next Update call.
   async InitReadLocks(ReadLockInit[] locks);
 
   sync Create(IntSize aSize);
   async DeleteCompositorAnimations(uint64_t[] aIds);
   async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                        LayoutSize aContentSize, ByteBuf aDL, BuiltDisplayListDescriptor aDLDesc,
                        WebRenderScrollData aScrollData,
-                       OpUpdateResource[] aResourceUpdates, Shmem[] aSmallShmems, Shmem[] aLargeShmems,
+                       OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
                        IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
   async EmptyTransaction(FocusTarget focusTarget,
                          WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                          IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
   async SetFocusTarget(FocusTarget focusTarget);
-  async UpdateResources(OpUpdateResource[] aResourceUpdates, Shmem[] aSmallShmems, Shmem[] aLargeShmems);
+  async UpdateResources(OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems);
   async ParentCommands(WebRenderParentCommand[] commands);
   sync GetSnapshot(PTexture texture);
   async AddPipelineIdForCompositable(PipelineId aImageId, CompositableHandle aHandle, bool aAsync);
   async RemovePipelineIdForCompositable(PipelineId aPipelineId);
   async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
   async RemoveExternalImageId(ExternalImageId aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.cpp
@@ -0,0 +1,108 @@
+/* -*- 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 "RefCountedShmem.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/WebRenderMessages.h"
+
+#define SHM_REFCOUNT_HEADER_SIZE 16
+
+namespace mozilla {
+namespace layers {
+
+uint8_t*
+RefCountedShm::GetBytes(const RefCountedShmem& aShm)
+{
+  uint8_t* data = aShm.buffer().get<uint8_t>();
+  if (!data) {
+    return nullptr;
+  }
+  return data + SHM_REFCOUNT_HEADER_SIZE;
+}
+
+size_t
+RefCountedShm::GetSize(const RefCountedShmem& aShm)
+{
+  if (!IsValid(aShm)) {
+    return 0;
+  }
+  size_t totalSize = aShm.buffer().Size<uint8_t>();
+  if (totalSize < SHM_REFCOUNT_HEADER_SIZE) {
+    return 0;
+  }
+
+  return totalSize - SHM_REFCOUNT_HEADER_SIZE;
+}
+
+bool
+RefCountedShm::IsValid(const RefCountedShmem& aShm)
+{
+  return aShm.buffer().IsWritable() && aShm.buffer().Size<uint8_t>() > SHM_REFCOUNT_HEADER_SIZE;
+}
+
+int32_t
+RefCountedShm::GetReferenceCount(const RefCountedShmem& aShm)
+{
+  if (!IsValid(aShm)) {
+    return 0;
+  }
+
+  return *aShm.buffer().get<Atomic<int32_t>>();
+}
+
+int32_t
+RefCountedShm::AddRef(const RefCountedShmem& aShm)
+{
+  if (!IsValid(aShm)) {
+    return 0;
+  }
+
+  auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+  if (counter) {
+      return (*counter)++;
+  }
+  return 0;
+}
+
+int32_t
+RefCountedShm::Release(const RefCountedShmem& aShm)
+{
+  if (!IsValid(aShm)) {
+    return 0;
+  }
+
+  auto* counter = aShm.buffer().get<Atomic<int32_t>>();
+  if (counter) {
+      return --(*counter);
+  }
+
+  return 0;
+}
+
+bool
+RefCountedShm::Alloc(IProtocol* aAllocator, size_t aSize,
+                       RefCountedShmem& aShm)
+{
+  MOZ_ASSERT(!IsValid(aShm));
+  auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+  auto size = aSize + SHM_REFCOUNT_HEADER_SIZE;
+  if (!aAllocator->AllocUnsafeShmem(size, shmType, &aShm.buffer())) {
+    return false;
+  }
+  return true;
+}
+
+void
+RefCountedShm::Dealloc(IProtocol* aAllocator, RefCountedShmem& aShm)
+{
+  aAllocator->DeallocShmem(aShm.buffer());
+  aShm.buffer() = ipc::Shmem();
+}
+
+} //namespace
+} //namespace
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/RefCountedShmem.h
@@ -0,0 +1,48 @@
+/* -*- 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_LAYERS_REFCOUNTED_SHMEM_H
+#define MOZILLA_LAYERS_REFCOUNTED_SHMEM_H
+
+#include "mozilla/ipc/Shmem.h"
+#include "chrome/common/ipc_message_utils.h"
+
+namespace mozilla {
+namespace ipc {
+class IProtocol;
+}
+
+namespace layers {
+
+// This class is IPDL-defined
+class RefCountedShmem;
+
+// This just implement the methods externally.
+class RefCountedShm
+{
+public:
+
+  static uint8_t* GetBytes(const RefCountedShmem& aShm);
+
+  static size_t GetSize(const RefCountedShmem& aShm);
+
+  static bool IsValid(const RefCountedShmem& aShm);
+
+  static bool Alloc(mozilla::ipc::IProtocol* aAllocator, size_t aSize, RefCountedShmem& aShm);
+
+  static void Dealloc(mozilla::ipc::IProtocol* aAllocator, RefCountedShmem& aShm);
+
+  static int32_t GetReferenceCount(const RefCountedShmem& aShm);
+
+  static int32_t AddRef(const RefCountedShmem& aShm);
+
+  static int32_t Release(const RefCountedShmem& aShm);
+};
+
+} // namespace
+} // namespace
+
+#endif
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -26,16 +26,20 @@ using mozilla::gfx::MaybeIntSize from "m
 using mozilla::LayoutDeviceRect from "Units.h";
 using mozilla::ImageIntRect from "Units.h";
 using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 
 namespace mozilla {
 namespace layers {
 
+struct RefCountedShmem {
+  Shmem buffer;
+};
+
 union OptionalTransform {
   Matrix4x4;
   void_t;
 };
 
 union OptionalOpacity {
   float;
   void_t;
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -183,16 +183,17 @@ EXPORTS.mozilla.layers += [
     'ipc/ImageBridgeParent.h',
     'ipc/ISurfaceAllocator.h',
     'ipc/KnowsCompositor.h',
     'ipc/LayerAnimationUtils.h',
     'ipc/LayersMessageUtils.h',
     'ipc/LayerTransactionChild.h',
     'ipc/LayerTransactionParent.h',
     'ipc/LayerTreeOwnerTracker.h',
+    'ipc/RefCountedShmem.h',
     'ipc/RemoteContentController.h',
     'ipc/ShadowLayers.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
     'ipc/SharedSurfacesChild.h',
     'ipc/SharedSurfacesParent.h',
     'ipc/SynchronousTask.h',
     'ipc/TextureForwarder.h',
@@ -406,16 +407,17 @@ UNIFIED_SOURCES += [
     'ipc/CrossProcessCompositorBridgeParent.cpp',
     'ipc/ImageBridgeChild.cpp',
     'ipc/ImageBridgeParent.cpp',
     'ipc/ISurfaceAllocator.cpp',
     'ipc/LayerAnimationUtils.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
     'ipc/LayerTreeOwnerTracker.cpp',
+    'ipc/RefCountedShmem.cpp',
     'ipc/RemoteContentController.cpp',
     'ipc/ShadowLayers.cpp',
     'ipc/SharedPlanarYCbCrImage.cpp',
     'ipc/SharedRGBImage.cpp',
     'ipc/SharedSurfacesChild.cpp',
     'ipc/SharedSurfacesParent.cpp',
     'ipc/UiCompositorControllerChild.cpp',
     'ipc/UiCompositorControllerParent.cpp',
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -115,32 +115,32 @@ AsyncImagePipelineManager::AddAsyncImage
   MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
   AsyncImagePipeline* holder = new AsyncImagePipeline();
   holder->mImageHost = aImageHost;
   mAsyncImagePipelines.Put(id, holder);
   AddPipeline(aPipelineId);
 }
 
 void
-AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId)
+AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn)
 {
   if (mDestroyed) {
     return;
   }
 
   uint64_t id = wr::AsUint64(aPipelineId);
   if (auto entry = mAsyncImagePipelines.Lookup(id)) {
     AsyncImagePipeline* holder = entry.Data();
     ++mAsyncImageEpoch; // Update webrender epoch
-    mApi->ClearDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
+    aTxn.ClearDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
     wr::ResourceUpdateQueue resources;
     for (wr::ImageKey key : holder->mKeys) {
       resources.DeleteImage(key);
     }
-    mApi->UpdateResources(resources);
+    aTxn.UpdateResources(resources);
     entry.Remove();
     RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
   }
 }
 
 void
 AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                                     const LayoutDeviceRect& aScBounds,
@@ -271,40 +271,50 @@ AsyncImagePipelineManager::ApplyAsyncIma
 {
   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   ++mAsyncImageEpoch; // Update webrender epoch
   wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
 
+  // TODO: We can improve upon this by using two transactions: one for everything that
+  // doesn't change the display list (in other words does not cause the scene to be
+  // re-built), and one for the rest. This way, if an async pipeline needs to re-build
+  // its display list, other async pipelines can still be rendered while the scene is
+  // building.
+  wr::TransactionBuilder txn;
+
   // We use a pipeline with a very small display list for each video element.
   // Update each of them if needed.
   for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::ResourceUpdateQueue resourceUpdates;
+
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     AsyncImagePipeline* pipeline = iter.Data();
 
     nsTArray<wr::ImageKey> keys;
     auto op = UpdateImageKeys(resourceUpdates, pipeline, keys);
 
+    txn.UpdateResources(resourceUpdates);
+
     bool updateDisplayList = pipeline->mInitialised &&
                              (pipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
                              !!pipeline->mCurrentTexture;
 
     // Request to generate frame if there is an update.
     if (updateDisplayList || !op.isNothing()) {
       SetWillGenerateFrame();
     }
 
     if (!updateDisplayList) {
       // We don't need to update the display list, either because we can't or because
       // the previous one is still up to date.
       // We may, however, have updated some resources.
-      mApi->UpdatePipelineResources(resourceUpdates, pipelineId, epoch);
+      txn.UpdateEpoch(pipelineId, epoch);
       if (pipeline->mCurrentTexture) {
         HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
       }
       continue;
     }
     pipeline->mIsChanged = false;
 
     wr::LayoutSize contentSize { pipeline->mScBounds.Width(), pipeline->mScBounds.Height() };
@@ -346,21 +356,24 @@ AsyncImagePipelineManager::ApplyAsyncIma
                         pipeline->mFilter,
                         keys[0]);
     }
     builder.PopStackingContext();
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
     builder.Finalize(builderContentSize, dl);
-    mApi->SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
-                         pipelineId, builderContentSize,
-                         dl.dl_desc, dl.dl,
-                         resourceUpdates);
+    txn.SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f),
+                       epoch,
+                       LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
+                       pipelineId, builderContentSize,
+                       dl.dl_desc, dl.dl);
   }
+
+  mApi->SendTransaction(txn);
 }
 
 void
 AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
   if (mDestroyed) {
     return;
   }
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -66,17 +66,17 @@ public:
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
   void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
-  void RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId);
+  void RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn);
 
   void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 const LayoutDeviceRect& aScBounds,
                                 const gfx::Matrix4x4& aScTransform,
                                 const gfx::MaybeIntSize& aScaleToSize,
                                 const wr::ImageRendering& aFilter,
                                 const wr::MixBlendMode& aMixBlendMode);
   void ApplyAsyncImages();
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -4,21 +4,24 @@
  * 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 "IpcResourceUpdateQueue.h"
 #include <string.h>
 #include <algorithm>
 #include "mozilla/Maybe.h"
 #include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
 
 namespace mozilla {
 namespace wr {
 
-ShmSegmentsWriter::ShmSegmentsWriter(ipc::IShmemAllocator* aAllocator, size_t aChunkSize)
+using namespace mozilla::layers;
+
+ShmSegmentsWriter::ShmSegmentsWriter(layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize)
 : mShmAllocator(aAllocator)
 , mCursor(0)
 , mChunkSize(aChunkSize)
 {
   MOZ_ASSERT(mShmAllocator);
 }
 
 ShmSegmentsWriter::~ShmSegmentsWriter()
@@ -44,18 +47,18 @@ ShmSegmentsWriter::Write(Range<uint8_t> 
   size_t srcCursor = 0;
   size_t dstCursor = mCursor;
   size_t currAllocLen = mSmallAllocs.Length();
 
   while (remainingBytesToCopy > 0) {
     if (dstCursor >= mSmallAllocs.Length() * mChunkSize) {
       if (!AllocChunk()) {
         for (size_t i = mSmallAllocs.Length() ; currAllocLen < i ; i--) {
-          ipc::Shmem shm = mSmallAllocs.ElementAt(i);
-          mShmAllocator->DeallocShmem(shm);
+          RefCountedShmem& shm = mSmallAllocs.ElementAt(i);
+          RefCountedShm::Dealloc(mShmAllocator, shm);
           mSmallAllocs.RemoveElementAt(i);
         }
         return layers::OffsetRange(0, start, 0);
       }
       continue;
     }
 
     const size_t dstMaxOffset = mChunkSize * mSmallAllocs.Length();
@@ -63,17 +66,17 @@ ShmSegmentsWriter::Write(Range<uint8_t> 
 
     MOZ_ASSERT(dstCursor >= dstBaseOffset);
     MOZ_ASSERT(dstCursor <= dstMaxOffset);
 
     size_t availableRange = dstMaxOffset - dstCursor;
     size_t copyRange = std::min<int>(availableRange, remainingBytesToCopy);
 
     uint8_t* srcPtr = &aBytes[srcCursor];
-    uint8_t* dstPtr = mSmallAllocs.LastElement().get<uint8_t>() + (dstCursor - dstBaseOffset);
+    uint8_t* dstPtr = RefCountedShm::GetBytes(mSmallAllocs.LastElement()) + (dstCursor - dstBaseOffset);
 
     memcpy(dstPtr, srcPtr, copyRange);
 
     srcCursor += copyRange;
     dstCursor += copyRange;
     remainingBytesToCopy -= copyRange;
 
     // sanity check
@@ -83,23 +86,23 @@ ShmSegmentsWriter::Write(Range<uint8_t> 
   mCursor += length;
 
   return layers::OffsetRange(0, start, length);
 }
 
 bool
 ShmSegmentsWriter::AllocChunk()
 {
-  ipc::Shmem shm;
-  auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
-  if (!mShmAllocator->AllocShmem(mChunkSize, shmType, &shm)) {
+  RefCountedShmem shm;
+  if (!mShmAllocator->AllocResourceShmem(mChunkSize, shm)) {
     gfxCriticalNote << "ShmSegmentsWriter failed to allocate chunk #" << mSmallAllocs.Length();
     MOZ_ASSERT(false, "ShmSegmentsWriter fails to allocate chunk");
     return false;
   }
+  RefCountedShm::AddRef(shm);
   mSmallAllocs.AppendElement(shm);
   return true;
 }
 
 layers::OffsetRange
 ShmSegmentsWriter::AllocLargeChunk(size_t aSize)
 {
   ipc::Shmem shm;
@@ -110,59 +113,53 @@ ShmSegmentsWriter::AllocLargeChunk(size_
     return layers::OffsetRange(0, 0, 0);
   }
   mLargeAllocs.AppendElement(shm);
 
   return layers::OffsetRange(mLargeAllocs.Length(), 0, aSize);
 }
 
 void
-ShmSegmentsWriter::Flush(nsTArray<ipc::Shmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs)
+ShmSegmentsWriter::Flush(nsTArray<RefCountedShmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs)
 {
-  aSmallAllocs.Clear();
-  aLargeAllocs.Clear();
+  MOZ_ASSERT(aSmallAllocs.IsEmpty());
+  MOZ_ASSERT(aLargeAllocs.IsEmpty());
   mSmallAllocs.SwapElements(aSmallAllocs);
   mLargeAllocs.SwapElements(aLargeAllocs);
 }
 
 void
 ShmSegmentsWriter::Clear()
 {
   if (mShmAllocator) {
-    for (auto& shm : mSmallAllocs) {
-      mShmAllocator->DeallocShmem(shm);
-    }
-    for (auto& shm : mLargeAllocs) {
-      mShmAllocator->DeallocShmem(shm);
-    }
+    IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mSmallAllocs);
+    IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mLargeAllocs);
   }
-  mSmallAllocs.Clear();
-  mLargeAllocs.Clear();
   mCursor = 0;
 }
 
-ShmSegmentsReader::ShmSegmentsReader(const nsTArray<ipc::Shmem>& aSmallShmems,
+ShmSegmentsReader::ShmSegmentsReader(const nsTArray<RefCountedShmem>& aSmallShmems,
                                      const nsTArray<ipc::Shmem>& aLargeShmems)
 : mSmallAllocs(aSmallShmems)
 , mLargeAllocs(aLargeShmems)
 , mChunkSize(0)
 {
   if (mSmallAllocs.IsEmpty()) {
     return;
   }
 
-  mChunkSize = mSmallAllocs[0].Size<uint8_t>();
+  mChunkSize = RefCountedShm::GetSize(mSmallAllocs[0]);
 
   // Check that all shmems are readable and have the same size. If anything
   // isn't right, set mChunkSize to zero which signifies that the reader is
   // in an invalid state and Read calls will return false;
   for (const auto& shm : mSmallAllocs) {
-    if (!shm.IsReadable()
-        || shm.Size<uint8_t>() != mChunkSize
-        || shm.get<uint8_t>() == nullptr) {
+    if (!RefCountedShm::IsValid(shm)
+        || RefCountedShm::GetSize(shm) != mChunkSize
+        || RefCountedShm::GetBytes(shm) == nullptr) {
       mChunkSize = 0;
       return;
     }
   }
 
   for (const auto& shm : mLargeAllocs) {
     if (!shm.IsReadable()
         || shm.get<uint8_t>() == nullptr) {
@@ -214,28 +211,28 @@ ShmSegmentsReader::Read(const layers::Of
   size_t initialLength = aInto.Length();
 
   size_t srcCursor = aRange.start();
   int remainingBytesToCopy = aRange.length();
   while (remainingBytesToCopy > 0) {
     const size_t shm_idx = srcCursor / mChunkSize;
     const size_t ptrOffset = srcCursor % mChunkSize;
     const size_t copyRange = std::min<int>(remainingBytesToCopy, mChunkSize - ptrOffset);
-    uint8_t* srcPtr = mSmallAllocs[shm_idx].get<uint8_t>() + ptrOffset;
+    uint8_t* srcPtr = RefCountedShm::GetBytes(mSmallAllocs[shm_idx]) + ptrOffset;
 
     aInto.PushBytes(Range<uint8_t>(srcPtr, copyRange));
 
     srcCursor += copyRange;
     remainingBytesToCopy -= copyRange;
   }
 
   return aInto.Length() - initialLength == aRange.length();
 }
 
-IpcResourceUpdateQueue::IpcResourceUpdateQueue(ipc::IShmemAllocator* aAllocator,
+IpcResourceUpdateQueue::IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator,
                                                size_t aChunkSize)
 : mWriter(Move(aAllocator), aChunkSize)
 {}
 
 bool
 IpcResourceUpdateQueue::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
                                  Range<uint8_t> aBytes)
 {
@@ -347,25 +344,47 @@ IpcResourceUpdateQueue::AddFontInstance(
 void
 IpcResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey)
 {
   mUpdates.AppendElement(layers::OpDeleteFontInstance(aKey));
 }
 
 void
 IpcResourceUpdateQueue::Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
-                              nsTArray<ipc::Shmem>& aSmallAllocs,
+                              nsTArray<layers::RefCountedShmem>& aSmallAllocs,
                               nsTArray<ipc::Shmem>& aLargeAllocs)
 {
   aUpdates.Clear();
   mUpdates.SwapElements(aUpdates);
   mWriter.Flush(aSmallAllocs, aLargeAllocs);
 }
 
 void
 IpcResourceUpdateQueue::Clear()
 {
   mWriter.Clear();
   mUpdates.Clear();
 }
 
+//static
+void
+IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator, nsTArray<layers::RefCountedShmem>& aShms)
+{
+  for (auto& shm : aShms) {
+    if (RefCountedShm::IsValid(shm) && RefCountedShm::Release(shm) == 0) {
+      RefCountedShm::Dealloc(aShmAllocator, shm);
+    }
+  }
+  aShms.Clear();
+}
+
+//static
+void
+IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator, nsTArray<ipc::Shmem>& aShms)
+{
+  for (auto& shm : aShms) {
+    aShmAllocator->DeallocShmem(shm);
+  }
+  aShms.Clear();
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -3,76 +3,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/. */
 
 #ifndef GFX_WR_IPCRESOURCEUPDATEQUEUE_H
 #define GFX_WR_IPCRESOURCEUPDATEQUEUE_H
 
 #include "mozilla/layers/WebRenderMessages.h"
+#include "mozilla/layers/RefCountedShmem.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace ipc {
 class IShmemAllocator;
 }
 namespace wr {
 
 /// ShmSegmentsWriter pushes bytes in a sequence of fixed size shmems for small
 /// allocations and creates dedicated shmems for large allocations.
 class ShmSegmentsWriter {
 public:
-  ShmSegmentsWriter(ipc::IShmemAllocator* aAllocator, size_t aChunkSize);
+  ShmSegmentsWriter(layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize);
   ~ShmSegmentsWriter();
 
   layers::OffsetRange Write(Range<uint8_t> aBytes);
 
   template<typename T>
   layers::OffsetRange WriteAsBytes(Range<T> aValues)
   {
     return Write(Range<uint8_t>((uint8_t*)aValues.begin().get(), aValues.length() * sizeof(T)));
   }
 
-  void Flush(nsTArray<ipc::Shmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs);
+  void Flush(nsTArray<layers::RefCountedShmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs);
 
   void Clear();
 
 protected:
   bool AllocChunk();
   layers::OffsetRange AllocLargeChunk(size_t aSize);
 
-  nsTArray<ipc::Shmem> mSmallAllocs;
+  nsTArray<layers::RefCountedShmem> mSmallAllocs;
   nsTArray<ipc::Shmem> mLargeAllocs;
-  ipc::IShmemAllocator* mShmAllocator;
+  layers::WebRenderBridgeChild* mShmAllocator;
   size_t mCursor;
   size_t mChunkSize;
 };
 
 class ShmSegmentsReader {
 public:
-  ShmSegmentsReader(const nsTArray<ipc::Shmem>& aSmallShmems,
+  ShmSegmentsReader(const nsTArray<layers::RefCountedShmem>& aSmallShmems,
                     const nsTArray<ipc::Shmem>& aLargeShmems);
 
   bool Read(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto);
 
 protected:
   bool ReadLarge(const layers::OffsetRange& aRange, wr::Vec<uint8_t>& aInto);
 
-  const nsTArray<ipc::Shmem>& mSmallAllocs;
+  const nsTArray<layers::RefCountedShmem>& mSmallAllocs;
   const nsTArray<ipc::Shmem>& mLargeAllocs;
   size_t mChunkSize;
 };
 
 class IpcResourceUpdateQueue {
 public:
   // Because we are using shmems, the size should be a multiple of the page size.
   // Each shmem has two guard pages, and the minimum shmem size (at least one Windows)
   // is 64k which is already quite large for a lot of the resources we use here.
-  // So we pick 64k - 2 * 4k = 57344 bytes as the defautl alloc
-  explicit IpcResourceUpdateQueue(ipc::IShmemAllocator* aAllocator, size_t aChunkSize = 57344);
+  // The RefCountedShmem type used to allocate the chunks keeps a 16 bytes header
+  // in the buffer which we account for here as well.
+  // So we pick 64k - 2 * 4k - 16 = 57328 bytes as the default alloc size.
+  explicit IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize = 57328);
 
   bool AddImage(wr::ImageKey aKey,
                 const ImageDescriptor& aDescriptor,
                 Range<uint8_t> aBytes);
 
   bool AddBlobImage(wr::ImageKey aKey,
                     const ImageDescriptor& aDescriptor,
                     Range<uint8_t> aBytes);
@@ -109,19 +112,21 @@ public:
                        const wr::FontInstancePlatformOptions* aPlatformOptions,
                        Range<const gfx::FontVariation> aVariations);
 
   void DeleteFontInstance(wr::FontInstanceKey aKey);
 
   void Clear();
 
   void Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
-             nsTArray<ipc::Shmem>& aSmallAllocs,
+             nsTArray<layers::RefCountedShmem>& aSmallAllocs,
              nsTArray<ipc::Shmem>& aLargeAllocs);
 
+  static void ReleaseShmems(ipc::IProtocol*, nsTArray<layers::RefCountedShmem>& aShmems);
+  static void ReleaseShmems(ipc::IProtocol*, nsTArray<ipc::Shmem>& aShmems);
 protected:
   ShmSegmentsWriter mWriter;
   nsTArray<layers::OpUpdateResource> mUpdates;
 };
 
 } // namespace
 } // namespace
 
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -63,16 +63,21 @@ void
 WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why)
 {
   DoDestroy();
 }
 
 void
 WebRenderBridgeChild::DoDestroy()
 {
+  if (RefCountedShm::IsValid(mResourceShm) && RefCountedShm::Release(mResourceShm) == 0) {
+    RefCountedShm::Dealloc(this, mResourceShm);
+    mResourceShm = RefCountedShmem();
+  }
+
   // mDestroyed is used to prevent calling Send__delete__() twice.
   // When this function is called from CompositorBridgeChild::Destroy().
   // mActiveResourceTracker is not cleared here, since it is
   // used by PersistentBufferProviderShared.
   mDestroyed = true;
   mManager = nullptr;
 }
 
@@ -120,17 +125,17 @@ void
 WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources)
 {
   if (!IPCOpen()) {
     aResources.Clear();
     return;
   }
 
   nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<ipc::Shmem> smallShmems;
+  nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
   this->SendUpdateResources(resourceUpdates, Move(smallShmems), Move(largeShmems));
 }
 
 void
 WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aContentSize,
@@ -149,17 +154,17 @@ WebRenderBridgeChild::EndTransaction(con
   aDL.dl.inner.data = nullptr;
 
   TimeStamp fwdTime;
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   fwdTime = TimeStamp::Now();
 #endif
 
   nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<ipc::Shmem> smallShmems;
+  nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
   this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
                            GetFwdTransactionId(), aTransactionId,
                            aContentSize, dlData, aDL.dl_desc, aScrollData,
                            Move(resourceUpdates), Move(smallShmems), Move(largeShmems),
                            mIdNamespace, aTxnStartTime, fwdTime);
@@ -316,17 +321,17 @@ WebRenderBridgeChild::GetFontKeyForScale
              (aScaledFont->GetType() == gfx::FontType::MAC) ||
              (aScaledFont->GetType() == gfx::FontType::FONTCONFIG));
 
   wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 };
   if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
     return instanceKey;
   }
 
-  wr::IpcResourceUpdateQueue resources(GetShmemAllocator());
+  wr::IpcResourceUpdateQueue resources(this);
 
   wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont());
   wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0};
   if (fontKey == nullKey) {
     return instanceKey;
   }
 
   instanceKey = GetNextFontInstanceKey();
@@ -349,17 +354,17 @@ WebRenderBridgeChild::GetFontKeyForScale
 
 wr::FontKey
 WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled)
 {
   MOZ_ASSERT(!mDestroyed);
 
   wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0};
   if (!mFontKeys.Get(aUnscaled, &fontKey)) {
-    wr::IpcResourceUpdateQueue resources(GetShmemAllocator());
+    wr::IpcResourceUpdateQueue resources(this);
     FontFileDataSink sink = { &fontKey, this, &resources };
     // First try to retrieve a descriptor for the font, as this is much cheaper
     // to send over IPC than the full raw font data. If this is not possible, then
     // and only then fall back to getting the raw font file data. If that fails,
     // then the only thing left to do is signal failure by returning a null font key.
     if (!aUnscaled->GetWRFontDescriptor(WriteFontDescriptor, &sink) &&
         !aUnscaled->GetFontFileData(WriteFontFileData, &sink)) {
       return fontKey;
@@ -371,17 +376,17 @@ WebRenderBridgeChild::GetFontKeyForUnsca
 
   return fontKey;
 }
 
 void
 WebRenderBridgeChild::RemoveExpiredFontKeys()
 {
   uint32_t counter = gfx::ScaledFont::DeletionCounter();
-  wr::IpcResourceUpdateQueue resources(GetShmemAllocator());
+  wr::IpcResourceUpdateQueue resources(this);
   if (mFontInstanceKeysDeleted != counter) {
     mFontInstanceKeysDeleted = counter;
     for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
         resources.DeleteFontInstance(iter.Data());
         iter.Remove();
       }
     }
@@ -632,10 +637,54 @@ WebRenderBridgeChild::GetShmemAllocator(
 
 RefPtr<KnowsCompositor>
 WebRenderBridgeChild::GetForMedia()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return MakeAndAddRef<KnowsCompositorMediaProxy>(GetTextureFactoryIdentifier());
 }
 
+bool
+WebRenderBridgeChild::AllocResourceShmem(size_t aSize, RefCountedShmem& aShm)
+{
+  // We keep a single shmem around to reuse later if it is reference count has
+  // dropped back to 1 (the reference held by the WebRenderBridgeChild).
+
+  // If the cached shmem exists, has the correct size and isn't held by anything
+  // other than us, recycle it.
+  bool alreadyAllocated = RefCountedShm::IsValid(mResourceShm);
+  if (alreadyAllocated) {
+    if (RefCountedShm::GetSize(mResourceShm) == aSize
+        && RefCountedShm::GetReferenceCount(mResourceShm) <= 1) {
+      MOZ_ASSERT(RefCountedShm::GetReferenceCount(mResourceShm) == 1);
+      aShm = mResourceShm;
+      return true;
+    }
+  }
+
+  // If there was no cached shmem or we couldn't recycle it, alloc a new one.
+  if (!RefCountedShm::Alloc(this, aSize, aShm)) {
+    return false;
+  }
+
+  // Now that we have a valid shmem, put it in the cache if we don't have one
+  // yet.
+  if (!alreadyAllocated) {
+    mResourceShm = aShm;
+    RefCountedShm::AddRef(aShm);
+  }
+
+  return true;
+}
+
+void
+WebRenderBridgeChild::DeallocResourceShmem(RefCountedShmem& aShm)
+{
+  if (!RefCountedShm::IsValid(aShm)) {
+    return;
+  }
+  MOZ_ASSERT(RefCountedShm::GetReferenceCount(aShm) == 0);
+
+  RefCountedShm::Dealloc(this, aShm);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -149,16 +149,27 @@ public:
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
   ipc::IShmemAllocator* GetShmemAllocator();
 
   virtual bool IsThreadSafe() const override { return false; }
 
   virtual RefPtr<KnowsCompositor> GetForMedia() override;
 
+  /// Alloc a specific type of shmem that is intended for use in
+  /// IpcResourceUpdateQueue only, and cache at most one of them,
+  /// when called multiple times.
+  ///
+  /// Do not use this for anything else.
+  bool AllocResourceShmem(size_t aSize, RefCountedShmem& aShm);
+  /// Dealloc shared memory that was allocated with AllocResourceShmem.
+  /// 
+  /// Do not use this for anything else.
+  void DeallocResourceShmem(RefCountedShmem& aShm);
+
 private:
   friend class CompositorBridgeChild;
 
   ~WebRenderBridgeChild();
 
   wr::ExternalImageId GetNextExternalImageId();
 
   // CompositableForwarder
@@ -219,14 +230,16 @@ private:
 
   uint32_t mFontKeysDeleted;
   nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
 
   uint32_t mFontInstanceKeysDeleted;
   nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
 
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
+
+  RefCountedShmem mResourceShm;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeChild_h
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -261,30 +261,19 @@ WebRenderBridgeParent::Destroy()
 {
   if (mDestroyed) {
     return;
   }
   mDestroyed = true;
   ClearResources();
 }
 
-void
-WebRenderBridgeParent::DeallocShmems(nsTArray<ipc::Shmem>& aShmems)
-{
-  if (IPCOpen()) {
-    for (auto& shm : aShmems) {
-      DeallocShmem(shm);
-    }
-  }
-  aShmems.Clear();
-}
-
 bool
 WebRenderBridgeParent::UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
-                                       const nsTArray<ipc::Shmem>& aSmallShmems,
+                                       const nsTArray<RefCountedShmem>& aSmallShmems,
                                        const nsTArray<ipc::Shmem>& aLargeShmems,
                                        wr::ResourceUpdateQueue& aUpdates)
 {
   wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
 
   for (const auto& cmd : aResourceUpdates) {
     switch (cmd.type()) {
       case OpUpdateResource::TOpAddImage: {
@@ -445,36 +434,37 @@ WebRenderBridgeParent::AddExternalImage(
   aResources.AddImage(keys[0], descriptor, data);
   dSurf->Unmap();
 
   return true;
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                           nsTArray<ipc::Shmem>&& aSmallShmems,
+                                           nsTArray<RefCountedShmem>&& aSmallShmems,
                                            nsTArray<ipc::Shmem>&& aLargeShmems)
 {
   if (mDestroyed) {
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   wr::ResourceUpdateQueue updates;
 
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, updates)) {
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
     IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
   }
 
-  mApi->UpdateResources(updates);
-  DeallocShmems(aSmallShmems);
-  DeallocShmems(aLargeShmems);
+  wr::TransactionBuilder txn;
+  txn.UpdateResources(updates);
+  mApi->SendTransaction(txn);
+
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -572,65 +562,67 @@ WebRenderBridgeParent::RecvSetDisplayLis
                                           InfallibleTArray<OpDestroy>&& aToDestroy,
                                           const uint64_t& aFwdTransactionId,
                                           const uint64_t& aTransactionId,
                                           const wr::LayoutSize& aContentSize,
                                           ipc::ByteBuf&& dl,
                                           const wr::BuiltDisplayListDescriptor& dlDesc,
                                           const WebRenderScrollData& aScrollData,
                                           nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                          nsTArray<ipc::Shmem>&& aSmallShmems,
+                                          nsTArray<RefCountedShmem>&& aSmallShmems,
                                           nsTArray<ipc::Shmem>&& aLargeShmems,
                                           const wr::IdNamespace& aIdNamespace,
                                           const TimeStamp& aTxnStartTime,
                                           const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   AUTO_PROFILER_TRACING("Paint", "SetDisplayList");
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   uint32_t wrEpoch = GetNextWrEpoch();
 
   mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+
   ProcessWebRenderParentCommands(aCommands);
 
   wr::ResourceUpdateQueue resources;
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
+  wr::TransactionBuilder txn;
+  txn.UpdateResources(resources);
 
   wr::Vec<uint8_t> dlData(Move(dl));
 
   // If id namespaces do not match, it means the command is obsolete, probably
   // because the tab just moved to a new window.
   // In that case do not send the commands to webrender.
   if (mIdNamespace == aIdNamespace) {
     if (mWidget) {
       LayoutDeviceIntSize size = mWidget->GetClientSize();
-      mApi->SetWindowParameters(size);
+      txn.SetWindowParameters(size);
     }
     gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
-    mApi->SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
-                        mPipelineId, aContentSize,
-                        dlDesc, dlData,
-                        resources);
+    txn.SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
+                       mPipelineId, aContentSize,
+                       dlDesc, dlData);
+
+    mApi->SendTransaction(txn);
 
     ScheduleGenerateFrame();
 
     if (ShouldParentObserveEpoch()) {
       mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
     }
   }
 
@@ -641,18 +633,18 @@ WebRenderBridgeParent::RecvSetDisplayLis
 
   if (mIdNamespace != aIdNamespace) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     TimeStamp now = TimeStamp::Now();
     mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
   }
 
-  DeallocShmems(aSmallShmems);
-  DeallocShmems(aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                             InfallibleTArray<OpDestroy>&& aToDestroy,
                                             const uint64_t& aFwdTransactionId,
@@ -681,36 +673,36 @@ WebRenderBridgeParent::RecvEmptyTransact
     ProcessWebRenderParentCommands(aCommands);
     ScheduleGenerateFrame();
   }
 
   mScrollData.SetFocusTarget(aFocusTarget);
   UpdateAPZ(false);
 
   if (!aCommands.IsEmpty()) {
+    wr::TransactionBuilder txn;
     uint32_t wrEpoch = GetNextWrEpoch();
-    // Send empty UpdatePipelineResources to WebRender just to notify a new epoch.
-    // The epoch is used to know a timing of calling DidComposite().
-    // This is much simpler than tracking an epoch of AsyncImagePipeline.
-    wr::ResourceUpdateQueue resourceUpdates;
-    mApi->UpdatePipelineResources(resourceUpdates, mPipelineId, wr::NewEpoch(wrEpoch));
+    txn.UpdateEpoch(mPipelineId, wr::NewEpoch(wrEpoch));
+    mApi->SendTransaction(txn);
+
     HoldPendingTransactionId(wrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
   } else {
     bool sendDidComposite = false;
     if (mPendingTransactionIds.empty()) {
       sendDidComposite = true;
     }
     HoldPendingTransactionId(mWrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
     // If WebRenderBridgeParent does not have pending DidComposites,
     // send DidComposite now.
     if (sendDidComposite) {
       TimeStamp now = TimeStamp::Now();
       mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
     }
   }
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   mScrollData.SetFocusTarget(aFocusTarget);
   UpdateAPZ(false);
@@ -878,20 +870,23 @@ WebRenderBridgeParent::RecvRemovePipelin
     return IPC_OK();
   }
 
   WebRenderImageHost* wrHost = mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get();
   if (!wrHost) {
     return IPC_OK();
   }
 
+  wr::TransactionBuilder txn;
+
   wrHost->ClearWrBridge();
-  mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId);
+  mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId, txn);
+  txn.RemovePipeline(aPipelineId);
+  mApi->SendTransaction(txn);
   mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
-  mApi->RemovePipeline(aPipelineId);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                              const CompositableHandle& aHandle)
 {
   if (mDestroyed) {
@@ -949,17 +944,19 @@ mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvClearCachedResources()
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), false);
 
   // Clear resources
-  mApi->ClearDisplayList(wr::NewEpoch(GetNextWrEpoch()), mPipelineId);
+  wr::TransactionBuilder txn;
+  txn.ClearDisplayList(wr::NewEpoch(GetNextWrEpoch()), mPipelineId);
+  mApi->SendTransaction(txn);
   // Schedule generate frame to clean up Pipeline
   ScheduleGenerateFrame();
   // Remove animations.
   for (std::unordered_set<uint64_t>::iterator iter = mActiveAnimations.begin(); iter != mActiveAnimations.end(); iter++) {
     mAnimStorage->ClearById(*iter);
   }
   mActiveAnimations.clear();
   return IPC_OK();
@@ -1238,21 +1235,25 @@ WebRenderBridgeParent::CompositeToTarget
 
   wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId());
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
   mApi->SetFrameStartTime(startTime);
 #endif
 
+  wr::TransactionBuilder txn;
+
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
-    mApi->GenerateFrame(opacityArray, transformArray);
-  } else {
-    mApi->GenerateFrame();
+    txn.UpdateDynamicProperties(opacityArray, transformArray);
   }
+
+  txn.GenerateFrame();
+
+  mApi->SendTransaction(txn);
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(uint32_t aWrEpoch,
                                                 uint64_t aTransactionId,
                                                 const TimeStamp& aTxnStartTime,
                                                 const TimeStamp& aFwdTime)
 {
@@ -1382,34 +1383,39 @@ WebRenderBridgeParent::Resume()
 void
 WebRenderBridgeParent::ClearResources()
 {
   if (!mApi) {
     return;
   }
 
   uint32_t wrEpoch = GetNextWrEpoch();
-  mApi->ClearDisplayList(wr::NewEpoch(wrEpoch), mPipelineId);
+
+  wr::TransactionBuilder txn;
+  txn.ClearDisplayList(wr::NewEpoch(wrEpoch), mPipelineId);
+
   // Schedule generate frame to clean up Pipeline
   ScheduleGenerateFrame();
   // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction.
   for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     RefPtr<WebRenderImageHost> host = iter.Data();
     host->ClearWrBridge();
-    mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId);
+    mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
   }
   mAsyncCompositables.Clear();
 
   mAsyncImageManager->RemovePipeline(mPipelineId, wr::NewEpoch(wrEpoch));
-  mApi->RemovePipeline(mPipelineId);
+  txn.RemovePipeline(mPipelineId);
+
+  mApi->SendTransaction(txn);
 
   for (std::unordered_set<uint64_t>::iterator iter = mActiveAnimations.begin(); iter != mActiveAnimations.end(); iter++) {
     mAnimStorage->ClearById(*iter);
   }
   mActiveAnimations.clear();
 
   if (mWidget) {
     mCompositorScheduler->Destroy();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -69,29 +69,29 @@ public:
 
   mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
 
   mozilla::ipc::IPCResult RecvCreate(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvShutdownSync() override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvUpdateResources(nsTArray<OpUpdateResource>&& aUpdates,
-                                              nsTArray<ipc::Shmem>&& aSmallShmems,
+                                              nsTArray<RefCountedShmem>&& aSmallShmems,
                                               nsTArray<ipc::Shmem>&& aLargeShmems) override;
   mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntSize& aSize,
                                              InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                              InfallibleTArray<OpDestroy>&& aToDestroy,
                                              const uint64_t& aFwdTransactionId,
                                              const uint64_t& aTransactionId,
                                              const wr::LayoutSize& aContentSize,
                                              ipc::ByteBuf&& dl,
                                              const wr::BuiltDisplayListDescriptor& dlDesc,
                                              const WebRenderScrollData& aScrollData,
                                              nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                             nsTArray<ipc::Shmem>&& aSmallShmems,
+                                             nsTArray<RefCountedShmem>&& aSmallShmems,
                                              nsTArray<ipc::Shmem>&& aLargeShmems,
                                              const wr::IdNamespace& aIdNamespace,
                                              const TimeStamp& aTxnStartTime,
                                              const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                                InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                                InfallibleTArray<OpDestroy>&& aToDestroy,
                                                const uint64_t& aFwdTransactionId,
@@ -187,23 +187,21 @@ public:
   void ScheduleGenerateFrame();
 
   void UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                        wr::WebRenderAPI* aApi,
                        AsyncImagePipelineManager* aImageMgr,
                        CompositorAnimationStorage* aAnimStorage);
 
 private:
-  void DeallocShmems(nsTArray<ipc::Shmem>& aShmems);
-
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
-                       const nsTArray<ipc::Shmem>& aSmallShmems,
+                       const nsTArray<RefCountedShmem>& aSmallShmems,
                        const nsTArray<ipc::Shmem>& aLargeShmems,
                        wr::ResourceUpdateQueue& aUpdates);
   bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
                         wr::ResourceUpdateQueue& aResources);
 
   uint64_t GetLayersId() const;
   void ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands);
 
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -262,17 +262,17 @@ WebRenderLayerManager::EndTransactionWit
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
   DiscardCompositorAnimations();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   wr::LayoutSize contentSize { (float)size.width, (float)size.height };
   wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize, mLastDisplayListSize);
-  wr::IpcResourceUpdateQueue resourceUpdates(WrBridge()->GetShmemAllocator());
+  wr::IpcResourceUpdateQueue resourceUpdates(WrBridge());
 
   mWebRenderCommandBuilder.BuildWebRenderCommands(builder,
                                                   resourceUpdates,
                                                   aDisplayList,
                                                   aDisplayListBuilder,
                                                   mScrollData,
                                                   contentSize);
 
@@ -408,17 +408,17 @@ void
 WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
 {
   mImageKeysToDeleteLater.AppendElement(key);
 }
 
 void
 WebRenderLayerManager::DiscardImages()
 {
-  wr::IpcResourceUpdateQueue resources(WrBridge()->GetShmemAllocator());
+  wr::IpcResourceUpdateQueue resources(WrBridge());
   for (const auto& key : mImageKeysToDeleteLater) {
     resources.DeleteImage(key);
   }
   for (const auto& key : mImageKeysToDelete) {
     resources.DeleteImage(key);
   }
   mImageKeysToDeleteLater.Clear();
   mImageKeysToDelete.Clear();
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -136,16 +136,108 @@ public:
     aRenderThread.RemoveRenderer(aWindowId);
     layers::AutoCompleteTask complete(mTask);
   }
 
 private:
   layers::SynchronousTask* mTask;
 };
 
+
+TransactionBuilder::TransactionBuilder()
+{
+  mTxn = wr_transaction_new();
+}
+
+TransactionBuilder::~TransactionBuilder()
+{
+  wr_transaction_delete(mTxn);
+}
+
+void
+TransactionBuilder::UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch)
+{
+  wr_transaction_update_epoch(mTxn, aPipelineId, aEpoch);
+}
+
+void
+TransactionBuilder::SetRootPipeline(PipelineId aPipelineId)
+{
+  wr_transaction_set_root_pipeline(mTxn, aPipelineId);
+}
+
+void
+TransactionBuilder::RemovePipeline(PipelineId aPipelineId)
+{
+  wr_transaction_remove_pipeline(mTxn, aPipelineId);
+}
+
+void
+TransactionBuilder::SetDisplayList(gfx::Color aBgColor,
+                                   Epoch aEpoch,
+                                   mozilla::LayerSize aViewportSize,
+                                   wr::WrPipelineId pipeline_id,
+                                   const wr::LayoutSize& content_size,
+                                   wr::BuiltDisplayListDescriptor dl_descriptor,
+                                   wr::Vec<uint8_t>& dl_data)
+{
+  wr_transaction_set_display_list(mTxn,
+                                  aEpoch,
+                                  ToColorF(aBgColor),
+                                  aViewportSize.width, aViewportSize.height,
+                                  pipeline_id,
+                                  content_size,
+                                  dl_descriptor,
+                                  &dl_data.inner);
+}
+
+void
+TransactionBuilder::ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipelineId)
+{
+  wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId);
+}
+
+void
+TransactionBuilder::GenerateFrame()
+{
+  wr_transaction_generate_frame(mTxn);
+}
+
+void
+TransactionBuilder::UpdateDynamicProperties(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
+                                     const nsTArray<wr::WrTransformProperty>& aTransformArray)
+{
+  wr_transaction_update_dynamic_properties(mTxn,
+                                           aOpacityArray.IsEmpty() ?
+                                             nullptr : aOpacityArray.Elements(),
+                                           aOpacityArray.Length(),
+                                           aTransformArray.IsEmpty() ?
+                                             nullptr : aTransformArray.Elements(),
+                                           aTransformArray.Length());
+}
+
+bool
+TransactionBuilder::IsEmpty() const
+{
+  return wr_transaction_is_empty(mTxn);
+}
+
+void
+TransactionBuilder::SetWindowParameters(LayoutDeviceIntSize size)
+{
+  wr_transaction_set_window_parameters(mTxn, size.width, size.height);
+}
+
+void
+TransactionBuilder::UpdateResources(ResourceUpdateQueue& aUpdates)
+{
+  wr_transaction_update_resources(mTxn, aUpdates.Raw());
+}
+
+
 /*static*/ void
 WebRenderAPI::InitExternalLogHandler()
 {
   // Redirect the webrender's log to gecko's log system.
   // The current log level is "error".
   mozilla::wr::wr_init_external_log_handler(wr::LogLevelFilter::Error);
 }
 
@@ -216,16 +308,22 @@ WebRenderAPI::~WebRenderAPI()
     RunOnRenderThread(Move(event));
     task.Wait();
   }
 
   wr_api_delete(mDocHandle);
 }
 
 void
+WebRenderAPI::SendTransaction(TransactionBuilder& aTxn)
+{
+  wr_api_send_transaction(mDocHandle, aTxn.Raw());
+}
+
+void
 WebRenderAPI::UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
                                    const layers::FrameMetrics::ViewID& aScrollId,
                                    const wr::LayoutPoint& aScrollPosition)
 {
   wr_scroll_layer_with_id(mDocHandle, aPipelineId, aScrollId, aScrollPosition);
 }
 
 bool
@@ -236,68 +334,16 @@ WebRenderAPI::HitTest(const wr::WorldPoi
 {
   static_assert(sizeof(gfx::CompositorHitTestInfo) == sizeof(uint16_t),
                 "CompositorHitTestInfo should be u16-sized");
   return wr_api_hit_test(mDocHandle, aPoint,
           &aOutPipelineId, &aOutScrollId, (uint16_t*)&aOutHitInfo);
 }
 
 void
-WebRenderAPI::GenerateFrame()
-{
-  wr_api_generate_frame(mDocHandle);
-}
-
-void
-WebRenderAPI::GenerateFrame(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
-                            const nsTArray<wr::WrTransformProperty>& aTransformArray)
-{
-  wr_api_generate_frame_with_properties(mDocHandle,
-                                        aOpacityArray.IsEmpty() ?
-                                          nullptr : aOpacityArray.Elements(),
-                                        aOpacityArray.Length(),
-                                        aTransformArray.IsEmpty() ?
-                                          nullptr : aTransformArray.Elements(),
-                                        aTransformArray.Length());
-}
-
-void
-WebRenderAPI::SetDisplayList(gfx::Color aBgColor,
-                             Epoch aEpoch,
-                             mozilla::LayerSize aViewportSize,
-                             wr::WrPipelineId pipeline_id,
-                             const LayoutSize& content_size,
-                             wr::BuiltDisplayListDescriptor dl_descriptor,
-                             wr::Vec<uint8_t>& dl_data,
-                             ResourceUpdateQueue& aResources)
-{
-  wr_api_set_display_list(mDocHandle,
-                          ToColorF(aBgColor),
-                          aEpoch,
-                          aViewportSize.width, aViewportSize.height,
-                          pipeline_id,
-                          content_size,
-                          dl_descriptor,
-                          &dl_data.inner,
-                          aResources.Raw());
-}
-
-void
-WebRenderAPI::ClearDisplayList(Epoch aEpoch, wr::WrPipelineId pipeline_id)
-{
-  wr_api_clear_display_list(mDocHandle, aEpoch, pipeline_id);
-}
-
-void
-WebRenderAPI::SetWindowParameters(LayoutDeviceIntSize size)
-{
-  wr_api_set_window_parameters(mDocHandle, size.width, size.height);
-}
-
-void
 WebRenderAPI::Readback(gfx::IntSize size,
                        uint8_t *buffer,
                        uint32_t buffer_size)
 {
     class Readback : public RendererEvent
     {
         public:
             explicit Readback(layers::SynchronousTask* aTask,
@@ -443,40 +489,16 @@ WebRenderAPI::WaitFlushed()
     auto event = MakeUnique<WaitFlushedEvent>(&task);
     // This event will be passed from wr_backend thread to renderer thread. That
     // implies that all frame data have been processed when the renderer runs this event.
     RunOnRenderThread(Move(event));
 
     task.Wait();
 }
 
-void
-WebRenderAPI::SetRootPipeline(PipelineId aPipeline)
-{
-  wr_api_set_root_pipeline(mDocHandle, aPipeline);
-}
-
-void
-WebRenderAPI::RemovePipeline(PipelineId aPipeline)
-{
-  wr_api_remove_pipeline(mDocHandle, aPipeline);
-}
-
-void
-WebRenderAPI::UpdateResources(ResourceUpdateQueue& aUpdates)
-{
-  wr_api_update_resources(mDocHandle, aUpdates.Raw());
-}
-
-void
-WebRenderAPI::UpdatePipelineResources(ResourceUpdateQueue& aUpdates, PipelineId aPipeline, Epoch aEpoch)
-{
-  wr_api_update_pipeline_resources(mDocHandle, aPipeline, aEpoch, aUpdates.Raw());
-}
-
 ResourceUpdateQueue::ResourceUpdateQueue()
 {
   mUpdates = wr_resource_updates_new();
 }
 
 ResourceUpdateQueue::ResourceUpdateQueue(ResourceUpdateQueue&& aFrom)
 {
   mUpdates = aFrom.mUpdates;
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -118,16 +118,54 @@ public:
 
 protected:
   explicit ResourceUpdateQueue(wr::ResourceUpdates* aUpdates)
   : mUpdates(aUpdates) {}
 
   wr::ResourceUpdates* mUpdates;
 };
 
+class TransactionBuilder {
+public:
+  TransactionBuilder();
+
+  ~TransactionBuilder();
+
+  void UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch);
+
+  void SetRootPipeline(PipelineId aPipelineId);
+
+  void RemovePipeline(PipelineId aPipelineId);
+
+  void SetDisplayList(gfx::Color aBgColor,
+                      Epoch aEpoch,
+                      mozilla::LayerSize aViewportSize,
+                      wr::WrPipelineId pipeline_id,
+                      const wr::LayoutSize& content_size,
+                      wr::BuiltDisplayListDescriptor dl_descriptor,
+                      wr::Vec<uint8_t>& dl_data);
+
+  void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
+
+  void GenerateFrame();
+
+  void UpdateDynamicProperties(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
+                               const nsTArray<wr::WrTransformProperty>& aTransformArray);
+
+  void SetWindowParameters(LayoutDeviceIntSize size);
+
+  void UpdateResources(ResourceUpdateQueue& aUpdates);
+
+  bool IsEmpty() const;
+
+  Transaction* Raw() { return mTxn; }
+protected:
+  Transaction* mTxn;
+};
+
 class WebRenderAPI
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderAPI);
 
 public:
   /// This can be called on the compositor thread only.
   static already_AddRefed<WebRenderAPI> Create(layers::CompositorBridgeParentBase* aBridge,
                                                RefPtr<widget::CompositorWidget>&& aWidget,
@@ -144,40 +182,17 @@ public:
   void UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
                             const layers::FrameMetrics::ViewID& aScrollId,
                             const wr::LayoutPoint& aScrollPosition);
   bool HitTest(const wr::WorldPoint& aPoint,
                wr::WrPipelineId& aOutPipelineId,
                layers::FrameMetrics::ViewID& aOutScrollId,
                gfx::CompositorHitTestInfo& aOutHitInfo);
 
-  void GenerateFrame();
-  void GenerateFrame(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
-                     const nsTArray<wr::WrTransformProperty>& aTransformArray);
-
-  void SetWindowParameters(LayoutDeviceIntSize size);
-
-  void SetDisplayList(gfx::Color aBgColor,
-                      Epoch aEpoch,
-                      mozilla::LayerSize aViewportSize,
-                      wr::WrPipelineId pipeline_id,
-                      const wr::LayoutSize& content_size,
-                      wr::BuiltDisplayListDescriptor dl_descriptor,
-                      wr::Vec<uint8_t>& dl_data,
-                      ResourceUpdateQueue& aResources);
-
-  void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId pipeline_id);
-
-  void SetRootPipeline(wr::PipelineId aPipeline);
-
-  void RemovePipeline(wr::PipelineId aPipeline);
-
-  void UpdateResources(ResourceUpdateQueue& aUpdates);
-
-  void UpdatePipelineResources(ResourceUpdateQueue& aUpdates, PipelineId aPipeline, Epoch aEpoch);
+  void SendTransaction(TransactionBuilder& aTxn);
 
   void SetFrameStartTime(const TimeStamp& aTime);
 
   void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
 
   void Readback(gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize);
 
   void Pause();
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -806,16 +806,158 @@ pub unsafe extern "C" fn wr_api_delete(d
     let handle = Box::from_raw(dh);
     if handle.document_id.0 == handle.api.get_namespace_id() {
         handle.api.delete_document(handle.document_id);
         handle.api.shut_down();
     }
 }
 
 #[no_mangle]
+pub extern "C" fn wr_transaction_new() -> *mut Transaction {
+    Box::into_raw(Box::new(Transaction::new()))
+}
+
+/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
+#[no_mangle]
+pub extern "C" fn wr_transaction_delete(txn: *mut Transaction) {
+    unsafe { let _ = Box::from_raw(txn); }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_is_empty(txn: &Transaction) -> bool {
+    txn.is_empty()
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_update_epoch(
+    txn: &mut Transaction,
+    pipeline_id: WrPipelineId,
+    epoch: WrEpoch,
+) {
+    txn.update_epoch(pipeline_id, epoch);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_root_pipeline(
+    txn: &mut Transaction,
+    pipeline_id: WrPipelineId,
+) {
+    txn.set_root_pipeline(pipeline_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_remove_pipeline(
+    txn: &mut Transaction,
+    pipeline_id: WrPipelineId,
+) {
+    txn.remove_pipeline(pipeline_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_display_list(
+    txn: &mut Transaction,
+    epoch: WrEpoch,
+    background: ColorF,
+    viewport_width: f32,
+    viewport_height: f32,
+    pipeline_id: WrPipelineId,
+    content_size: LayoutSize,
+    dl_descriptor: BuiltDisplayListDescriptor,
+    dl_data: &mut WrVecU8,
+) {
+    let color = if background.a == 0.0 { None } else { Some(background) };
+
+    // See the documentation of set_display_list in api.rs. I don't think
+    // it makes a difference in gecko at the moment(until APZ is figured out)
+    // but I suppose it is a good default.
+    let preserve_frame_state = true;
+
+    let dl_vec = dl_data.flush_into_vec();
+    let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
+
+    txn.set_display_list(
+        epoch,
+        color,
+        LayoutSize::new(viewport_width, viewport_height),
+        (pipeline_id, content_size, dl),
+        preserve_frame_state,
+    );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_update_resources(
+    txn: &mut Transaction,
+    resource_updates: &mut ResourceUpdates
+) {
+    txn.update_resources(mem::replace(resource_updates, ResourceUpdates::new()));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_window_parameters(
+    txn: &mut Transaction,
+    window_width: i32,
+    window_height: i32,
+) {
+    let size = DeviceUintSize::new(window_width as u32, window_height as u32);
+    txn.set_window_parameters(
+        size,
+        DeviceUintRect::new(DeviceUintPoint::new(0, 0), size),
+        1.0,
+    );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction) {
+    txn.generate_frame();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_update_dynamic_properties(
+    txn: &mut Transaction,
+    opacity_array: *const WrOpacityProperty,
+    opacity_count: usize,
+    transform_array: *const WrTransformProperty,
+    transform_count: usize,
+) {
+    debug_assert!(transform_count > 0 || opacity_count > 0);
+
+    let mut properties = DynamicProperties {
+        transforms: Vec::new(),
+        floats: Vec::new(),
+    };
+
+    if transform_count > 0 {
+        let transform_slice = make_slice(transform_array, transform_count);
+
+        for element in transform_slice.iter() {
+            let prop = PropertyValue {
+                key: PropertyBindingKey::new(element.id),
+                value: element.transform.into(),
+            };
+
+            properties.transforms.push(prop);
+        }
+    }
+
+    if opacity_count > 0 {
+        let opacity_slice = make_slice(opacity_array, opacity_count);
+
+        for element in opacity_slice.iter() {
+            let prop = PropertyValue {
+                key: PropertyBindingKey::new(element.id),
+                value: element.opacity,
+            };
+            properties.floats.push(prop);
+        }
+    }
+
+    txn.update_dynamic_properties(properties);
+}
+
+#[no_mangle]
 pub extern "C" fn wr_resource_updates_add_image(
     resources: &mut ResourceUpdates,
     image_key: WrImageKey,
     descriptor: &WrImageDescriptor,
     bytes: &mut WrVecU8,
 ) {
     resources.add_image(
         image_key,
@@ -921,160 +1063,45 @@ pub extern "C" fn wr_resource_updates_up
 pub extern "C" fn wr_resource_updates_delete_image(
     resources: &mut ResourceUpdates,
     key: WrImageKey
 ) {
     resources.delete_image(key);
 }
 
 #[no_mangle]
-pub extern "C" fn wr_api_update_resources(
+pub extern "C" fn wr_api_send_transaction(
     dh: &mut DocumentHandle,
-    resources: &mut ResourceUpdates
-) {
-    let resource_updates = mem::replace(resources, ResourceUpdates::new());
-    dh.api.update_resources(resource_updates);
-}
-
-#[no_mangle]
-pub extern "C" fn wr_api_update_pipeline_resources(
-    dh: &mut DocumentHandle,
-    pipeline_id: WrPipelineId,
-    epoch: WrEpoch,
-    resources: &mut ResourceUpdates
+    transaction: &mut Transaction
 ) {
-    let resource_updates = mem::replace(resources, ResourceUpdates::new());
-    dh.api.update_pipeline_resources(resource_updates, dh.document_id, pipeline_id, epoch);
-}
-
-
-#[no_mangle]
-pub extern "C" fn wr_api_set_root_pipeline(dh: &mut DocumentHandle,
-                                           pipeline_id: WrPipelineId) {
-    dh.api.set_root_pipeline(dh.document_id, pipeline_id);
-}
-
-#[no_mangle]
-pub extern "C" fn wr_api_remove_pipeline(dh: &mut DocumentHandle,
-                                         pipeline_id: WrPipelineId) {
-    dh.api.remove_pipeline(dh.document_id, pipeline_id);
+    if transaction.is_empty() {
+        return;
+    }
+    let txn = mem::replace(transaction, Transaction::new());
+    dh.api.send_transaction(dh.document_id, txn);
 }
 
 #[no_mangle]
-pub extern "C" fn wr_api_set_window_parameters(dh: &mut DocumentHandle,
-                                               width: i32,
-                                               height: i32) {
-    let size = DeviceUintSize::new(width as u32, height as u32);
-    dh.api.set_window_parameters(dh.document_id,
-                                 size,
-                                 DeviceUintRect::new(DeviceUintPoint::new(0, 0), size),
-                                 1.0);
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn wr_api_set_display_list(
-    dh: &mut DocumentHandle,
-    color: ColorF,
-    epoch: WrEpoch,
-    viewport_width: f32,
-    viewport_height: f32,
-    pipeline_id: WrPipelineId,
-    content_size: LayoutSize,
-    dl_descriptor: BuiltDisplayListDescriptor,
-    dl_data: &mut WrVecU8,
-    resources: &mut ResourceUpdates,
-) {
-    let resource_updates = mem::replace(resources, ResourceUpdates::new());
-
-    let color = if color.a == 0.0 { None } else { Some(color) };
-
-    // See the documentation of set_display_list in api.rs. I don't think
-    // it makes a difference in gecko at the moment(until APZ is figured out)
-    // but I suppose it is a good default.
-    let preserve_frame_state = true;
-
-    let dl_vec = dl_data.flush_into_vec();
-    let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
-
-    dh.api.set_display_list(
-        dh.document_id,
-        epoch,
-        color,
-        LayoutSize::new(viewport_width, viewport_height),
-        (pipeline_id, content_size, dl),
-        preserve_frame_state,
-        resource_updates
-    );
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn wr_api_clear_display_list(
-    dh: &mut DocumentHandle,
+pub unsafe extern "C" fn wr_transaction_clear_display_list(
+    txn: &mut Transaction,
     epoch: WrEpoch,
     pipeline_id: WrPipelineId,
 ) {
     let preserve_frame_state = true;
     let frame_builder = WebRenderFrameBuilder::new(pipeline_id, LayoutSize::zero());
-    let resource_updates = ResourceUpdates::new();
 
-    dh.api.set_display_list(
-        dh.document_id,
+    txn.set_display_list(
         epoch,
         None,
         LayoutSize::new(0.0, 0.0),
         frame_builder.dl_builder.finalize(),
         preserve_frame_state,
-        resource_updates
     );
 }
 
-#[no_mangle]
-pub extern "C" fn wr_api_generate_frame(dh: &mut DocumentHandle) {
-    dh.api.generate_frame(dh.document_id, None);
-}
-
-#[no_mangle]
-pub extern "C" fn wr_api_generate_frame_with_properties(dh: &mut DocumentHandle,
-                                                        opacity_array: *const WrOpacityProperty,
-                                                        opacity_count: usize,
-                                                        transform_array: *const WrTransformProperty,
-                                                        transform_count: usize) {
-    let mut properties = DynamicProperties {
-        transforms: Vec::new(),
-        floats: Vec::new(),
-    };
-
-    if transform_count > 0 {
-        let transform_slice = make_slice(transform_array, transform_count);
-
-        for element in transform_slice.iter() {
-            let prop = PropertyValue {
-                key: PropertyBindingKey::new(element.id),
-                value: element.transform.into(),
-            };
-
-            properties.transforms.push(prop);
-        }
-    }
-
-    if opacity_count > 0 {
-        let opacity_slice = make_slice(opacity_array, opacity_count);
-
-        for element in opacity_slice.iter() {
-            let prop = PropertyValue {
-                key: PropertyBindingKey::new(element.id),
-                value: element.opacity,
-            };
-            properties.floats.push(prop);
-        }
-    }
-
-    dh.api.generate_frame(dh.document_id, Some(properties));
-}
-
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub extern "C" fn wr_api_send_external_event(dh: &mut DocumentHandle,
                                              evt: usize) {
     assert!(unsafe { !is_in_render_thread() });
 
     dh.api.send_external_event(ExternalEvent::from_raw(evt));
 }
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -252,16 +252,24 @@ struct LayerPixel;
 struct Renderer;
 
 // The resource updates for a given transaction (they must be applied in the same frame).
 struct ResourceUpdates;
 
 // Offset in number of tiles.
 struct Tiles;
 
+// A Transaction is a group of commands to apply atomically to a document.
+//
+// This mechanism ensures that:
+//  - no other message can be interleaved between two commands that need to be applied together.
+//  - no redundant work is performed if two commands in the same transaction cause the scene or
+//    the frame to be rebuilt.
+struct Transaction;
+
 // The default unit.
 struct UnknownUnit;
 
 template<typename T>
 struct Vec;
 
 // Geometry in the document's coordinate space (logical pixels).
 struct WorldPixel;
@@ -302,52 +310,16 @@ struct FontKey {
 };
 
 using WrFontKey = FontKey;
 
 using VecU8 = Vec<uint8_t>;
 
 using ArcVecU8 = Arc<VecU8>;
 
-struct Epoch {
-  uint32_t mHandle;
-
-  bool operator==(const Epoch& aOther) const {
-    return mHandle == aOther.mHandle;
-  }
-  bool operator<(const Epoch& aOther) const {
-    return mHandle < aOther.mHandle;
-  }
-  bool operator<=(const Epoch& aOther) const {
-    return mHandle <= aOther.mHandle;
-  }
-};
-
-using WrEpoch = Epoch;
-
-// This type carries no valuable semantics for WR. However, it reflects the fact that
-// clients (Servo) may generate pipelines by different semi-independent sources.
-// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
-// Having this extra Id field enables them to generate `PipelineId` without collision.
-using PipelineSourceId = uint32_t;
-
-// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
-// it's clonable, serializable, comparable, and hashable.
-struct PipelineId {
-  PipelineSourceId mNamespace;
-  uint32_t mHandle;
-
-  bool operator==(const PipelineId& aOther) const {
-    return mNamespace == aOther.mNamespace &&
-           mHandle == aOther.mHandle;
-  }
-};
-
-using WrPipelineId = PipelineId;
-
 template<typename T, typename U>
 struct TypedSize2D {
   T width;
   T height;
 
   bool operator==(const TypedSize2D& aOther) const {
     return width == aOther.width &&
            height == aOther.height;
@@ -384,122 +356,52 @@ struct WrVecU8 {
 
   bool operator==(const WrVecU8& aOther) const {
     return data == aOther.data &&
            length == aOther.length &&
            capacity == aOther.capacity;
   }
 };
 
-struct WrOpacityProperty {
-  uint64_t id;
-  float opacity;
-
-  bool operator==(const WrOpacityProperty& aOther) const {
-    return id == aOther.id &&
-           opacity == aOther.opacity;
-  }
-};
-
-// Geometry in a stacking context's local coordinate space (logical pixels).
-//
-// For now layout pixels are equivalent to layer pixels, but it may change.
-using LayoutPixel = LayerPixel;
-
-// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
-//
-// Transforms can be parametrized over the source and destination units, to describe a
-// transformation from a space to another.
-// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
-// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
-//
-// Transforms expose a set of convenience methods for pre- and post-transformations.
-// A pre-transformation corresponds to adding an operation that is applied before
-// the rest of the transformation, while a post-transformation adds an operation
-// that is applied after.
-template<typename T, typename Src, typename Dst>
-struct TypedTransform3D {
-  T m11;
-  T m12;
-  T m13;
-  T m14;
-  T m21;
-  T m22;
-  T m23;
-  T m24;
-  T m31;
-  T m32;
-  T m33;
-  T m34;
-  T m41;
-  T m42;
-  T m43;
-  T m44;
-
-  bool operator==(const TypedTransform3D& aOther) const {
-    return m11 == aOther.m11 &&
-           m12 == aOther.m12 &&
-           m13 == aOther.m13 &&
-           m14 == aOther.m14 &&
-           m21 == aOther.m21 &&
-           m22 == aOther.m22 &&
-           m23 == aOther.m23 &&
-           m24 == aOther.m24 &&
-           m31 == aOther.m31 &&
-           m32 == aOther.m32 &&
-           m33 == aOther.m33 &&
-           m34 == aOther.m34 &&
-           m41 == aOther.m41 &&
-           m42 == aOther.m42 &&
-           m43 == aOther.m43 &&
-           m44 == aOther.m44;
-  }
-};
-
-using LayoutTransform = TypedTransform3D<float, LayoutPixel, LayoutPixel>;
-
-struct WrTransformProperty {
-  uint64_t id;
-  LayoutTransform transform;
-};
-
 using WrIdNamespace = IdNamespace;
 
 // A 2d Point tagged with a unit.
 template<typename T, typename U>
 struct TypedPoint2D {
   T x;
   T y;
 
   bool operator==(const TypedPoint2D& aOther) const {
     return x == aOther.x &&
            y == aOther.y;
   }
 };
 
 using WorldPoint = TypedPoint2D<float, WorldPixel>;
 
-// Represents RGBA screen colors with floating point numbers.
-//
-// All components must be between 0.0 and 1.0.
-// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
-struct ColorF {
-  float r;
-  float g;
-  float b;
-  float a;
+// This type carries no valuable semantics for WR. However, it reflects the fact that
+// clients (Servo) may generate pipelines by different semi-independent sources.
+// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
+// Having this extra Id field enables them to generate `PipelineId` without collision.
+using PipelineSourceId = uint32_t;
 
-  bool operator==(const ColorF& aOther) const {
-    return r == aOther.r &&
-           g == aOther.g &&
-           b == aOther.b &&
-           a == aOther.a;
+// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
+// it's clonable, serializable, comparable, and hashable.
+struct PipelineId {
+  PipelineSourceId mNamespace;
+  uint32_t mHandle;
+
+  bool operator==(const PipelineId& aOther) const {
+    return mNamespace == aOther.mNamespace &&
+           mHandle == aOther.mHandle;
   }
 };
 
+using WrPipelineId = PipelineId;
+
 // A 2d Rectangle optionally tagged with a unit.
 template<typename T, typename U>
 struct TypedRect {
   TypedPoint2D<T, U> origin;
   TypedSize2D<T, U> size;
 
   bool operator==(const TypedRect& aOther) const {
     return origin == aOther.origin &&
@@ -611,16 +513,34 @@ struct BorderWidths {
   bool operator==(const BorderWidths& aOther) const {
     return left == aOther.left &&
            top == aOther.top &&
            right == aOther.right &&
            bottom == aOther.bottom;
   }
 };
 
+// Represents RGBA screen colors with floating point numbers.
+//
+// All components must be between 0.0 and 1.0.
+// An alpha value of 1.0 is opaque while 0.0 is fully transparent.
+struct ColorF {
+  float r;
+  float g;
+  float b;
+  float a;
+
+  bool operator==(const ColorF& aOther) const {
+    return r == aOther.r &&
+           g == aOther.g &&
+           b == aOther.b &&
+           a == aOther.a;
+  }
+};
+
 struct BorderSide {
   ColorF color;
   BorderStyle style;
 
   bool operator==(const BorderSide& aOther) const {
     return color == aOther.color &&
            style == aOther.style;
   }
@@ -688,16 +608,73 @@ struct WrAnimationProperty {
   uint64_t id;
 
   bool operator==(const WrAnimationProperty& aOther) const {
     return effect_type == aOther.effect_type &&
            id == aOther.id;
   }
 };
 
+// Geometry in a stacking context's local coordinate space (logical pixels).
+//
+// For now layout pixels are equivalent to layer pixels, but it may change.
+using LayoutPixel = LayerPixel;
+
+// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
+//
+// Transforms can be parametrized over the source and destination units, to describe a
+// transformation from a space to another.
+// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
+// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
+//
+// Transforms expose a set of convenience methods for pre- and post-transformations.
+// A pre-transformation corresponds to adding an operation that is applied before
+// the rest of the transformation, while a post-transformation adds an operation
+// that is applied after.
+template<typename T, typename Src, typename Dst>
+struct TypedTransform3D {
+  T m11;
+  T m12;
+  T m13;
+  T m14;
+  T m21;
+  T m22;
+  T m23;
+  T m24;
+  T m31;
+  T m32;
+  T m33;
+  T m34;
+  T m41;
+  T m42;
+  T m43;
+  T m44;
+
+  bool operator==(const TypedTransform3D& aOther) const {
+    return m11 == aOther.m11 &&
+           m12 == aOther.m12 &&
+           m13 == aOther.m13 &&
+           m14 == aOther.m14 &&
+           m21 == aOther.m21 &&
+           m22 == aOther.m22 &&
+           m23 == aOther.m23 &&
+           m24 == aOther.m24 &&
+           m31 == aOther.m31 &&
+           m32 == aOther.m32 &&
+           m33 == aOther.m33 &&
+           m34 == aOther.m34 &&
+           m41 == aOther.m41 &&
+           m42 == aOther.m42 &&
+           m43 == aOther.m43 &&
+           m44 == aOther.m44;
+  }
+};
+
+using LayoutTransform = TypedTransform3D<float, LayoutPixel, LayoutPixel>;
+
 struct WrFilterOp {
   WrFilterOpType filter_type;
   float argument;
   LayoutVector2D offset;
   ColorF color;
 
   bool operator==(const WrFilterOp& aOther) const {
     return filter_type == aOther.filter_type &&
@@ -774,16 +751,32 @@ struct WrWindowId {
   bool operator<(const WrWindowId& aOther) const {
     return mHandle < aOther.mHandle;
   }
   bool operator<=(const WrWindowId& aOther) const {
     return mHandle <= aOther.mHandle;
   }
 };
 
+struct Epoch {
+  uint32_t mHandle;
+
+  bool operator==(const Epoch& aOther) const {
+    return mHandle == aOther.mHandle;
+  }
+  bool operator<(const Epoch& aOther) const {
+    return mHandle < aOther.mHandle;
+  }
+  bool operator<=(const Epoch& aOther) const {
+    return mHandle <= aOther.mHandle;
+  }
+};
+
+using WrEpoch = Epoch;
+
 struct WrDebugFlags {
   uint32_t mBits;
 
   bool operator==(const WrDebugFlags& aOther) const {
     return mBits == aOther.mBits;
   }
 };
 
@@ -914,16 +907,31 @@ struct FontInstancePlatformOptions {
     return lcd_filter == aOther.lcd_filter &&
            hinting == aOther.hinting;
   }
 };
 #endif
 
 using DeviceUintRect = TypedRect<uint32_t, DevicePixel>;
 
+struct WrOpacityProperty {
+  uint64_t id;
+  float opacity;
+
+  bool operator==(const WrOpacityProperty& aOther) const {
+    return id == aOther.id &&
+           opacity == aOther.opacity;
+  }
+};
+
+struct WrTransformProperty {
+  uint64_t id;
+  LayoutTransform transform;
+};
+
 extern "C" {
 
 /* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/rlhunt/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
  */
@@ -964,105 +972,51 @@ extern bool is_in_main_thread();
 
 extern bool is_in_render_thread();
 
 WR_INLINE
 const VecU8 *wr_add_ref_arc(const ArcVecU8 *aArc)
 WR_FUNC;
 
 WR_INLINE
-void wr_api_clear_display_list(DocumentHandle *aDh,
-                               WrEpoch aEpoch,
-                               WrPipelineId aPipelineId)
-WR_FUNC;
-
-WR_INLINE
 void wr_api_clone(DocumentHandle *aDh,
                   DocumentHandle **aOutHandle)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_delete(DocumentHandle *aDh)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_api_finalize_builder(WrState *aState,
                              LayoutSize *aContentSize,
                              BuiltDisplayListDescriptor *aDlDescriptor,
                              WrVecU8 *aDlData)
 WR_FUNC;
 
 WR_INLINE
-void wr_api_generate_frame(DocumentHandle *aDh)
-WR_FUNC;
-
-WR_INLINE
-void wr_api_generate_frame_with_properties(DocumentHandle *aDh,
-                                           const WrOpacityProperty *aOpacityArray,
-                                           size_t aOpacityCount,
-                                           const WrTransformProperty *aTransformArray,
-                                           size_t aTransformCount)
-WR_FUNC;
-
-WR_INLINE
 WrIdNamespace wr_api_get_namespace(DocumentHandle *aDh)
 WR_FUNC;
 
 WR_INLINE
 bool wr_api_hit_test(DocumentHandle *aDh,
                      WorldPoint aPoint,
                      WrPipelineId *aOutPipelineId,
                      uint64_t *aOutScrollId,
                      uint16_t *aOutHitInfo)
 WR_FUNC;
 
 WR_INLINE
-void wr_api_remove_pipeline(DocumentHandle *aDh,
-                            WrPipelineId aPipelineId)
-WR_FUNC;
-
-WR_INLINE
 void wr_api_send_external_event(DocumentHandle *aDh,
                                 size_t aEvt)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
-void wr_api_set_display_list(DocumentHandle *aDh,
-                             ColorF aColor,
-                             WrEpoch aEpoch,
-                             float aViewportWidth,
-                             float aViewportHeight,
-                             WrPipelineId aPipelineId,
-                             LayoutSize aContentSize,
-                             BuiltDisplayListDescriptor aDlDescriptor,
-                             WrVecU8 *aDlData,
-                             ResourceUpdates *aResources)
-WR_FUNC;
-
-WR_INLINE
-void wr_api_set_root_pipeline(DocumentHandle *aDh,
-                              WrPipelineId aPipelineId)
-WR_FUNC;
-
-WR_INLINE
-void wr_api_set_window_parameters(DocumentHandle *aDh,
-                                  int32_t aWidth,
-                                  int32_t aHeight)
-WR_FUNC;
-
-WR_INLINE
-void wr_api_update_pipeline_resources(DocumentHandle *aDh,
-                                      WrPipelineId aPipelineId,
-                                      WrEpoch aEpoch,
-                                      ResourceUpdates *aResources)
-WR_FUNC;
-
-WR_INLINE
-void wr_api_update_resources(DocumentHandle *aDh,
-                             ResourceUpdates *aResources)
+void wr_api_send_transaction(DocumentHandle *aDh,
+                             Transaction *aTransaction)
 WR_FUNC;
 
 WR_INLINE
 void wr_clear_item_tag(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
 void wr_dec_ref_arc(const VecU8 *aArc)
@@ -1577,16 +1531,85 @@ WR_INLINE
 void wr_thread_pool_delete(WrThreadPool *aThreadPool)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 WrThreadPool *wr_thread_pool_new()
 WR_FUNC;
 
 WR_INLINE
+void wr_transaction_clear_display_list(Transaction *aTxn,
+                                       WrEpoch aEpoch,
+                                       WrPipelineId aPipelineId)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_delete(Transaction *aTxn)
+WR_DESTRUCTOR_SAFE_FUNC;
+
+WR_INLINE
+void wr_transaction_generate_frame(Transaction *aTxn)
+WR_FUNC;
+
+WR_INLINE
+bool wr_transaction_is_empty(const Transaction *aTxn)
+WR_FUNC;
+
+WR_INLINE
+Transaction *wr_transaction_new()
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_remove_pipeline(Transaction *aTxn,
+                                    WrPipelineId aPipelineId)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_set_display_list(Transaction *aTxn,
+                                     WrEpoch aEpoch,
+                                     ColorF aBackground,
+                                     float aViewportWidth,
+                                     float aViewportHeight,
+                                     WrPipelineId aPipelineId,
+                                     LayoutSize aContentSize,
+                                     BuiltDisplayListDescriptor aDlDescriptor,
+                                     WrVecU8 *aDlData)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_set_root_pipeline(Transaction *aTxn,
+                                      WrPipelineId aPipelineId)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_set_window_parameters(Transaction *aTxn,
+                                          int32_t aWindowWidth,
+                                          int32_t aWindowHeight)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_update_dynamic_properties(Transaction *aTxn,
+                                              const WrOpacityProperty *aOpacityArray,
+                                              size_t aOpacityCount,
+                                              const WrTransformProperty *aTransformArray,
+                                              size_t aTransformCount)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_update_epoch(Transaction *aTxn,
+                                 WrPipelineId aPipelineId,
+                                 WrEpoch aEpoch)
+WR_FUNC;
+
+WR_INLINE
+void wr_transaction_update_resources(Transaction *aTxn,
+                                     ResourceUpdates *aResourceUpdates)
+WR_FUNC;
+
+WR_INLINE
 void wr_vec_u8_free(WrVecU8 aV)
 WR_FUNC;
 
 WR_INLINE
 void wr_vec_u8_push_bytes(WrVecU8 *aV,
                           ByteSlice aBytes)
 WR_FUNC;
 
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -380,16 +380,20 @@ namespace detail {
 // store their dynamic slots pointer.
 
 struct ProxyReservedSlots
 {
     Value slots[1];
 
     static inline int offsetOfPrivateSlot();
 
+    static inline int offsetOfSlot(size_t slot) {
+        return offsetof(ProxyReservedSlots, slots[0]) + slot * sizeof(Value);
+    }
+
     void init(size_t nreserved) {
         for (size_t i = 0; i < nreserved; i++)
             slots[i] = JS::UndefinedValue();
     }
 
     ProxyReservedSlots(const ProxyReservedSlots&) = delete;
     void operator=(const ProxyReservedSlots&) = delete;
 };
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1000,17 +1000,17 @@ GeneralParser<ParseHandler, CharT>::pars
 
     Node pn = statementList(YieldIsName);
     if (!pn)
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
-    if (tt != TOK_EOF) {
+    if (tt != TokenKind::TOK_EOF) {
         error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt));
         return null();
     }
     if (foldConstants) {
         if (!FoldConstants(context, &pn, this))
             return null();
     }
 
@@ -1020,23 +1020,23 @@ GeneralParser<ParseHandler, CharT>::pars
 /*
  * Strict mode forbids introducing new definitions for 'eval', 'arguments',
  * 'let', 'static', 'yield', or for any strict mode reserved word.
  */
 bool
 ParserBase::isValidStrictBinding(PropertyName* name)
 {
     TokenKind tt = ReservedWordTokenKind(name);
-    if (tt == TOK_NAME) {
+    if (tt == TokenKind::TOK_NAME) {
         return name != context->names().eval &&
                name != context->names().arguments;
     }
-    return tt != TOK_LET &&
-           tt != TOK_STATIC &&
-           tt != TOK_YIELD &&
+    return tt != TokenKind::TOK_LET &&
+           tt != TokenKind::TOK_STATIC &&
+           tt != TokenKind::TOK_YIELD &&
            !TokenKindIsStrictReservedWord(tt);
 }
 
 /*
  * Returns true if all parameter names are valid strict mode binding names and
  * no duplicate parameter names are present.
  */
 bool
@@ -1697,22 +1697,22 @@ PerHandlerParser<ParseHandler>::propagat
 
 template <typename CharT>
 bool
 Parser<FullParseHandler, CharT>::checkStatementsEOF()
 {
     // This is designed to be paired with parsing a statement list at the top
     // level.
     //
-    // The statementList() call breaks on TOK_RC, so make sure we've
+    // The statementList() call breaks on TokenKind::TOK_RC, so make sure we've
     // reached EOF here.
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return false;
-    if (tt != TOK_EOF) {
+    if (tt != TokenKind::TOK_EOF) {
         error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
         return false;
     }
     return true;
 }
 
 template <typename Scope>
 typename Scope::Data*
@@ -2271,17 +2271,17 @@ Parser<FullParseHandler, CharT>::moduleB
         return null();
 
     MOZ_ASSERT(pn->isKind(ParseNodeKind::StatementList));
     mn->pn_body = pn;
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
-    if (tt != TOK_EOF) {
+    if (tt != TokenKind::TOK_EOF) {
         error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt));
         return null();
     }
 
     if (!modulesc->builder.buildTables())
         return null();
 
     // Check exported local bindings exist and mark them as closed over.
@@ -2550,26 +2550,26 @@ Parser<FullParseHandler, CharT>::standal
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     // Skip prelude.
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (asyncKind == FunctionAsyncKind::AsyncFunction) {
-        MOZ_ASSERT(tt == TOK_ASYNC);
+        MOZ_ASSERT(tt == TokenKind::TOK_ASYNC);
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
     }
-    MOZ_ASSERT(tt == TOK_FUNCTION);
+    MOZ_ASSERT(tt == TokenKind::TOK_FUNCTION);
 
     if (!tokenStream.getToken(&tt))
         return null();
     if (generatorKind == GeneratorKind::Generator) {
-        MOZ_ASSERT(tt == TOK_MUL);
+        MOZ_ASSERT(tt == TokenKind::TOK_MUL);
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     // Skip function name, if present.
     if (TokenKindIsPossibleIdentifierName(tt)) {
         MOZ_ASSERT(anyChars.currentName() == fun->explicitName());
     } else {
@@ -2603,17 +2603,17 @@ Parser<FullParseHandler, CharT>::standal
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
-    if (tt != TOK_EOF) {
+    if (tt != TokenKind::TOK_EOF) {
         error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt));
         return null();
     }
 
     if (!FoldConstants(context, &fn, this))
         return null();
 
     return fn;
@@ -2860,48 +2860,52 @@ ParserBase::newFunction(HandleAtom atom,
     }
     return fun;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::matchOrInsertSemicolon()
 {
-    TokenKind tt = TOK_EOF;
+    TokenKind tt = TokenKind::TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
-    if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
+    if (tt != TokenKind::TOK_EOF &&
+        tt != TokenKind::TOK_EOL &&
+        tt != TokenKind::TOK_SEMI &&
+        tt != TokenKind::TOK_RC)
+    {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
          *
          *   await f();
          *        ^
          *        |
          *        tried to insert semicolon here
          *
          * Detect this situation and throw an understandable error.  Otherwise
          * we'd throw a confusing "unexpected token: (unexpected token)" error.
          */
-        if (!pc->isAsync() && anyChars.currentToken().type == TOK_AWAIT) {
+        if (!pc->isAsync() && anyChars.currentToken().type == TokenKind::TOK_AWAIT) {
             error(JSMSG_AWAIT_OUTSIDE_ASYNC);
             return false;
         }
-        if (!yieldExpressionsSupported() && anyChars.currentToken().type == TOK_YIELD) {
+        if (!yieldExpressionsSupported() && anyChars.currentToken().type == TokenKind::TOK_YIELD) {
             error(JSMSG_YIELD_OUTSIDE_GENERATOR);
             return false;
         }
 
         /* Advance the scanner for proper error location reporting. */
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
         error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(tt));
         return false;
     }
     bool matched;
-    return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand);
+    return tokenStream.matchToken(&matched, TokenKind::TOK_SEMI, TokenStream::Operand);
 }
 
 bool
 ParserBase::leaveInnerFunction(ParseContext* outerpc)
 {
     MOZ_ASSERT(pc != outerpc);
 
     // If the current function allows super.property but cannot have a home
@@ -2995,17 +2999,17 @@ GeneralParser<ParseHandler, CharT>::func
         }
     }
 
     TokenPos firstTokenPos;
     if (!parenFreeArrow) {
         TokenKind tt;
         if (!tokenStream.getToken(&tt, firstTokenModifier))
             return false;
-        if (tt != TOK_LP) {
+        if (tt != TokenKind::TOK_LP) {
             error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         firstTokenPos = pos();
 
         // Record the start of function source (for FunctionToString). If we
         // are parenFreeArrow, we will set this below, after consuming the NAME.
@@ -3022,17 +3026,17 @@ GeneralParser<ParseHandler, CharT>::func
         return false;
     handler.setFunctionFormalParametersAndBody(funcpn, argsbody);
 
     bool hasArguments = false;
     if (parenFreeArrow) {
         hasArguments = true;
     } else {
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_RP, TokenStream::Operand))
             return false;
         if (!matched)
             hasArguments = true;
     }
     if (hasArguments) {
         bool hasRest = false;
         bool hasDefault = false;
         bool duplicatedParam = false;
@@ -3051,17 +3055,17 @@ GeneralParser<ParseHandler, CharT>::func
             }
 
             TokenKind tt;
             if (!tokenStream.getToken(&tt, argModifier))
                 return false;
             argModifier = TokenStream::Operand;
             MOZ_ASSERT_IF(parenFreeArrow, TokenKindIsPossibleIdentifier(tt));
 
-            if (tt == TOK_TRIPLEDOT) {
+            if (tt == TokenKind::TOK_TRIPLEDOT) {
                 if (IsSetterKind(kind)) {
                     error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 disallowDuplicateParams = true;
                 if (duplicatedParam) {
                     // Has duplicated args before the rest parameter.
@@ -3070,25 +3074,28 @@ GeneralParser<ParseHandler, CharT>::func
                 }
 
                 hasRest = true;
                 funbox->setHasRest();
 
                 if (!tokenStream.getToken(&tt))
                     return false;
 
-                if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) {
+                if (!TokenKindIsPossibleIdentifier(tt) &&
+                    tt != TokenKind::TOK_LB &&
+                    tt != TokenKind::TOK_LC)
+                {
                     error(JSMSG_NO_REST_NAME);
                     return false;
                 }
             }
 
             switch (tt) {
-              case TOK_LB:
-              case TOK_LC: {
+              case TokenKind::TOK_LB:
+              case TokenKind::TOK_LC: {
                 disallowDuplicateParams = true;
                 if (duplicatedParam) {
                     // Has duplicated args before the destructuring parameter.
                     error(JSMSG_BAD_DUP_ARGS);
                     return false;
                 }
 
                 funbox->hasDestructuringArgs = true;
@@ -3140,17 +3147,17 @@ GeneralParser<ParseHandler, CharT>::func
             // parentheses-free arrow function, |a => ...|, the '=' necessary
             // for a default expression would really be an assignment operator:
             // that is, |a = b => 42;| would parse as |a = (b => 42);|.  So we
             // should stop parsing arguments here.
             if (parenFreeArrow)
                 break;
 
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_ASSIGN, TokenStream::Operand))
                 return false;
             if (matched) {
                 // A default argument without parentheses would look like:
                 // a = expr => body, but both operators are right-associative, so
                 // that would have been parsed as a = (expr => body) instead.
                 // Therefore it's impossible to get here with parenFreeArrow.
                 MOZ_ASSERT(!parenFreeArrow);
 
@@ -3179,34 +3186,34 @@ GeneralParser<ParseHandler, CharT>::func
                 if (!handler.setLastFunctionFormalParameterDefault(funcpn, def_expr))
                     return false;
             }
 
             // Setter syntax uniquely requires exactly one argument.
             if (IsSetterKind(kind))
                 break;
 
-            if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
                 return false;
             if (!matched)
                 break;
 
             if (!hasRest) {
                 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                     return null();
-                if (tt == TOK_RP)
+                if (tt == TokenKind::TOK_RP)
                     break;
             }
         }
 
         if (!parenFreeArrow) {
             TokenKind tt;
             if (!tokenStream.getToken(&tt, TokenStream::Operand))
                 return false;
-            if (tt != TOK_RP) {
+            if (tt != TokenKind::TOK_RP) {
                 if (IsSetterKind(kind)) {
                     error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 error(JSMSG_PAREN_AFTER_FORMAL);
                 return false;
             }
@@ -3299,17 +3306,17 @@ GeneralParser<ParseHandler, CharT>::addE
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
-    if (tt != TOK_RC) {
+    if (tt != TokenKind::TOK_RC) {
         error(JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
 template <class ParseHandler, typename CharT>
@@ -3320,17 +3327,17 @@ GeneralParser<ParseHandler, CharT>::tagg
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
     handler.addList(nodeList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
             return false;
-        if (tt != TOK_TEMPLATE_HEAD)
+        if (tt != TokenKind::TOK_TEMPLATE_HEAD)
             break;
 
         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
@@ -3352,17 +3359,17 @@ GeneralParser<ParseHandler, CharT>::temp
         if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt))
             return null();
 
         pn = noSubstitutionUntaggedTemplate();
         if (!pn)
             return null();
 
         handler.addList(nodeList, pn);
-    } while (tt == TOK_TEMPLATE_HEAD);
+    } while (tt == TokenKind::TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::functionDefinition(Node funcNode, uint32_t toStringStart,
                                                        InHandling inHandling, YieldHandling yieldHandling,
                                                        HandleAtom funName, FunctionSyntaxKind kind,
@@ -3737,17 +3744,17 @@ GeneralParser<ParseHandler, CharT>::func
         if (!varScope->init(pc))
             return false;
     } else {
         pc->functionScope().useAsVarScope(pc);
     }
 
     if (kind == Arrow) {
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_ARROW))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_ARROW))
             return false;
         if (!matched) {
             error(JSMSG_BAD_ARROW_ARGS);
             return false;
         }
     }
 
     // When parsing something for new Function() we have to make sure to
@@ -3758,17 +3765,17 @@ GeneralParser<ParseHandler, CharT>::func
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
     uint32_t openedPos = 0;
-    if (tt != TOK_LC) {
+    if (tt != TokenKind::TOK_LC) {
         if (kind != Arrow) {
             if (funbox->isGenerator() || funbox->isAsync() || kind == Method ||
                 kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
                 IsConstructorKind(kind) || kind == PrimaryExpression)
             {
                 error(JSMSG_CURLY_BEFORE_BODY);
                 return false;
             }
@@ -3832,17 +3839,17 @@ GeneralParser<ParseHandler, CharT>::func
         // we don't need call AutoAwaitIsKeyword here.
 
         uint32_t nameOffset = handler.getFunctionNameOffset(pn, anyChars);
         if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling))
             return false;
     }
 
     if (bodyType == StatementListBody) {
-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
                                                               JSMSG_CURLY_OPENED, openedPos));
         funbox->setEnd(anyChars);
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
         if (anyChars.hadError())
@@ -3867,17 +3874,17 @@ GeneralParser<ParseHandler, CharT>::func
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart,
                                                  YieldHandling yieldHandling,
                                                  DefaultHandling defaultHandling,
                                                  FunctionAsyncKind asyncKind)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FUNCTION));
 
     // In sloppy mode, Annex B.3.2 allows labelled function declarations.
     // Otherwise it's a parse error.
     ParseContext::Statement* declaredInStmt = pc->innermostStatement();
     if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) {
         MOZ_ASSERT(!pc->sc()->strict(),
                    "labeled functions shouldn't be parsed in strict mode");
 
@@ -3893,17 +3900,17 @@ GeneralParser<ParseHandler, CharT>::func
         }
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     GeneratorKind generatorKind = GeneratorKind::NotGenerator;
-    if (tt == TOK_MUL) {
+    if (tt == TokenKind::TOK_MUL) {
         generatorKind = GeneratorKind::Generator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
@@ -3958,25 +3965,25 @@ GeneralParser<ParseHandler, CharT>::func
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
                                                  ExpressionClosure expressionClosureHandling,
                                                  InvokedPrediction invoked,
                                                  FunctionAsyncKind asyncKind)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FUNCTION));
 
     AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
     GeneratorKind generatorKind = GeneratorKind::NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
-    if (tt == TOK_MUL) {
+    if (tt == TokenKind::TOK_MUL) {
         generatorKind = GeneratorKind::Generator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
     RootedPropertyName name(context);
@@ -4182,23 +4189,23 @@ GeneralParser<ParseHandler, CharT>::stat
 
     bool canHaveDirectives = pc->atBodyLevel();
     if (canHaveDirectives)
         anyChars.clearSawOctalEscape();
     bool afterReturn = false;
     bool warnedAboutStatementsAfterReturn = false;
     uint32_t statementBegin = 0;
     for (;;) {
-        TokenKind tt = TOK_EOF;
+        TokenKind tt = TokenKind::TOK_EOF;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
             if (anyChars.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
-        if (tt == TOK_EOF || tt == TOK_RC) {
+        if (tt == TokenKind::TOK_EOF || tt == TokenKind::TOK_RC) {
             TokenPos pos;
             if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) {
                 return null();
             }
             handler.setListEndPosition(pn, pos);
             break;
         }
         if (afterReturn) {
@@ -4234,38 +4241,38 @@ GeneralParser<ParseHandler, CharT>::stat
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
 {
-    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
+    MUST_MATCH_TOKEN(TokenKind::TOK_LP, JSMSG_PAREN_BEFORE_COND);
 
     Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
     if (!pn)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(pn)) {
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
                                                MutableHandle<PropertyName*> label)
 {
-    TokenKind tt = TOK_EOF;
+    TokenKind tt = TokenKind::TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
     if (TokenKindIsPossibleIdentifier(tt)) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
         label.set(labelIdentifier(yieldHandling));
         if (!label)
@@ -4431,17 +4438,17 @@ GeneralParser<ParseHandler, CharT>::Poss
     transferErrorTo(ErrorKind::Expression, other);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
                                                        YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
         pc->functionBox()->hasParameterExprs = true;
 
     Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!rhs)
         return null();
 
@@ -4474,58 +4481,58 @@ GeneralParser<ParseHandler, CharT>::bind
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind,
                                                                YieldHandling yieldHandling,
                                                                TokenKind tt)
 {
-    if (tt == TOK_LB)
+    if (tt == TokenKind::TOK_LB)
         return arrayBindingPattern(kind, yieldHandling);
 
-    if (tt == TOK_LC)
+    if (tt == TokenKind::TOK_LC)
         return objectBindingPattern(kind, yieldHandling);
 
     if (!TokenKindIsPossibleIdentifierName(tt)) {
         error(JSMSG_NO_VARIABLE_NAME);
         return null();
     }
 
     return bindingIdentifier(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
                                                          YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LC));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newObjectLiteral(begin);
     if (!literal)
         return null();
 
     Maybe<DeclarationKind> declKind = Some(kind);
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
-        if (tt == TOK_RC) {
+        if (tt == TokenKind::TOK_RC) {
             anyChars.addModifierException(TokenStream::OperandIsNone);
             break;
         }
 
-        if (tt == TOK_TRIPLEDOT) {
-            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+        if (tt == TokenKind::TOK_TRIPLEDOT) {
+            tokenStream.consumeKnownToken(TokenKind::TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return null();
 
             if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_VARIABLE_NAME);
@@ -4552,17 +4559,17 @@ GeneralParser<ParseHandler, CharT>::obje
                 if (!tokenStream.getToken(&tt, TokenStream::Operand))
                     return null();
 
                 Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
                 if (!binding)
                     return null();
 
                 bool hasInitializer;
-                if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN, TokenStream::Operand))
+                if (!tokenStream.matchToken(&hasInitializer, TokenKind::TOK_ASSIGN, TokenStream::Operand))
                     return null();
 
                 Node bindingExpr = hasInitializer
                                    ? bindingInitializer(binding, kind, yieldHandling)
                                    : binding;
                 if (!bindingExpr)
                     return null();
 
@@ -4583,55 +4590,55 @@ GeneralParser<ParseHandler, CharT>::obje
                 // Handle e.g., |var {x=1, y=2} = o| as destructuring
                 // shorthand with default values.
                 MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt));
 
                 Node binding = bindingIdentifier(kind, yieldHandling);
                 if (!binding)
                     return null();
 
-                tokenStream.consumeKnownToken(TOK_ASSIGN);
+                tokenStream.consumeKnownToken(TokenKind::TOK_ASSIGN);
 
                 Node bindingExpr = bindingInitializer(binding, kind, yieldHandling);
                 if (!bindingExpr)
                     return null();
 
                 if (!handler.addPropertyDefinition(literal, propName, bindingExpr))
                     return null();
             } else {
                 errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME);
                 return null();
             }
         }
 
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
             return null();
         if (!matched)
             break;
-        if (tt == TOK_TRIPLEDOT) {
+        if (tt == TokenKind::TOK_TRIPLEDOT) {
             error(JSMSG_REST_WITH_COMMA);
             return null();
         }
     }
 
-    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
                                                         YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LB));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
@@ -4642,26 +4649,26 @@ GeneralParser<ParseHandler, CharT>::arra
              error(JSMSG_ARRAY_INIT_TOO_BIG);
              return null();
          }
 
          TokenKind tt;
          if (!tokenStream.getToken(&tt))
              return null();
 
-         if (tt == TOK_RB) {
+         if (tt == TokenKind::TOK_RB) {
              anyChars.ungetToken();
              anyChars.addModifierException(TokenStream::OperandIsNone);
              break;
          }
 
-         if (tt == TOK_COMMA) {
+         if (tt == TokenKind::TOK_COMMA) {
              if (!handler.addElision(literal, pos()))
                  return null();
-         } else if (tt == TOK_TRIPLEDOT) {
+         } else if (tt == TokenKind::TOK_TRIPLEDOT) {
              uint32_t begin = pos().begin;
 
              TokenKind tt;
              if (!tokenStream.getToken(&tt))
                  return null();
 
              Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
              if (!inner)
@@ -4670,61 +4677,61 @@ GeneralParser<ParseHandler, CharT>::arra
              if (!handler.addSpreadElement(literal, begin, inner))
                  return null();
          } else {
              Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt);
              if (!binding)
                  return null();
 
              bool hasInitializer;
-             if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN, TokenStream::Operand))
+             if (!tokenStream.matchToken(&hasInitializer, TokenKind::TOK_ASSIGN, TokenStream::Operand))
                  return null();
 
              Node element = hasInitializer
                             ? bindingInitializer(binding, kind, yieldHandling)
                             : binding;
              if (!element)
                  return null();
 
              handler.addArrayElement(literal, element);
          }
 
-         if (tt != TOK_COMMA) {
-             // If we didn't already match TOK_COMMA in above case.
+         if (tt != TokenKind::TOK_COMMA) {
+             // If we didn't already match TokenKind::TOK_COMMA in above case.
              bool matched;
-             if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+             if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
                  return null();
              if (!matched)
                  break;
 
-             if (tt == TOK_TRIPLEDOT) {
+             if (tt == TokenKind::TOK_TRIPLEDOT) {
                  error(JSMSG_REST_WITH_COMMA);
                  return null();
              }
          }
      }
 
-     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::Operand,
+     MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RB, TokenStream::Operand,
                                       reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
                                                            JSMSG_BRACKET_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
                                                              YieldHandling yieldHandling,
                                                              TokenKind tt)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
-    MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC);
-
-    return tt == TOK_LB
+    MOZ_ASSERT(tt == TokenKind::TOK_LB || tt == TokenKind::TOK_LC);
+
+    return tt == TokenKind::TOK_LB
            ? arrayBindingPattern(kind, yieldHandling)
            : objectBindingPattern(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
                                                                                 YieldHandling yieldHandling,
@@ -4746,29 +4753,29 @@ GeneralParser<ParseHandler, CharT>::dest
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
                                                    unsigned errorNumber)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LC));
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     Node list = statementList(yieldHandling);
     if (!list)
         return null();
 
-    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
                                                           openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
@@ -4785,18 +4792,18 @@ GeneralParser<ParseHandler, CharT>::expr
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt,
                                                        bool initialDeclaration,
                                                        YieldHandling yieldHandling,
                                                        ParseNodeKind* forHeadKind,
                                                        Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB) ||
-               anyChars.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LB) ||
+               anyChars.isCurrentTokenType(TokenKind::TOK_LC));
 
     Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
     if (!pattern)
         return null();
 
     if (initialDeclaration && forHeadKind) {
         bool isForIn, isForOf;
         if (!matchInOrOf(&isForIn, &isForOf))
@@ -4818,17 +4825,17 @@ GeneralParser<ParseHandler, CharT>::decl
             *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
             if (!*forInOrOfExpression)
                 return null();
 
             return pattern;
         }
     }
 
-    MUST_MATCH_TOKEN_MOD(TOK_ASSIGN, TokenStream::Operand, JSMSG_BAD_DESTRUCT_DECL);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_ASSIGN, TokenStream::Operand, JSMSG_BAD_DESTRUCT_DECL);
 
     Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
                            yieldHandling, TripledotProhibited);
     if (!init)
         return null();
 
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
@@ -4839,17 +4846,17 @@ template <class ParseHandler, typename C
 bool
 GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
                                                                  DeclarationKind declKind,
                                                                  bool initialDeclaration,
                                                                  YieldHandling yieldHandling,
                                                                  ParseNodeKind* forHeadKind,
                                                                  Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_ASSIGN));
 
     uint32_t initializerOffset;
     if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
         return false;
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                   yieldHandling, TripledotProhibited);
     if (!initializer)
@@ -4923,17 +4930,17 @@ GeneralParser<ParseHandler, CharT>::decl
     // The '=' context after a variable name in a declaration is an opportunity
     // for ASI, and thus for the next token to start an ExpressionStatement:
     //
     //  var foo   // VariableDeclaration
     //  /bar/g;   // ExpressionStatement
     //
     // Therefore get the token here as Operand.
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TokenKind::TOK_ASSIGN, TokenStream::Operand))
         return null();
 
     if (matched) {
         if (!initializerInNameDeclaration(binding, declKind, initialDeclaration,
                                           yieldHandling, forHeadKind, forInOrOfExpression))
         {
             return null();
         }
@@ -5013,34 +5020,34 @@ GeneralParser<ParseHandler, CharT>::decl
     do {
         MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
                       *forHeadKind == ParseNodeKind::ForHead);
 
         TokenKind tt;
         if (!tokenStream.getToken(&tt))
             return null();
 
-        Node binding = (tt == TOK_LB || tt == TOK_LC)
+        Node binding = (tt == TokenKind::TOK_LB || tt == TokenKind::TOK_LC)
                        ? declarationPattern(declKind, tt, initialDeclaration, yieldHandling,
                                             forHeadKind, forInOrOfExpression)
                        : declarationName(declKind, tt, initialDeclaration, yieldHandling,
                                          forHeadKind, forInOrOfExpression);
         if (!binding)
             return null();
 
         handler.addList(decl, binding);
 
         // If we have a for-in/of loop, the above call matches the entirety
         // of the loop head (up to the closing parenthesis).
         if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead)
             break;
 
         initialDeclaration = false;
 
-        if (!tokenStream.matchToken(&moreDeclarations, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&moreDeclarations, TokenKind::TOK_COMMA, TokenStream::Operand))
             return null();
     } while (moreDeclarations);
 
     return decl;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
@@ -5069,37 +5076,37 @@ GeneralParser<ParseHandler, CharT>::lexi
 
     return decl;
 }
 
 template <typename CharT>
 bool
 Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
 {
-    if (tt == TOK_LC) {
+    if (tt == TokenKind::TOK_LC) {
         while (true) {
             // Handle the forms |import {} from 'a'| and
             // |import { ..., } from 'a'| (where ... is non empty), by
             // escaping the loop early if the next token is }.
             if (!tokenStream.getToken(&tt))
                 return false;
 
-            if (tt == TOK_RC)
+            if (tt == TokenKind::TOK_RC)
                 break;
 
             if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_IMPORT_NAME);
                 return false;
             }
 
             Rooted<PropertyName*> importName(context, anyChars.currentName());
             TokenPos importNamePos = pos();
 
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_AS))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_AS))
                 return null();
 
             if (matched) {
                 TokenKind afterAs;
                 if (!tokenStream.getToken(&afterAs))
                     return false;
 
                 if (!TokenKindIsPossibleIdentifierName(afterAs)) {
@@ -5136,28 +5143,28 @@ Parser<FullParseHandler, CharT>::namedIm
                 return false;
 
             handler.addList(importSpecSet, importSpec);
 
             TokenKind next;
             if (!tokenStream.getToken(&next))
                 return false;
 
-            if (next == TOK_RC)
+            if (next == TokenKind::TOK_RC)
                 break;
 
-            if (next != TOK_COMMA) {
+            if (next != TokenKind::TOK_COMMA) {
                 error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
                 return false;
             }
         }
     } else {
-        MOZ_ASSERT(tt == TOK_MUL);
-
-        MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
+        MOZ_ASSERT(tt == TokenKind::TOK_MUL);
+
+        MUST_MATCH_TOKEN(TokenKind::TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
 
         MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME);
 
         Node importName = newName(context->names().star);
         if (!importName)
             return false;
 
         // Namespace imports are are not indirect bindings but lexical
@@ -5186,38 +5193,38 @@ Parser<FullParseHandler, CharT>::namedIm
 
     return true;
 }
 
 template<typename CharT>
 ParseNode*
 Parser<FullParseHandler, CharT>::importDeclaration()
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_IMPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     Node importSpecSet = handler.newList(ParseNodeKind::ImportSpecList, pos());
     if (!importSpecSet)
         return null();
 
-    if (tt == TOK_STRING) {
+    if (tt == TokenKind::TOK_STRING) {
         // Handle the form |import 'a'| by leaving the list empty. This is
         // equivalent to |import {} from 'a'|.
         importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
     } else {
-        if (tt == TOK_LC || tt == TOK_MUL) {
+        if (tt == TokenKind::TOK_LC || tt == TokenKind::TOK_MUL) {
             if (!namedImportsOrNamespaceImport(tt, importSpecSet))
                 return null();
         } else if (TokenKindIsPossibleIdentifierName(tt)) {
             // Handle the form |import a from 'b'|, by adding a single import
             // specifier to the list, with 'default' as the import name and
             // 'a' as the binding name. This is equivalent to
             // |import { default as a } from 'b'|.
             Node importName = newName(context->names().default_);
@@ -5239,37 +5246,37 @@ Parser<FullParseHandler, CharT>::importD
             if (!importSpec)
                 return null();
 
             handler.addList(importSpecSet, importSpec);
 
             if (!tokenStream.peekToken(&tt))
                 return null();
 
-            if (tt == TOK_COMMA) {
+            if (tt == TokenKind::TOK_COMMA) {
                 tokenStream.consumeKnownToken(tt);
                 if (!tokenStream.getToken(&tt))
                     return null();
 
-                if (tt != TOK_LC && tt != TOK_MUL) {
+                if (tt != TokenKind::TOK_LC && tt != TokenKind::TOK_MUL) {
                     error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
                     return null();
                 }
 
                 if (!namedImportsOrNamespaceImport(tt, importSpecSet))
                     return null();
             }
         } else {
             error(JSMSG_DECLARATION_AFTER_IMPORT);
             return null();
         }
 
-        MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
-
-        MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+        MUST_MATCH_TOKEN(TokenKind::TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE);
+
+        MUST_MATCH_TOKEN(TokenKind::TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
     }
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
     if (!matchOrInsertSemicolon())
         return null();
@@ -5458,22 +5465,22 @@ PerHandlerParser<SyntaxParseHandler>::pr
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FROM));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FROM));
 
     if (!abortIfSyntaxParser())
         return null();
 
-    MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+    MUST_MATCH_TOKEN(TokenKind::TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
     if (!matchOrInsertSemicolon())
         return null();
 
@@ -5489,31 +5496,31 @@ GeneralParser<ParseHandler, CharT>::expo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_MUL));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_MUL));
 
     Node kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
     if (!kid)
         return null();
 
     // Handle the form |export *| by adding a special export batch
     // specifier to the list.
     Node exportSpec = handler.newExportBatchSpec(pos());
     if (!exportSpec)
         return null();
 
     handler.addList(kid, exportSpec);
 
-    MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
+    MUST_MATCH_TOKEN(TokenKind::TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
 
     return exportFrom(begin, kid);
 }
 
 template<typename CharT>
 bool
 Parser<FullParseHandler, CharT>::checkLocalExportNames(ParseNode* node)
 {
@@ -5547,43 +5554,43 @@ GeneralParser<ParseHandler, CharT>::chec
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LC));
 
     Node kid = handler.newList(ParseNodeKind::ExportSpecList, pos());
     if (!kid)
         return null();
 
     TokenKind tt;
     while (true) {
         // Handle the forms |export {}| and |export { ..., }| (where ... is non
         // empty), by escaping the loop early if the next token is }.
         if (!tokenStream.getToken(&tt))
             return null();
 
-        if (tt == TOK_RC)
+        if (tt == TokenKind::TOK_RC)
             break;
 
         if (!TokenKindIsPossibleIdentifierName(tt)) {
             error(JSMSG_NO_BINDING_NAME);
             return null();
         }
 
         Node bindingName = newName(anyChars.currentName());
         if (!bindingName)
             return null();
 
         bool foundAs;
-        if (!tokenStream.matchToken(&foundAs, TOK_AS))
+        if (!tokenStream.matchToken(&foundAs, TokenKind::TOK_AS))
             return null();
         if (foundAs)
             MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
 
         Node exportName = newName(anyChars.currentName());
         if (!exportName)
             return null();
 
@@ -5595,20 +5602,20 @@ GeneralParser<ParseHandler, CharT>::expo
             return null();
 
         handler.addList(kid, exportSpec);
 
         TokenKind next;
         if (!tokenStream.getToken(&next))
             return null();
 
-        if (next == TOK_RC)
+        if (next == TokenKind::TOK_RC)
             break;
 
-        if (next != TOK_COMMA) {
+        if (next != TokenKind::TOK_COMMA) {
             error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
             return null();
         }
     }
 
     // Careful!  If |from| follows, even on a new line, it must start a
     // FromClause:
     //
@@ -5618,17 +5625,17 @@ GeneralParser<ParseHandler, CharT>::expo
     // But if it doesn't, we might have an ASI opportunity in Operand context:
     //
     //   export { x }   // ExportDeclaration, terminated by ASI
     //   fro\u006D      // ExpressionStatement, the name "from"
     //
     // In that case let matchOrInsertSemicolon sort out ASI or any necessary
     // error.
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TokenKind::TOK_FROM, TokenStream::Operand))
         return null();
 
     if (matched)
         return exportFrom(begin, kid);
 
     if (!matchOrInsertSemicolon())
         return null();
 
@@ -5647,17 +5654,17 @@ GeneralParser<ParseHandler, CharT>::expo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_VAR));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_VAR));
 
     Node kid = declarationList(YieldIsName, ParseNodeKind::Var);
     if (!kid)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
@@ -5676,17 +5683,17 @@ template <class ParseHandler, typename C
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin,
                                                               uint32_t toStringStart,
                                                               FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind);
     if (!kid)
         return null();
 
     if (!checkExportedNameForFunction(kid))
         return null();
 
@@ -5702,17 +5709,17 @@ GeneralParser<ParseHandler, CharT>::expo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
     if (!kid)
         return null();
 
     if (!checkExportedNameForClass(kid))
         return null();
 
@@ -5729,18 +5736,18 @@ GeneralParser<ParseHandler, CharT>::expo
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
-    MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TOK_CONST));
-    MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TOK_LET));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TokenKind::TOK_CONST));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TokenKind::TOK_LET));
 
     Node kid = lexicalDeclaration(YieldIsName, kind);
     if (!kid)
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
 
     Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
@@ -5757,17 +5764,17 @@ template <class ParseHandler, typename C
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
                                                                      uint32_t toStringStart,
                                                                      FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind);
     if (!kid)
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
     if (!node)
         return null();
@@ -5780,17 +5787,17 @@ GeneralParser<ParseHandler, CharT>::expo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName);
     if (!kid)
         return null();
 
     Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end));
     if (!node)
         return null();
@@ -5836,112 +5843,112 @@ GeneralParser<ParseHandler, CharT>::expo
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_DEFAULT));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_DEFAULT));
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (!checkExportedName(context->names().default_))
         return null();
 
     switch (tt) {
-      case TOK_FUNCTION:
+      case TokenKind::TOK_FUNCTION:
         return exportDefaultFunctionDeclaration(begin, pos().begin);
 
-      case TOK_ASYNC: {
-        TokenKind nextSameLine = TOK_EOF;
+      case TokenKind::TOK_ASYNC: {
+        TokenKind nextSameLine = TokenKind::TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
-        if (nextSameLine == TOK_FUNCTION) {
+        if (nextSameLine == TokenKind::TOK_FUNCTION) {
             uint32_t toStringStart = pos().begin;
-            tokenStream.consumeKnownToken(TOK_FUNCTION);
+            tokenStream.consumeKnownToken(TokenKind::TOK_FUNCTION);
             return exportDefaultFunctionDeclaration(begin, toStringStart,
                                                     FunctionAsyncKind::AsyncFunction);
         }
 
         anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
       }
 
-      case TOK_CLASS:
+      case TokenKind::TOK_CLASS:
         return exportDefaultClassDeclaration(begin);
 
       default:
         anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_EXPORT));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_EXPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     switch (tt) {
-      case TOK_MUL:
+      case TokenKind::TOK_MUL:
         return exportBatch(begin);
 
-      case TOK_LC:
+      case TokenKind::TOK_LC:
         return exportClause(begin);
 
-      case TOK_VAR:
+      case TokenKind::TOK_VAR:
         return exportVariableStatement(begin);
 
-      case TOK_FUNCTION:
+      case TokenKind::TOK_FUNCTION:
         return exportFunctionDeclaration(begin, pos().begin);
 
-      case TOK_ASYNC: {
-        TokenKind nextSameLine = TOK_EOF;
+      case TokenKind::TOK_ASYNC: {
+        TokenKind nextSameLine = TokenKind::TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
-        if (nextSameLine == TOK_FUNCTION) {
+        if (nextSameLine == TokenKind::TOK_FUNCTION) {
             uint32_t toStringStart = pos().begin;
-            tokenStream.consumeKnownToken(TOK_FUNCTION);
+            tokenStream.consumeKnownToken(TokenKind::TOK_FUNCTION);
             return exportFunctionDeclaration(begin, toStringStart,
                                              FunctionAsyncKind::AsyncFunction);
         }
 
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
       }
 
-      case TOK_CLASS:
+      case TokenKind::TOK_CLASS:
         return exportClassDeclaration(begin);
 
-      case TOK_CONST:
+      case TokenKind::TOK_CONST:
         return exportLexicalDeclaration(begin, DeclarationKind::Const);
 
-      case TOK_LET:
+      case TokenKind::TOK_LET:
         return exportLexicalDeclaration(begin, DeclarationKind::Let);
 
-      case TOK_DEFAULT:
+      case TokenKind::TOK_DEFAULT:
         return exportDefault(begin);
 
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 }
 
@@ -5969,31 +5976,31 @@ GeneralParser<ParseHandler, CharT>::cons
         return null();
 
     // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in
     // non-strict code act as if they were braced: |if (x) function f() {}|
     // parses as |if (x) { function f() {} }|.
     //
     // Careful!  FunctionDeclaration doesn't include generators or async
     // functions.
-    if (next == TOK_FUNCTION) {
+    if (next == TokenKind::TOK_FUNCTION) {
         tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
         // Parser::statement would handle this, but as this function handles
         // every other error case, it seems best to handle this.
         if (pc->sc()->strict()) {
             error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
             return null();
         }
 
         TokenKind maybeStar;
         if (!tokenStream.peekToken(&maybeStar))
             return null();
 
-        if (maybeStar == TOK_MUL) {
+        if (maybeStar == TokenKind::TOK_MUL) {
             error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations");
             return null();
         }
 
         ParseContext::Statement stmt(pc, StatementKind::Block);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
@@ -6030,33 +6037,33 @@ GeneralParser<ParseHandler, CharT>::ifSt
         /* An IF node has three kids: condition, then, and optional else. */
         Node cond = condition(InAllowed, yieldHandling);
         if (!cond)
             return null();
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
-        if (tt == TOK_SEMI) {
+        if (tt == TokenKind::TOK_SEMI) {
             if (!extraWarning(JSMSG_EMPTY_CONSEQUENT))
                 return null();
         }
 
         Node thenBranch = consequentOrAlternative(yieldHandling);
         if (!thenBranch)
             return null();
 
         if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin))
             return null();
 
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_ELSE, TokenStream::Operand))
             return null();
         if (matched) {
-            if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_IF, TokenStream::Operand))
                 return null();
             if (matched)
                 continue;
             elseBranch = consequentOrAlternative(yieldHandling);
             if (!elseBranch)
                 return null();
         } else {
             elseBranch = null();
@@ -6077,29 +6084,29 @@ template <class ParseHandler, typename C
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
 {
     uint32_t begin = pos().begin;
     ParseContext::Statement stmt(pc, StatementKind::DoLoop);
     Node body = statement(yieldHandling);
     if (!body)
         return null();
-    MUST_MATCH_TOKEN_MOD(TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_WHILE, TokenStream::Operand, JSMSG_WHILE_AFTER_DO);
     Node cond = condition(InAllowed, yieldHandling);
     if (!cond)
         return null();
 
     // The semicolon after do-while is even more optional than most
     // semicolons in JS.  Web compat required this by 2004:
     //   http://bugzilla.mozilla.org/show_bug.cgi?id=238945
     // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
     //   https://bugs.ecmascript.org/show_bug.cgi?id=157
     // To parse |do {} while (true) false| correctly, use Operand.
     bool ignored;
-    if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand))
+    if (!tokenStream.matchToken(&ignored, TokenKind::TOK_SEMI, TokenStream::Operand))
         return null();
     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
 {
@@ -6117,74 +6124,74 @@ GeneralParser<ParseHandler, CharT>::whil
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
 
-    *isForInp = tt == TOK_IN;
-    *isForOfp = tt == TOK_OF;
+    *isForInp = tt == TokenKind::TOK_IN;
+    *isForOfp = tt == TokenKind::TOK_OF;
     if (!*isForInp && !*isForOfp)
         anyChars.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
                                                  ParseNodeKind* forHeadKind, Node* forInitialPart,
                                                  Maybe<ParseContext::Scope>& forLoopLexicalScope,
                                                  Node* forInOrOfExpression)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LP));
 
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
         return null();
 
     // Super-duper easy case: |for (;| is a C-style for-loop with no init
     // component.
-    if (tt == TOK_SEMI) {
+    if (tt == TokenKind::TOK_SEMI) {
         *forInitialPart = null();
         *forHeadKind = ParseNodeKind::ForHead;
         return true;
     }
 
     // Parsing after |for (var| is also relatively simple (from this method's
     // point of view).  No block-related work complicates matters, so delegate
     // to Parser::declaration.
-    if (tt == TOK_VAR) {
+    if (tt == TokenKind::TOK_VAR) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
         // Pass null for block object because |var| declarations don't use one.
         *forInitialPart = declarationList(yieldHandling, ParseNodeKind::Var, forHeadKind,
                                           forInOrOfExpression);
         return *forInitialPart != null();
     }
 
     // Otherwise we have a lexical declaration or an expression.
 
     // For-in loop backwards compatibility requires that |let| starting a
     // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
     // parse as an identifier.  (|let| in for-of is always a declaration.)
     bool parsingLexicalDeclaration = false;
     bool letIsIdentifier = false;
-    if (tt == TOK_CONST) {
+    if (tt == TokenKind::TOK_CONST) {
         parsingLexicalDeclaration = true;
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
-    } else if (tt == TOK_LET) {
+    } else if (tt == TokenKind::TOK_LET) {
         // We could have a {For,Lexical}Declaration, or we could have a
         // LeftHandSideExpression with lookahead restrictions so it's not
         // ambiguous with the former.  Check for a continuation of the former
         // to decide which we have.
-        tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand);
+        tokenStream.consumeKnownToken(TokenKind::TOK_LET, TokenStream::Operand);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return false;
 
         parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next);
         if (!parsingLexicalDeclaration) {
             anyChars.ungetToken();
@@ -6198,17 +6205,17 @@ GeneralParser<ParseHandler, CharT>::forH
             return null();
 
         // Push a temporary ForLoopLexicalHead Statement that allows for
         // lexical declarations, as they are usually allowed only in braced
         // statements.
         ParseContext::Statement forHeadStmt(pc, StatementKind::ForLoopLexicalHead);
 
         *forInitialPart = declarationList(yieldHandling,
-                                          tt == TOK_CONST
+                                          tt == TokenKind::TOK_CONST
                                           ? ParseNodeKind::Const
                                           : ParseNodeKind::Let,
                                           forHeadKind, forInOrOfExpression);
         return *forInitialPart != null();
     }
 
     uint32_t exprOffset;
     if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand))
@@ -6285,38 +6292,38 @@ GeneralParser<ParseHandler, CharT>::forH
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FOR));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_FOR));
 
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     IteratorKind iterKind = IteratorKind::Sync;
     unsigned iflags = 0;
 
     if (pc->isAsync()) {
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_AWAIT))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_AWAIT))
             return null();
 
         if (matched) {
             iflags |= JSITER_FORAWAITOF;
             iterKind = IteratorKind::Async;
         }
     }
 
-    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_LP, TokenStream::None,
-                                     error((token == TOK_AWAIT && !pc->isAsync())
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_LP, TokenStream::None,
+                                     error((token == TokenKind::TOK_AWAIT && !pc->isAsync())
                                            ? JSMSG_FOR_AWAIT_OUTSIDE_ASYNC
                                            : JSMSG_PAREN_AFTER_FOR));
 
     // ParseNodeKind::ForHead, ParseNodeKind::ForIn, or
     // ParseNodeKind::ForOf depending on the loop type.
     ParseNodeKind headKind;
 
     // |x| in either |for (x; ...; ...)| or |for (x in/of ...)|.
@@ -6364,46 +6371,46 @@ GeneralParser<ParseHandler, CharT>::forS
     }
 
     Node forHead;
     if (headKind == ParseNodeKind::ForHead) {
         Node init = startNode;
 
         // Look for an operand: |for (;| means we might have already examined
         // this semicolon with that modifier.
-        MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_INIT);
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
 
         Node test;
-        if (tt == TOK_SEMI) {
+        if (tt == TokenKind::TOK_SEMI) {
             test = null();
         } else {
             test = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!test)
                 return null();
         }
 
-        MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_COND);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_COND);
 
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
 
         Node update;
-        if (tt == TOK_RP) {
+        if (tt == TokenKind::TOK_RP) {
             update = null();
         } else {
             update = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!update)
                 return null();
         }
 
-        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         TokenPos headPos(begin, pos().end);
         forHead = handler.newForHead(init, test, update, headPos);
         if (!forHead)
             return null();
     } else {
         MOZ_ASSERT(headKind == ParseNodeKind::ForIn || headKind == ParseNodeKind::ForOf);
 
@@ -6416,17 +6423,17 @@ GeneralParser<ParseHandler, CharT>::forS
         if (headKind == ParseNodeKind::ForIn)
             stmt.refineForKind(StatementKind::ForInLoop);
         else
             stmt.refineForKind(StatementKind::ForOfLoop);
 
         // Parser::declaration consumed everything up to the closing ')'.  That
         // token follows an {Assignment,}Expression and so must be interpreted
         // as an operand to be consistent with normal expression tokenizing.
-        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         TokenPos headPos(begin, pos().end);
         forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
         if (!forHead)
             return null();
     }
 
     Node body = statement(yieldHandling);
@@ -6442,81 +6449,81 @@ GeneralParser<ParseHandler, CharT>::forS
 
     return forLoop;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_SWITCH));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_SWITCH));
     uint32_t begin = pos().begin;
 
-    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
+    MUST_MATCH_TOKEN(TokenKind::TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
-    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
+    MUST_MATCH_TOKEN(TokenKind::TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
 
     ParseContext::Statement stmt(pc, StatementKind::Switch);
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     Node caseList = handler.newStatementList(pos());
     if (!caseList)
         return null();
 
     bool seenDefault = false;
     TokenKind tt;
     while (true) {
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
-        if (tt == TOK_RC)
+        if (tt == TokenKind::TOK_RC)
             break;
         uint32_t caseBegin = pos().begin;
 
         Node caseExpr;
         switch (tt) {
-          case TOK_DEFAULT:
+          case TokenKind::TOK_DEFAULT:
             if (seenDefault) {
                 error(JSMSG_TOO_MANY_DEFAULTS);
                 return null();
             }
             seenDefault = true;
             caseExpr = null();  // The default case has pn_left == nullptr.
             break;
 
-          case TOK_CASE:
+          case TokenKind::TOK_CASE:
             caseExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!caseExpr)
                 return null();
             break;
 
           default:
             error(JSMSG_BAD_SWITCH);
             return null();
         }
 
-        MUST_MATCH_TOKEN_MOD(TOK_COLON, TokenStream::Operand, JSMSG_COLON_AFTER_CASE);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_COLON, TokenStream::Operand, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pos());
         if (!body)
             return null();
 
         bool afterReturn = false;
         bool warnedAboutStatementsAfterReturn = false;
         uint32_t statementBegin = 0;
         while (true) {
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
-            if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
+            if (tt == TokenKind::TOK_RC || tt == TokenKind::TOK_CASE || tt == TokenKind::TOK_DEFAULT)
                 break;
             if (afterReturn) {
                 if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
                     return null();
             }
             Node stmt = statementListItem(yieldHandling);
             if (!stmt)
                 return null();
@@ -6549,17 +6556,17 @@ GeneralParser<ParseHandler, CharT>::swit
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CONTINUE));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
     auto validity = pc->checkContinueStatement(label);
     if (validity.isErr()) {
@@ -6579,17 +6586,17 @@ GeneralParser<ParseHandler, CharT>::cont
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_BREAK));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_BREAK));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
     // Labeled 'break' statements target the nearest labeled statements (could
     // be any kind) with the same label. Unlabeled 'break' statements target
@@ -6619,34 +6626,34 @@ GeneralParser<ParseHandler, CharT>::brea
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_RETURN));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_RETURN));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isFunctionBox());
     pc->functionBox()->usesReturn = true;
 
     // Parse an optional operand.
     //
     // This is ugly, but we don't want to require a semicolon.
     Node exprNode;
-    TokenKind tt = TOK_EOF;
+    TokenKind tt = TokenKind::TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     switch (tt) {
-      case TOK_EOL:
-      case TOK_EOF:
-      case TOK_SEMI:
-      case TOK_RC:
+      case TokenKind::TOK_EOL:
+      case TokenKind::TOK_EOF:
+      case TokenKind::TOK_SEMI:
+      case TokenKind::TOK_RC:
         exprNode = null();
         break;
       default: {
         exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!exprNode)
             return null();
       }
     }
@@ -6656,87 +6663,87 @@ GeneralParser<ParseHandler, CharT>::retu
 
     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_YIELD));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_YIELD));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isGenerator());
     MOZ_ASSERT(pc->isFunctionBox());
 
     pc->lastYieldOffset = begin;
 
     Node exprNode;
     ParseNodeKind kind = ParseNodeKind::Yield;
-    TokenKind tt = TOK_EOF;
+    TokenKind tt = TokenKind::TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     switch (tt) {
-      // TOK_EOL is special; it implements the [no LineTerminator here]
+      // TokenKind::TOK_EOL is special; it implements the [no LineTerminator here]
       // quirk in the grammar.
-      case TOK_EOL:
+      case TokenKind::TOK_EOL:
       // The rest of these make up the complete set of tokens that can
       // appear after any of the places where AssignmentExpression is used
       // throughout the grammar.  Conveniently, none of them can also be the
       // start an expression.
-      case TOK_EOF:
-      case TOK_SEMI:
-      case TOK_RC:
-      case TOK_RB:
-      case TOK_RP:
-      case TOK_COLON:
-      case TOK_COMMA:
-      case TOK_IN:
+      case TokenKind::TOK_EOF:
+      case TokenKind::TOK_SEMI:
+      case TokenKind::TOK_RC:
+      case TokenKind::TOK_RB:
+      case TokenKind::TOK_RP:
+      case TokenKind::TOK_COLON:
+      case TokenKind::TOK_COMMA:
+      case TokenKind::TOK_IN:
         // No value.
         exprNode = null();
         anyChars.addModifierException(TokenStream::NoneIsOperand);
         break;
-      case TOK_MUL:
+      case TokenKind::TOK_MUL:
         kind = ParseNodeKind::YieldStar;
-        tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
+        tokenStream.consumeKnownToken(TokenKind::TOK_MUL, TokenStream::Operand);
         MOZ_FALLTHROUGH;
       default:
         exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
         if (!exprNode)
             return null();
     }
     if (kind == ParseNodeKind::YieldStar)
         return handler.newYieldStarExpression(begin, exprNode);
     return handler.newYieldExpression(begin, exprNode);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_WITH));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_WITH));
     uint32_t begin = pos().begin;
 
     // Usually we want the constructs forbidden in strict mode code to be a
     // subset of those that ContextOptions::extraWarnings() warns about, and we
     // use strictModeError directly.  But while 'with' is forbidden in strict
     // mode code, it doesn't even merit a warning in non-strict code.  See
     // https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
     if (pc->sc()->strict()) {
         if (!strictModeError(JSMSG_STRICT_CODE_WITH))
             return null();
     }
 
-    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
+    MUST_MATCH_TOKEN(TokenKind::TOK_LP, JSMSG_PAREN_BEFORE_WITH);
 
     Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!objectExpr)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
 
     Node innerBlock;
     {
         ParseContext::Statement stmt(pc, StatementKind::With);
         innerBlock = statement(yieldHandling);
         if (!innerBlock)
             return null();
     }
@@ -6749,24 +6756,24 @@ GeneralParser<ParseHandler, CharT>::with
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
-    if (tt == TOK_FUNCTION) {
+    if (tt == TokenKind::TOK_FUNCTION) {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
         // GeneratorDeclaration is only matched by HoistableDeclaration in
         // StatementListItem, so generators can't be inside labels.
-        if (next == TOK_MUL) {
+        if (next == TokenKind::TOK_MUL) {
             error(JSMSG_GENERATOR_LABEL);
             return null();
         }
 
         // Per 13.13.1 it's a syntax error if LabelledItem: FunctionDeclaration
         // is ever matched.  Per Annex B.3.2 that modifies this text, this
         // applies only to strict mode code.
         if (pc->sc()->strict()) {
@@ -6795,43 +6802,43 @@ GeneralParser<ParseHandler, CharT>::labe
 
     uint32_t begin = pos().begin;
 
     if (pc->template findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
         errorAt(begin, JSMSG_DUPLICATE_LABEL);
         return null();
     }
 
-    tokenStream.consumeKnownToken(TOK_COLON);
+    tokenStream.consumeKnownToken(TokenKind::TOK_COLON);
 
     /* Push a label struct and parse the statement. */
     ParseContext::LabelStatement stmt(pc, label);
     Node pn = labeledItem(yieldHandling);
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_THROW));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
-    TokenKind tt = TOK_EOF;
+    TokenKind tt = TokenKind::TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
-    if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
+    if (tt == TokenKind::TOK_EOF || tt == TokenKind::TOK_SEMI || tt == TokenKind::TOK_RC) {
         error(JSMSG_MISSING_EXPR_AFTER_THROW);
         return null();
     }
-    if (tt == TOK_EOL) {
+    if (tt == TokenKind::TOK_EOL) {
         error(JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
     }
 
     Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!throwExpr)
         return null();
 
@@ -6840,95 +6847,95 @@ GeneralParser<ParseHandler, CharT>::thro
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_TRY));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_TRY));
     uint32_t begin = pos().begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
      * kid3 is the finally statement
      *
      * catch nodes are binary.
      * left is the catch-name/pattern or null
      * right is the catch block
      *
      * catch lvalue nodes are either:
      *   a single identifier
-     *   TOK_RB or TOK_RC for a destructuring left-hand side
+     *   TokenKind::TOK_RB or TokenKind::TOK_RC for a destructuring left-hand side
      *
-     * finally nodes are TOK_LC statement lists.
+     * finally nodes are TokenKind::TOK_LC statement lists.
      */
 
     Node innerBlock;
     {
-        MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
+        MUST_MATCH_TOKEN(TokenKind::TOK_LC, JSMSG_CURLY_BEFORE_TRY);
 
         uint32_t openedPos = pos().begin;
 
         ParseContext::Statement stmt(pc, StatementKind::Try);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
         innerBlock = statementList(yieldHandling);
         if (!innerBlock)
             return null();
 
         innerBlock = finishLexicalScope(scope, innerBlock);
         if (!innerBlock)
             return null();
 
-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     }
 
     Node catchScope = null();
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
-    if (tt == TOK_CATCH) {
+    if (tt == TokenKind::TOK_CATCH) {
         /*
          * Create a lexical scope node around the whole catch clause,
          * including the head.
          */
         ParseContext::Statement stmt(pc, StatementKind::Catch);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
         /*
          * Legal catch forms are:
          *   catch (lhs) {
          *   catch {
          * where lhs is a name or a destructuring left-hand side.
          */
         bool omittedBinding;
-        if (!tokenStream.matchToken(&omittedBinding, TOK_LC))
+        if (!tokenStream.matchToken(&omittedBinding, TokenKind::TOK_LC))
             return null();
 
         Node catchName;
         if (omittedBinding) {
             catchName = null();
         } else {
-            MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
+            MUST_MATCH_TOKEN(TokenKind::TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
 
             if (!tokenStream.getToken(&tt))
                 return null();
             switch (tt) {
-              case TOK_LB:
-              case TOK_LC:
+              case TokenKind::TOK_LB:
+              case TokenKind::TOK_LC:
                 catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
                                                      yieldHandling, tt);
                 if (!catchName)
                     return null();
                 break;
 
               default: {
                 if (!TokenKindIsPossibleIdentifierName(tt)) {
@@ -6939,19 +6946,19 @@ GeneralParser<ParseHandler, CharT>::tryS
                 catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
                                               yieldHandling);
                 if (!catchName)
                     return null();
                 break;
               }
             }
 
-            MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
-
-            MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
+            MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
+
+            MUST_MATCH_TOKEN(TokenKind::TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
         }
 
         Node catchBody = catchBlockStatement(yieldHandling, scope);
         if (!catchBody)
             return null();
 
         catchScope = finishLexicalScope(scope, catchBody);
         if (!catchScope)
@@ -6962,35 +6969,35 @@ GeneralParser<ParseHandler, CharT>::tryS
         handler.setEndPosition(catchScope, pos().end);
 
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
     }
 
     Node finallyBlock = null();
 
-    if (tt == TOK_FINALLY) {
-        MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
+    if (tt == TokenKind::TOK_FINALLY) {
+        MUST_MATCH_TOKEN(TokenKind::TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
 
         uint32_t openedPos = pos().begin;
 
         ParseContext::Statement stmt(pc, StatementKind::Finally);
         ParseContext::Scope scope(this);
         if (!scope.init(pc))
             return null();
 
         finallyBlock = statementList(yieldHandling);
         if (!finallyBlock)
             return null();
 
         finallyBlock = finishLexicalScope(scope, finallyBlock);
         if (!finallyBlock)
             return null();
 
-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     } else {
         anyChars.ungetToken();
     }
     if (!catchScope && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
@@ -7020,17 +7027,17 @@ GeneralParser<ParseHandler, CharT>::catc
     // block, so declare the name in the inner scope.
     if (!scope.addCatchParameters(pc, catchParamScope))
         return null();
 
     Node list = statementList(yieldHandling);
     if (!list)
         return null();
 
-    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     // The catch parameter names are not bound in the body scope, so remove
     // them before generating bindings.
     scope.removeCatchParameters(pc, catchParamScope);
     return finishLexicalScope(scope, list);
 }
@@ -7075,17 +7082,17 @@ ToAccessorType(PropertyType propType)
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
                                                     ClassContext classContext,
                                                     DefaultHandling defaultHandling)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_CLASS));
 
     uint32_t classStartOffset = pos().begin;
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
@@ -7127,55 +7134,55 @@ GeneralParser<ParseHandler, CharT>::clas
 
     // Because the binding definitions keep track of their blockId, we need to
     // create at least the inner binding later. Keep track of the name's position
     // in order to provide it for the nodes created later.
     TokenPos namePos = pos();
 
     Node classHeritage = null();
     bool hasHeritage;
-    if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
+    if (!tokenStream.matchToken(&hasHeritage, TokenKind::TOK_EXTENDS))
         return null();
     if (hasHeritage) {
         if (!tokenStream.getToken(&tt))
             return null();
         classHeritage = memberExpr(yieldHandling, TripledotProhibited,
                                    ExpressionClosure::Forbidden, tt);
         if (!classHeritage)
             return null();
     }
 
-    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
+    MUST_MATCH_TOKEN(TokenKind::TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
 
     Node classMethods = handler.newClassMethodList(pos().begin);
     if (!classMethods)
         return null();
 
     Maybe<DeclarationKind> declKind = Nothing();
     for (;;) {
         TokenKind tt;
         if (!tokenStream.getToken(&tt))
             return null();
-        if (tt == TOK_RC)
+        if (tt == TokenKind::TOK_RC)
             break;
 
-        if (tt == TOK_SEMI)
+        if (tt == TokenKind::TOK_SEMI)
             continue;
 
         bool isStatic = false;
-        if (tt == TOK_STATIC) {
+        if (tt == TokenKind::TOK_STATIC) {
             if (!tokenStream.peekToken(&tt))
                 return null();
-            if (tt == TOK_RC) {
+            if (tt == TokenKind::TOK_RC) {
                 tokenStream.consumeKnownToken(tt);
                 error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
                 return null();
             }
 
-            if (tt != TOK_LP)
+            if (tt != TokenKind::TOK_LP)
                 isStatic = true;
             else
                 anyChars.ungetToken();
         } else {
             anyChars.ungetToken();
         }
 
         uint32_t nameOffset;
@@ -7216,28 +7223,28 @@ GeneralParser<ParseHandler, CharT>::clas
             errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
         RootedAtom funName(context);
         switch (propType) {
           case PropertyType::GetterNoExpressionClosure:
           case PropertyType::SetterNoExpressionClosure:
-            if (!anyChars.isCurrentTokenType(TOK_RB)) {
+            if (!anyChars.isCurrentTokenType(TokenKind::TOK_RB)) {
                 funName = prefixAccessorName(propType, propAtom);
                 if (!funName)
                     return null();
             }
             break;
           case PropertyType::Constructor:
           case PropertyType::DerivedConstructor:
             funName = name;
             break;
           default:
-            if (!anyChars.isCurrentTokenType(TOK_RB))
+            if (!anyChars.isCurrentTokenType(TokenKind::TOK_RB))
                 funName = propAtom;
         }
 
         // Calling toString on constructors need to return the source text for
         // the entire class. The end offset is unknown at this point in
         // parsing and will be amended when class parsing finishes below.
         Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset,
                                    propType, funName);
@@ -7301,23 +7308,23 @@ GeneralParser<ParseHandler, CharT>::clas
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
 bool
 ParserBase::nextTokenContinuesLetDeclaration(TokenKind next)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LET));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LET));
     MOZ_ASSERT(anyChars.nextToken().type == next);
 
     TokenStreamShared::verifyConsistentModifier(TokenStreamShared::None, anyChars.nextToken());
 
     // Destructuring continues a let declaration.
-    if (next == TOK_LB || next == TOK_LC)
+    if (next == TokenKind::TOK_LB || next == TokenKind::TOK_LC)
         return true;
 
     // A "let" edge case deserves special comment.  Consider this:
     //
     //   let     // not an ASI opportunity
     //   let;
     //
     // Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds
@@ -7352,211 +7359,211 @@ GeneralParser<ParseHandler, CharT>::stat
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     switch (tt) {
       // BlockStatement[?Yield, ?Return]
-      case TOK_LC:
+      case TokenKind::TOK_LC:
         return blockStatement(yieldHandling);
 
       // VariableStatement[?Yield]
-      case TOK_VAR:
+      case TokenKind::TOK_VAR:
         return variableStatement(yieldHandling);
 
       // EmptyStatement
-      case TOK_SEMI:
+      case TokenKind::TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
 
-      case TOK_YIELD: {
+      case TokenKind::TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
         Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
 
-        if (next == TOK_COLON)
+        if (next == TokenKind::TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
       default: {
         // Avoid getting next token with None.
-        if (tt == TOK_AWAIT && pc->isAsync())
+        if (tt == TokenKind::TOK_AWAIT && pc->isAsync())
             return expressionStatement(yieldHandling);
 
         if (!TokenKindIsPossibleIdentifier(tt))
             return expressionStatement(yieldHandling);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
         // |let| here can only be an Identifier, not a declaration.  Give nicer
         // errors for declaration-looking typos.
-        if (tt == TOK_LET) {
+        if (tt == TokenKind::TOK_LET) {
             bool forbiddenLetDeclaration = false;
 
-            if (next == TOK_LB) {
+            if (next == TokenKind::TOK_LB) {
                 // Enforce ExpressionStatement's 'let [' lookahead restriction.
                 forbiddenLetDeclaration = true;
-            } else if (next == TOK_LC || TokenKindIsPossibleIdentifier(next)) {
+            } else if (next == TokenKind::TOK_LC || TokenKindIsPossibleIdentifier(next)) {
                 // 'let {' and 'let foo' aren't completely forbidden, if ASI
                 // causes 'let' to be the entire Statement.  But if they're
                 // same-line, we can aggressively give a better error message.
                 //
-                // Note that this ignores 'yield' as TOK_YIELD: we'll handle it
+                // Note that this ignores 'yield' as TokenKind::TOK_YIELD: we'll handle it
                 // correctly but with a worse error message.
                 TokenKind nextSameLine;
                 if (!tokenStream.peekTokenSameLine(&nextSameLine))
                     return null();
 
                 MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) ||
-                           nextSameLine == TOK_LC ||
-                           nextSameLine == TOK_EOL);
-
-                forbiddenLetDeclaration = nextSameLine != TOK_EOL;
+                           nextSameLine == TokenKind::TOK_LC ||
+                           nextSameLine == TokenKind::TOK_EOL);
+
+                forbiddenLetDeclaration = nextSameLine != TokenKind::TOK_EOL;
             }
 
             if (forbiddenLetDeclaration) {
                 error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations");
                 return null();
             }
-        } else if (tt == TOK_ASYNC) {
+        } else if (tt == TokenKind::TOK_ASYNC) {
             // Peek only on the same line: ExpressionStatement's lookahead
             // restriction is phrased as
             //
             //   [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]
             //
             // meaning that code like this is valid:
             //
             //   if (true)
             //     async       // ASI opportunity
             //   function clownshoes() {}
             TokenKind maybeFunction;
             if (!tokenStream.peekTokenSameLine(&maybeFunction))
                 return null();
 
-            if (maybeFunction == TOK_FUNCTION) {
+            if (maybeFunction == TokenKind::TOK_FUNCTION) {
                 error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations");
                 return null();
             }
 
             // Otherwise this |async| begins an ExpressionStatement or is a
             // label name.
         }
 
         // NOTE: It's unfortunately allowed to have a label named 'let' in
         //       non-strict code.  💯
-        if (next == TOK_COLON)
+        if (next == TokenKind::TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
-      case TOK_NEW:
+      case TokenKind::TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
 
       // IfStatement[?Yield, ?Return]
-      case TOK_IF:
+      case TokenKind::TOK_IF:
         return ifStatement(yieldHandling);
 
       // BreakableStatement[?Yield, ?Return]
       //
       // BreakableStatement[Yield, Return]:
       //   IterationStatement[?Yield, ?Return]
       //   SwitchStatement[?Yield, ?Return]
-      case TOK_DO:
+      case TokenKind::TOK_DO:
         return doWhileStatement(yieldHandling);
 
-      case TOK_WHILE:
+      case TokenKind::TOK_WHILE:
         return whileStatement(yieldHandling);
 
-      case TOK_FOR:
+      case TokenKind::TOK_FOR:
         return forStatement(yieldHandling);
 
-      case TOK_SWITCH:
+      case TokenKind::TOK_SWITCH:
         return switchStatement(yieldHandling);
 
       // ContinueStatement[?Yield]
-      case TOK_CONTINUE:
+      case TokenKind::TOK_CONTINUE:
         return continueStatement(yieldHandling);
 
       // BreakStatement[?Yield]
-      case TOK_BREAK:
+      case TokenKind::TOK_BREAK:
         return breakStatement(yieldHandling);
 
       // [+Return] ReturnStatement[?Yield]
-      case TOK_RETURN:
+      case TokenKind::TOK_RETURN:
         // The Return parameter is only used here, and the effect is easily
         // detected this way, so don't bother passing around an extra parameter
         // everywhere.
         if (!pc->isFunctionBox()) {
             error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
             return null();
         }
         return returnStatement(yieldHandling);
 
       // WithStatement[?Yield, ?Return]
-      case TOK_WITH:
+      case TokenKind::TOK_WITH:
         return withStatement(yieldHandling);
 
       // LabelledStatement[?Yield, ?Return]
-      // This is really handled by default and TOK_YIELD cases above.
+      // This is really handled by default and TokenKind::TOK_YIELD cases above.
 
       // ThrowStatement[?Yield]
-      case TOK_THROW:
+      case TokenKind::TOK_THROW:
         return throwStatement(yieldHandling);
 
       // TryStatement[?Yield, ?Return]
-      case TOK_TRY:
+      case TokenKind::TOK_TRY:
         return tryStatement(yieldHandling);
 
       // DebuggerStatement
-      case TOK_DEBUGGER:
+      case TokenKind::TOK_DEBUGGER:
         return debuggerStatement();
 
       // |function| is forbidden by lookahead restriction (unless as child
       // statement of |if| or |else|, but Parser::consequentOrAlternative
       // handles that).
-      case TOK_FUNCTION:
+      case TokenKind::TOK_FUNCTION:
         error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations");
         return null();
 
       // |class| is also forbidden by lookahead restriction.
-      case TOK_CLASS:
+      case TokenKind::TOK_CLASS:
         error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
         return null();
 
       // ImportDeclaration (only inside modules)
-      case TOK_IMPORT:
+      case TokenKind::TOK_IMPORT:
         return importDeclaration();
 
       // ExportDeclaration (only inside modules)
-      case TOK_EXPORT:
+      case TokenKind::TOK_EXPORT:
         return exportDeclaration();
 
       // Miscellaneous error cases arguably better caught here than elsewhere.
 
-      case TOK_CATCH:
+      case TokenKind::TOK_CATCH:
         error(JSMSG_CATCH_WITHOUT_TRY);
         return null();
 
-      case TOK_FINALLY:
+      case TokenKind::TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
@@ -7570,186 +7577,186 @@ GeneralParser<ParseHandler, CharT>::stat
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     switch (tt) {
       // BlockStatement[?Yield, ?Return]
-      case TOK_LC:
+      case TokenKind::TOK_LC:
         return blockStatement(yieldHandling);
 
       // VariableStatement[?Yield]
-      case TOK_VAR:
+      case TokenKind::TOK_VAR:
         return variableStatement(yieldHandling);
 
       // EmptyStatement
-      case TOK_SEMI:
+      case TokenKind::TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
       //
       // These should probably be handled by a single ExpressionStatement
       // function in a default, not split up this way.
-      case TOK_STRING:
+      case TokenKind::TOK_STRING:
         if (!canHaveDirectives && anyChars.currentToken().atom() == context->names().useAsm) {
             if (!abortIfSyntaxParser())
                 return null();
             if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement(yieldHandling);
 
-      case TOK_YIELD: {
+      case TokenKind::TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
         Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
 
-        if (next == TOK_COLON)
+        if (next == TokenKind::TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
       default: {
         // Avoid getting next token with None.
-        if (tt == TOK_AWAIT && pc->isAsync())
+        if (tt == TokenKind::TOK_AWAIT && pc->isAsync())
             return expressionStatement(yieldHandling);
 
         if (!TokenKindIsPossibleIdentifier(tt))
             return expressionStatement(yieldHandling);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
-        if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next))
+        if (tt == TokenKind::TOK_LET && nextTokenContinuesLetDeclaration(next))
             return lexicalDeclaration(yieldHandling, DeclarationKind::Let);
 
-        if (tt == TOK_ASYNC) {
-            TokenKind nextSameLine = TOK_EOF;
+        if (tt == TokenKind::TOK_ASYNC) {
+            TokenKind nextSameLine = TokenKind::TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
-            if (nextSameLine == TOK_FUNCTION) {
+            if (nextSameLine == TokenKind::TOK_FUNCTION) {
                 uint32_t toStringStart = pos().begin;
-                tokenStream.consumeKnownToken(TOK_FUNCTION);
+                tokenStream.consumeKnownToken(TokenKind::TOK_FUNCTION);
                 return functionStmt(toStringStart, yieldHandling, NameRequired,
                                     FunctionAsyncKind::AsyncFunction);
             }
         }
 
-        if (next == TOK_COLON)
+        if (next == TokenKind::TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
-      case TOK_NEW:
+      case TokenKind::TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
 
       // IfStatement[?Yield, ?Return]
-      case TOK_IF:
+      case TokenKind::TOK_IF:
         return ifStatement(yieldHandling);
 
       // BreakableStatement[?Yield, ?Return]
       //
       // BreakableStatement[Yield, Return]:
       //   IterationStatement[?Yield, ?Return]
       //   SwitchStatement[?Yield, ?Return]
-      case TOK_DO:
+      case TokenKind::TOK_DO:
         return doWhileStatement(yieldHandling);
 
-      case TOK_WHILE:
+      case TokenKind::TOK_WHILE:
         return whileStatement(yieldHandling);
 
-      case TOK_FOR:
+      case TokenKind::TOK_FOR:
         return forStatement(yieldHandling);
 
-      case TOK_SWITCH:
+      case TokenKind::TOK_SWITCH:
         return switchStatement(yieldHandling);
 
       // ContinueStatement[?Yield]
-      case TOK_CONTINUE:
+      case TokenKind::TOK_CONTINUE:
         return continueStatement(yieldHandling);
 
       // BreakStatement[?Yield]
-      case TOK_BREAK:
+      case TokenKind::TOK_BREAK:
         return breakStatement(yieldHandling);
 
       // [+Return] ReturnStatement[?Yield]
-      case TOK_RETURN:
+      case TokenKind::TOK_RETURN:
         // The Return parameter is only used here, and the effect is easily
         // detected this way, so don't bother passing around an extra parameter
         // everywhere.
         if (!pc->isFunctionBox()) {
             error(JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
             return null();
         }
         return returnStatement(yieldHandling);
 
       // WithStatement[?Yield, ?Return]
-      case TOK_WITH:
+      case TokenKind::TOK_WITH:
         return withStatement(yieldHandling);
 
       // LabelledStatement[?Yield, ?Return]
-      // This is really handled by default and TOK_YIELD cases above.
+      // This is really handled by default and TokenKind::TOK_YIELD cases above.
 
       // ThrowStatement[?Yield]
-      case TOK_THROW:
+      case TokenKind::TOK_THROW:
         return throwStatement(yieldHandling);
 
       // TryStatement[?Yield, ?Return]
-      case TOK_TRY:
+      case TokenKind::TOK_TRY:
         return tryStatement(yieldHandling);
 
       // DebuggerStatement
-      case TOK_DEBUGGER:
+      case TokenKind::TOK_DEBUGGER:
         return debuggerStatement();
 
       // Declaration[Yield]:
 
       //   HoistableDeclaration[?Yield, ~Default]
-      case TOK_FUNCTION:
+      case TokenKind::TOK_FUNCTION:
         return functionStmt(pos().begin, yieldHandling, NameRequired);
 
       //   ClassDeclaration[?Yield, ~Default]
-      case TOK_CLASS:
+      case TokenKind::TOK_CLASS:
         return classDefinition(yieldHandling, ClassStatement, NameRequired);
 
       //   LexicalDeclaration[In, ?Yield]
       //     LetOrConst BindingList[?In, ?Yield]
-      case TOK_CONST:
+      case TokenKind::TOK_CONST:
         // [In] is the default behavior, because for-loops specially parse
         // their heads to handle |in| in this situation.
         return lexicalDeclaration(yieldHandling, DeclarationKind::Const);
 
       // ImportDeclaration (only inside modules)
-      case TOK_IMPORT:
+      case TokenKind::TOK_IMPORT:
         return importDeclaration();
 
       // ExportDeclaration (only inside modules)
-      case TOK_EXPORT:
+      case TokenKind::TOK_EXPORT:
         return exportDeclaration();
 
       // Miscellaneous error cases arguably better caught here than elsewhere.
 
-      case TOK_CATCH:
+      case TokenKind::TOK_CATCH:
         error(JSMSG_CATCH_WITHOUT_TRY);
         return null();
 
-      case TOK_FINALLY:
+      case TokenKind::TOK_FINALLY:
         error(JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
@@ -7760,17 +7767,17 @@ GeneralParser<ParseHandler, CharT>::expr
                                          InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
                          possibleError, invoked);
     if (!pn)
         return null();
 
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
         return null();
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
@@ -7779,23 +7786,23 @@ GeneralParser<ParseHandler, CharT>::expr
         // directly under CoverParenthesizedExpressionAndArrowParameterList,
         // and the next two tokens are closing parenthesis and arrow. If all
         // are present allow the trailing comma.
         if (tripledotHandling == TripledotAllowed) {
             TokenKind tt;
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
 
-            if (tt == TOK_RP) {
-                tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
+            if (tt == TokenKind::TOK_RP) {
+                tokenStream.consumeKnownToken(TokenKind::TOK_RP, TokenStream::Operand);
 
                 if (!tokenStream.peekToken(&tt))
                     return null();
-                if (tt != TOK_ARROW) {
-                    error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
+                if (tt != TokenKind::TOK_ARROW) {
+                    error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::TOK_RP));
                     return null();
                 }
 
                 anyChars.ungetToken();  // put back right paren
                 break;
             }
         }
 
@@ -7814,29 +7821,29 @@ GeneralParser<ParseHandler, CharT>::expr
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
 
         handler.addList(seq, pn);
 
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
             return null();
         if (!matched)
             break;
     }
     return seq;
 }
 
 static ParseNodeKind
 BinaryOpTokenKindToParseNodeKind(TokenKind tok)
 {
     MOZ_ASSERT(TokenKindIsBinaryOp(tok));
-    return ParseNodeKind(size_t(ParseNodeKind::BinOpFirst) + (tok - TOK_BINOP_FIRST));
+    return ParseNodeKind(size_t(ParseNodeKind::BinOpFirst) + (size_t(tok) - size_t(TokenKind::TOK_BINOP_FIRST)));
 }
 
 static const int PrecedenceTable[] = {
     1, /* ParseNodeKind::PipeLine */
     2, /* ParseNodeKind::Or */
     3, /* ParseNodeKind::And */
     4, /* ParseNodeKind::BitOr */
     5, /* ParseNodeKind::BitXor */
@@ -7908,29 +7915,29 @@ GeneralParser<ParseHandler, CharT>::orEx
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
 
         ParseNodeKind pnk;
-        if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) {
+        if (tok == TokenKind::TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) {
             // We're definitely not in a destructuring context, so report any
             // pending expression error now.
             if (possibleError && !possibleError->checkForExpressionError())
                 return null();
             // Report an error for unary expressions on the LHS of **.
-            if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) {
+            if (tok == TokenKind::TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) {
                 error(JSMSG_BAD_POW_LEFTSIDE);
                 return null();
             }
             pnk = BinaryOpTokenKindToParseNodeKind(tok);
         } else {
-            tok = TOK_EOF;
+            tok = TokenKind::TOK_EOF;
             pnk = ParseNodeKind::Limit;
         }
 
         // From this point on, destructuring defaults are definitely an error.
         possibleError = nullptr;
 
         // If pnk has precedence less than or equal to another operator on the
         // stack, reduce. This combines nodes on the stack until we form the
@@ -7978,26 +7985,26 @@ GeneralParser<ParseHandler, CharT>::cond
                             expressionClosureHandling, possibleError, invoked);
     if (!condition)
         return null();
 
     if (handler.isExpressionClosure(condition))
         return condition;
 
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_HOOK))
+    if (!tokenStream.matchToken(&matched, TokenKind::TOK_HOOK))
         return null();
     if (!matched)
         return condition;
 
     Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!thenExpr)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_COLON, TokenStream::Operand, JSMSG_COLON_IN_COND);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_COLON, TokenStream::Operand, JSMSG_COLON_IN_COND);
 
     Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     if (!elseExpr)
         return null();
 
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
@@ -8028,48 +8035,48 @@ GeneralParser<ParseHandler, CharT>::assi
 
     TokenPos exprPos = pos();
 
     bool endsExpr;
 
     // This only handles identifiers that *never* have special meaning anywhere
     // in the language.  Contextual keywords, reserved words in strict mode,
     // and other hard cases are handled outside this fast path.
-    if (firstToken == TOK_NAME) {
+    if (firstToken == TokenKind::TOK_NAME) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr) {
             Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
             if (!name)
                 return null();
 
             return identifierReference(name);
         }
     }
 
-    if (firstToken == TOK_NUMBER) {
+    if (firstToken == TokenKind::TOK_NUMBER) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return newNumber(anyChars.currentToken());
     }
 
-    if (firstToken == TOK_STRING) {
+    if (firstToken == TokenKind::TOK_STRING) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return stringLiteral();
     }
 
-    if (firstToken == TOK_YIELD && yieldExpressionsSupported())
+    if (firstToken == TokenKind::TOK_YIELD && yieldExpressionsSupported())
         return yieldExpression(inHandling);
 
     bool maybeAsyncArrow = false;
-    if (firstToken == TOK_ASYNC) {
-        TokenKind nextSameLine = TOK_EOF;
+    if (firstToken == TokenKind::TOK_ASYNC) {
+        TokenKind nextSameLine = TokenKind::TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
         if (TokenKindIsPossibleIdentifier(nextSameLine))
             maybeAsyncArrow = true;
     }
 
     anyChars.ungetToken();
@@ -8079,31 +8086,31 @@ GeneralParser<ParseHandler, CharT>::assi
     typename TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     PossibleError possibleErrorInner(*this);
     Node lhs;
     TokenKind tokenAfterLHS;
     bool isArrow;
     if (maybeAsyncArrow) {
-        tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand);
+        tokenStream.consumeKnownToken(TokenKind::TOK_ASYNC, TokenStream::Operand);
 
         TokenKind tokenAfterAsync;
         if (!tokenStream.getToken(&tokenAfterAsync))
             return null();
         MOZ_ASSERT(TokenKindIsPossibleIdentifier(tokenAfterAsync));
 
         // Check yield validity here.
         RootedPropertyName name(context, bindingIdentifier(yieldHandling));
         if (!name)
             return null();
 
         if (!tokenStream.peekTokenSameLine(&tokenAfterLHS))
             return null();
-        if (tokenAfterLHS != TOK_ARROW) {
+        if (tokenAfterLHS != TokenKind::TOK_ARROW) {
             error(JSMSG_UNEXPECTED_TOKEN,
                   "'=>' on the same line after an argument list", TokenKindToDesc(tokenAfterLHS));
             return null();
         }
 
         isArrow = true;
     } else {
         lhs = condExpr(inHandling, yieldHandling, tripledotHandling, ExpressionClosure::Allowed,
@@ -8112,41 +8119,41 @@ GeneralParser<ParseHandler, CharT>::assi
             return null();
 
         // Use Operand here because the ConditionalExpression parsed above
         // could be the entirety of this AssignmentExpression, and then ASI
         // permits this token to be a regular expression.
         if (!tokenStream.peekTokenSameLine(&tokenAfterLHS, TokenStream::Operand))
             return null();
 
-        isArrow = tokenAfterLHS == TOK_ARROW;
+        isArrow = tokenAfterLHS == TokenKind::TOK_ARROW;
     }
 
     if (isArrow) {
         tokenStream.seek(start);
 
         TokenKind next;
         if (!tokenStream.getToken(&next, TokenStream::Operand))
             return null();
         uint32_t toStringStart = pos().begin;
         anyChars.ungetToken();
 
         FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction;
 
-        if (next == TOK_ASYNC) {
+        if (next == TokenKind::TOK_ASYNC) {
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
-            TokenKind nextSameLine = TOK_EOF;
+            TokenKind nextSameLine = TokenKind::TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
 
             // The AsyncArrowFunction production are
             //   async [no LineTerminator here] AsyncArrowBindingIdentifier ...
             //   async [no LineTerminator here] ArrowFormalParameters ...
-            if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TOK_LP)
+            if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::TOK_LP)
                 asyncKind = FunctionAsyncKind::AsyncFunction;
             else
                 anyChars.ungetToken();
         }
 
         Node pn = handler.newArrowFunction(pos());
         if (!pn)
             return null();
@@ -8154,29 +8161,29 @@ GeneralParser<ParseHandler, CharT>::assi
         return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr, Arrow,
                                   GeneratorKind::NotGenerator, asyncKind);
     }
 
     MOZ_ALWAYS_TRUE(tokenStream.getToken(&tokenAfterLHS, TokenStream::Operand));
 
     ParseNodeKind kind;
     switch (tokenAfterLHS) {
-      case TOK_ASSIGN:       kind = ParseNodeKind::Assign;       break;
-      case TOK_ADDASSIGN:    kind = ParseNodeKind::AddAssign;    break;
-      case TOK_SUBASSIGN:    kind = ParseNodeKind::SubAssign;    break;
-      case TOK_BITORASSIGN:  kind = ParseNodeKind::BitOrAssign;  break;
-      case TOK_BITXORASSIGN: kind = ParseNodeKind::BitXorAssign; break;
-      case TOK_BITANDASSIGN: kind = ParseNodeKind::BitAndAssign; break;
-      case TOK_LSHASSIGN:    kind = ParseNodeKind::LshAssign;    break;
-      case TOK_RSHASSIGN:    kind = ParseNodeKind::RshAssign;    break;
-      case TOK_URSHASSIGN:   kind = ParseNodeKind::UrshAssign;   break;
-      case TOK_MULASSIGN:    kind = ParseNodeKind::MulAssign;    break;
-      case TOK_DIVASSIGN:    kind = ParseNodeKind::DivAssign;    break;
-      case TOK_MODASSIGN:    kind = ParseNodeKind::ModAssign;    break;
-      case TOK_POWASSIGN:    kind = ParseNodeKind::PowAssign;    break;
+      case TokenKind::TOK_ASSIGN:       kind = ParseNodeKind::Assign;       break;
+      case TokenKind::TOK_ADDASSIGN:    kind = ParseNodeKind::AddAssign;    break;
+      case TokenKind::TOK_SUBASSIGN:    kind = ParseNodeKind::SubAssign;    break;
+      case TokenKind::TOK_BITORASSIGN:  kind = ParseNodeKind::BitOrAssign;  break;
+      case TokenKind::TOK_BITXORASSIGN: kind = ParseNodeKind::BitXorAssign; break;
+      case TokenKind::TOK_BITANDASSIGN: kind = ParseNodeKind::BitAndAssign; break;
+      case TokenKind::TOK_LSHASSIGN:    kind = ParseNodeKind::LshAssign;    break;
+      case TokenKind::TOK_RSHASSIGN:    kind = ParseNodeKind::RshAssign;    break;
+      case TokenKind::TOK_URSHASSIGN:   kind = ParseNodeKind::UrshAssign;   break;
+      case TokenKind::TOK_MULASSIGN:    kind = ParseNodeKind::MulAssign;    break;
+      case TokenKind::TOK_DIVASSIGN:    kind = ParseNodeKind::DivAssign;    break;
+      case TokenKind::TOK_MODASSIGN:    kind = ParseNodeKind::ModAssign;    break;
+      case TokenKind::TOK_POWASSIGN:    kind = ParseNodeKind::PowAssign;    break;
 
       default:
         MOZ_ASSERT(!anyChars.isCurrentTokenAssignment());
         if (!possibleError) {
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
@@ -8319,28 +8326,28 @@ GeneralParser<ParseHandler, CharT>::unar
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
-      case TOK_VOID:
+      case TokenKind::TOK_VOID:
         return unaryOpExpr(yieldHandling, ParseNodeKind::Void, begin);
-      case TOK_NOT:
+      case TokenKind::TOK_NOT:
         return unaryOpExpr(yieldHandling, ParseNodeKind::Not, begin);
-      case TOK_BITNOT:
+      case TokenKind::TOK_BITNOT:
         return unaryOpExpr(yieldHandling, ParseNodeKind::BitNot, begin);
-      case TOK_ADD:
+      case TokenKind::TOK_ADD:
         return unaryOpExpr(yieldHandling, ParseNodeKind::Pos, begin);
-      case TOK_SUB:
+      case TokenKind::TOK_SUB:
         return unaryOpExpr(yieldHandling, ParseNodeKind::Neg, begin);
 
-      case TOK_TYPEOF: {
+      case TokenKind::TOK_TYPEOF: {
         // The |typeof| operator is specially parsed to distinguish its
         // application to a name, from its application to a non-name
         // expression:
         //
         //   // Looks up the name, doesn't find it and so evaluates to
         //   // "undefined".
         //   assertEq(typeof nonExistentName, "undefined");
         //
@@ -8349,35 +8356,35 @@ GeneralParser<ParseHandler, CharT>::unar
         //   typeof (1, nonExistentName);
         Node kid = unaryExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden);
         if (!kid)
             return null();
 
         return handler.newTypeof(begin, kid);
       }
 
-      case TOK_INC:
-      case TOK_DEC:
+      case TokenKind::TOK_INC:
+      case TokenKind::TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
 
         uint32_t operandOffset = pos().begin;
         Node operand =
             memberExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden, tt2);
         if (!operand || !checkIncDecOperand(operand, operandOffset))
             return null();
-        ParseNodeKind pnk = (tt == TOK_INC)
+        ParseNodeKind pnk = (tt == TokenKind::TOK_INC)
                             ? ParseNodeKind::PreIncrement
                             : ParseNodeKind::PreDecrement;
         return handler.newUpdate(pnk, begin, operand);
       }
 
-      case TOK_DELETE: {
+      case TokenKind::TOK_DELETE: {
         uint32_t exprOffset;
         if (!tokenStream.peekOffset(&exprOffset, TokenStream::Operand))
             return null();
 
         Node expr = unaryExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden);
         if (!expr)
             return null();
 
@@ -8388,17 +8395,17 @@ GeneralParser<ParseHandler, CharT>::unar
                 return null();
 
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
-      case TOK_AWAIT: {
+      case TokenKind::TOK_AWAIT: {
         if (pc->isAsync()) {
             Node kid = unaryExpr(yieldHandling, tripledotHandling, ExpressionClosure::Forbidden,
                                  possibleError, invoked);
             if (!kid)
                 return null();
             pc->lastAwaitOffset = begin;
             return handler.newAwaitExpression(begin, kid);
         }
@@ -8414,24 +8421,24 @@ GeneralParser<ParseHandler, CharT>::unar
 
         if (handler.isExpressionClosure(expr))
             return expr;
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
             return null();
 
-        if (tt != TOK_INC && tt != TOK_DEC)
+        if (tt != TokenKind::TOK_INC && tt != TokenKind::TOK_DEC)
             return expr;
 
         tokenStream.consumeKnownToken(tt);
         if (!checkIncDecOperand(expr, begin))
             return null();
 
-        ParseNodeKind pnk = (tt == TOK_INC)
+        ParseNodeKind pnk = (tt == TokenKind::TOK_INC)
                             ? ParseNodeKind::PostIncrement
                             : ParseNodeKind::PostDecrement;
         return handler.newUpdate(pnk, begin, expr);
       }
     }
 }
 
 
@@ -8457,27 +8464,27 @@ GeneralParser<ParseHandler, CharT>::assi
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
                                                  bool* isSpread,
                                                  PossibleError* possibleError /* = nullptr */)
 {
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TokenKind::TOK_RP, TokenStream::Operand))
         return false;
     if (matched) {
         handler.setEndPosition(listNode, pos().end);
         return true;
     }
 
     while (true) {
         bool spread = false;
         uint32_t begin = 0;
-        if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_TRIPLEDOT, TokenStream::Operand))
             return false;
         if (matched) {
             spread = true;
             begin = pos().begin;
             *isSpread = true;
         }
 
         Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
@@ -8487,29 +8494,29 @@ GeneralParser<ParseHandler, CharT>::argu
             argNode = handler.newSpread(begin, argNode);
             if (!argNode)
                 return false;
         }
 
         handler.addList(listNode, argNode);
 
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
             return false;
         if (!matched)
             break;
 
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
-        if (tt == TOK_RP)
+        if (tt == TokenKind::TOK_RP)
             break;
     }
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
 
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 bool
 ParserBase::checkAndMarkSuperScope()
 {
@@ -8532,17 +8539,17 @@ GeneralParser<ParseHandler, CharT>::memb
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
 
     Node lhs;
 
     if (!CheckRecursionLimit(context))
         return null();
 
     /* Check for new expression first. */
-    if (tt == TOK_NEW) {
+    if (tt == TokenKind::TOK_NEW) {
         uint32_t newBegin = pos().begin;
         // Make sure this wasn't a |new.target| in disguise.
         Node newTarget;
         if (!tryNewTarget(newTarget))
             return null();
         if (newTarget) {
             lhs = newTarget;
         } else {
@@ -8555,93 +8562,93 @@ GeneralParser<ParseHandler, CharT>::memb
             if (!ctorExpr)
                 return null();
 
             lhs = handler.newNewExpression(newBegin, ctorExpr);
             if (!lhs)
                 return null();
 
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_LP))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_LP))
                 return null();
             if (matched) {
                 bool isSpread = false;
                 if (!argumentList(yieldHandling, lhs, &isSpread))
                     return null();
                 if (isSpread)
                     handler.setOp(lhs, JSOP_SPREADNEW);
             }
         }
-    } else if (tt == TOK_SUPER) {
+    } else if (tt == TokenKind::TOK_SUPER) {
         Node thisName = newThisName();
         if (!thisName)
             return null();
         lhs = handler.newSuperBase(thisName, pos());
         if (!lhs)
             return null();
     } else {
         lhs = primaryExpr(yieldHandling, tripledotHandling, expressionClosureHandling, tt,
                           possibleError, invoked);
         if (!lhs)
             return null();
 
         if (handler.isExpressionClosure(lhs))
             return lhs;
     }
 
-    MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TOK_SUPER));
+    MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TokenKind::TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
-        if (tt == TOK_EOF)
+        if (tt == TokenKind::TOK_EOF)
             break;
 
         Node nextMember;
-        if (tt == TOK_DOT) {
+        if (tt == TokenKind::TOK_DOT) {
             if (!tokenStream.getToken(&tt))
                 return null();
             if (TokenKindIsPossibleIdentifierName(tt)) {
                 PropertyName* field = anyChars.currentName();
                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     error(JSMSG_BAD_SUPERPROP, "property");
                     return null();
                 }
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 error(JSMSG_NAME_AFTER_DOT);
                 return null();
             }
-        } else if (tt == TOK_LB) {
+        } else if (tt == TokenKind::TOK_LB) {
             Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
-            MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
+            MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
 
             if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                 error(JSMSG_BAD_SUPERPROP, "member");
                 return null();
             }
             nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
             if (!nextMember)
                 return null();
-        } else if ((allowCallSyntax && tt == TOK_LP) ||
-                   tt == TOK_TEMPLATE_HEAD ||
-                   tt == TOK_NO_SUBS_TEMPLATE)
+        } else if ((allowCallSyntax && tt == TokenKind::TOK_LP) ||
+                   tt == TokenKind::TOK_TEMPLATE_HEAD ||
+                   tt == TokenKind::TOK_NO_SUBS_TEMPLATE)
         {
             if (handler.isSuperBase(lhs)) {
                 if (!pc->sc()->allowSuperCall()) {
                     error(JSMSG_BAD_SUPERCALL);
                     return null();
                 }
 
-                if (tt != TOK_LP) {
+                if (tt != TokenKind::TOK_LP) {
                     error(JSMSG_BAD_SUPER);
                     return null();
                 }
 
                 nextMember = handler.newSuperCall(lhs);
                 if (!nextMember)
                     return null();
 
@@ -8664,17 +8671,17 @@ GeneralParser<ParseHandler, CharT>::memb
                     return null();
             } else {
                 if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
                     error(JSMSG_SELFHOSTED_METHOD_CALL);
                     return null();
                 }
 
                 TokenPos nextMemberPos = pos();
-                nextMember = tt == TOK_LP
+                nextMember = tt == TokenKind::TOK_LP
                              ? handler.newCall(nextMemberPos)
                              : handler.newTaggedTemplate(nextMemberPos);
                 if (!nextMember)
                     return null();
 
                 JSOp op = JSOP_CALL;
                 bool maybeAsyncArrow = false;
                 if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
@@ -8682,17 +8689,17 @@ GeneralParser<ParseHandler, CharT>::memb
                     // right syntax.
                     if (prop == context->names().apply) {
                         op = JSOP_FUNAPPLY;
                         if (pc->isFunctionBox())
                             pc->functionBox()->usesApply = true;
                     } else if (prop == context->names().call) {
                         op = JSOP_FUNCALL;
                     }
-                } else if (tt == TOK_LP) {
+                } else if (tt == TokenKind::TOK_LP) {
                     if (handler.isAsyncKeyword(lhs, context)) {
                         // |async (| can be the start of an async arrow
                         // function, so we need to defer reporting possible
                         // errors from destructuring syntax. To give better
                         // error messages, we only allow the AsyncArrowHead
                         // part of the CoverCallExpressionAndAsyncArrowHead
                         // syntax when the initial name is "async".
                         maybeAsyncArrow = true;
@@ -8714,17 +8721,17 @@ GeneralParser<ParseHandler, CharT>::memb
                         // ignore the return value.)
                         checkAndMarkSuperScope();
                     }
                 }
 
                 handler.setBeginPosition(nextMember, lhs);
                 handler.addList(nextMember, lhs);
 
-                if (tt == TOK_LP) {
+                if (tt == TokenKind::TOK_LP) {
                     bool isSpread = false;
                     PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
                     if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
                         return null();
                     if (isSpread) {
                         if (op == JSOP_EVAL)
                             op = JSOP_SPREADEVAL;
                         else if (op == JSOP_STRICTEVAL)
@@ -8770,54 +8777,54 @@ PerHandlerParser<ParseHandler>::newName(
     return handler.newName(name, pos, context);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
                                                                     uint32_t offset,
                                                                     YieldHandling yieldHandling,
-                                                                    TokenKind hint /* = TOK_LIMIT */)
+                                                                    TokenKind hint /* = TokenKind::TOK_LIMIT */)
 {
     TokenKind tt;
-    if (hint == TOK_LIMIT) {
+    if (hint == TokenKind::TOK_LIMIT) {
         tt = ReservedWordTokenKind(ident);
     } else {
         MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind");
         tt = hint;
     }
 
-    if (tt == TOK_NAME)
+    if (tt == TokenKind::TOK_NAME)
         return true;
     if (TokenKindIsContextualKeyword(tt)) {
-        if (tt == TOK_YIELD) {
+        if (tt == TokenKind::TOK_YIELD) {
             if (yieldHandling == YieldIsKeyword) {
                 errorAt(offset, JSMSG_RESERVED_ID, "yield");
                 return false;
             }
             if (pc->sc()->needStrictChecks()) {
                 if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield"))
                     return false;
             }
             return true;
         }
-        if (tt == TOK_AWAIT) {
+        if (tt == TokenKind::TOK_AWAIT) {
             if (awaitIsKeyword()) {
                 errorAt(offset, JSMSG_RESERVED_ID, "await");
                 return false;
             }
             return true;
         }
         if (pc->sc()->needStrictChecks()) {
-            if (tt == TOK_LET) {
+            if (tt == TokenKind::TOK_LET) {
                 if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let"))
                     return false;
                 return true;
             }
-            if (tt == TOK_STATIC) {
+            if (tt == TokenKind::TOK_STATIC) {
                 if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static"))
                     return false;
                 return true;
             }
         }
         return true;
     }
     if (TokenKindIsStrictReservedWord(tt)) {
@@ -8838,17 +8845,17 @@ GeneralParser<ParseHandler, CharT>::chec
     MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind.");
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::checkBindingIdentifier(PropertyName* ident, uint32_t offset,
                                                            YieldHandling yieldHandling,
-                                                           TokenKind hint /* = TOK_LIMIT */)
+                                                           TokenKind hint /* = TokenKind::TOK_LIMIT */)
 {
     if (pc->sc()->needStrictChecks()) {
         if (ident == context->names().arguments) {
             if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments"))
                 return false;
             return true;
         }
 
@@ -8872,30 +8879,30 @@ GeneralParser<ParseHandler, CharT>::labe
     //   Identifier whose code point sequence is the same as a ReservedWord.
     //
     // Use PropertyName* instead of TokenKind to reflect the normalization.
 
     // Unless the name contains escapes, we can reuse the current TokenKind
     // to determine if the name is a restricted identifier.
     TokenKind hint = !anyChars.currentNameHasEscapes()
                      ? anyChars.currentToken().type
-                     : TOK_LIMIT;
+                     : TokenKind::TOK_LIMIT;
     RootedPropertyName ident(context, anyChars.currentName());
     if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler, typename CharT>
 PropertyName*
 GeneralParser<ParseHandler, CharT>::bindingIdentifier(YieldHandling yieldHandling)
 {
     TokenKind hint = !anyChars.currentNameHasEscapes()
                      ? anyChars.currentToken().type
-                     : TOK_LIMIT;
+                     : TokenKind::TOK_LIMIT;
     RootedPropertyName ident(context, anyChars.currentName());
     if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint))
         return nullptr;
     return ident;
 }
 
 template <class ParseHandler>
 typename ParseHandler::Node
@@ -9099,28 +9106,28 @@ GeneralParser<ParseHandler, CharT>::chec
     return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
                                                      PossibleError* possibleError)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
-    if (tt == TOK_RB) {
+    if (tt == TokenKind::TOK_RB) {
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
         handler.setListFlag(literal, PNX_NONCONST);
     } else {
         anyChars.ungetToken();
 
@@ -9128,28 +9135,28 @@ GeneralParser<ParseHandler, CharT>::arra
             if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
                 error(JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt;
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
-            if (tt == TOK_RB)
+            if (tt == TokenKind::TOK_RB)
                 break;
 
-            if (tt == TOK_COMMA) {
-                tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
+            if (tt == TokenKind::TOK_COMMA) {
+                tokenStream.consumeKnownToken(TokenKind::TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
                 continue;
             }
 
-            if (tt == TOK_TRIPLEDOT) {
-                tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
+            if (tt == TokenKind::TOK_TRIPLEDOT) {
+                tokenStream.consumeKnownToken(TokenKind::TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
 
                 TokenPos innerPos;
                 if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
                     return null();
 
                 PossibleError possibleErrorInner(*this);
                 Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
@@ -9180,26 +9187,26 @@ GeneralParser<ParseHandler, CharT>::arra
                     return null();
                 }
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+            if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
                 return null();
             if (!matched)
                 break;
 
-            if (tt == TOK_TRIPLEDOT && possibleError)
+            if (tt == TokenKind::TOK_TRIPLEDOT && possibleError)
                 possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
         }
 
-        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::Operand,
+        MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RB, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
                                                               JSMSG_BRACKET_OPENED, begin));
     }
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
@@ -9218,22 +9225,22 @@ GeneralParser<ParseHandler, CharT>::prop
                                                  Node propList,
                                                  PropertyType* propType,
                                                  MutableHandleAtom propAtom)
 {
     TokenKind ltok;
     if (!tokenStream.getToken(&ltok))
         return null();
 
-    MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
+    MOZ_ASSERT(ltok != TokenKind::TOK_RC, "caller should have handled TokenKind::TOK_RC");
 
     bool isGenerator = false;
     bool isAsync = false;
 
-    if (ltok == TOK_ASYNC) {
+    if (ltok == TokenKind::TOK_ASYNC) {
         // AsyncMethod[Yield, Await]:
         //   async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
         //
         //  AsyncGeneratorMethod[Yield, Await]:
         //    async [no LineTerminator here] * PropertyName[?Yield, ?Await] ...
         //
         // PropertyName:
         //   LiteralPropertyName
@@ -9241,161 +9248,161 @@ GeneralParser<ParseHandler, CharT>::prop
         //
         // LiteralPropertyName:
         //   IdentifierName
         //   StringLiteral
         //   NumericLiteral
         //
         // ComputedPropertyName[Yield, Await]:
         //   [ ...
-        TokenKind tt = TOK_EOF;
+        TokenKind tt = TokenKind::TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&tt))
             return null();
-        if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB ||
-            TokenKindIsPossibleIdentifierName(tt) || tt == TOK_MUL)
+        if (tt == TokenKind::TOK_STRING || tt == TokenKind::TOK_NUMBER || tt == TokenKind::TOK_LB ||
+            TokenKindIsPossibleIdentifierName(tt) || tt == TokenKind::TOK_MUL)
         {
             isAsync = true;
             tokenStream.consumeKnownToken(tt);
             ltok = tt;
         }
     }
 
-    if (ltok == TOK_MUL) {
+    if (ltok == TokenKind::TOK_MUL) {
         isGenerator = true;
         if (!tokenStream.getToken(&ltok))
             return null();
     }
 
     propAtom.set(nullptr);
     Node propName;
     switch (ltok) {
-      case TOK_NUMBER:
+      case TokenKind::TOK_NUMBER:
         propAtom.set(DoubleToAtom(context, anyChars.currentToken().number()));
         if (!propAtom.get())
             return null();
         propName = newNumber(anyChars.currentToken());
         if (!propName)
             return null();
         break;
 
-      case TOK_STRING: {
+      case TokenKind::TOK_STRING: {
         propAtom.set(anyChars.currentToken().atom());
         uint32_t index;
         if (propAtom->isIndex(&index)) {
             propName = handler.newNumber(index, NoDecimal, pos());
             if (!propName)
                 return null();
             break;
         }
         propName = stringLiteral();
         if (!propName)
             return null();
         break;
       }
 
-      case TOK_LB:
+      case TokenKind::TOK_LB:
         propName = computedPropertyName(yieldHandling, maybeDecl, propList);
         if (!propName)
             return null();
         break;
 
       default: {
         if (!TokenKindIsPossibleIdentifierName(ltok)) {
             error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
             return null();
         }
 
         propAtom.set(anyChars.currentName());
         // Do not look for accessor syntax on generator or async methods.
-        if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) {
+        if (isGenerator || isAsync || !(ltok == TokenKind::TOK_GET || ltok == TokenKind::TOK_SET)) {
             propName = handler.newObjectLiteralPropertyName(propAtom, pos());
             if (!propName)
                 return null();
             break;
         }
 
-        *propType = ltok == TOK_GET ? PropertyType::Getter : PropertyType::Setter;
+        *propType = ltok == TokenKind::TOK_GET ? PropertyType::Getter : PropertyType::Setter;
 
         // We have parsed |get| or |set|. Look for an accessor property
         // name next.
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
         if (TokenKindIsPossibleIdentifierName(tt)) {
             tokenStream.consumeKnownToken(tt);
 
             propAtom.set(anyChars.currentName());
             return handler.newObjectLiteralPropertyName(propAtom, pos());
         }
-        if (tt == TOK_STRING) {
-            tokenStream.consumeKnownToken(TOK_STRING);
+        if (tt == TokenKind::TOK_STRING) {
+            tokenStream.consumeKnownToken(TokenKind::TOK_STRING);
 
             propAtom.set(anyChars.currentToken().atom());
 
             uint32_t index;
             if (propAtom->isIndex(&index)) {
                 propAtom.set(DoubleToAtom(context, index));
                 if (!propAtom.get())
                     return null();
                 return handler.newNumber(index, NoDecimal, pos());
             }
             return stringLiteral();
         }
-        if (tt == TOK_NUMBER) {
-            tokenStream.consumeKnownToken(TOK_NUMBER);
+        if (tt == TokenKind::TOK_NUMBER) {
+            tokenStream.consumeKnownToken(TokenKind::TOK_NUMBER);
 
             propAtom.set(DoubleToAtom(context, anyChars.currentToken().number()));
             if (!propAtom.get())
                 return null();
             return newNumber(anyChars.currentToken());
         }
-        if (tt == TOK_LB) {
-            tokenStream.consumeKnownToken(TOK_LB);
+        if (tt == TokenKind::TOK_LB) {
+            tokenStream.consumeKnownToken(TokenKind::TOK_LB);
 
             return computedPropertyName(yieldHandling, maybeDecl, propList);
         }
 
         // Not an accessor property after all.
         propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
         if (!propName)
             return null();
         break;
       }
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
-    if (tt == TOK_COLON) {
+    if (tt == TokenKind::TOK_COLON) {
         if (isGenerator || isAsync) {
             error(JSMSG_BAD_PROP_ID);
             return null();
         }
         *propType = PropertyType::Normal;
         return propName;
     }
 
     if (TokenKindIsPossibleIdentifierName(ltok) &&
-        (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN))
+        (tt == TokenKind::TOK_COMMA || tt == TokenKind::TOK_RC || tt == TokenKind::TOK_ASSIGN))
     {
         if (isGenerator || isAsync) {
             error(JSMSG_BAD_PROP_ID);
             return null();
         }
 
         anyChars.ungetToken();
         anyChars.addModifierException(TokenStream::OperandIsNone);
-        *propType = tt == TOK_ASSIGN
+        *propType = tt == TokenKind::TOK_ASSIGN
                     ? PropertyType::CoverInitializedName
                     : PropertyType::Shorthand;
         return propName;
     }
 
-    if (tt == TOK_LP) {
+    if (tt == TokenKind::TOK_LP) {
         anyChars.ungetToken();
 
         if (isGenerator && isAsync)
             *propType = PropertyType::AsyncGeneratorMethod;
         else if (isGenerator)
             *propType = PropertyType::GeneratorMethod;
         else if (isAsync)
             *propType = PropertyType::AsyncMethod;
@@ -9409,63 +9416,63 @@ GeneralParser<ParseHandler, CharT>::prop
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
                                                          const Maybe<DeclarationKind>& maybeDecl,
                                                          Node literal)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LB));
 
     uint32_t begin = pos().begin;
 
     if (maybeDecl) {
         if (*maybeDecl == DeclarationKind::FormalParameter)
             pc->functionBox()->hasParameterExprs = true;
     } else {
         handler.setListFlag(literal, PNX_NONCONST);
     }
 
     Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!assignNode)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
+    MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RB, TokenStream::Operand, JSMSG_COMP_PROP_UNTERM_EXPR);
     return handler.newComputedName(assignNode, begin, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
                                                   PossibleError* possibleError)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LC));
 
     uint32_t openedPos = pos().begin;
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     bool seenCoverInitializedName = false;
     Maybe<DeclarationKind> declKind = Nothing();
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return null();
-        if (tt == TOK_RC) {
+        if (tt == TokenKind::TOK_RC) {
             anyChars.addModifierException(TokenStream::OperandIsNone);
             break;
         }
 
-        if (tt == TOK_TRIPLEDOT) {
-            tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
+        if (tt == TokenKind::TOK_TRIPLEDOT) {
+            tokenStream.consumeKnownToken(TokenKind::TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenPos innerPos;
             if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
                 return null();
 
             PossibleError possibleErrorInner(*this);
             Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
@@ -9521,17 +9528,17 @@ GeneralParser<ParseHandler, CharT>::obje
 
                         // Otherwise delay error reporting until we've
                         // determined whether or not we're destructuring.
                         possibleError->setPendingExpressionErrorAt(namePos,
                                                                    JSMSG_DUPLICATE_PROTO_PROPERTY);
                     }
                     seenPrototypeMutation = true;
 
-                    // Note: this occurs *only* if we observe TOK_COLON!  Only
+                    // Note: this occurs *only* if we observe TokenKind::TOK_COLON!  Only
                     // __proto__: v mutates [[Prototype]].  Getters, setters,
                     // method/generator definitions, computed property name
                     // versions of all of these, and shorthands do not.
                     if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr))
                         return null();
                 } else {
                     if (!handler.isConstant(propExpr))
                         handler.setListFlag(literal, PNX_NONCONST);
@@ -9566,17 +9573,17 @@ GeneralParser<ParseHandler, CharT>::obje
                 Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
                 if (!name)
                     return null();
 
                 Node lhs = identifierReference(name);
                 if (!lhs)
                     return null();
 
-                tokenStream.consumeKnownToken(TOK_ASSIGN);
+                tokenStream.consumeKnownToken(TokenKind::TOK_ASSIGN);
 
                 if (!seenCoverInitializedName) {
                     // "shorthand default" or "CoverInitializedName" syntax is
                     // only valid in the case of destructuring.
                     seenCoverInitializedName = true;
 
                     if (!possibleError) {
                         // Destructuring defaults are definitely not allowed
@@ -9610,17 +9617,17 @@ GeneralParser<ParseHandler, CharT>::obje
                 Node propExpr = handler.newAssignment(ParseNodeKind::Assign, lhs, rhs);
                 if (!propExpr)
                     return null();
 
                 if (!handler.addPropertyDefinition(literal, propName, propExpr))
                     return null();
             } else {
                 RootedAtom funName(context);
-                if (!anyChars.isCurrentTokenType(TOK_RB)) {
+                if (!anyChars.isCurrentTokenType(TokenKind::TOK_RB)) {
                     funName = propAtom;
 
                     if (propType == PropertyType::Getter || propType == PropertyType::Setter) {
                         funName = prefixAccessorName(propType, propAtom);
                         if (!funName)
                             return null();
                     }
                 }
@@ -9638,25 +9645,25 @@ GeneralParser<ParseHandler, CharT>::obje
                 if (possibleError) {
                     possibleError->setPendingDestructuringErrorAt(namePos,
                                                                   JSMSG_BAD_DESTRUCT_TARGET);
                 }
             }
         }
 
         bool matched;
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TokenKind::TOK_COMMA, TokenStream::Operand))
             return null();
         if (!matched)
             break;
-        if (tt == TOK_TRIPLEDOT && possibleError)
+        if (tt == TokenKind::TOK_TRIPLEDOT && possibleError)
             possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
     }
 
-    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
+    MUST_MATCH_TOKEN_MOD_WITH_REPORT(TokenKind::TOK_RC, TokenStream::Operand,
                                      reportMissingClosing(JSMSG_CURLY_AFTER_LIST,
                                                           JSMSG_CURLY_OPENED, openedPos));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
@@ -9720,39 +9727,39 @@ GeneralParser<ParseHandler, CharT>::meth
     return functionDefinition(pn, toStringStart, InAllowed, yieldHandling, funName, kind,
                               generatorKind, asyncKind);
 }
 
 template <class ParseHandler, typename CharT>
 bool
 GeneralParser<ParseHandler, CharT>::tryNewTarget(Node &newTarget)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_NEW));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_NEW));
 
     newTarget = null();
 
     Node newHolder = handler.newPosHolder(pos());
     if (!newHolder)
         return false;
 
     uint32_t begin = pos().begin;
 
     // |new| expects to look for an operand, so we will honor that.
     TokenKind next;
     if (!tokenStream.getToken(&next, TokenStream::Operand))
         return false;
 
     // Don't unget the token, since lookahead cannot handle someone calling
     // getToken() with a different modifier. Callers should inspect currentToken().
-    if (next != TOK_DOT)
+    if (next != TokenKind::TOK_DOT)
         return true;
 
     if (!tokenStream.getToken(&next))
         return false;
-    if (next != TOK_TARGET) {
+    if (next != TokenKind::TOK_TARGET) {
         error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next));
         return false;
     }
 
     if (!pc->sc()->allowNewTarget()) {
         errorAt(begin, JSMSG_BAD_NEWTARGET);
         return false;
     }
@@ -9773,136 +9780,136 @@ GeneralParser<ParseHandler, CharT>::prim
                                                 TokenKind tt, PossibleError* possibleError,
                                                 InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     if (!CheckRecursionLimit(context))
         return null();
 
     switch (tt) {
-      case TOK_FUNCTION:
+      case TokenKind::TOK_FUNCTION:
         return functionExpr(pos().begin, expressionClosureHandling, invoked,
                             FunctionAsyncKind::SyncFunction);
 
-      case TOK_CLASS:
+      case TokenKind::TOK_CLASS:
         return classDefinition(yieldHandling, ClassExpression, NameRequired);
 
-      case TOK_LB:
+      case TokenKind::TOK_LB:
         return arrayInitializer(yieldHandling, possibleError);
 
-      case TOK_LC:
+      case TokenKind::TOK_LC:
         return objectLiteral(yieldHandling, possibleError);
 
-      case TOK_LP: {
+      case TokenKind::TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
-        if (next == TOK_RP) {
+        if (next == TokenKind::TOK_RP) {
             // Not valid expression syntax, but this is valid in an arrow function
             // with no params: `() => body`.
-            tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
+            tokenStream.consumeKnownToken(TokenKind::TOK_RP, TokenStream::Operand);
 
             if (!tokenStream.peekToken(&next))
                 return null();
-            if (next != TOK_ARROW) {
-                error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
+            if (next != TokenKind::TOK_ARROW) {
+                error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::TOK_RP));
                 return null();
             }
 
             // Now just return something that will allow parsing to continue.
             // It doesn't matter what; when we reach the =>, we will rewind and
             // reparse the whole arrow function. See Parser::assignExpr.
             return handler.newNullLiteral(pos());
         }
 
         // Pass |possibleError| to support destructuring in arrow parameters.
         Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
         if (!expr)
             return null();
-        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
+        MUST_MATCH_TOKEN_MOD(TokenKind::TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
         return handler.parenthesize(expr);
       }
 
-      case TOK_TEMPLATE_HEAD:
+      case TokenKind::TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
-      case TOK_NO_SUBS_TEMPLATE:
+      case TokenKind::TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionUntaggedTemplate();
 
-      case TOK_STRING:
+      case TokenKind::TOK_STRING:
         return stringLiteral();
 
       default: {
         if (!TokenKindIsPossibleIdentifier(tt)) {
             error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
             return null();
         }
 
-        if (tt == TOK_ASYNC) {
-            TokenKind nextSameLine = TOK_EOF;
+        if (tt == TokenKind::TOK_ASYNC) {
+            TokenKind nextSameLine = TokenKind::TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
 
-            if (nextSameLine == TOK_FUNCTION) {
+            if (nextSameLine == TokenKind::TOK_FUNCTION) {
                 uint32_t toStringStart = pos().begin;
-                tokenStream.consumeKnownToken(TOK_FUNCTION);
+                tokenStream.consumeKnownToken(TokenKind::TOK_FUNCTION);
                 return functionExpr(toStringStart, expressionClosureHandling, PredictUninvoked,
                                     FunctionAsyncKind::AsyncFunction);
             }
         }
 
         Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
         if (!name)
             return null();
 
         return identifierReference(name);
       }
 
-      case TOK_REGEXP:
+      case TokenKind::TOK_REGEXP:
         return newRegExp();
 
-      case TOK_NUMBER:
+      case TokenKind::TOK_NUMBER:
         return newNumber(anyChars.currentToken());
 
-      case TOK_TRUE:
+      case TokenKind::TOK_TRUE:
         return handler.newBooleanLiteral(true, pos());
-      case TOK_FALSE:
+      case TokenKind::TOK_FALSE:
         return handler.newBooleanLiteral(false, pos());
-      case TOK_THIS: {
+      case TokenKind::TOK_THIS: {
         if (pc->isFunctionBox())
             pc->functionBox()->usesThis = true;
         Node thisName = null();
         if (pc->sc()->thisBinding() == ThisBinding::Function) {
             thisName = newThisName();
             if (!thisName)
                 return null();
         }
         return handler.newThisLiteral(pos(), thisName);
       }
-      case TOK_NULL:
+      case TokenKind::TOK_NULL:
         return handler.newNullLiteral(pos());
 
-      case TOK_TRIPLEDOT: {
+      case TokenKind::TOK_TRIPLEDOT: {
         // This isn't valid expression syntax, but it's valid in an arrow
         // function as a trailing rest param: `(a, b, ...rest) => body`.  Check
         // if it's directly under
         // CoverParenthesizedExpressionAndArrowParameterList, and check for a
         // name, closing parenthesis, and arrow, and allow it only if all are
         // present.
         if (tripledotHandling != TripledotAllowed) {
             error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
             return null();
         }
 
         TokenKind next;
         if (!tokenStream.getToken(&next))
             return null();
 
-        if (next == TOK_LB || next == TOK_LC) {
+        if (next == TokenKind::TOK_LB || next == TokenKind::TOK_LC) {
             // Validate, but don't store the pattern right now. The whole arrow
             // function is reparsed in functionFormalParametersAndBody().
             if (!destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling,
                                           next))
             {
                 return null();
             }
         } else {
@@ -9913,46 +9920,46 @@ GeneralParser<ParseHandler, CharT>::prim
             if (!TokenKindIsPossibleIdentifier(next)) {
                 error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next));
                 return null();
             }
         }
 
         if (!tokenStream.getToken(&next))
             return null();
-        if (next != TOK_RP) {
+        if (next != TokenKind::TOK_RP) {
             error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next));
             return null();
         }
 
         if (!tokenStream.peekToken(&next))
             return null();
-        if (next != TOK_ARROW) {
+        if (next != TokenKind::TOK_ARROW) {
             // Advance the scanner for proper error location reporting.
             tokenStream.consumeKnownToken(next);
             error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next));
             return null();
         }
 
         anyChars.ungetToken();  // put back right paren
 
-        // Return an arbitrary expression node. See case TOK_RP above.
+        // Return an arbitrary expression node. See case TokenKind::TOK_RP above.
         return handler.newNullLiteral(pos());
       }
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::exprInParens(InHandling inHandling,
                                                  YieldHandling yieldHandling,
                                                  TripledotHandling tripledotHandling,
                                                  PossibleError* possibleError /* = nullptr */)
 {
-    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LP));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
 }
 
 void
 ParserBase::addTelemetry(DeprecatedLanguageExtension e)
 {
     if (context->helperThread())
         return;
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1153,17 +1153,17 @@ class GeneralParser
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
 
     bool checkBindingIdentifier(PropertyName* ident,
                                 uint32_t offset,
                                 YieldHandling yieldHandling,
-                                TokenKind hint = TOK_LIMIT);
+                                TokenKind hint = TokenKind::TOK_LIMIT);
 
     PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
 
     PropertyName* labelIdentifier(YieldHandling yieldHandling) {
         return labelOrIdentifierReference(yieldHandling);
     }
 
     PropertyName* identifierReference(YieldHandling yieldHandling) {
@@ -1227,17 +1227,18 @@ class GeneralParser
 
   protected:
     // Match the current token against the BindingIdentifier production with
     // the given Yield parameter.  If there is no match, report a syntax
     // error.
     PropertyName* bindingIdentifier(YieldHandling yieldHandling);
 
     bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset,
-                                         YieldHandling yieldHandling, TokenKind hint = TOK_LIMIT);
+                                         YieldHandling yieldHandling,
+                                         TokenKind hint = TokenKind::TOK_LIMIT);
 
     Node statementList(YieldHandling yieldHandling);
 
     bool innerFunction(Node funcNode, ParseContext* outerpc, HandleFunction fun,
                        uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling,
                        FunctionSyntaxKind kind, GeneratorKind generatorKind,
                        FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives,
                        Directives* newDirectives);
--- a/js/src/frontend/ReservedWords.h
+++ b/js/src/frontend/ReservedWords.h
@@ -5,76 +5,76 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* A higher-order macro for enumerating reserved word tokens. */
 
 #ifndef vm_ReservedWords_h
 #define vm_ReservedWords_h
 
 #define FOR_EACH_JAVASCRIPT_RESERVED_WORD(macro) \
-    macro(false, false_, TOK_FALSE) \
-    macro(true, true_, TOK_TRUE) \
-    macro(null, null, TOK_NULL) \
+    macro(false, false_, TokenKind::TOK_FALSE) \
+    macro(true, true_, TokenKind::TOK_TRUE) \
+    macro(null, null, TokenKind::TOK_NULL) \
     \
     /* Keywords. */ \
-    macro(break, break_, TOK_BREAK) \
-    macro(case, case_, TOK_CASE) \
-    macro(catch, catch_, TOK_CATCH) \
-    macro(const, const_, TOK_CONST) \
-    macro(continue, continue_, TOK_CONTINUE) \
-    macro(debugger, debugger, TOK_DEBUGGER) \
-    macro(default, default_, TOK_DEFAULT) \
-    macro(delete, delete_, TOK_DELETE) \
-    macro(do, do_, TOK_DO) \
-    macro(else, else_, TOK_ELSE) \
-    macro(finally, finally_, TOK_FINALLY) \
-    macro(for, for_, TOK_FOR) \
-    macro(function, function, TOK_FUNCTION) \
-    macro(if, if_, TOK_IF) \
-    macro(in, in, TOK_IN) \
-    macro(instanceof, instanceof, TOK_INSTANCEOF) \
-    macro(new, new_, TOK_NEW) \
-    macro(return, return_, TOK_RETURN) \
-    macro(switch, switch_, TOK_SWITCH) \
-    macro(this, this_, TOK_THIS) \
-    macro(throw, throw_, TOK_THROW) \
-    macro(try, try_, TOK_TRY) \
-    macro(typeof, typeof_, TOK_TYPEOF) \
-    macro(var, var, TOK_VAR) \
-    macro(void, void_, TOK_VOID) \
-    macro(while, while_, TOK_WHILE) \
-    macro(with, with, TOK_WITH) \
-    macro(import, import, TOK_IMPORT) \
-    macro(export, export_, TOK_EXPORT) \
-    macro(class, class_, TOK_CLASS) \
-    macro(extends, extends, TOK_EXTENDS) \
-    macro(super, super, TOK_SUPER) \
+    macro(break, break_, TokenKind::TOK_BREAK) \
+    macro(case, case_, TokenKind::TOK_CASE) \
+    macro(catch, catch_, TokenKind::TOK_CATCH) \
+    macro(const, const_, TokenKind::TOK_CONST) \
+    macro(continue, continue_, TokenKind::TOK_CONTINUE) \
+    macro(debugger, debugger, TokenKind::TOK_DEBUGGER) \
+    macro(default, default_, TokenKind::TOK_DEFAULT) \
+    macro(delete, delete_, TokenKind::TOK_DELETE) \
+    macro(do, do_, TokenKind::TOK_DO) \
+    macro(else, else_, TokenKind::TOK_ELSE) \
+    macro(finally, finally_, TokenKind::TOK_FINALLY) \
+    macro(for, for_, TokenKind::TOK_FOR) \
+    macro(function, function, TokenKind::TOK_FUNCTION) \
+    macro(if, if_, TokenKind::TOK_IF) \
+    macro(in, in, TokenKind::TOK_IN) \
+    macro(instanceof, instanceof, TokenKind::TOK_INSTANCEOF) \
+    macro(new, new_, TokenKind::TOK_NEW) \
+    macro(return, return_, TokenKind::TOK_RETURN) \
+    macro(switch, switch_, TokenKind::TOK_SWITCH) \
+    macro(this, this_, TokenKind::TOK_THIS) \
+    macro(throw, throw_, TokenKind::TOK_THROW) \
+    macro(try, try_, TokenKind::TOK_TRY) \
+    macro(typeof, typeof_, TokenKind::TOK_TYPEOF) \
+    macro(var, var, TokenKind::TOK_VAR) \
+    macro(void, void_, TokenKind::TOK_VOID) \
+    macro(while, while_, TokenKind::TOK_WHILE) \
+    macro(with, with, TokenKind::TOK_WITH) \
+    macro(import, import, TokenKind::TOK_IMPORT) \
+    macro(export, export_, TokenKind::TOK_EXPORT) \
+    macro(class, class_, TokenKind::TOK_CLASS) \
+    macro(extends, extends, TokenKind::TOK_EXTENDS) \
+    macro(super, super, TokenKind::TOK_SUPER) \
     \
     /* Future reserved words. */ \
-    macro(enum, enum_, TOK_ENUM) \
+    macro(enum, enum_, TokenKind::TOK_ENUM) \
     \
     /* Future reserved words, but only in strict mode. */ \
-    macro(implements, implements, TOK_IMPLEMENTS) \
-    macro(interface, interface, TOK_INTERFACE) \
-    macro(package, package, TOK_PACKAGE) \
-    macro(private, private_, TOK_PRIVATE) \
-    macro(protected, protected_, TOK_PROTECTED) \
-    macro(public, public_, TOK_PUBLIC) \
+    macro(implements, implements, TokenKind::TOK_IMPLEMENTS) \
+    macro(interface, interface, TokenKind::TOK_INTERFACE) \
+    macro(package, package, TokenKind::TOK_PACKAGE) \
+    macro(private, private_, TokenKind::TOK_PRIVATE) \
+    macro(protected, protected_, TokenKind::TOK_PROTECTED) \
+    macro(public, public_, TokenKind::TOK_PUBLIC) \
     \
     /* Contextual keywords. */ \
-    macro(as, as, TOK_AS) \
-    macro(async, async, TOK_ASYNC) \
-    macro(await, await, TOK_AWAIT) \
-    macro(from, from, TOK_FROM) \
-    macro(get, get, TOK_GET) \
-    macro(let, let, TOK_LET) \
-    macro(of, of, TOK_OF) \
-    macro(set, set, TOK_SET) \
-    macro(static, static_, TOK_STATIC) \
-    macro(target, target, TOK_TARGET) \
+    macro(as, as, TokenKind::TOK_AS) \
+    macro(async, async, TokenKind::TOK_ASYNC) \
+    macro(await, await, TokenKind::TOK_AWAIT) \
+    macro(from, from, TokenKind::TOK_FROM) \
+    macro(get, get, TokenKind::TOK_GET) \
+    macro(let, let, TokenKind::TOK_LET) \
+    macro(of, of, TokenKind::TOK_OF) \
+    macro(set, set, TokenKind::TOK_SET) \
+    macro(static, static_, TokenKind::TOK_STATIC) \
+    macro(target, target, TokenKind::TOK_TARGET) \
     /* \
      * Yield is a token inside function*.  Outside of a function*, it is a \
      * future reserved word in strict mode, but a keyword in JS1.7 even \
      * when strict.  Punt logic to parser. \
      */ \
-    macro(yield, yield, TOK_YIELD)
+    macro(yield, yield, TokenKind::TOK_YIELD)
 
 #endif /* vm_ReservedWords_h */
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -230,99 +230,99 @@
 #define FOR_EACH_TOKEN_KIND(macro) \
     FOR_EACH_TOKEN_KIND_WITH_RANGE(macro, TOKEN_KIND_RANGE_EMIT_NONE)
 
 namespace js {
 namespace frontend {
 
 // Values of this type are used to index into arrays such as isExprEnding[],
 // so the first value must be zero.
-enum TokenKind {
+enum class TokenKind {
 #define EMIT_ENUM(name, desc) TOK_##name,
 #define EMIT_ENUM_RANGE(name, value) TOK_##name = TOK_##value,
     FOR_EACH_TOKEN_KIND_WITH_RANGE(EMIT_ENUM, EMIT_ENUM_RANGE)
 #undef EMIT_ENUM
 #undef EMIT_ENUM_RANGE
     TOK_LIMIT                      // domain size
 };
 
 inline bool
 TokenKindIsBinaryOp(TokenKind tt)
 {
-    return TOK_BINOP_FIRST <= tt && tt <= TOK_BINOP_LAST;
+    return TokenKind::TOK_BINOP_FIRST <= tt && tt <= TokenKind::TOK_BINOP_LAST;
 }
 
 inline bool
 TokenKindIsEquality(TokenKind tt)
 {
-    return TOK_EQUALITY_START <= tt && tt <= TOK_EQUALITY_LAST;
+    return TokenKind::TOK_EQUALITY_START <= tt && tt <= TokenKind::TOK_EQUALITY_LAST;
 }
 
 inline bool
 TokenKindIsRelational(TokenKind tt)
 {
-    return TOK_RELOP_START <= tt && tt <= TOK_RELOP_LAST;
+    return TokenKind::TOK_RELOP_START <= tt && tt <= TokenKind::TOK_RELOP_LAST;
 }
 
 inline bool
 TokenKindIsShift(TokenKind tt)
 {
-    return TOK_SHIFTOP_START <= tt && tt <= TOK_SHIFTOP_LAST;
+    return TokenKind::TOK_SHIFTOP_START <= tt && tt <= TokenKind::TOK_SHIFTOP_LAST;
 }
 
 inline bool
 TokenKindIsAssignment(TokenKind tt)
 {
-    return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
+    return TokenKind::TOK_ASSIGNMENT_START <= tt && tt <= TokenKind::TOK_ASSIGNMENT_LAST;
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsKeyword(TokenKind tt)
 {
-    return (TOK_KEYWORD_FIRST <= tt && tt <= TOK_KEYWORD_LAST) ||
-           (TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TOK_KEYWORD_BINOP_LAST) ||
-           (TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TOK_KEYWORD_UNOP_LAST);
+    return (TokenKind::TOK_KEYWORD_FIRST <= tt && tt <= TokenKind::TOK_KEYWORD_LAST) ||
+           (TokenKind::TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TokenKind::TOK_KEYWORD_BINOP_LAST) ||
+           (TokenKind::TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TokenKind::TOK_KEYWORD_UNOP_LAST);
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsContextualKeyword(TokenKind tt)
 {
-    return TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TOK_CONTEXTUAL_KEYWORD_LAST;
+    return TokenKind::TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TokenKind::TOK_CONTEXTUAL_KEYWORD_LAST;
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsFutureReservedWord(TokenKind tt)
 {
-    return TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_FUTURE_RESERVED_KEYWORD_LAST;
+    return TokenKind::TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TokenKind::TOK_FUTURE_RESERVED_KEYWORD_LAST;
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsStrictReservedWord(TokenKind tt)
 {
-    return TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_STRICT_RESERVED_KEYWORD_LAST;
+    return TokenKind::TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TokenKind::TOK_STRICT_RESERVED_KEYWORD_LAST;
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsReservedWordLiteral(TokenKind tt)
 {
-    return TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TOK_RESERVED_WORD_LITERAL_LAST;
+    return TokenKind::TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TokenKind::TOK_RESERVED_WORD_LITERAL_LAST;
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsReservedWord(TokenKind tt)
 {
     return TokenKindIsKeyword(tt) ||
            TokenKindIsFutureReservedWord(tt) ||
            TokenKindIsReservedWordLiteral(tt);
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsPossibleIdentifier(TokenKind tt)
 {
-    return tt == TOK_NAME ||
+    return tt == TokenKind::TOK_NAME ||
            TokenKindIsContextualKeyword(tt) ||
            TokenKindIsStrictReservedWord(tt);
 }
 
 inline MOZ_MUST_USE bool
 TokenKindIsPossibleIdentifierName(TokenKind tt)
 {
     return TokenKindIsPossibleIdentifier(tt) ||
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -205,46 +205,46 @@ IsKeyword(JSLinearString* str)
 }
 
 TokenKind
 ReservedWordTokenKind(PropertyName* str)
 {
     if (const ReservedWordInfo* rw = FindReservedWord(str))
         return rw->tokentype;
 
-    return TOK_NAME;
+    return TokenKind::TOK_NAME;
 }
 
 const char*
 ReservedWordToCharZ(PropertyName* str)
 {
     if (const ReservedWordInfo* rw = FindReservedWord(str))
         return ReservedWordToCharZ(rw->tokentype);
 
     return nullptr;
 }
 
 const char*
 ReservedWordToCharZ(TokenKind tt)
 {
-    MOZ_ASSERT(tt != TOK_NAME);
+    MOZ_ASSERT(tt != TokenKind::TOK_NAME);
     switch (tt) {
 #define EMIT_CASE(word, name, type) case type: return js_##word##_str;
       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
 #undef EMIT_CASE
       default:
         MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName.");
     }
     return nullptr;
 }
 
 PropertyName*
 TokenStreamAnyChars::reservedWordToPropertyName(TokenKind tt) const
 {
-    MOZ_ASSERT(tt != TOK_NAME);
+    MOZ_ASSERT(tt != TokenKind::TOK_NAME);
     switch (tt) {
 #define EMIT_CASE(word, name, type) case type: return cx->names().name;
       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
 #undef EMIT_CASE
       default:
         MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
     }
     return nullptr;
@@ -421,22 +421,22 @@ TokenStreamAnyChars::TokenStreamAnyChars
     strictModeGetter(smg)
 {
     // Nb: the following tables could be static, but initializing them here is
     // much easier.  Don't worry, the time to initialize them for each
     // TokenStream is trivial.  See bug 639420.
 
     // See Parser::assignExpr() for an explanation of isExprEnding[].
     PodArrayZero(isExprEnding);
-    isExprEnding[TOK_COMMA] = 1;
-    isExprEnding[TOK_SEMI] = 1;
-    isExprEnding[TOK_COLON] = 1;
-    isExprEnding[TOK_RP] = 1;
-    isExprEnding[TOK_RB] = 1;
-    isExprEnding[TOK_RC] = 1;
+    isExprEnding[size_t(TokenKind::TOK_COMMA)] = 1;
+    isExprEnding[size_t(TokenKind::TOK_SEMI)] = 1;
+    isExprEnding[size_t(TokenKind::TOK_COLON)] = 1;
+    isExprEnding[size_t(TokenKind::TOK_RP)] = 1;
+    isExprEnding[size_t(TokenKind::TOK_RB)] = 1;
+    isExprEnding[size_t(TokenKind::TOK_RC)] = 1;
 }
 
 template<typename CharT>
 TokenStreamCharsBase<CharT>::TokenStreamCharsBase(JSContext* cx, const CharT* chars, size_t length,
                                                   size_t startOffset)
   : userbuf(chars, length, startOffset),
     tokenbuf(cx)
 {}
@@ -791,17 +791,17 @@ TokenStreamAnyChars::fillExcludingContex
     err->filename = filename_;
     srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
     return true;
 }
 
 bool
 TokenStreamAnyChars::hasTokenizationStarted() const
 {
-    return isCurrentTokenType(TOK_EOF) && !isEOF();
+    return isCurrentTokenType(TokenKind::TOK_EOF) && !isEOF();
 }
 
 void
 TokenStreamAnyChars::lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const
 {
     srcCoords.lineNumAndColumnIndex(offset, line, column);
 }
 
@@ -1247,19 +1247,19 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
     return tp;
 }
 
 #ifdef DEBUG
 static bool
 IsTokenSane(Token* tp)
 {
-    // Nb: TOK_EOL should never be used in an actual Token;  it should only be
-    // returned as a TokenKind from peekTokenSameLine().
-    if (tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
+    // Nb: TokenKind::TOK_EOL should never be used in an actual Token;
+    // it should only be returned as a TokenKind from peekTokenSameLine().
+    if (tp->type >= TokenKind::TOK_LIMIT || tp->type == TokenKind::TOK_EOL)
         return false;
 
     if (tp->pos.end < tp->pos.begin)
         return false;
 
     return true;
 }
 #endif
@@ -1351,23 +1351,23 @@ enum FirstCharKind {
     // token that cannot also be a prefix of a longer token.  E.g. ';' has the
     // OneChar kind, but '+' does not, because '++' and '+=' are valid longer tokens
     // that begin with '+'.
     //
     // The few token kinds satisfying these properties cover roughly 35--45%
     // of the tokens seen in practice.
     //
     // We represent the 'OneChar' kind with any positive value less than
-    // TOK_LIMIT.  This representation lets us associate each one-char token
-    // char16_t with a TokenKind and thus avoid a subsequent char16_t-to-TokenKind
-    // conversion.
+    // TokenKind::TOK_LIMIT.  This representation lets us associate
+    // each one-char token char16_t with a TokenKind and thus avoid
+    // a subsequent char16_t-to-TokenKind conversion.
     OneChar_Min = 0,
-    OneChar_Max = TOK_LIMIT - 1,
+    OneChar_Max = size_t(TokenKind::TOK_LIMIT) - 1,
 
-    Space = TOK_LIMIT,
+    Space = size_t(TokenKind::TOK_LIMIT),
     Ident,
     Dec,
     String,
     EOL,
     BasePrefix,
     Other,
 
     LastCharKind = Other
@@ -1380,40 +1380,56 @@ enum FirstCharKind {
 // Equals:  61: '='
 // String:  34, 39: '"', '\''
 // Dec:     49..57: '1'..'9'
 // Plus:    43: '+'
 // BasePrefix:  48: '0'
 // Space:   9, 11, 12, 32: '\t', '\v', '\f', ' '
 // EOL:     10, 13: '\n', '\r'
 //
-#define T_COMMA     TOK_COMMA
-#define T_COLON     TOK_COLON
-#define T_BITNOT    TOK_BITNOT
+#define T_COMMA     size_t(TokenKind::TOK_COMMA)
+#define T_COLON     size_t(TokenKind::TOK_COLON)
+#define T_BITNOT    size_t(TokenKind::TOK_BITNOT)
+#define T_LP        size_t(TokenKind::TOK_LP)
+#define T_RP        size_t(TokenKind::TOK_RP)
+#define T_SEMI      size_t(TokenKind::TOK_SEMI)
+#define T_HOOK      size_t(TokenKind::TOK_HOOK)
+#define T_LB        size_t(TokenKind::TOK_LB)
+#define T_RB        size_t(TokenKind::TOK_RB)
+#define T_LC        size_t(TokenKind::TOK_LC)
+#define T_RC        size_t(TokenKind::TOK_RC)
 #define Templat     String
 #define _______     Other
 static const uint8_t firstCharKinds[] = {
 /*         0        1        2        3        4        5        6        7        8        9    */
 /*   0+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______,   Space,
 /*  10+ */     EOL,   Space,   Space,     EOL, _______, _______, _______, _______, _______, _______,
 /*  20+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
 /*  30+ */ _______, _______,   Space, _______,  String, _______,   Ident, _______, _______,  String,
-/*  40+ */  TOK_LP,  TOK_RP, _______, _______, T_COMMA,_______,  _______, _______,BasePrefix,  Dec,
-/*  50+ */     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,    Dec,  T_COLON,TOK_SEMI,
-/*  60+ */ _______, _______, _______,TOK_HOOK, _______,   Ident,   Ident,   Ident,   Ident,   Ident,
+/*  40+ */    T_LP,    T_RP, _______, _______, T_COMMA,_______,  _______, _______,BasePrefix,  Dec,
+/*  50+ */     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,    Dec,  T_COLON,  T_SEMI,
+/*  60+ */ _______, _______, _______,  T_HOOK, _______,   Ident,   Ident,   Ident,   Ident,   Ident,
 /*  70+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
 /*  80+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
-/*  90+ */   Ident,  TOK_LB, _______,  TOK_RB, _______,   Ident, Templat,   Ident,   Ident,   Ident,
+/*  90+ */   Ident,    T_LB, _______,    T_RB, _______,   Ident, Templat,   Ident,   Ident,   Ident,
 /* 100+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
 /* 110+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
-/* 120+ */   Ident,   Ident,   Ident,  TOK_LC, _______,  TOK_RC,T_BITNOT, _______
+/* 120+ */   Ident,   Ident,   Ident,    T_LC, _______,    T_RC,T_BITNOT, _______
 };
 #undef T_COMMA
 #undef T_COLON
 #undef T_BITNOT
+#undef T_LP
+#undef T_RP
+#undef T_SEMI
+#undef T_HOOK
+#undef T_LB
+#undef T_RB
+#undef T_LC
+#undef T_RC
 #undef Templat
 #undef _______
 
 static_assert(LastCharKind < (1 << (sizeof(firstCharKinds[0]) * 8)),
               "Elements of firstCharKinds[] are too small");
 
 template<typename CharT, class AnyCharsAccess>
 MOZ_MUST_USE bool
@@ -1435,17 +1451,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         if (!getStringOrTemplateToken('`', &tp))
             goto error;
         goto out;
     }
 
   retry:
     if (MOZ_UNLIKELY(!userbuf.hasRawChars())) {
         tp = newToken(0);
-        tp->type = TOK_EOF;
+        tp->type = TokenKind::TOK_EOF;
         anyCharsAccess().flags.isEOF = true;
         goto out;
     }
 
     c = userbuf.getRawChar();
     MOZ_ASSERT(c != EOF);
 
     // Chars not in the range 0..127 are rare.  Getting them out of the way
@@ -1575,17 +1591,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                 tp->type = rw->tokentype;
                 goto out;
             }
         }
 
         JSAtom* atom = atomizeChars(anyCharsAccess().cx, chars, length);
         if (!atom)
             goto error;
-        tp->type = TOK_NAME;
+        tp->type = TokenKind::TOK_NAME;
         tp->setName(atom->asPropertyName());
         goto out;
     }
 
     // Look for a decimal number.
     //
     if (c1kind == Dec) {
         tp = newToken(-1);
@@ -1648,17 +1664,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         } else {
             const CharT* dummy;
             if (!js_strtod(anyCharsAccess().cx, numStart, userbuf.addressOfNextRawChar(),
                            &dummy, &dval))
             {
                 goto error;
             }
         }
-        tp->type = TOK_NUMBER;
+        tp->type = TokenKind::TOK_NUMBER;
         tp->setNumber(dval, decimalPoint);
         goto out;
     }
 
     // Look for a string or a template string.
     //
     if (c1kind == String) {
         if (!getStringOrTemplateToken(c, &tp))
@@ -1763,17 +1779,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         double dval;
         const char16_t* dummy;
         if (!GetPrefixInteger(anyCharsAccess().cx, numStart, userbuf.addressOfNextRawChar(),
                               radix, &dummy, &dval))
         {
             goto error;
         }
 
-        tp->type = TOK_NUMBER;
+        tp->type = TokenKind::TOK_NUMBER;
         tp->setNumber(dval, NoDecimal);
         goto out;
     }
 
     // This handles everything else.
     //
     MOZ_ASSERT(c1kind == Other);
     tp = newToken(-1);
@@ -1783,114 +1799,114 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         if (JS7_ISDEC(c)) {
             numStart = userbuf.addressOfNextRawChar() - 2;
             decimalPoint = HasDecimal;
             hasExp = false;
             goto decimal_dot;
         }
         if (c == '.') {
             if (matchChar('.')) {
-                tp->type = TOK_TRIPLEDOT;
+                tp->type = TokenKind::TOK_TRIPLEDOT;
                 goto out;
             }
         }
         ungetCharIgnoreEOL(c);
-        tp->type = TOK_DOT;
+        tp->type = TokenKind::TOK_DOT;
         goto out;
 
       case '=':
         if (matchChar('='))
-            tp->type = matchChar('=') ? TOK_STRICTEQ : TOK_EQ;
+            tp->type = matchChar('=') ? TokenKind::TOK_STRICTEQ : TokenKind::TOK_EQ;
         else if (matchChar('>'))
-            tp->type = TOK_ARROW;
+            tp->type = TokenKind::TOK_ARROW;
         else
-            tp->type = TOK_ASSIGN;
+            tp->type = TokenKind::TOK_ASSIGN;
         goto out;
 
       case '+':
         if (matchChar('+'))
-            tp->type = TOK_INC;
+            tp->type = TokenKind::TOK_INC;
         else
-            tp->type = matchChar('=') ? TOK_ADDASSIGN : TOK_ADD;
+            tp->type = matchChar('=') ? TokenKind::TOK_ADDASSIGN : TokenKind::TOK_ADD;
         goto out;
 
       case '\\': {
         uint32_t escapeLength = matchUnicodeEscapeIdStart(&qc);
         if (escapeLength > 0) {
             identStart = userbuf.addressOfNextRawChar() - escapeLength - 1;
             hadUnicodeEscape = true;
             goto identifier;
         }
         goto badchar;
       }
 
       case '|':
         if (matchChar('|'))
-            tp->type = TOK_OR;
+            tp->type = TokenKind::TOK_OR;
 #ifdef ENABLE_PIPELINE_OPERATOR
         else if (matchChar('>'))
-            tp->type = TOK_PIPELINE;
+            tp->type = TokenKind::TOK_PIPELINE;
 #endif
         else
-            tp->type = matchChar('=') ? TOK_BITORASSIGN : TOK_BITOR;
+            tp->type = matchChar('=') ? TokenKind::TOK_BITORASSIGN : TokenKind::TOK_BITOR;
         goto out;
 
       case '^':
-        tp->type = matchChar('=') ? TOK_BITXORASSIGN : TOK_BITXOR;
+        tp->type = matchChar('=') ? TokenKind::TOK_BITXORASSIGN : TokenKind::TOK_BITXOR;
         goto out;
 
       case '&':
         if (matchChar('&'))
-            tp->type = TOK_AND;
+            tp->type = TokenKind::TOK_AND;
         else
-            tp->type = matchChar('=') ? TOK_BITANDASSIGN : TOK_BITAND;
+            tp->type = matchChar('=') ? TokenKind::TOK_BITANDASSIGN : TokenKind::TOK_BITAND;
         goto out;
 
       case '!':
         if (matchChar('='))
-            tp->type = matchChar('=') ? TOK_STRICTNE : TOK_NE;
+            tp->type = matchChar('=') ? TokenKind::TOK_STRICTNE : TokenKind::TOK_NE;
         else
-            tp->type = TOK_NOT;
+            tp->type = TokenKind::TOK_NOT;
         goto out;
 
       case '<':
         if (anyCharsAccess().options().allowHTMLComments) {
             // Treat HTML begin-comment as comment-till-end-of-line.
             if (matchChar('!')) {
                 if (matchChar('-')) {
                     if (matchChar('-'))
                         goto skipline;
                     ungetChar('-');
                 }
                 ungetChar('!');
             }
         }
         if (matchChar('<')) {
-            tp->type = matchChar('=') ? TOK_LSHASSIGN : TOK_LSH;
+            tp->type = matchChar('=') ? TokenKind::TOK_LSHASSIGN : TokenKind::TOK_LSH;
         } else {
-            tp->type = matchChar('=') ? TOK_LE : TOK_LT;
+            tp->type = matchChar('=') ? TokenKind::TOK_LE : TokenKind::TOK_LT;
         }
         goto out;
 
       case '>':
         if (matchChar('>')) {
             if (matchChar('>'))
-                tp->type = matchChar('=') ? TOK_URSHASSIGN : TOK_URSH;
+                tp->type = matchChar('=') ? TokenKind::TOK_URSHASSIGN : TokenKind::TOK_URSH;
             else
-                tp->type = matchChar('=') ? TOK_RSHASSIGN : TOK_RSH;
+                tp->type = matchChar('=') ? TokenKind::TOK_RSHASSIGN : TokenKind::TOK_RSH;
         } else {
-            tp->type = matchChar('=') ? TOK_GE : TOK_GT;
+            tp->type = matchChar('=') ? TokenKind::TOK_GE : TokenKind::TOK_GT;
         }
         goto out;
 
       case '*':
         if (matchChar('*'))
-            tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW;
+            tp->type = matchChar('=') ? TokenKind::TOK_POWASSIGN : TokenKind::TOK_POW;
         else
-            tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
+            tp->type = matchChar('=') ? TokenKind::TOK_MULASSIGN : TokenKind::TOK_MUL;
         goto out;
 
       case '/':
         // Look for a single-line comment.
         if (matchChar('/')) {
             if (!peekChar(&c))
                 goto error;
             if (c == '@' || c == '#') {
@@ -2000,44 +2016,44 @@ TokenStreamSpecific<CharT, AnyCharsAcces
             if (JS7_ISLET(c)) {
                 char buf[2] = { '\0', '\0' };
                 tp->pos.begin += length + 1;
                 buf[0] = char(c);
                 reportError(JSMSG_BAD_REGEXP_FLAG, buf);
                 consumeKnownChar(c);
                 goto error;
             }
-            tp->type = TOK_REGEXP;
+            tp->type = TokenKind::TOK_REGEXP;
             tp->setRegExpFlags(reflags);
             goto out;
         }
 
-        tp->type = matchChar('=') ? TOK_DIVASSIGN : TOK_DIV;
+        tp->type = matchChar('=') ? TokenKind::TOK_DIVASSIGN : TokenKind::TOK_DIV;
         goto out;
 
       case '%':
-        tp->type = matchChar('=') ? TOK_MODASSIGN : TOK_MOD;
+        tp->type = matchChar('=') ? TokenKind::TOK_MODASSIGN : TokenKind::TOK_MOD;
         goto out;
 
       case '-':
         if (matchChar('-')) {
             if (anyCharsAccess().options().allowHTMLComments &&
                 !anyCharsAccess().flags.isDirtyLine)
             {
                 int32_t c2;
                 if (!peekChar(&c2))
                     goto error;
 
                 if (c2 == '>')
                     goto skipline;
             }
 
-            tp->type = TOK_DEC;
+            tp->type = TokenKind::TOK_DEC;
         } else {
-            tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB;
+            tp->type = matchChar('=') ? TokenKind::TOK_SUBASSIGN : TokenKind::TOK_SUB;
         }
         goto out;
 
       badchar:
       default:
         reportError(JSMSG_ILLEGAL_CHARACTER);
         goto error;
     }
@@ -2315,52 +2331,52 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         }
     }
 
     JSAtom* atom = atomizeChars(anyCharsAccess().cx, tokenbuf.begin(), tokenbuf.length());
     if (!atom)
         return false;
 
     if (!parsingTemplate) {
-        (*tp)->type = TOK_STRING;
+        (*tp)->type = TokenKind::TOK_STRING;
     } else {
         if (c == '$' && nc == '{')
-            (*tp)->type = TOK_TEMPLATE_HEAD;
+            (*tp)->type = TokenKind::TOK_TEMPLATE_HEAD;
         else
-            (*tp)->type = TOK_NO_SUBS_TEMPLATE;
+            (*tp)->type = TokenKind::TOK_NO_SUBS_TEMPLATE;
     }
 
     (*tp)->setAtom(atom);
     return true;
 }
 
 const char*
 TokenKindToDesc(TokenKind tt)
 {
     switch (tt) {
-#define EMIT_CASE(name, desc) case TOK_##name: return desc;
+#define EMIT_CASE(name, desc) case TokenKind::TOK_##name: return desc;
       FOR_EACH_TOKEN_KIND(EMIT_CASE)
 #undef EMIT_CASE
-      case TOK_LIMIT:
-        MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
+      case TokenKind::TOK_LIMIT:
+        MOZ_ASSERT_UNREACHABLE("TokenKind::TOK_LIMIT should not be passed.");
         break;
     }
 
     return "<bad TokenKind>";
 }
 
 #ifdef DEBUG
 const char*
 TokenKindToString(TokenKind tt)
 {
     switch (tt) {
-#define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
+#define EMIT_CASE(name, desc) case TokenKind::TOK_##name: return "TokenKind::TOK_" #name;
       FOR_EACH_TOKEN_KIND(EMIT_CASE)
 #undef EMIT_CASE
-      case TOK_LIMIT: break;
+      case TokenKind::TOK_LIMIT: break;
     }
 
     return "<bad TokenKind>";
 }
 #endif
 
 template class frontend::TokenStreamCharsBase<char16_t>;
 
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -308,66 +308,66 @@ struct Token
 #ifdef DEBUG
     Modifier modifier;                  // Modifier used to get this token
     ModifierException modifierException; // Exception for this modifier
 #endif
 
     // Mutators
 
     void setName(PropertyName* name) {
-        MOZ_ASSERT(type == TOK_NAME);
+        MOZ_ASSERT(type == TokenKind::TOK_NAME);
         u.name = name;
     }
 
     void setAtom(JSAtom* atom) {
-        MOZ_ASSERT(type == TOK_STRING ||
-                   type == TOK_TEMPLATE_HEAD ||
-                   type == TOK_NO_SUBS_TEMPLATE);
+        MOZ_ASSERT(type == TokenKind::TOK_STRING ||
+                   type == TokenKind::TOK_TEMPLATE_HEAD ||
+                   type == TokenKind::TOK_NO_SUBS_TEMPLATE);
         u.atom = atom;
     }
 
     void setRegExpFlags(RegExpFlag flags) {
-        MOZ_ASSERT(type == TOK_REGEXP);
+        MOZ_ASSERT(type == TokenKind::TOK_REGEXP);
         MOZ_ASSERT((flags & AllFlags) == flags);
         u.reflags = flags;
     }
 
     void setNumber(double n, DecimalPoint decimalPoint) {
-        MOZ_ASSERT(type == TOK_NUMBER);
+        MOZ_ASSERT(type == TokenKind::TOK_NUMBER);
         u.number.value = n;
         u.number.decimalPoint = decimalPoint;
     }
 
     // Type-safe accessors
 
     PropertyName* name() const {
-        MOZ_ASSERT(type == TOK_NAME);
+        MOZ_ASSERT(type == TokenKind::TOK_NAME);
         return u.name->JSAtom::asPropertyName(); // poor-man's type verification
     }
 
     JSAtom* atom() const {
-        MOZ_ASSERT(type == TOK_STRING ||
-                   type == TOK_TEMPLATE_HEAD ||
-                   type == TOK_NO_SUBS_TEMPLATE);
+        MOZ_ASSERT(type == TokenKind::TOK_STRING ||
+                   type == TokenKind::TOK_TEMPLATE_HEAD ||
+                   type == TokenKind::TOK_NO_SUBS_TEMPLATE);
         return u.atom;
     }
 
     RegExpFlag regExpFlags() const {
-        MOZ_ASSERT(type == TOK_REGEXP);
+        MOZ_ASSERT(type == TokenKind::TOK_REGEXP);
         MOZ_ASSERT((u.reflags & AllFlags) == u.reflags);
         return u.reflags;
     }
 
     double number() const {
-        MOZ_ASSERT(type == TOK_NUMBER);
+        MOZ_ASSERT(type == TokenKind::TOK_NUMBER);
         return u.number.value;
     }
 
     DecimalPoint decimalPoint() const {
-        MOZ_ASSERT(type == TOK_NUMBER);
+        MOZ_ASSERT(type == TokenKind::TOK_NUMBER);
         return u.number.decimalPoint;
     }
 };
 
 extern TokenKind
 ReservedWordTokenKind(PropertyName* str);
 
 extern const char*
@@ -485,35 +485,35 @@ class TokenStreamAnyChars
 
   private:
     PropertyName* reservedWordToPropertyName(TokenKind tt) const;
 
     void undoGetChar();
 
   public:
     PropertyName* currentName() const {
-        if (isCurrentTokenType(TOK_NAME))
+        if (isCurrentTokenType(TokenKind::TOK_NAME))
             return currentToken().name();
 
         MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type));
         return reservedWordToPropertyName(currentToken().type);
     }
 
     bool currentNameHasEscapes() const {
-        if (isCurrentTokenType(TOK_NAME)) {
+        if (isCurrentTokenType(TokenKind::TOK_NAME)) {
             TokenPos pos = currentToken().pos;
             return (pos.end - pos.begin) != currentToken().name()->length();
         }
 
         MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type));
         return false;
     }
 
     PropertyName* nextName() const {
-        if (nextToken().type != TOK_NAME)
+        if (nextToken().type != TokenKind::TOK_NAME)
             return nextToken().name();
 
         MOZ_ASSERT(TokenKindIsPossibleIdentifierName(nextToken().type));
         return reservedWordToPropertyName(nextToken().type);
     }
 
     bool isCurrentTokenAssignment() const {
         return TokenKindIsAssignment(currentToken().type);
@@ -560,33 +560,33 @@ class TokenStreamAnyChars
         // after parsing all of an arrow function.
         if (next.modifierException == modifierException)
             return;
 
         if (next.modifierException == NoneIsOperand) {
             // Token after yield expression without operand already has
             // NoneIsOperand exception.
             MOZ_ASSERT(modifierException == OperandIsNone);
-            MOZ_ASSERT(next.type != TOK_DIV,
+            MOZ_ASSERT(next.type != TokenKind::TOK_DIV,
                        "next token requires contextual specifier to be parsed unambiguously");
 
             // Do not update modifierException.
             return;
         }
 
         MOZ_ASSERT(next.modifierException == NoException);
         switch (modifierException) {
           case NoneIsOperand:
             MOZ_ASSERT(next.modifier == Operand);
-            MOZ_ASSERT(next.type != TOK_DIV,
+            MOZ_ASSERT(next.type != TokenKind::TOK_DIV,
                        "next token requires contextual specifier to be parsed unambiguously");
             break;
           case OperandIsNone:
             MOZ_ASSERT(next.modifier == None);
-            MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
+            MOZ_ASSERT(next.type != TokenKind::TOK_DIV && next.type != TokenKind::TOK_REGEXP,
                        "next token requires contextual specifier to be parsed unambiguously");
             break;
           default:
             MOZ_CRASH("unexpected modifier exception");
         }
         tokens[(cursor + 1) & ntokensMask].modifierException = modifierException;
 #endif
     }
@@ -765,17 +765,17 @@ class TokenStreamAnyChars
     unsigned            lookahead;          // count of lookahead tokens
     unsigned            lineno;             // current line number
     TokenStreamFlags    flags;              // flags -- see above
     size_t              linebase;           // start of current line
     size_t              prevLinebase;       // start of previous line;  size_t(-1) if on the first line
     const char*         filename_;          // input filename or null
     UniqueTwoByteChars  displayURL_;        // the user's requested source URL or null
     UniqueTwoByteChars  sourceMapURL_;      // source map's filename or null
-    uint8_t             isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
+    uint8_t             isExprEnding[size_t(TokenKind::TOK_LIMIT)];// which tokens definitely terminate exprs?
     JSContext* const    cx;
     bool                mutedErrors;
     StrictModeGetter*   strictModeGetter;  // used to test for strict mode
 };
 
 template<typename CharT>
 class TokenStreamCharsBase
 {
@@ -1111,21 +1111,21 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     bool reportStrictModeErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
                                        bool strictMode, unsigned errorNumber, va_list* args);
     bool reportExtraWarningErrorNumberVA(UniquePtr<JSErrorNotes> notes, uint32_t offset,
                                          unsigned errorNumber, va_list* args);
 
     JSAtom* getRawTemplateStringAtom() {
         TokenStreamAnyChars& anyChars = anyCharsAccess();
 
-        MOZ_ASSERT(anyChars.currentToken().type == TOK_TEMPLATE_HEAD ||
-                   anyChars.currentToken().type == TOK_NO_SUBS_TEMPLATE);
+        MOZ_ASSERT(anyChars.currentToken().type == TokenKind::TOK_TEMPLATE_HEAD ||
+                   anyChars.currentToken().type == TokenKind::TOK_NO_SUBS_TEMPLATE);
         const CharT* cur = userbuf.rawCharPtrAt(anyChars.currentToken().pos.begin + 1);
         const CharT* end;
-        if (anyChars.currentToken().type == TOK_TEMPLATE_HEAD) {
+        if (anyChars.currentToken().type == TokenKind::TOK_TEMPLATE_HEAD) {
             // Of the form    |`...${|   or   |}...${|
             end = userbuf.rawCharPtrAt(anyChars.currentToken().pos.end - 2);
         } else {
             // NO_SUBS_TEMPLATE is of the form   |`...`|   or   |}...`|
             end = userbuf.rawCharPtrAt(anyChars.currentToken().pos.end - 1);
         }
 
         CharBuffer charbuf(anyChars.cx);
@@ -1176,17 +1176,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     MOZ_MUST_USE bool getToken(TokenKind* ttp, Modifier modifier = None) {
         // Check for a pushed-back token resulting from mismatching lookahead.
         TokenStreamAnyChars& anyChars = anyCharsAccess();
         if (anyChars.lookahead != 0) {
             MOZ_ASSERT(!anyChars.flags.hadError);
             anyChars.lookahead--;
             anyChars.cursor = (anyChars.cursor + 1) & ntokensMask;
             TokenKind tt = anyChars.currentToken().type;
-            MOZ_ASSERT(tt != TOK_EOL);
+            MOZ_ASSERT(tt != TokenKind::TOK_EOL);
             verifyConsistentModifier(modifier, anyChars.currentToken());
             *ttp = tt;
             return true;
         }
 
         return getTokenInternal(ttp, modifier);
     }
 
@@ -1270,17 +1270,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
         if (!getToken(&tmp, modifier))
             return false;
         const Token& next = anyChars.currentToken();
         anyChars.ungetToken();
 
         const auto& srcCoords = anyChars.srcCoords;
         *ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
              ? next.type
-             : TOK_EOL;
+             : TokenKind::TOK_EOL;
         return true;
     }
 
     // Get the next token from the stream if its kind is |tt|.
     MOZ_MUST_USE bool matchToken(bool* matchedp, TokenKind tt, Modifier modifier = None) {
         TokenKind token;
         if (!getToken(&token, modifier))
             return false;
@@ -1300,17 +1300,17 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
         MOZ_ALWAYS_TRUE(matched);
     }
 
     MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) {
         TokenKind tt;
         if (!peekToken(&tt))
             return false;
 
-        *endsExpr = anyCharsAccess().isExprEnding[tt];
+        *endsExpr = anyCharsAccess().isExprEnding[size_t(tt)];
         if (*endsExpr) {
             // If the next token ends an overall Expression, we'll parse this
             // Expression without ever invoking Parser::orExpr().  But we need
             // that function's side effect of adding this modifier exception,
             // so we have to do it manually here.
             anyCharsAccess().addModifierException(OperandIsNone);
         }
         return true;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4073,38 +4073,57 @@ CodeGenerator::visitCallNative(LCallNati
     // is no need for leaveFakeExitFrame.
 
     // Move the StackPointer back to its original location, unwinding the native exit frame.
     masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
     MOZ_ASSERT(masm.framePushed() == initialStack);
 }
 
 static void
-LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv)
+LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv, DOMObjectKind kind)
 {
     // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
     // will be in the first slot but may be fixed or non-fixed.
     MOZ_ASSERT(obj != priv);
 
-    // Check shape->numFixedSlots != 0.
-    masm.loadPtr(Address(obj, ShapedObject::offsetOfShape()), priv);
-
-    Label hasFixedSlots, done;
-    masm.branchTest32(Assembler::NonZero,
-                      Address(priv, Shape::offsetOfSlotInfo()),
-                      Imm32(Shape::fixedSlotsMask()),
-                      &hasFixedSlots);
-
-    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), priv);
-    masm.loadPrivate(Address(priv, 0), priv);
-
-    masm.jump(&done);
-    masm.bind(&hasFixedSlots);
-
-    masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
+    // Check if it's a proxy.
+    Label isProxy, done;
+    if (kind == DOMObjectKind::Unknown)
+        masm.branchTestObjectIsProxy(true, obj, priv, &isProxy);
+
+    if (kind != DOMObjectKind::Proxy) {
+#ifdef DEBUG
+        // If it's a native object, the value must be in a fixed slot.
+        Label hasFixedSlots;
+        masm.loadPtr(Address(obj, ShapedObject::offsetOfShape()), priv);
+        masm.branchTest32(Assembler::NonZero,
+                          Address(priv, Shape::offsetOfSlotInfo()),
+                          Imm32(Shape::fixedSlotsMask()),
+                          &hasFixedSlots);
+        masm.assumeUnreachable("Expected a fixed slot");
+        masm.bind(&hasFixedSlots);
+#endif
+        masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
+        if (kind == DOMObjectKind::Unknown)
+            masm.jump(&done);
+    }
+
+    if (kind != DOMObjectKind::Native) {
+        masm.bind(&isProxy);
+#ifdef DEBUG
+        // Sanity check: it must be a DOM proxy.
+        Label isDOMProxy;
+        masm.branchTestProxyHandlerFamily(Assembler::Equal, obj, priv,
+                                          GetDOMProxyHandlerFamily(), &isDOMProxy);
+        masm.assumeUnreachable("Expected a DOM proxy");
+        masm.bind(&isDOMProxy);
+#endif
+        masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv);
+        masm.loadPrivate(Address(priv, detail::ProxyReservedSlots::offsetOfSlot(0)), priv);
+    }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitCallDOMNative(LCallDOMNative* call)
 {
     WrappedFunction* target = call->getSingleTarget();
@@ -4147,17 +4166,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // Now compute the argv value.  Since StackPointer is pointing to &vp[0] and
     // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
     // StackPointer.
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
     JS_STATIC_ASSERT(JSJitMethodCallArgsTraits::offsetOfArgc ==
                      IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
     masm.computeEffectiveAddress(Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
 
-    LoadDOMPrivate(masm, obj, argPrivate);
+    LoadDOMPrivate(masm, obj, argPrivate, static_cast<MCallDOMNative*>(call->mir())->objectKind());
 
     // Push argc from the call instruction into what will become the IonExitFrame
     masm.Push(Imm32(call->numActualArgs()));
 
     // Push our argv onto the stack
     masm.Push(argArgs);
     // And store our JSJitMethodCallArgs* in argArgs.
     masm.moveStackPtrTo(argArgs);
@@ -11629,17 +11648,17 @@ CodeGenerator::visitGetDOMProperty(LGetD
     masm.Push(UndefinedValue());
     // We pass the pointer to our out param as an instance of
     // JSJitGetterCallArgs, since on the binary level it's the same thing.
     JS_STATIC_ASSERT(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
     masm.moveStackPtrTo(ValueReg);
 
     masm.Push(ObjectReg);
 
-    LoadDOMPrivate(masm, ObjectReg, PrivateReg);
+    LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.enterFakeExitFrame(JSContextReg, JSContextReg, ExitFrameType::IonDOMGetter);
 
@@ -11728,17 +11747,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
     masm.Push(argVal);
     // We pass the pointer to our out param as an instance of
     // JSJitGetterCallArgs, since on the binary level it's the same thing.
     JS_STATIC_ASSERT(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
     masm.moveStackPtrTo(ValueReg);
 
     masm.Push(ObjectReg);
 
-    LoadDOMPrivate(masm, ObjectReg, PrivateReg);
+    LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.enterFakeExitFrame(JSContextReg, JSContextReg, ExitFrameType::IonDOMSetter);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5521,32 +5521,33 @@ IonBuilder::makeCallHelper(JSFunction* t
     uint32_t targetArgs = callInfo.argc();
 
     // Collect number of missing arguments provided that the target is
     // scripted. Native functions are passed an explicit 'argc' parameter.
     if (target && !target->isNative())
         targetArgs = Max<uint32_t>(target->nargs(), callInfo.argc());
 
     bool isDOMCall = false;
+    DOMObjectKind objKind = DOMObjectKind::Unknown;
     if (target && !callInfo.constructing()) {
         // We know we have a single call target.  Check whether the "this" types
         // are DOM types and our function a DOM function, and if so flag the
         // MCall accordingly.
         TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
         if (thisTypes &&
             thisTypes->getKnownMIRType() == MIRType::Object &&
-            thisTypes->isDOMClass(constraints()))
+            thisTypes->isDOMClass(constraints(), &objKind))
         {
             MOZ_TRY_VAR(isDOMCall, testShouldDOMCall(thisTypes, target, JSJitInfo::Method));
         }
     }
 
     MCall* call = MCall::New(alloc(), target, targetArgs + 1 + callInfo.constructing(),
                              callInfo.argc(), callInfo.constructing(),
-                             callInfo.ignoresReturnValue(), isDOMCall);
+                             callInfo.ignoresReturnValue(), isDOMCall, objKind);
     if (!call)
         return abort(AbortReason::Alloc);
 
     if (callInfo.constructing())
         call->addArg(targetArgs + 1, callInfo.getNewTarget());
 
     // Explicitly pad any missing arguments with |undefined|.
     // This permits skipping the argumentsRectifier.
@@ -11019,28 +11020,30 @@ IonBuilder::getPropTryCommonGetter(bool*
                 return Ok();
             }
         } else {
             // The Baseline IC didn't have any information we can use.
             return Ok();
         }
     }
 
-    bool isDOM = objTypes && objTypes->isDOMClass(constraints());
+    DOMObjectKind objKind = DOMObjectKind::Unknown;
+    bool isDOM = objTypes && objTypes->isDOMClass(constraints(), &objKind);
     if (isDOM)
         MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter));
 
     if (isDOM) {
         const JSJitInfo* jitinfo = commonGetter->jitInfo();
         // We don't support MGetDOMProperty/MGetDOMMember on things that might
         // be proxies when the value might be in a slot, because the
         // CodeGenerator code for LGetDOMProperty/LGetDOMMember doesn't handle
         // that case correctly.
-        if ((!jitinfo->isAlwaysInSlot && !jitinfo->isLazilyCachedInSlot) ||
-            !objTypes->maybeProxy(constraints())) {
+        if (objKind == DOMObjectKind::Native ||
+            (!jitinfo->isAlwaysInSlot && !jitinfo->isLazilyCachedInSlot))
+        {
             MInstruction* get;
             if (jitinfo->isAlwaysInSlot) {
                 // If our object is a singleton and we know the property is
                 // constant (which is true if and only if the get doesn't alias
                 // anything), we can just read the slot here and use that
                 // constant.
                 JSObject* singleton = objTypes->maybeSingleton();
                 if (singleton && jitinfo->aliasSet() == JSJitInfo::AliasNone) {
@@ -11050,17 +11053,17 @@ IonBuilder::getPropTryCommonGetter(bool*
                     return Ok();
                 }
 
                 // We can't use MLoadFixedSlot here because it might not have
                 // the right aliasing behavior; we want to alias DOM setters as
                 // needed.
                 get = MGetDOMMember::New(alloc(), jitinfo, obj, guard, globalGuard);
             } else {
-                get = MGetDOMProperty::New(alloc(), jitinfo, obj, guard, globalGuard);
+                get = MGetDOMProperty::New(alloc(), jitinfo, objKind, obj, guard, globalGuard);
             }
             if (!get)
                 return abort(AbortReason::Alloc);
             current->add(get);
             current->push(get);
 
             if (get->isEffectful())
                 MOZ_TRY(resumeAfter(get));
@@ -11722,27 +11725,29 @@ IonBuilder::setPropTryCommonSetter(bool*
 
 AbortReasonOr<Ok>
 IonBuilder::setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
                                       MDefinition* value, JSFunction* setter,
                                       TemporaryTypeSet* objTypes)
 {
     MOZ_ASSERT(*emitted == false);
 
-    if (!objTypes || !objTypes->isDOMClass(constraints()))
+    DOMObjectKind objKind = DOMObjectKind::Unknown;
+    if (!objTypes || !objTypes->isDOMClass(constraints(), &objKind))
         return Ok();
 
     bool isDOM = false;
     MOZ_TRY_VAR(isDOM, testShouldDOMCall(objTypes, setter, JSJitInfo::Setter));
     if (!isDOM)
         return Ok();
 
     // Emit SetDOMProperty.
     MOZ_ASSERT(setter->jitInfo()->type() == JSJitInfo::Setter);
-    MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, obj, value);
+    MSetDOMProperty* set = MSetDOMProperty::New(alloc(), setter->jitInfo()->setter, objKind,
+                                                obj, value);
 
     current->add(set);
     current->push(value);
 
     MOZ_TRY(resumeAfter(set));
 
     *emitted = true;
     return Ok();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2031,24 +2031,24 @@ WrappedFunction::WrappedFunction(JSFunct
     isNative_(fun->isNative()),
     isConstructor_(fun->isConstructor()),
     isClassConstructor_(fun->isClassConstructor()),
     isSelfHostedBuiltin_(fun->isSelfHostedBuiltin())
 {}
 
 MCall*
 MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
-           bool construct, bool ignoresReturnValue, bool isDOMCall)
+           bool construct, bool ignoresReturnValue, bool isDOMCall, DOMObjectKind objectKind)
 {
     WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr;
     MOZ_ASSERT(maxArgc >= numActualArgs);
     MCall* ins;
     if (isDOMCall) {
         MOZ_ASSERT(!construct);
-        ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs);
+        ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs, objectKind);
     } else {
         ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct, ignoresReturnValue);
     }
     if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
         return nullptr;
     return ins;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4217,17 +4217,18 @@ class MCall
         needsArgCheck_(true)
     {
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(Call)
     static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
-                      bool construct, bool ignoresReturnValue, bool isDOMCall);
+                      bool construct, bool ignoresReturnValue, bool isDOMCall,
+                      DOMObjectKind objectKind);
 
     void initFunction(MDefinition* func) {
         initOperand(FunctionOperandIndex, func);
     }
 
     bool needsArgCheck() const {
         return needsArgCheck_;
     }
@@ -4307,38 +4308,46 @@ class MCall
 };
 
 class MCallDOMNative : public MCall
 {
     // A helper class for MCalls for DOM natives.  Note that this is NOT
     // actually a separate MIR op from MCall, because all sorts of places use
     // isCall() to check for calls and all we really want is to overload a few
     // virtual things from MCall.
-  protected:
-    MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs)
-        : MCall(target, numActualArgs, false, false)
+
+    DOMObjectKind objectKind_;
+
+  protected:
+    MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs, DOMObjectKind objectKind)
+      : MCall(target, numActualArgs, false, false),
+        objectKind_(objectKind)
     {
         MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
 
         // If our jitinfo is not marked eliminatable, that means that our C++
         //