Merge mozilla-central to autoland r=merge
authorDorel Luca <dluca@mozilla.com>
Thu, 14 Dec 2017 11:58:34 +0200
changeset 448185 16bcfaad13e10377b1e5ba557da0545b4b3b4f75
parent 448184 bcdf5a72af42869141893e69aa1c3b0b4149c3e9 (current diff)
parent 448118 8062887ff0d9382ea84177f2c21f62dc0e613d9e (diff)
child 448186 70eb26bf87605ed49e36e4c193801ac89e477936
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland r=merge
intl/lwbrk/nsILineBreaker.h
intl/lwbrk/nsIWordBreaker.h
intl/lwbrk/nsJISx4051LineBreaker.cpp
intl/lwbrk/nsJISx4051LineBreaker.h
intl/lwbrk/nsSampleWordBreaker.cpp
intl/lwbrk/nsSampleWordBreaker.h
js/src/tests/test262/built-ins/RegExp/incomplete_hex_unicode_escape.js
js/src/tests/test262/language/expressions/typeof/native-no-call.js
taskcluster/ci/release-notify-publish/kind.yml
--- a/build/sparse-profiles/taskgraph
+++ b/build/sparse-profiles/taskgraph
@@ -1,15 +1,16 @@
 %include build/sparse-profiles/mach
 
 [include]
 # These files are read as part of generating the taskgraph.
 path:browser/locales/l10n-changesets.json
 path:mobile/locales/l10n-changesets.json
 path:browser/config/version_display.txt
+path:browser/config/version.txt
 
 # Lots of random files in here are read. Just pull in the whole thing.
 path:build/
 
 # TODO remove once bug 1402010 is resolved and test manifests aren't
 # processed in Files() reading mode in moz.build files.
 path:layout/tools/reftest/
 path:testing/web-platform/tests/tools/
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -617,17 +617,18 @@
   position: absolute;
   overflow: visible;
 }
 
 :-moz-native-anonymous .shapes-polygon,
 :-moz-native-anonymous .shapes-ellipse,
 :-moz-native-anonymous .shapes-rect,
 :-moz-native-anonymous .shapes-bounding-box,
-:-moz-native-anonymous .shapes-rotate-line {
+:-moz-native-anonymous .shapes-rotate-line,
+:-moz-native-anonymous .shapes-quad {
   fill: transparent;
   stroke: var(--highlighter-guide-color);
   shape-rendering: geometricPrecision;
   vector-effect: non-scaling-stroke;
 }
 
 :-moz-native-anonymous .shapes-markers {
   fill: #fff;
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -115,16 +115,106 @@ class ShapesHighlighter extends AutoRefr
         "id": "shape-container",
         "class": "shape-container",
         "viewBox": "0 0 100 100",
         "preserveAspectRatio": "none"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
+    // This clipPath and its children make sure the element quad outline
+    // is only shown when the shape extends past the element quads.
+    let clipSvg = createSVGNode(this.win, {
+      nodeType: "clipPath",
+      parent: mainSvg,
+      attributes: {
+        "id": "clip-path",
+        "class": "clip-path",
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "polygon",
+      parent: clipSvg,
+      attributes: {
+        "id": "clip-polygon",
+        "class": "clip-polygon",
+        "hidden": "true"
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "ellipse",
+      parent: clipSvg,
+      attributes: {
+        "id": "clip-ellipse",
+        "class": "clip-ellipse",
+        "hidden": true
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "rect",
+      parent: clipSvg,
+      attributes: {
+        "id": "clip-rect",
+        "class": "clip-rect",
+        "hidden": true
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // Rectangle that displays the element quads. Only shown for shape-outside.
+    // Only the parts of the rectangle's outline that overlap with the shape is shown.
+    createSVGNode(this.win, {
+      nodeType: "rect",
+      parent: mainSvg,
+      attributes: {
+        "id": "quad",
+        "class": "quad",
+        "hidden": "true",
+        "clip-path": "url(#shapes-clip-path)",
+        "x": 0,
+        "y": 0,
+        "width": 100,
+        "height": 100
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    // clipPath that corresponds to the element's quads. Only applied for shape-outside.
+    // This ensures only the parts of the shape that are within the element's quads are
+    // outlined by a solid line.
+    let shapeClipSvg = createSVGNode(this.win, {
+      nodeType: "clipPath",
+      parent: mainSvg,
+      attributes: {
+        "id": "quad-clip-path",
+        "class": "quad-clip-path",
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "rect",
+      parent: shapeClipSvg,
+      attributes: {
+        "id": "quad-clip",
+        "class": "quad-clip",
+        "x": -1,
+        "y": -1,
+        "width": 102,
+        "height": 102
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
     let mainGroup = createSVGNode(this.win, {
       nodeType: "g",
       parent: mainSvg,
       attributes: {
         "id": "group",
       },
       prefix: this.ID_CLASS_PREFIX
     });
@@ -160,16 +250,54 @@ class ShapesHighlighter extends AutoRefr
       attributes: {
         "id": "rect",
         "class": "rect",
         "hidden": true
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
+    // Dashed versions of each shape. Only shown for the parts of the shape
+    // that extends past the element's quads.
+    createSVGNode(this.win, {
+      nodeType: "polygon",
+      parent: mainGroup,
+      attributes: {
+        "id": "dashed-polygon",
+        "class": "polygon",
+        "hidden": "true",
+        "stroke-dasharray": "5, 5",
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "ellipse",
+      parent: mainGroup,
+      attributes: {
+        "id": "dashed-ellipse",
+        "class": "ellipse",
+        "hidden": "true",
+        "stroke-dasharray": "5, 5",
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
+    createSVGNode(this.win, {
+      nodeType: "rect",
+      parent: mainGroup,
+      attributes: {
+        "id": "dashed-rect",
+        "class": "rect",
+        "hidden": "true",
+        "stroke-dasharray": "5, 5",
+      },
+      prefix: this.ID_CLASS_PREFIX
+    });
+
     createSVGNode(this.win, {
       nodeType: "path",
       parent: mainGroup,
       attributes: {
         "id": "bounding-box",
         "class": "bounding-box",
         "stroke-dasharray": "5, 5",
         "hidden": true
@@ -314,24 +442,25 @@ class ShapesHighlighter extends AutoRefr
     let { target, type, pageX, pageY } = event;
 
     // For events on highlighted nodes in an iframe, when the event takes place
     // outside the iframe. Check if event target belongs to the iframe. If it doesn't,
     // adjust pageX/pageY to be relative to the iframe rather than the parent.
     let nodeDocument = this.currentNode.ownerDocument;
     if (target !== nodeDocument && target.ownerDocument !== nodeDocument) {
       let [xOffset, yOffset] = getFrameOffsets(target.ownerGlobal, this.currentNode);
+      let zoom = getCurrentZoom(this.win);
       // xOffset/yOffset are relative to the viewport, so first find the top/left
       // edges of the viewport relative to the page.
       let viewportLeft = pageX - event.clientX;
       let viewportTop = pageY - event.clientY;
       // Also adjust for scrolling in the iframe.
       let { scrollTop, scrollLeft } = nodeDocument.documentElement;
-      pageX -= viewportLeft + xOffset - scrollLeft;
-      pageY -= viewportTop + yOffset - scrollTop;
+      pageX -= viewportLeft + xOffset / zoom - scrollLeft;
+      pageY -= viewportTop + yOffset / zoom - scrollTop;
     }
 
     switch (type) {
       case "pagehide":
         // If a page hide event is triggered for current window's highlighter, hide the
         // highlighter.
         if (target.defaultView === this.win) {
           this.destroy();
@@ -2007,16 +2136,23 @@ class ShapesHighlighter extends AutoRefr
   _hideShapes() {
     this.getElement("ellipse").setAttribute("hidden", true);
     this.getElement("polygon").setAttribute("hidden", true);
     this.getElement("rect").setAttribute("hidden", true);
     this.getElement("bounding-box").setAttribute("hidden", true);
     this.getElement("markers").setAttribute("d", "");
     this.getElement("markers-outline").setAttribute("d", "");
     this.getElement("rotate-line").setAttribute("d", "");
+    this.getElement("quad").setAttribute("hidden", true);
+    this.getElement("clip-ellipse").setAttribute("hidden", true);
+    this.getElement("clip-polygon").setAttribute("hidden", true);
+    this.getElement("clip-rect").setAttribute("hidden", true);
+    this.getElement("dashed-polygon").setAttribute("hidden", true);
+    this.getElement("dashed-ellipse").setAttribute("hidden", true);
+    this.getElement("dashed-rect").setAttribute("hidden", true);
   }
 
   /**
    * Update the highlighter for the current node. Called whenever the element's quads
    * or CSS shape has changed.
    * @returns {Boolean} whether the highlighter was successfully updated
    */
   _update() {
@@ -2041,16 +2177,32 @@ class ShapesHighlighter extends AutoRefr
     } else if (this.shapeType === "circle") {
       this._updateCircleShape(width, height, zoom);
     } else if (this.shapeType === "ellipse") {
       this._updateEllipseShape(width, height, zoom);
     } else if (this.shapeType === "inset") {
       this._updateInsetShape(width, height, zoom);
     }
 
+    if (this.property === "shape-outside") {
+      // For shape-outside, the element's quads are displayed for the parts that overlap
+      // with the shape, and the parts of the shape that extend past the element's quads
+      // are shown with a dashed line.
+      let quadRect = this.getElement("quad");
+      quadRect.removeAttribute("hidden");
+
+      this.getElement("polygon").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
+      this.getElement("ellipse").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
+      this.getElement("rect").setAttribute("clip-path", "url(#shapes-quad-clip-path)");
+    } else {
+      this.getElement("polygon").removeAttribute("clip-path");
+      this.getElement("ellipse").removeAttribute("clip-path");
+      this.getElement("rect").removeAttribute("clip-path");
+    }
+
     let { width: winWidth, height: winHeight } = this._winDimensions;
     root.removeAttribute("hidden");
     root.setAttribute("style",
       `position:absolute; width:${winWidth}px;height:${winHeight}px; overflow:hidden;`);
 
     this._handleMarkerHover(this.hoveredPoint);
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
@@ -2079,27 +2231,49 @@ class ShapesHighlighter extends AutoRefr
 
     if (this.shapeType === "polygon") {
       let points = this.coordinates.map(point => point.join(",")).join(" ");
 
       let polygonEl = this.getElement("polygon");
       polygonEl.setAttribute("points", points);
       polygonEl.removeAttribute("hidden");
 
+      let clipPolygon = this.getElement("clip-polygon");
+      clipPolygon.setAttribute("points", points);
+      clipPolygon.removeAttribute("hidden");
+
+      let dashedPolygon = this.getElement("dashed-polygon");
+      dashedPolygon.setAttribute("points", points);
+      dashedPolygon.removeAttribute("hidden");
+
       markerPoints.push(rotatePoint);
       let rotateLine = `M ${center.join(" ")} L ${rotatePoint.join(" ")}`;
       this.getElement("rotate-line").setAttribute("d", rotateLine);
     } else if (this.shapeType === "circle" || this.shapeType === "ellipse") {
       let { rx, ry, cx, cy } = this.coordinates;
       let ellipseEl = this.getElement("ellipse");
       ellipseEl.setAttribute("rx", rx);
       ellipseEl.setAttribute("ry", ry);
       ellipseEl.setAttribute("cx", cx);
       ellipseEl.setAttribute("cy", cy);
       ellipseEl.removeAttribute("hidden");
+
+      let clipEllipse = this.getElement("clip-ellipse");
+      clipEllipse.setAttribute("rx", rx);
+      clipEllipse.setAttribute("ry", ry);
+      clipEllipse.setAttribute("cx", cx);
+      clipEllipse.setAttribute("cy", cy);
+      clipEllipse.removeAttribute("hidden");
+
+      let dashedEllipse = this.getElement("dashed-ellipse");
+      dashedEllipse.setAttribute("rx", rx);
+      dashedEllipse.setAttribute("ry", ry);
+      dashedEllipse.setAttribute("cx", cx);
+      dashedEllipse.setAttribute("cy", cy);
+      dashedEllipse.removeAttribute("hidden");
     }
     this._drawMarkers(markerPoints, width, height, zoom);
   }
 
   /**
    * Update the SVG polygon to fit the CSS polygon.
    * @param {Number} width the width of the element quads
    * @param {Number} height the height of the element quads
@@ -2108,16 +2282,23 @@ class ShapesHighlighter extends AutoRefr
   _updatePolygonShape(width, height, zoom) {
     // Draw and show the polygon.
     let points = this.coordinates.map(point => point.join(",")).join(" ");
 
     let polygonEl = this.getElement("polygon");
     polygonEl.setAttribute("points", points);
     polygonEl.removeAttribute("hidden");
 
+    let clipPolygon = this.getElement("clip-polygon");
+    clipPolygon.setAttribute("points", points);
+    clipPolygon.removeAttribute("hidden");
+
+    let dashedPolygon = this.getElement("dashed-polygon");
+    dashedPolygon.setAttribute("points", points);
+    dashedPolygon.removeAttribute("hidden");
     this._drawMarkers(this.coordinates, width, height, zoom);
   }
 
   /**
    * Update the SVG ellipse to fit the CSS circle.
    * @param {Number} width the width of the element quads
    * @param {Number} height the height of the element quads
    * @param {Number} zoom the zoom level of the window
@@ -2126,16 +2307,30 @@ class ShapesHighlighter extends AutoRefr
     let { rx, ry, cx, cy } = this.coordinates;
     let ellipseEl = this.getElement("ellipse");
     ellipseEl.setAttribute("rx", rx);
     ellipseEl.setAttribute("ry", ry);
     ellipseEl.setAttribute("cx", cx);
     ellipseEl.setAttribute("cy", cy);
     ellipseEl.removeAttribute("hidden");
 
+    let clipEllipse = this.getElement("clip-ellipse");
+    clipEllipse.setAttribute("rx", rx);
+    clipEllipse.setAttribute("ry", ry);
+    clipEllipse.setAttribute("cx", cx);
+    clipEllipse.setAttribute("cy", cy);
+    clipEllipse.removeAttribute("hidden");
+
+    let dashedEllipse = this.getElement("dashed-ellipse");
+    dashedEllipse.setAttribute("rx", rx);
+    dashedEllipse.setAttribute("ry", ry);
+    dashedEllipse.setAttribute("cx", cx);
+    dashedEllipse.setAttribute("cy", cy);
+    dashedEllipse.removeAttribute("hidden");
+
     this._drawMarkers([[cx, cy], [cx + rx, cy]], width, height, zoom);
   }
 
   /**
    * Update the SVG ellipse to fit the CSS ellipse.
    * @param {Number} width the width of the element quads
    * @param {Number} height the height of the element quads
    * @param {Number} zoom the zoom level of the window
@@ -2144,16 +2339,29 @@ class ShapesHighlighter extends AutoRefr
     let { rx, ry, cx, cy } = this.coordinates;
     let ellipseEl = this.getElement("ellipse");
     ellipseEl.setAttribute("rx", rx);
     ellipseEl.setAttribute("ry", ry);
     ellipseEl.setAttribute("cx", cx);
     ellipseEl.setAttribute("cy", cy);
     ellipseEl.removeAttribute("hidden");
 
+    let clipEllipse = this.getElement("clip-ellipse");
+    clipEllipse.setAttribute("rx", rx);
+    clipEllipse.setAttribute("ry", ry);
+    clipEllipse.setAttribute("cx", cx);
+    clipEllipse.setAttribute("cy", cy);
+    clipEllipse.removeAttribute("hidden");
+
+    let dashedEllipse = this.getElement("dashed-ellipse");
+    dashedEllipse.setAttribute("rx", rx);
+    dashedEllipse.setAttribute("ry", ry);
+    dashedEllipse.setAttribute("cx", cx);
+    dashedEllipse.setAttribute("cy", cy);
+    dashedEllipse.removeAttribute("hidden");
     let markerCoords = [ [cx, cy], [cx + rx, cy], [cx, cy + ry] ];
     this._drawMarkers(markerCoords, width, height, zoom);
   }
 
   /**
    * Update the SVG rect to fit the CSS inset.
    * @param {Number} width the width of the element quads
    * @param {Number} height the height of the element quads
@@ -2163,16 +2371,30 @@ class ShapesHighlighter extends AutoRefr
     let { top, left, right, bottom } = this.coordinates;
     let rectEl = this.getElement("rect");
     rectEl.setAttribute("x", left);
     rectEl.setAttribute("y", top);
     rectEl.setAttribute("width", 100 - left - right);
     rectEl.setAttribute("height", 100 - top - bottom);
     rectEl.removeAttribute("hidden");
 
+    let clipRect = this.getElement("clip-rect");
+    clipRect.setAttribute("x", left);
+    clipRect.setAttribute("y", top);
+    clipRect.setAttribute("width", 100 - left - right);
+    clipRect.setAttribute("height", 100 - top - bottom);
+    clipRect.removeAttribute("hidden");
+
+    let dashedRect = this.getElement("dashed-rect");
+    dashedRect.setAttribute("x", left);
+    dashedRect.setAttribute("y", top);
+    dashedRect.setAttribute("width", 100 - left - right);
+    dashedRect.setAttribute("height", 100 - top - bottom);
+    dashedRect.removeAttribute("hidden");
+
     let centerX = (left + (100 - right)) / 2;
     let centerY = (top + (100 - bottom)) / 2;
     let markerCoords = [[centerX, top], [100 - right, centerY],
                         [centerX, 100 - bottom], [left, centerY]];
     this._drawMarkers(markerCoords, width, height, zoom);
   }
 
   /**
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -145,17 +145,16 @@
 #include "nsIFragmentContentSink.h"
 #include "nsContainerFrame.h"
 #include "nsIHTMLDocument.h"
 #include "nsIIdleService.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIIOService.h"
-#include "nsILineBreaker.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroup.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMIMEHeaderParam.h"
 #include "nsIMIMEService.h"
 #include "nsINode.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsIObjectLoadingContent.h"
@@ -176,20 +175,18 @@
 #include "nsIScrollable.h"
 #include "nsIStreamConverterService.h"
 #include "nsIStringBundle.h"
 #include "nsIURI.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsIURL.h"
 #include "nsIWebNavigation.h"
 #include "nsIWindowMediator.h"
-#include "nsIWordBreaker.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
-#include "nsLWBrkCIID.h"
 #include "nsMappedAttributes.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsNodeInfoManager.h"
 #include "NullPrincipal.h"
 #include "nsParserCIID.h"
 #include "nsParserConstants.h"
 #include "nsPIDOMWindow.h"
@@ -262,18 +259,18 @@ nsIUUIDGenerator *nsContentUtils::sUUIDG
 nsIConsoleService *nsContentUtils::sConsoleService;
 nsDataHashtable<nsRefPtrHashKey<nsAtom>, EventNameMapping>* nsContentUtils::sAtomEventTable = nullptr;
 nsDataHashtable<nsStringHashKey, EventNameMapping>* nsContentUtils::sStringEventTable = nullptr;
 nsTArray<RefPtr<nsAtom>>* nsContentUtils::sUserDefinedEvents = nullptr;
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 bool nsContentUtils::sTriedToGetContentPolicy = false;
-nsILineBreaker *nsContentUtils::sLineBreaker;
-nsIWordBreaker *nsContentUtils::sWordBreaker;
+RefPtr<mozilla::intl::LineBreaker> nsContentUtils::sLineBreaker;
+RefPtr<mozilla::intl::WordBreaker> nsContentUtils::sWordBreaker;
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
 uint32_t nsContentUtils::sScriptBlockerCount = 0;
 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr;
 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
 
 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
@@ -602,21 +599,19 @@ nsContentUtils::Init()
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
     // This makes life easier, but we can live without it.
 
     sIOService = nullptr;
   }
 
-  rv = CallGetService(NS_LBRK_CONTRACTID, &sLineBreaker);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = CallGetService(NS_WBRK_CONTRACTID, &sWordBreaker);
-  NS_ENSURE_SUCCESS(rv, rv);
+  sLineBreaker = mozilla::intl::LineBreaker::Create();
+
+  sWordBreaker = mozilla::intl::WordBreaker::Create();
 
   if (!InitializeEventTable())
     return NS_ERROR_FAILURE;
 
   if (!sEventListenerManagersHash) {
     static const PLDHashTableOps hash_table_ops =
     {
       PLDHashTable::HashVoidPtrKeyStub,
@@ -2115,18 +2110,18 @@ nsContentUtils::Shutdown()
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   sXPConnect = nullptr;
   NS_IF_RELEASE(sSecurityManager);
   NS_IF_RELEASE(sSystemPrincipal);
   NS_IF_RELEASE(sNullSubjectPrincipal);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sUUIDGenerator);
-  NS_IF_RELEASE(sLineBreaker);
-  NS_IF_RELEASE(sWordBreaker);
+  sLineBreaker = nullptr;
+  sWordBreaker = nullptr;
   NS_IF_RELEASE(sBidiKeyboard);
 
   delete sAtomEventTable;
   sAtomEventTable = nullptr;
   delete sStringEventTable;
   sStringEventTable = nullptr;
   delete sUserDefinedEvents;
   sUserDefinedEvents = nullptr;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -29,16 +29,18 @@
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "nsTArrayForwardDeclare.h"
 #include "Units.h"
 #include "mozilla/dom/AutocompleteInfoBinding.h"
 #include "mozilla/dom/BindingDeclarations.h" // For CallerType
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
+#include "mozilla/intl/LineBreaker.h"
+#include "mozilla/intl/WordBreaker.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Logging.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RangeBoundary.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMMouseEvent.h"
@@ -75,17 +77,16 @@ class nsIDOMKeyEvent;
 class nsIDOMNode;
 class nsIDragSession;
 class nsIEventTarget;
 class nsIFragmentContentSink;
 class nsIFrame;
 class nsIImageLoadingContent;
 class nsIInterfaceRequestor;
 class nsIIOService;
-class nsILineBreaker;
 class nsILoadInfo;
 class nsILoadGroup;
 class nsIMessageBroadcaster;
 class nsNameSpaceManager;
 class nsIObserver;
 class nsIParser;
 class nsIPluginTag;
 class nsIPresShell;
@@ -95,17 +96,16 @@ class nsIRunnable;
 class nsIScriptContext;
 class nsIScriptSecurityManager;
 class nsIStringBundle;
 class nsIStringBundleService;
 class nsISupportsHashKey;
 class nsIURI;
 class nsIUUIDGenerator;
 class nsIWidget;
-class nsIWordBreaker;
 class nsIXPConnect;
 class nsNodeInfoManager;
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 class nsPresContext;
 class nsStringBuffer;
 class nsStringHashKey;
 class nsTextFragment;
@@ -747,24 +747,24 @@ public:
   // system principal, and true for a null principal.
   // This version checks the permission for an exact host match on
   // the principal
   static bool IsExactSitePermDeny(nsIPrincipal* aPrincipal, const char* aType);
 
   // Returns true if aDoc1 and aDoc2 have equal NodePrincipal()s.
   static bool HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2);
 
-  static nsILineBreaker* LineBreaker()
+  static mozilla::intl::LineBreaker* LineBreaker()
   {
-    return sLineBreaker;
+    return sLineBreaker.get();
   }
 
-  static nsIWordBreaker* WordBreaker()
+  static mozilla::intl::WordBreaker* WordBreaker()
   {
-    return sWordBreaker;
+    return sWordBreaker.get();
   }
 
   /**
    * Regster aObserver as a shutdown observer. A strong reference is held
    * to aObserver until UnregisterShutdownObserver is called.
    */
   static void RegisterShutdownObserver(nsIObserver* aObserver);
   static void UnregisterShutdownObserver(nsIObserver* aObserver);
@@ -3389,18 +3389,18 @@ private:
   static nsTArray<RefPtr<nsAtom>>* sUserDefinedEvents;
 
   static nsIStringBundleService* sStringBundleService;
   static nsIStringBundle* sStringBundles[PropertiesFile_COUNT];
 
   static nsIContentPolicy* sContentPolicyService;
   static bool sTriedToGetContentPolicy;
 
-  static nsILineBreaker* sLineBreaker;
-  static nsIWordBreaker* sWordBreaker;
+  static RefPtr<mozilla::intl::LineBreaker> sLineBreaker;
+  static RefPtr<mozilla::intl::WordBreaker> sWordBreaker;
 
   static nsIBidiKeyboard* sBidiKeyboard;
 
   static bool sInitialized;
   static uint32_t sScriptBlockerCount;
   static uint32_t sDOMNodeRemovedSuppressCount;
 
   // Not an nsCOMArray because removing elements from those is slower
--- a/dom/base/nsGlobalWindowCommands.cpp
+++ b/dom/base/nsGlobalWindowCommands.cpp
@@ -26,17 +26,16 @@
 #include "nsIWebNavigation.h"
 #include "nsIContentViewerEdit.h"
 #include "nsIContentViewer.h"
 #include "nsFocusManager.h"
 #include "nsCopySupport.h"
 #include "nsIClipboard.h"
 #include "ContentEventHandler.h"
 #include "nsContentUtils.h"
-#include "nsIWordBreaker.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/layers/KeyboardMap.h"
 
 #include "nsIClipboardDragDropHooks.h"
 #include "nsIClipboardDragDropHookList.h"
@@ -1124,22 +1123,22 @@ nsLookUpDictionaryCommand::DoCommandPara
   handler.OnQueryTextContent(&textContent);
   if (NS_WARN_IF(!textContent.mSucceeded ||
                  textContent.mReply.mString.IsEmpty())) {
     return NS_ERROR_FAILURE;
   }
 
   // XXX nsIWordBreaker doesn't use contextual breaker.
   // If OS provides it, widget should use it if contextual breaker is needed.
-  nsCOMPtr<nsIWordBreaker> wordBreaker = nsContentUtils::WordBreaker();
+  RefPtr<mozilla::intl::WordBreaker> wordBreaker = nsContentUtils::WordBreaker();
   if (NS_WARN_IF(!wordBreaker)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsWordRange range =
+  mozilla::intl::WordRange range =
     wordBreaker->FindWord(textContent.mReply.mString.get(),
                           textContent.mReply.mString.Length(),
                           charAt.mReply.mOffset - offset);
   if (range.mEnd == range.mBegin) {
     return NS_ERROR_FAILURE;
   }
   range.mBegin += offset;
   range.mEnd += offset;
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -22,17 +22,16 @@
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsEscape.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
-#include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsAttrName.h"
 #include "nsIDocShell.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "mozilla/dom/Element.h"
 #include "nsParserConstants.h"
 
--- a/dom/base/nsLineBreaker.cpp
+++ b/dom/base/nsLineBreaker.cpp
@@ -1,28 +1,30 @@
 /* -*- 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 "nsLineBreaker.h"
 #include "nsContentUtils.h"
-#include "nsILineBreaker.h"
 #include "gfxTextRun.h" // for the gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_* values
 #include "nsHyphenationManager.h"
 #include "nsHyphenator.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/intl/LineBreaker.h"
+
+using mozilla::intl::LineBreaker;
 
 nsLineBreaker::nsLineBreaker()
   : mCurrentWordLanguage(nullptr),
     mCurrentWordContainsMixedLang(false),
     mCurrentWordContainsComplexChar(false),
     mAfterBreakableSpace(false), mBreakHere(false),
-    mWordBreak(nsILineBreaker::kWordBreak_Normal)
+    mWordBreak(LineBreaker::kWordBreak_Normal)
 {
 }
 
 nsLineBreaker::~nsLineBreaker()
 {
   NS_ASSERTION(mCurrentWord.Length() == 0, "Should have Reset() before destruction!");
 }
 
@@ -64,17 +66,17 @@ nsLineBreaker::FlushCurrentWord()
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsTArray<bool> capitalizationState;
 
   if (!mCurrentWordContainsComplexChar) {
     // For break-strict set everything internal to "break", otherwise
     // to "no break"!
     memset(breakState.Elements(),
-           mWordBreak == nsILineBreaker::kWordBreak_BreakAll ?
+           mWordBreak == LineBreaker::kWordBreak_BreakAll ?
              gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
              gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE,
            length*sizeof(uint8_t));
   } else {
     nsContentUtils::LineBreaker()->
       GetJISx4051Breaks(mCurrentWord.Elements(), length, mWordBreak,
                         breakState.Elements());
   }
@@ -231,17 +233,17 @@ nsLineBreaker::AppendText(nsAtom* aHyphe
   for (;;) {
     char16_t ch = aText[offset];
     bool isSpace = IsSpace(ch);
     bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink && !noBreaksNeeded) {
       breakState[offset] =
         mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
-        (mWordBreak == nsILineBreaker::kWordBreak_BreakAll)  ?
+        (mWordBreak == LineBreaker::kWordBreak_BreakAll)  ?
           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = false;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace || ch == '\n') {
       if (offset > wordStart && aSink) {
@@ -398,17 +400,17 @@ nsLineBreaker::AppendText(nsAtom* aHyphe
     bool isSpace = IsSpace(ch);
     bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
 
     if (aSink) {
       // Consider word-break style.  Since the break position of CJK scripts
       // will be set by nsILineBreaker, we don't consider CJK at this point.
       breakState[offset] =
         mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ||
-        (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ?
+        (mWordBreak == LineBreaker::kWordBreak_BreakAll) ?
           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL :
           gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE;
     }
     mBreakHere = false;
     mAfterBreakableSpace = isBreakableSpace;
 
     if (isSpace) {
       if (offset > wordStart && wordHasComplexChar) {
--- a/dom/base/nsLineBreaker.h
+++ b/dom/base/nsLineBreaker.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSLINEBREAKER_H_
 #define NSLINEBREAKER_H_
 
 #include "nsString.h"
 #include "nsTArray.h"
-#include "nsILineBreaker.h"
 
 class nsAtom;
 class nsHyphenator;
 
 /**
  * A receiver of line break data.
  */
 class nsILineBreakSink {
@@ -48,54 +47,55 @@ public:
  * with each text chunk, which might happen during the corresponding AppendText
  * call, or might happen during a later AppendText call or even a Reset()
  * call.
  *
  * The linebreak results MUST NOT depend on how the text is broken up
  * into AppendText calls.
  *
  * The current strategy is that we break the overall text into
- * whitespace-delimited "words". Then those words are passed to the nsILineBreaker
+ * whitespace-delimited "words". Then those words are passed to the LineBreaker
  * service for deeper analysis if they contain a "complex" character as described
  * below.
  *
  * This class also handles detection of which characters should be capitalized
  * for text-transform:capitalize. This is a good place to handle that because
  * we have all the context we need.
  */
 class nsLineBreaker {
 public:
   nsLineBreaker();
   ~nsLineBreaker();
 
-  static inline bool IsSpace(char16_t u) { return NS_IsSpace(u); }
+  static inline bool IsSpace(char16_t u) { return mozilla::intl::NS_IsSpace(u); }
 
   static inline bool IsComplexASCIIChar(char16_t u)
   {
     return !((0x0030 <= u && u <= 0x0039) ||
              (0x0041 <= u && u <= 0x005A) ||
              (0x0061 <= u && u <= 0x007A) ||
              (0x000a == u));
   }
 
   static inline bool IsComplexChar(char16_t u)
   {
     return IsComplexASCIIChar(u) ||
-           NS_NeedsPlatformNativeHandling(u) ||
+           mozilla::intl::NS_NeedsPlatformNativeHandling(u) ||
            (0x1100 <= u && u <= 0x11ff) || // Hangul Jamo
            (0x2000 <= u && u <= 0x21ff) || // Punctuations and Symbols
            (0x2e80 <= u && u <= 0xd7ff) || // several CJK blocks
            (0xf900 <= u && u <= 0xfaff) || // CJK Compatibility Idographs
            (0xff00 <= u && u <= 0xffef);   // Halfwidth and Fullwidth Forms
   }
 
   // Break opportunities exist at the end of each run of breakable whitespace
   // (see IsSpace above). Break opportunities can also exist between pairs of
-  // non-whitespace characters, as determined by nsILineBreaker. We pass a whitespace-
-  // delimited word to nsILineBreaker if it contains at least one character
+  // non-whitespace characters, as determined by mozilla::intl::LineBreaker.
+  // We pass a whitespace-
+  // delimited word to LineBreaker if it contains at least one character
   // matching IsComplexChar.
   // We provide flags to control on a per-chunk basis where breaks are allowed.
   // At any character boundary, exactly one text chunk governs whether a
   // break is allowed at that boundary.
   //
   // We operate on text after whitespace processing has been applied, so
   // other characters (e.g. tabs and newlines) may have been converted to
   // spaces.
@@ -167,17 +167,17 @@ public:
    * @param aTrailingBreak this is set to true when there is a break opportunity
    * at the end of the text. This will normally only be declared true when there
    * is breakable whitespace at the end.
    */
   nsresult Reset(bool* aTrailingBreak);
 
   /*
    * Set word-break mode for linebreaker.  This is set by word-break property.
-   * @param aMode is nsILineBreaker::kWordBreak_* value.
+   * @param aMode is LineBreaker::kWordBreak_* value.
    */
   void SetWordBreak(uint8_t aMode) { mWordBreak = aMode; }
 
 private:
   // This is a list of text sources that make up the "current word" (i.e.,
   // run of text which does not contain any whitespace). All the mLengths
   // are are nonzero, these cannot overlap.
   struct TextItem {
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -6,17 +6,16 @@
 
 /*
  * nsIContentSerializer implementation that can be used with an
  * nsIDocumentEncoder to convert a DOM into plaintext in a nice way
  * (eg for copy/paste as plaintext).
  */
 
 #include "nsPlainTextSerializer.h"
-#include "nsLWBrkCIID.h"
 #include "nsIServiceManager.h"
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -9,22 +9,22 @@
  * nsIDocumentEncoder to convert a DOM into plaintext in a nice way
  * (eg for copy/paste as plaintext).
  */
 
 #ifndef nsPlainTextSerializer_h__
 #define nsPlainTextSerializer_h__
 
 #include "mozilla/Attributes.h"
+#include "mozilla/intl/LineBreaker.h"
 #include "nsCOMPtr.h"
 #include "nsAtom.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContentSerializer.h"
 #include "nsIDocumentEncoder.h"
-#include "nsILineBreaker.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include <stack>
 
 class nsIContent;
 
 namespace mozilla {
@@ -226,17 +226,17 @@ private:
 
   // The stack for ordered lists
   int32_t         *mOLStack;
   uint32_t         mOLStackIndex;
 
   uint32_t         mULCount;
 
   nsString                     mLineBreak;
-  nsCOMPtr<nsILineBreaker>     mLineBreaker;
+  RefPtr<mozilla::intl::LineBreaker> mLineBreaker;
 
   // Conveniance constant. It would be nice to have it as a const static
   // variable, but that causes issues with OpenBSD and module unloading.
   const nsString          kSpace;
 
   // If nsIDocumentEncoder::OutputNonTextContentAsPlaceholder is set, the child
   // nodes of specific nodes - <iframe>, <canvas>, etc. should be ignored.
   // mIgnoredChildNodeLevel is used to tell if current node is an ignorable
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -22,17 +22,16 @@
 #include "nsIServiceManager.h"
 #include "nsIDocumentEncoder.h"
 #include "nsGkAtoms.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsEscape.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
-#include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsAttrName.h"
 #include "nsComputedDOMStyle.h"
 #include "mozilla/dom/Element.h"
 
 static const int32_t kLongLineLen = 128;
 
--- a/dom/base/nsXMLContentSerializer.cpp
+++ b/dom/base/nsXMLContentSerializer.cpp
@@ -24,18 +24,18 @@
 #include "nsNameSpaceManager.h"
 #include "nsTextFragment.h"
 #include "nsString.h"
 #include "mozilla/Sprintf.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsAttrName.h"
-#include "nsILineBreaker.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/intl/LineBreaker.h"
 #include "nsParserConstants.h"
 #include "mozilla/Encoding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define kXMLNS "xmlns"
 
@@ -1595,17 +1595,18 @@ nsXMLContentSerializer::AppendWrapped_No
       }
       else {
         // we must wrap
         onceAgainBecauseWeAddedBreakInFront = false;
         bool foundWrapPosition = false;
         int32_t wrapPosition = 0;
 
         if (mAllowLineBreaking) {
-          nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker();
+          mozilla::intl::LineBreaker* lineBreaker =
+            nsContentUtils::LineBreaker();
 
           wrapPosition = lineBreaker->Prev(aSequenceStart,
                                            (aEnd - aSequenceStart),
                                            (aPos - aSequenceStart) + 1);
           if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) {
             foundWrapPosition = true;
           }
           else {
--- a/dom/events/test/pointerevents/test_bug1403055.html
+++ b/dom/events/test/pointerevents/test_bug1403055.html
@@ -15,21 +15,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="target0" style="width: 50px; height: 50px; background: green"></div>
 <div id="target1" style="width: 50px; height: 50px; background: red"></div>
 <div id="done" style="width: 50px; height: 50px; background: black"></div>
 <script type="text/javascript">
 /** Test for Bug 1403055 **/
 SimpleTest.waitForExplicitFinish();
 
-function runTests() {
-  let target0 = window.document.getElementById("target0");
-  let target1 = window.document.getElementById("target1");
-  let done = window.document.getElementById("done");
+var target0 = window.document.getElementById("target0");
+var target1 = window.document.getElementById("target1");
+var done = window.document.getElementById("done");
 
+function withoutImplicitlyPointerCaptureForTouch() {
   let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove"];
   let target1_events = ["pointerover", "pointerenter", "pointermove", "pointercancel", "pointerout", "pointerleave"];
 
   target0_events.forEach((elem, index, arr) => {
     target0.addEventListener(elem, (event) => {
       is(event.type, target0_events[0], "receive " + event.type + " on target0");
       target0_events = target0_events.filter(item => item !== event.type);
     }, { once: true });
@@ -40,26 +40,53 @@ function runTests() {
       is(event.type, target1_events[0], "receive " + event.type + " on target1");
       target1_events = target1_events.filter(item => item !== event.type);
     }, { once: true });
   });
 
   done.addEventListener("mouseup", () => {
     ok(target0_events.length == 0, " should receive " + target0_events + " on target0");
     ok(target1_events.length == 0, " should receive " + target1_events + " on target1");
+    SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
+                                       ["dom.w3c_pointer_events.implicit_capture", true]]},
+                              withImplicitlyPointerCaptureForTouch);
+  }, {once : true});
+
+  synthesizeTouch(target0, 5, 5, { type: "touchstart" });
+  synthesizeTouch(target0, 5, 5, { type: "touchmove" });
+  synthesizeTouch(target1, 5, 5, { type: "touchmove" });
+  synthesizeTouch(target1, 5, 5, { type: "touchcancel" });
+  synthesizeMouseAtCenter(done, { type: "mousedown" });
+  synthesizeMouseAtCenter(done, { type: "mouseup" });
+}
+
+function withImplicitlyPointerCaptureForTouch() {
+  let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove", "pointercancel", "pointerout", "pointerleave"];
+
+  target0_events.forEach((elem, index, arr) => {
+    target0.addEventListener(elem, (event) => {
+      is(event.type, target0_events[0], "receive " + event.type + " on target0");
+      target0_events = target0_events.filter(item => item !== event.type);
+    }, { once: true });
+  });
+
+  done.addEventListener("mouseup", () => {
+    ok(target0_events.length == 0, " should receive " + target0_events + " on target0");
     SimpleTest.finish();
-  });
+  }, {once : true});
 
   synthesizeTouch(target0, 5, 5, { type: "touchstart" });
   synthesizeTouch(target0, 5, 5, { type: "touchmove" });
   synthesizeTouch(target1, 5, 5, { type: "touchmove" });
   synthesizeTouch(target1, 5, 5, { type: "touchcancel" });
   synthesizeMouseAtCenter(done, { type: "mousedown" });
   synthesizeMouseAtCenter(done, { type: "mouseup" });
 }
 
 SimpleTest.waitForFocus(() => {
-  SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true]]}, runTests);
+  SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.enabled", true],
+                                     ["dom.w3c_pointer_events.implicit_capture", false]]},
+                            withoutImplicitlyPointerCaptureForTouch);
 });
 
 </script>
 </body>
 </html>
--- a/dom/events/test/pointerevents/touch_action_helpers.js
+++ b/dom/events/test/pointerevents/touch_action_helpers.js
@@ -1,15 +1,24 @@
 // Some common helpers
 
 function touchActionSetup(testDriver) {
   add_completion_callback(subtestDone);
   document.body.addEventListener('touchend', testDriver, { passive: true });
 }
 
+function touchActionSetupAndWaitTestDone(testDriver) {
+  let testDone = new Promise(resolve => {
+    add_completion_callback(resolve);
+  });
+
+  document.body.addEventListener('touchend', testDriver, { passive: true });
+  return testDone;
+}
+
 function touchScrollRight(aSelector = '#target0', aX = 20, aY = 20) {
   var target = document.querySelector(aSelector);
   return ok(synthesizeNativeTouchDrag(target, aX + 40, aY, -40, 0), "Synthesized horizontal drag");
 }
 
 function touchScrollDown(aSelector = '#target0', aX = 20, aY = 20) {
   var target = document.querySelector(aSelector);
   return ok(synthesizeNativeTouchDrag(target, aX, aY + 40, 0, -40), "Synthesized vertical drag");
@@ -30,21 +39,23 @@ function waitForResetScrollLeft(aSelecto
       }
     });
   });
 }
 
 // The main body functions to simulate the input events required for the named test
 
 function* pointerevent_touch_action_auto_css_touch_manual(testDriver) {
-  touchActionSetup(testDriver);
+  let testDone = touchActionSetupAndWaitTestDone(testDriver);
 
   yield touchScrollRight();
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollDown();
+  yield testDone.then(testDriver);
+  subtestDone();
 }
 
 function* pointerevent_touch_action_button_test_touch_manual(testDriver) {
   touchActionSetup(testDriver);
 
   yield touchScrollDown();
   yield waitForApzFlushedRepaints(testDriver);
   yield setTimeout(testDriver, 2 * scrollReturnInterval);
@@ -98,21 +109,23 @@ function* pointerevent_touch_action_inhe
   yield touchScrollDown('#target0 > div div');
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight('#target0 > div div');
   yield waitForApzFlushedRepaints(testDriver);
   yield tapComplete();
 }
 
 function* pointerevent_touch_action_inherit_highest_parent_none_touch_manual(testDriver) {
-  touchActionSetup(testDriver);
+  let testDone = touchActionSetupAndWaitTestDone(testDriver);
 
   yield touchScrollDown('#target0 > div');
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight('#target0 > div');
+  yield testDone.then(testDriver);
+  subtestDone();
 }
 
 function* pointerevent_touch_action_inherit_parent_none_touch_manual(testDriver) {
   touchActionSetup(testDriver);
 
   yield touchScrollDown();
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight();
@@ -146,21 +159,23 @@ function* pointerevent_touch_action_pan_
   yield touchScrollDown('#target0 > div div');
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight('#target0 > div div');
   yield waitForApzFlushedRepaints(testDriver);
   yield tapComplete();
 }
 
 function* pointerevent_touch_action_pan_x_pan_y_touch_manual(testDriver) {
-  touchActionSetup(testDriver);
+  let testDone = touchActionSetupAndWaitTestDone(testDriver);
 
   yield touchScrollDown();
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight();
+  yield testDone.then(testDriver);
+  subtestDone();
 }
 
 function* pointerevent_touch_action_pan_y_css_touch_manual(testDriver) {
   touchActionSetup(testDriver);
 
   yield touchScrollDown();
   yield waitForApzFlushedRepaints(testDriver);
   yield touchScrollRight();
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -182,17 +182,19 @@ struct AudioChunk {
         if (aOther.mChannelData[channel] != AddAudioSampleOffset(mChannelData[channel],
             mBufferFormat, int32_t(mDuration))) {
           return false;
         }
       }
     }
     return true;
   }
-  bool IsNull() const { return mBuffer == nullptr; }
+  bool IsNull() const {
+    return mBuffer == nullptr;
+  }
   void SetNull(StreamTime aDuration)
   {
     mBuffer = nullptr;
     mChannelData.Clear();
     mDuration = aDuration;
     mVolume = 1.0f;
     mBufferFormat = AUDIO_FORMAT_SILENCE;
     mPrincipalHandle = PRINCIPAL_HANDLE_NONE;
@@ -328,54 +330,63 @@ public:
       }
       mDuration += c.mDuration;
     }
   }
 
   void ResampleChunks(SpeexResamplerState* aResampler,
                       uint32_t aInRate,
                       uint32_t aOutRate);
-
   void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
                     const nsTArray<const float*>& aChannelData,
                     int32_t aDuration, const PrincipalHandle& aPrincipalHandle)
   {
     AudioChunk* chunk = AppendChunk(aDuration);
     chunk->mBuffer = aBuffer;
+
+    MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?");
+
     for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
       chunk->mChannelData.AppendElement(aChannelData[channel]);
     }
     chunk->mBufferFormat = AUDIO_FORMAT_FLOAT32;
 #ifdef MOZILLA_INTERNAL_API
     chunk->mTimeStamp = TimeStamp::Now();
 #endif
     chunk->mPrincipalHandle = aPrincipalHandle;
   }
   void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
                     const nsTArray<const int16_t*>& aChannelData,
                     int32_t aDuration, const PrincipalHandle& aPrincipalHandle)
   {
     AudioChunk* chunk = AppendChunk(aDuration);
     chunk->mBuffer = aBuffer;
+
+    MOZ_ASSERT(chunk->mBuffer || aChannelData.IsEmpty(), "Appending invalid data ?");
+
     for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
       chunk->mChannelData.AppendElement(aChannelData[channel]);
     }
     chunk->mBufferFormat = AUDIO_FORMAT_S16;
 #ifdef MOZILLA_INTERNAL_API
     chunk->mTimeStamp = TimeStamp::Now();
 #endif
     chunk->mPrincipalHandle = aPrincipalHandle;
+
   }
   // Consumes aChunk, and returns a pointer to the persistent copy of aChunk
   // in the segment.
   AudioChunk* AppendAndConsumeChunk(AudioChunk* aChunk)
   {
     AudioChunk* chunk = AppendChunk(aChunk->mDuration);
     chunk->mBuffer = aChunk->mBuffer.forget();
     chunk->mChannelData.SwapElements(aChunk->mChannelData);
+
+    MOZ_ASSERT(chunk->mBuffer || aChunk->mChannelData.IsEmpty(), "Appending invalid data ?");
+
     chunk->mVolume = aChunk->mVolume;
     chunk->mBufferFormat = aChunk->mBufferFormat;
 #ifdef MOZILLA_INTERNAL_API
     chunk->mTimeStamp = TimeStamp::Now();
 #endif
     chunk->mPrincipalHandle = aChunk->mPrincipalHandle;
     return chunk;
   }
--- a/dom/media/StreamTracks.h
+++ b/dom/media/StreamTracks.h
@@ -11,27 +11,27 @@
 #include "TrackID.h"
 
 namespace mozilla {
 
 inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
                                             TrackRate aInRate,
                                             TrackTicks aTicks)
 {
-  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
-  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
-  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
+  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
+  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
+  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
   return (aTicks * aOutRate) / aInRate;
 }
 inline TrackTicks RateConvertTicksRoundUp(TrackRate aOutRate,
                                           TrackRate aInRate, TrackTicks aTicks)
 {
-  NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
-  NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
-  NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
+  MOZ_ASSERT(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
+  MOZ_ASSERT(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
+  MOZ_ASSERT(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
   return (aTicks * aOutRate + aInRate - 1) / aInRate;
 }
 
 /**
  * This object contains the decoded data for a stream's tracks.
  * A StreamTracks can be appended to. Logically a StreamTracks only gets longer,
  * but we also have the ability to "forget" data before a certain time that
  * we know won't be used again. (We prune a whole number of seconds internally.)
--- a/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_mediaElementCapture_audio.html
@@ -11,17 +11,17 @@ createHTML({
   bug: "1259788",
   title: "Test CaptureStream audio content on HTMLMediaElement playing a gUM MediaStream",
   visible: true
 });
 
 var audioContext;
 var gUMAudioElement;
 var analyser;
-runTest(() => getUserMedia({audio: true})
+runTest(() => getUserMedia({audio: { echoCancellation: false }})
   .then(stream => {
     gUMAudioElement = createMediaElement("audio", "gUMAudio");
     gUMAudioElement.srcObject = stream;
 
     audioContext = new AudioContext();
     info("Capturing");
 
     analyser = new AudioStreamAnalyser(audioContext,
--- a/dom/media/webrtc/AudioOutputObserver.h
+++ b/dom/media/webrtc/AudioOutputObserver.h
@@ -12,23 +12,23 @@
 
 namespace webrtc {
 class SingleRwFifo;
 }
 
 namespace mozilla {
 
 typedef struct FarEndAudioChunk_ {
-  uint16_t mSamples;
+  size_t mSamples;
   bool mOverrun;
-  int16_t mData[1]; // variable-length
+  AudioDataValue mData[1]; // variable-length
 } FarEndAudioChunk;
 
 // This class is used to packetize and send the mixed audio from an MSG, in
-// int16, to the AEC module of WebRTC.org.
+// float, to the AEC module of WebRTC.org.
 class AudioOutputObserver
 {
 public:
   AudioOutputObserver();
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioOutputObserver);
 
   void Clear();
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -48,18 +48,16 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
 
   static const int DEFAULT_VIDEO_FPS = 30;
   static const int DEFAULT_43_VIDEO_WIDTH = 640;
   static const int DEFAULT_43_VIDEO_HEIGHT = 480;
   static const int DEFAULT_169_VIDEO_WIDTH = 1280;
   static const int DEFAULT_169_VIDEO_HEIGHT = 720;
 
-  static const int DEFAULT_SAMPLE_RATE = 32000;
-
   // This allows using whatever rate the graph is using for the
   // MediaStreamTrack. This is useful for microphone data, we know it's already
   // at the correct rate for insertion in the MSG.
   static const int USE_GRAPH_RATE = -1;
 
   /* Populate an array of video sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -21,17 +21,16 @@
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsISupportsUtils.h"
 #endif
 
 #ifdef MOZ_WEBRTC
 #include "YuvStamper.h"
 #endif
 
-#define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE
 #define DEFAULT_AUDIO_TIMER_MS 10
 namespace mozilla {
 
 using namespace mozilla::gfx;
 
 NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback, nsINamed)
 /**
  * Default video source.
@@ -329,16 +328,17 @@ MediaEngineDefaultVideoSource::NotifyPul
  * Default audio source.
  */
 
 NS_IMPL_ISUPPORTS0(MediaEngineDefaultAudioSource)
 
 MediaEngineDefaultAudioSource::MediaEngineDefaultAudioSource()
   : MediaEngineAudioSource(kReleased)
   , mLastNotify(0)
+  , mFreq(1000)
 {}
 
 MediaEngineDefaultAudioSource::~MediaEngineDefaultAudioSource()
 {}
 
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName) const
 {
@@ -379,20 +379,18 @@ MediaEngineDefaultAudioSource::Allocate(
   }
 
   // Mock failure for automated tests.
   if (aConstraints.mDeviceId.IsString() &&
       aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) {
     return NS_ERROR_FAILURE;
   }
 
+  mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000;
   mState = kAllocated;
-  // generate sine wave (default 1KHz)
-  mSineGenerator = new SineWaveGenerator(AUDIO_RATE,
-                                         static_cast<uint32_t>(aPrefs.mFreq ? aPrefs.mFreq : 1000));
   *aOutHandle = nullptr;
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Deallocate(AllocationHandle* aHandle)
 {
   MOZ_ASSERT(!aHandle);
@@ -406,19 +404,25 @@ MediaEngineDefaultAudioSource::Deallocat
 nsresult
 MediaEngineDefaultAudioSource::Start(SourceMediaStream* aStream, TrackID aID,
                                      const PrincipalHandle& aPrincipalHandle)
 {
   if (mState != kAllocated) {
     return NS_ERROR_FAILURE;
   }
 
+
+  if (!mSineGenerator) {
+    // generate sine wave (default 1KHz)
+    mSineGenerator = new SineWaveGenerator(aStream->GraphRate(), mFreq);
+  }
+
   // AddTrack will take ownership of segment
   AudioSegment* segment = new AudioSegment();
-  aStream->AddAudioTrack(aID, AUDIO_RATE, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
+  aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
 
   // Remember TrackID so we can finish later
   mTrackID = aID;
 
   mLastNotify = 0;
   mState = kStarted;
   return NS_OK;
 }
@@ -464,17 +468,17 @@ MediaEngineDefaultAudioSource::NotifyPul
                                           SourceMediaStream *aSource,
                                           TrackID aID,
                                           StreamTime aDesiredTime,
                                           const PrincipalHandle& aPrincipalHandle)
 {
   MOZ_ASSERT(aID == mTrackID);
   AudioSegment segment;
   // avoid accumulating rounding errors
-  TrackTicks desired = aSource->TimeToTicksRoundUp(AUDIO_RATE, aDesiredTime);
+  TrackTicks desired = aSource->TimeToTicksRoundUp(aGraph->GraphRate(), aDesiredTime);
   TrackTicks delta = desired - mLastNotify;
   mLastNotify += delta;
   AppendToSegment(segment, delta, aPrincipalHandle);
   aSource->AppendToTrack(mTrackID, &segment);
 }
 
 void
 MediaEngineDefault::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -183,18 +183,19 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
 protected:
   ~MediaEngineDefaultAudioSource();
 
   TrackID mTrackID;
 
   TrackTicks mLastNotify; // Accessed in ::Start(), then on NotifyPull (from MSG thread)
+  uint32_t mFreq; // ditto
 
-  // Created on Allocate, then accessed from NotifyPull (MSG thread)
+  // Created on Start, then accessed from NotifyPull (MSG thread)
   nsAutoPtr<SineWaveGenerator> mSineGenerator;
 };
 
 
 class MediaEngineDefault : public MediaEngine
 {
   typedef MediaEngine Super;
 public:
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -102,17 +102,16 @@ void AudioInputCubeb::UpdateDeviceList()
   StaticMutexAutoLock lock(sMutex);
   // swap state
   cubeb_device_collection_destroy(cubebContext, &mDevices);
   mDevices = devices;
 }
 
 MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
   : mMutex("mozilla::MediaEngineWebRTC"),
-    mVoiceEngine(nullptr),
     mAudioInput(nullptr),
     mFullDuplex(aPrefs.mFullDuplex),
     mDelayAgnostic(aPrefs.mDelayAgnostic),
     mExtendedFilter(aPrefs.mExtendedFilter),
     mHasTabVideoSource(false)
 {
   nsCOMPtr<nsIComponentRegistrar> compMgr;
   NS_GetComponentRegistrar(getter_AddRefs(compMgr));
@@ -275,53 +274,21 @@ MediaEngineWebRTC::EnumerateAudioDevices
 
   if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
       new MediaEngineWebRTCAudioCaptureSource(nullptr);
     aASources->AppendElement(audioCaptureSource);
     return;
   }
 
-#ifdef MOZ_WIDGET_ANDROID
-  JavaVM* jvm = mozilla::jni::GetVM();
-  jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
-
-  if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) {
-    LOG(("VoiceEngine:SetAndroidObjects Failed"));
-    return;
-  }
-#endif
-
-  if (!mVoiceEngine) {
-    mVoiceEngine = webrtc::VoiceEngine::Create();
-    if (!mVoiceEngine) {
+  if (!mAudioInput) {
+    if (!SupportsDuplex()) {
       return;
     }
-  }
-
-  ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-  if (!ptrVoEBase) {
-    return;
-  }
-
-  // Always re-init the voice engine, since if we close the last use we
-  // DeInitEngine() and Terminate(), which shuts down Process() - but means
-  // we have to Init() again before using it.  Init() when already inited is
-  // just a no-op, so call always.
-  if (ptrVoEBase->Init() < 0) {
-    return;
-  }
-
-  if (!mAudioInput) {
-    if (SupportsDuplex()) {
-      // The platform_supports_full_duplex.
-      mAudioInput = new mozilla::AudioInputCubeb(mVoiceEngine);
-    } else {
-      mAudioInput = new mozilla::AudioInputWebRTC(mVoiceEngine);
-    }
+    mAudioInput = new mozilla::AudioInputCubeb();
   }
 
   int nDevices = 0;
   mAudioInput->GetNumOfRecordingDevices(nDevices);
   int i;
 #if defined(MOZ_WIDGET_ANDROID)
   i = 0; // Bug 1037025 - let the OS handle defaulting for now on android/b2g
 #else
@@ -339,36 +306,26 @@ MediaEngineWebRTC::EnumerateAudioDevices
     int error = mAudioInput->GetRecordingDeviceName(i, deviceName, uniqueId);
     if (error) {
       LOG((" AudioInput::GetRecordingDeviceName: Failed %d", error));
       continue;
     }
 
     if (uniqueId[0] == '\0') {
       // Mac and Linux don't set uniqueId!
-      MOZ_ASSERT(sizeof(deviceName) == sizeof(uniqueId)); // total paranoia
       strcpy(uniqueId, deviceName); // safe given assert and initialization/error-check
     }
 
     RefPtr<MediaEngineAudioSource> aSource;
     NS_ConvertUTF8toUTF16 uuid(uniqueId);
     if (mAudioSources.Get(uuid, getter_AddRefs(aSource))) {
       // We've already seen this device, just append.
       aASources->AppendElement(aSource.get());
     } else {
-      AudioInput* audioinput = mAudioInput;
-      if (SupportsDuplex()) {
-        // The platform_supports_full_duplex.
-
-        // For cubeb, it has state (the selected ID)
-        // XXX just use the uniqueID for cubeb and support it everywhere, and get rid of this
-        // XXX Small window where the device list/index could change!
-        audioinput = new mozilla::AudioInputCubeb(mVoiceEngine, i);
-      }
-      aSource = new MediaEngineWebRTCMicrophoneSource(mVoiceEngine, audioinput,
+      aSource = new MediaEngineWebRTCMicrophoneSource(new mozilla::AudioInputCubeb(i),
                                                       i, deviceName, uniqueId,
                                                       mDelayAgnostic, mExtendedFilter);
       mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
       aASources->AppendElement(aSource);
     }
   }
 }
 
@@ -396,20 +353,13 @@ MediaEngineWebRTC::Shutdown()
     MediaEngineAudioSource* source = iter.UserData();
     if (source) {
       source->Shutdown();
     }
   }
   mVideoSources.Clear();
   mAudioSources.Clear();
 
-  if (mVoiceEngine) {
-    mVoiceEngine->SetTraceCallback(nullptr);
-    webrtc::VoiceEngine::Delete(mVoiceEngine);
-  }
-
-  mVoiceEngine = nullptr;
-
   mozilla::camera::Shutdown();
   AudioInputCubeb::CleanupGlobalData();
 }
 
 }
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -136,17 +136,17 @@ protected:
   virtual ~MediaEngineWebRTCAudioCaptureSource() {}
   nsCString mUUID;
 };
 
 // Small subset of VoEHardware
 class AudioInput
 {
 public:
-  explicit AudioInput(webrtc::VoiceEngine* aVoiceEngine) : mVoiceEngine(aVoiceEngine) {};
+  AudioInput() = default;
   // Threadsafe because it's referenced from an MicrophoneSource, which can
   // had references to it on other threads.
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioInput)
 
   virtual int GetNumOfRecordingDevices(int& aDevices) = 0;
   virtual int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128],
                                      char aStrGuidUTF8[128]) = 0;
   virtual int GetRecordingDeviceStatus(bool& aIsAvailable) = 0;
@@ -155,25 +155,23 @@ public:
   virtual void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) = 0;
   virtual void StopRecording(SourceMediaStream *aStream) = 0;
   virtual int SetRecordingDevice(int aIndex) = 0;
   virtual void SetUserChannelCount(uint32_t aChannels) = 0;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~AudioInput() {}
-
-  webrtc::VoiceEngine* mVoiceEngine;
 };
 
 class AudioInputCubeb final : public AudioInput
 {
 public:
-  explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) :
-    AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0)
+  explicit AudioInputCubeb(int aIndex = 0) :
+    AudioInput(), mSelectedDevice(aIndex), mInUseCount(0)
   {
     if (!mDeviceIndexes) {
       mDeviceIndexes = new nsTArray<int>;
       mDeviceNames = new nsTArray<nsCString>;
       mDefaultDevice = -1;
     }
   }
 
@@ -309,24 +307,17 @@ public:
   {
 #ifdef MOZ_WIDGET_ANDROID
     // OpenSL ES does not support enumerating devices.
     MOZ_ASSERT(mDevices.count == 0);
 #else
     MOZ_ASSERT(mDevices.count > 0);
 #endif
 
-    if (mInUseCount == 0) {
-      ScopedCustomReleasePtr<webrtc::VoEExternalMedia> ptrVoEXMedia;
-      ptrVoEXMedia = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
-      if (ptrVoEXMedia) {
-        ptrVoEXMedia->SetExternalRecordingStatus(true);
-      }
-      mAnyInUse = true;
-    }
+    mAnyInUse = true;
     mInUseCount++;
     // Always tell the stream we're using it for input
     aStream->OpenAudioInput(mSelectedDevice, aListener);
   }
 
   void StopRecording(SourceMediaStream *aStream)
   {
     aStream->CloseAudioInput();
@@ -364,87 +355,16 @@ private:
   static int mDefaultDevice; // -1 == not set
   static nsTArray<nsCString>* mDeviceNames;
   static cubeb_device_collection mDevices;
   static bool mAnyInUse;
   static StaticMutex sMutex;
   static uint32_t sUserChannelCount;
 };
 
-class AudioInputWebRTC final : public AudioInput
-{
-public:
-  explicit AudioInputWebRTC(webrtc::VoiceEngine* aVoiceEngine) : AudioInput(aVoiceEngine) {}
-
-  int GetNumOfRecordingDevices(int& aDevices)
-  {
-    ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
-    ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-    if (!ptrVoEBase)  {
-      return 1;
-    }
-    aDevices = ptrVoEBase->audio_device_module()->RecordingDevices();
-    return 0;
-  }
-
-  int GetRecordingDeviceName(int aIndex, char (&aStrNameUTF8)[128],
-                             char aStrGuidUTF8[128])
-  {
-    ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
-    ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-    if (!ptrVoEBase)  {
-      return 1;
-    }
-    return ptrVoEBase->audio_device_module()->RecordingDeviceName(aIndex,
-                                                                  aStrNameUTF8,
-                                                                  aStrGuidUTF8);
-  }
-
-  int GetRecordingDeviceStatus(bool& aIsAvailable)
-  {
-    ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
-    ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-    if (!ptrVoEBase)  {
-      return 1;
-    }
-    return ptrVoEBase->audio_device_module()->RecordingIsAvailable(&aIsAvailable);
-  }
-
-  void GetChannelCount(uint32_t& aChannels)
-  {
-    aChannels = 1; // default to mono
-  }
-
-  int GetMaxAvailableChannels(uint32_t& aChannels)
-  {
-    aChannels = 1;
-    return 0;
-  }
-
-  void SetUserChannelCount(uint32_t aChannels)
-  {}
-
-  void StartRecording(SourceMediaStream *aStream, AudioDataListener *aListener) {}
-  void StopRecording(SourceMediaStream *aStream) {}
-
-  int SetRecordingDevice(int aIndex)
-  {
-    ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
-    ptrVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-    if (!ptrVoEBase)  {
-      return 1;
-    }
-    return ptrVoEBase->audio_device_module()->SetRecordingDevice(aIndex);
-  }
-
-protected:
-  // Protected destructor, to discourage deletion outside of Release():
-  ~AudioInputWebRTC() {}
-};
-
 class WebRTCAudioDataListener : public AudioDataListener
 {
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~WebRTCAudioDataListener() {}
 
 public:
   explicit WebRTCAudioDataListener(MediaEngineAudioSource* aAudioSource)
@@ -485,23 +405,21 @@ public:
     mAudioSource = nullptr;
   }
 
 private:
   Mutex mMutex;
   RefPtr<MediaEngineAudioSource> mAudioSource;
 };
 
-class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource,
-                                          public webrtc::VoEMediaProcess
+class MediaEngineWebRTCMicrophoneSource : public MediaEngineAudioSource
 {
   typedef MediaEngineAudioSource Super;
 public:
-  MediaEngineWebRTCMicrophoneSource(webrtc::VoiceEngine* aVoiceEnginePtr,
-                                    mozilla::AudioInput* aAudioInput,
+  MediaEngineWebRTCMicrophoneSource(mozilla::AudioInput* aAudioInput,
                                     int aIndex,
                                     const char* name,
                                     const char* uuid,
                                     bool aDelayAgnostic,
                                     bool aExtendedFilter);
 
   void GetName(nsAString& aName) const override;
   void GetUUID(nsACString& aUUID) const override;
@@ -545,114 +463,109 @@ public:
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
       const nsString& aDeviceId) const override;
 
-  // VoEMediaProcess.
-  virtual void Process(int channel, webrtc::ProcessingTypes type,
-                       int16_t audio10ms[], size_t length,
-                       int samplingFreq, bool isStereo) override;
-
   void Shutdown() override;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 protected:
   ~MediaEngineWebRTCMicrophoneSource() {}
 
 private:
   nsresult
   UpdateSingleSource(const AllocationHandle* aHandle,
                      const NormalizedConstraints& aNetConstraints,
                      const NormalizedConstraints& aNewConstraint,
                      const MediaEnginePrefs& aPrefs,
                      const nsString& aDeviceId,
                      const char** aOutBadConstraint) override;
 
+
+  void UpdateAECSettingsIfNeeded(bool aEnable, webrtc::EcModes aMode);
+  void UpdateAGCSettingsIfNeeded(bool aEnable, webrtc::AgcModes aMode);
+  void UpdateNSSettingsIfNeeded(bool aEnable, webrtc::NsModes aMode);
+
   void SetLastPrefs(const MediaEnginePrefs& aPrefs);
 
   // These allocate/configure and release the channel
   bool AllocChannel();
   void FreeChannel();
-  // These start/stop VoEBase and associated interfaces
-  bool InitEngine();
-  void DeInitEngine();
-
-  // This is true when all processing is disabled, we can skip
-  // packetization, resampling and other processing passes.
-  bool PassThrough() {
-    return mSkipProcessing;
-  }
   template<typename T>
   void InsertInGraph(const T* aBuffer,
                      size_t aFrames,
                      uint32_t aChannels);
 
   void PacketizeAndProcess(MediaStreamGraph* aGraph,
                            const AudioDataValue* aBuffer,
                            size_t aFrames,
                            TrackRate aRate,
                            uint32_t aChannels);
 
-  webrtc::VoiceEngine* mVoiceEngine;
+
+  // This is true when all processing is disabled, we can skip
+  // packetization, resampling and other processing passes.
+  bool PassThrough() {
+    return mSkipProcessing;
+  }
+  void SetPassThrough(bool aPassThrough) {
+    mSkipProcessing = aPassThrough;
+  }
+
   RefPtr<mozilla::AudioInput> mAudioInput;
   RefPtr<WebRTCAudioDataListener> mListener;
-  RefPtr<AudioOutputObserver> mAudioOutputObserver;
 
-  // Note: shared across all microphone sources - we don't want to Terminate()
-  // the VoEBase until there are no active captures
+  // Note: shared across all microphone sources
   static int sChannelsOpen;
-  static ScopedCustomReleasePtr<webrtc::VoEBase> mVoEBase;
-  static ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
-  static ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
-  static ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
 
+  const UniquePtr<webrtc::AudioProcessing> mAudioProcessing;
+  const RefPtr<AudioOutputObserver> mAudioOutputObserver;
 
   // accessed from the GraphDriver thread except for deletion
-  nsAutoPtr<AudioPacketizer<AudioDataValue, int16_t>> mPacketizer;
+  nsAutoPtr<AudioPacketizer<AudioDataValue, float>> mPacketizer;
   ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERenderListener;
 
   // mMonitor protects mSources[] and mPrinicpalIds[] access/changes, and
   // transitions of mState from kStarted to kStopped (which are combined with
   // EndTrack()). mSources[] and mPrincipalHandles[] are accessed from webrtc
   // threads.
   Monitor mMonitor;
   nsTArray<RefPtr<SourceMediaStream>> mSources;
   nsTArray<PrincipalHandle> mPrincipalHandles; // Maps to mSources.
 
   int mCapIndex;
-  int mChannel;
   bool mDelayAgnostic;
   bool mExtendedFilter;
   MOZ_INIT_OUTSIDE_CTOR TrackID mTrackID;
   bool mStarted;
 
   nsString mDeviceName;
   nsCString mDeviceUUID;
 
   int32_t mSampleFrequency;
   uint64_t mTotalFrames;
   uint64_t mLastLogFrames;
 
-  NullTransport *mNullTransport;
-
-  nsTArray<int16_t> mInputBuffer;
   // mSkipProcessing is true if none of the processing passes are enabled,
   // because of prefs or constraints. This allows simply copying the audio into
   // the MSG, skipping resampling and the whole webrtc.org code.
+  // This is read and written to only on the MSG thread.
   bool mSkipProcessing;
 
   // To only update microphone when needed, we keep track of previous settings.
   MediaEnginePrefs mLastPrefs;
 
-  AlignedShortBuffer mInputDownmixBuffer;
+  AlignedFloatBuffer mInputBuffer;
+  AlignedFloatBuffer mDeinterleavedBuffer;
+  AlignedAudioBuffer mInputDownmixBuffer;
 };
 
 class MediaEngineWebRTC : public MediaEngine
 {
   typedef MediaEngine Super;
 public:
   explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
 
@@ -671,17 +584,16 @@ public:
                              nsTArray<RefPtr<MediaEngineAudioSource>>*) override;
 private:
   ~MediaEngineWebRTC() {}
 
   nsCOMPtr<nsIThread> mThread;
 
   // gUM runnables can e.g. Enumerate from multiple threads
   Mutex mMutex;
-  webrtc::VoiceEngine* mVoiceEngine;
   RefPtr<mozilla::AudioInput> mAudioInput;
   bool mFullDuplex;
   bool mDelayAgnostic;
   bool mExtendedFilter;
   bool mHasTabVideoSource;
 
   // Store devices we've already seen in a hashtable for quick return.
   // Maps UUID to MediaEngineSource (one set for audio, one for video).
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -5,29 +5,28 @@
 #include "MediaEngineWebRTC.h"
 #include <stdio.h>
 #include <algorithm>
 #include "mozilla/Assertions.h"
 #include "MediaTrackConstraints.h"
 #include "mtransport/runnable_utils.h"
 #include "nsAutoPtr.h"
 #include "AudioConverter.h"
+#include "MediaStreamGraphImpl.h"
 
 // scoped_ptr.h uses FF
 #ifdef FF
 #undef FF
 #endif
 #include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
+#include "webrtc/voice_engine/voice_engine_defines.h"
+#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/common_audio/include/audio_util.h"
 
-#define CHANNELS 1
-#define ENCODING "L16"
-#define DEFAULT_PORT 5555
-
-#define SAMPLE_RATE(freq) ((freq)*2*8) // bps, 16-bit samples
-#define SAMPLE_LENGTH(freq) (((freq)*10)/1000)
+using namespace webrtc;
 
 // These are restrictions from the webrtc.org code
 #define MAX_CHANNELS 2
 #define MAX_SAMPLING_FREQ 48000 // Hz - multiple of 100
 
 #define MAX_AEC_FIFO_DEPTH 200 // ms - multiple of 10
 static_assert(!(MAX_AEC_FIFO_DEPTH % 10), "Invalid MAX_AEC_FIFO_DEPTH");
 
@@ -48,31 +47,27 @@ LogModule* AudioLogModule() {
 
 /**
  * Webrtc microphone source source.
  */
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCMicrophoneSource)
 NS_IMPL_ISUPPORTS0(MediaEngineWebRTCAudioCaptureSource)
 
 int MediaEngineWebRTCMicrophoneSource::sChannelsOpen = 0;
-ScopedCustomReleasePtr<webrtc::VoEBase> MediaEngineWebRTCMicrophoneSource::mVoEBase;
-ScopedCustomReleasePtr<webrtc::VoEExternalMedia> MediaEngineWebRTCMicrophoneSource::mVoERender;
-ScopedCustomReleasePtr<webrtc::VoENetwork> MediaEngineWebRTCMicrophoneSource::mVoENetwork;
-ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> MediaEngineWebRTCMicrophoneSource::mVoEProcessing;
 
 AudioOutputObserver::AudioOutputObserver()
   : mPlayoutFreq(0)
   , mPlayoutChannels(0)
   , mChunkSize(0)
   , mSaved(nullptr)
   , mSamplesSaved(0)
   , mDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100)
 {
   // Buffers of 10ms chunks
-  mPlayoutFifo = new webrtc::SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
+  mPlayoutFifo = new SingleRwFifo(MAX_AEC_FIFO_DEPTH/10);
 }
 
 AudioOutputObserver::~AudioOutputObserver()
 {
   Clear();
   free(mSaved);
   mSaved = nullptr;
 }
@@ -138,38 +133,38 @@ AudioOutputObserver::InsertFarEnd(const 
     aOverran = false;
   }
   // Rechunk to 10ms.
   // The AnalyzeReverseStream() and WebRtcAec_BufferFarend() functions insist on 10ms
   // samples per call.  Annoying...
   while (aFrames) {
     if (!mSaved) {
       mSaved = (FarEndAudioChunk *) moz_xmalloc(sizeof(FarEndAudioChunk) +
-                                                (mChunkSize * channels - 1)*sizeof(int16_t));
+                                                (mChunkSize * channels - 1)*sizeof(AudioDataValue));
       mSaved->mSamples = mChunkSize;
       mSaved->mOverrun = aOverran;
       aOverran = false;
     }
     uint32_t to_copy = mChunkSize - mSamplesSaved;
     if (to_copy > aFrames) {
       to_copy = aFrames;
     }
 
-    int16_t* dest = &(mSaved->mData[mSamplesSaved * channels]);
+    AudioDataValue* dest = &(mSaved->mData[mSamplesSaved * channels]);
     if (aChannels > MAX_CHANNELS) {
       AudioConverter converter(AudioConfig(aChannels, 0), AudioConfig(channels, 0));
       converter.Process(mDownmixBuffer, aBuffer, to_copy);
       ConvertAudioSamples(mDownmixBuffer.Data(), dest, to_copy * channels);
     } else {
       ConvertAudioSamples(aBuffer, dest, to_copy * channels);
     }
 
 #ifdef LOG_FAREND_INSERTION
     if (fp) {
-      fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(int16_t), fp);
+      fwrite(&(mSaved->mData[mSamplesSaved * aChannels]), to_copy * aChannels, sizeof(AudioDataValue), fp);
     }
 #endif
     aFrames -= to_copy;
     mSamplesSaved += to_copy;
     aBuffer += to_copy * aChannels;
 
     if (mSamplesSaved >= mChunkSize) {
       int free_slots = mPlayoutFifo->capacity() - mPlayoutFifo->size();
@@ -182,41 +177,38 @@ AudioOutputObserver::InsertFarEnd(const 
         mSaved = nullptr;
         mSamplesSaved = 0;
       }
     }
   }
 }
 
 MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
-    webrtc::VoiceEngine* aVoiceEnginePtr,
     mozilla::AudioInput* aAudioInput,
     int aIndex,
     const char* name,
     const char* uuid,
     bool aDelayAgnostic,
     bool aExtendedFilter)
   : MediaEngineAudioSource(kReleased)
-  , mVoiceEngine(aVoiceEnginePtr)
   , mAudioInput(aAudioInput)
+  , mAudioProcessing(AudioProcessing::Create())
+  , mAudioOutputObserver(new AudioOutputObserver())
   , mMonitor("WebRTCMic.Monitor")
   , mCapIndex(aIndex)
-  , mChannel(-1)
   , mDelayAgnostic(aDelayAgnostic)
   , mExtendedFilter(aExtendedFilter)
   , mTrackID(TRACK_NONE)
   , mStarted(false)
-  , mSampleFrequency(MediaEngine::DEFAULT_SAMPLE_RATE)
+  , mSampleFrequency(MediaEngine::USE_GRAPH_RATE)
   , mTotalFrames(0)
   , mLastLogFrames(0)
-  , mNullTransport(nullptr)
   , mSkipProcessing(false)
   , mInputDownmixBuffer(MAX_SAMPLING_FREQ * MAX_CHANNELS / 100)
 {
-  MOZ_ASSERT(aVoiceEnginePtr);
   MOZ_ASSERT(aAudioInput);
   mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
   mDeviceUUID.Assign(uuid);
   mListener = new mozilla::WebRTCAudioDataListener(this);
   mSettings->mEchoCancellation.Construct(0);
   mSettings->mAutoGainControl.Construct(0);
   mSettings->mNoiseSuppression.Construct(0);
   mSettings->mChannelCount.Construct(0);
@@ -270,16 +262,150 @@ MediaEngineWebRTCMicrophoneSource::Resta
                               aOutBadConstraint);
 }
 
 bool operator == (const MediaEnginePrefs& a, const MediaEnginePrefs& b)
 {
   return !memcmp(&a, &b, sizeof(MediaEnginePrefs));
 };
 
+// This does an early return in case of error.
+#define HANDLE_APM_ERROR(fn)                                \
+do {                                                        \
+  int rv = fn;                                              \
+  if (rv != AudioProcessing::kNoError) {                    \
+    MOZ_ASSERT_UNREACHABLE("APM error in " #fn);            \
+    return;                                                 \
+  }                                                         \
+} while(0);
+
+void MediaEngineWebRTCMicrophoneSource::UpdateAECSettingsIfNeeded(bool aEnable, EcModes aMode)
+{
+  using webrtc::EcModes;
+
+  EchoCancellation::SuppressionLevel level;
+
+  switch(aMode) {
+    case EcModes::kEcUnchanged:
+      level = mAudioProcessing->echo_cancellation()->suppression_level();
+      break;
+    case EcModes::kEcConference:
+      level = EchoCancellation::kHighSuppression;
+      break;
+    case EcModes::kEcDefault:
+      level = EchoCancellation::kModerateSuppression;
+      break;
+    case EcModes::kEcAec:
+      level = EchoCancellation::kModerateSuppression;
+      break;
+    case EcModes::kEcAecm:
+      // No suppression level to set for the mobile echo canceller
+      break;
+    default:
+      MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Bad EcMode value"));
+      MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
+                             " for the echo cancelation mode.");
+      // fall back to something sensible in release
+      level = EchoCancellation::kModerateSuppression;
+      break;
+  }
+
+  // AECm and AEC are mutually exclusive.
+  if (aMode == EcModes::kEcAecm) {
+    HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(false));
+    HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(aEnable));
+  } else {
+    HANDLE_APM_ERROR(mAudioProcessing->echo_control_mobile()->Enable(false));
+    HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->Enable(aEnable));
+    HANDLE_APM_ERROR(mAudioProcessing->echo_cancellation()->set_suppression_level(level));
+  }
+}
+
+void
+MediaEngineWebRTCMicrophoneSource::UpdateAGCSettingsIfNeeded(bool aEnable, AgcModes aMode)
+{
+#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID)
+  if (aMode == kAgcAdaptiveAnalog) {
+    MOZ_LOG(GetMediaManagerLog(),
+            LogLevel::Error,
+            ("Invalid AGC mode kAgcAdaptiveAnalog on mobile"));
+    MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
+                           " for the auto gain, on mobile.");
+    aMode = kAgcDefault;
+  }
+#endif
+  GainControl::Mode mode = kDefaultAgcMode;
+
+  switch (aMode) {
+    case AgcModes::kAgcDefault:
+      mode = kDefaultAgcMode;
+      break;
+    case AgcModes::kAgcUnchanged:
+      mode = mAudioProcessing->gain_control()->mode();
+      break;
+    case AgcModes::kAgcFixedDigital:
+      mode = GainControl::Mode::kFixedDigital;
+      break;
+    case AgcModes::kAgcAdaptiveAnalog:
+      mode = GainControl::Mode::kAdaptiveAnalog;
+      break;
+    case AgcModes::kAgcAdaptiveDigital:
+      mode = GainControl::Mode::kAdaptiveDigital;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
+                             " for the auto gain.");
+      // This is a good fallback, it works regardless of the platform.
+      mode = GainControl::Mode::kAdaptiveDigital;
+      break;
+  }
+
+  HANDLE_APM_ERROR(mAudioProcessing->gain_control()->set_mode(mode));
+  HANDLE_APM_ERROR(mAudioProcessing->gain_control()->Enable(aEnable));
+}
+
+void
+MediaEngineWebRTCMicrophoneSource::UpdateNSSettingsIfNeeded(bool aEnable, NsModes aMode)
+{
+  NoiseSuppression::Level nsLevel;
+
+  switch (aMode) {
+    case NsModes::kNsDefault:
+      nsLevel = kDefaultNsMode;
+      break;
+    case NsModes::kNsUnchanged:
+      nsLevel = mAudioProcessing->noise_suppression()->level();
+      break;
+    case NsModes::kNsConference:
+      nsLevel = NoiseSuppression::kHigh;
+      break;
+    case NsModes::kNsLowSuppression:
+      nsLevel = NoiseSuppression::kLow;
+      break;
+    case NsModes::kNsModerateSuppression:
+      nsLevel = NoiseSuppression::kModerate;
+      break;
+    case NsModes::kNsHighSuppression:
+      nsLevel = NoiseSuppression::kHigh;
+      break;
+    case NsModes::kNsVeryHighSuppression:
+      nsLevel = NoiseSuppression::kVeryHigh;
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("Bad pref set in all.js or in about:config"
+                             " for the noise suppression.");
+      // Pick something sensible as a faillback in release.
+      nsLevel = NoiseSuppression::kModerate;
+  }
+  HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->set_level(nsLevel));
+  HANDLE_APM_ERROR(mAudioProcessing->noise_suppression()->Enable(aEnable));
+}
+
+#undef HANDLE_APM_ERROR
+
 nsresult
 MediaEngineWebRTCMicrophoneSource::UpdateSingleSource(
     const AllocationHandle* aHandle,
     const NormalizedConstraints& aNetConstraints,
     const NormalizedConstraints& aNewConstraint, /* Ignored */
     const MediaEnginePrefs& aPrefs,
     const nsString& aDeviceId,
     const char** aOutBadConstraint)
@@ -313,22 +439,17 @@ MediaEngineWebRTCMicrophoneSource::Updat
       prefs.mAecOn ? prefs.mAec : -1,
       prefs.mAgcOn ? prefs.mAgc : -1,
       prefs.mNoiseOn ? prefs.mNoise : -1,
       prefs.mChannels));
 
   switch (mState) {
     case kReleased:
       MOZ_ASSERT(aHandle);
-      if (sChannelsOpen == 0) {
-        if (!InitEngine()) {
-          LOG(("Audio engine is not initalized"));
-          return NS_ERROR_FAILURE;
-        }
-      } else {
+      if (sChannelsOpen != 0) {
         // Until we fix (or wallpaper) support for multiple mic input
         // (Bug 1238038) fail allocation for a second device
         return NS_ERROR_FAILURE;
       }
       if (mAudioInput->SetRecordingDevice(mCapIndex)) {
          return NS_ERROR_FAILURE;
       }
       mAudioInput->SetUserChannelCount(prefs.mChannels);
@@ -350,29 +471,29 @@ MediaEngineWebRTCMicrophoneSource::Updat
 
     case kStarted:
       if (prefs == mLastPrefs) {
         return NS_OK;
       }
 
       if (prefs.mChannels != mLastPrefs.mChannels) {
         MOZ_ASSERT(mSources.Length() > 0);
+        // If the channel count changed, tell the MSG to open a new driver with
+        // the correct channel count.
         auto& source = mSources.LastElement();
         mAudioInput->SetUserChannelCount(prefs.mChannels);
         // Get validated number of channel
         uint32_t channelCount = 0;
         mAudioInput->GetChannelCount(channelCount);
         MOZ_ASSERT(channelCount > 0 && mLastPrefs.mChannels > 0);
-        // Check if new validated channels is the same as previous
-        if (static_cast<uint32_t>(mLastPrefs.mChannels) != channelCount &&
+        if (mLastPrefs.mChannels != prefs.mChannels &&
             !source->OpenNewAudioCallbackDriver(mListener)) {
+          MOZ_LOG(GetMediaManagerLog(), LogLevel::Error, ("Could not open a new AudioCallbackDriver for input"));
           return NS_ERROR_FAILURE;
         }
-        // Update settings
-        prefs.mChannels = channelCount;
       }
 
       if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
         MonitorAutoLock lock(mMonitor);
         if (mSources.IsEmpty()) {
           LOG(("Audio device %d reallocated", mCapIndex));
         } else {
           LOG(("Audio device %d allocated shared", mCapIndex));
@@ -381,74 +502,72 @@ MediaEngineWebRTCMicrophoneSource::Updat
       break;
 
     default:
       LOG(("Audio device %d in ignored state %d", mCapIndex, mState));
       break;
   }
 
   if (sChannelsOpen > 0) {
-    int error;
+    UpdateAGCSettingsIfNeeded(prefs.mAgcOn, static_cast<AgcModes>(prefs.mAgc));
+    UpdateNSSettingsIfNeeded(prefs.mNoiseOn, static_cast<NsModes>(prefs.mNoise));
+    UpdateAECSettingsIfNeeded(prefs.mAecOn, static_cast<EcModes>(prefs.mAec));
 
-    error = mVoEProcessing->SetEcStatus(prefs.mAecOn, (webrtc::EcModes)prefs.mAec);
-    if (error) {
-      LOG(("%s Error setting Echo Status: %d ",__FUNCTION__, error));
-      // Overhead of capturing all the time is very low (<0.1% of an audio only call)
-      if (prefs.mAecOn) {
-        error = mVoEProcessing->SetEcMetricsStatus(true);
-        if (error) {
-          LOG(("%s Error setting Echo Metrics: %d ",__FUNCTION__, error));
-        }
-      }
-    }
-    error = mVoEProcessing->SetAgcStatus(prefs.mAgcOn, (webrtc::AgcModes)prefs.mAgc);
-    if (error) {
-      LOG(("%s Error setting AGC Status: %d ",__FUNCTION__, error));
-    }
-    error = mVoEProcessing->SetNsStatus(prefs.mNoiseOn, (webrtc::NsModes)prefs.mNoise);
-    if (error) {
-      LOG(("%s Error setting NoiseSuppression Status: %d ",__FUNCTION__, error));
-    }
-  }
-
-  // we don't allow switching from non-fast-path to fast-path on the fly yet
-  if (mState != kStarted) {
-    mSkipProcessing = !(prefs.mAecOn || prefs.mAgcOn || prefs.mNoiseOn);
-    if (mSkipProcessing) {
-      mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
-    } else {
-      // make sure we route a copy of the mixed audio output of this MSG to the
-      // AEC
-      if (!mAudioOutputObserver) {
-        mAudioOutputObserver = new AudioOutputObserver();
-      }
-    }
+    webrtc::Config config;
+    config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter));
+    config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic));
+    mAudioProcessing->SetExtraOptions(config);
   }
   SetLastPrefs(prefs);
   return NS_OK;
 }
 
+#undef HANDLE_APM_ERROR
+
 void
-MediaEngineWebRTCMicrophoneSource::SetLastPrefs(
-    const MediaEnginePrefs& aPrefs)
+MediaEngineWebRTCMicrophoneSource::SetLastPrefs(const MediaEnginePrefs& aPrefs)
 {
   mLastPrefs = aPrefs;
 
   RefPtr<MediaEngineWebRTCMicrophoneSource> that = this;
 
   NS_DispatchToMainThread(media::NewRunnableFrom([that, aPrefs]() mutable {
     that->mSettings->mEchoCancellation.Value() = aPrefs.mAecOn;
     that->mSettings->mAutoGainControl.Value() = aPrefs.mAgcOn;
     that->mSettings->mNoiseSuppression.Value() = aPrefs.mNoiseOn;
     that->mSettings->mChannelCount.Value() = aPrefs.mChannels;
+
+    class Message : public ControlMessage {
+    public:
+      Message(MediaEngineWebRTCMicrophoneSource* aSource,
+              bool aPassThrough)
+        : ControlMessage(nullptr)
+        , mMicrophoneSource(aSource)
+        , mPassThrough(aPassThrough)
+        {}
+
+      void Run() override
+      {
+        mMicrophoneSource->SetPassThrough(mPassThrough);
+      }
+
+    protected:
+      RefPtr<MediaEngineWebRTCMicrophoneSource> mMicrophoneSource;
+      bool mPassThrough;
+    };
+
+    bool passThrough = !(aPrefs.mAecOn || aPrefs.mAgcOn || aPrefs.mNoiseOn);
+    if (!that->mSources.IsEmpty()) {
+      that->mSources[0]->GraphImpl()->AppendMessage(MakeUnique<Message>(that, passThrough));
+    }
+
     return NS_OK;
   }));
 }
 
-
 nsresult
 MediaEngineWebRTCMicrophoneSource::Deallocate(AllocationHandle* aHandle)
 {
   AssertIsOnOwningThread();
 
   Super::Deallocate(aHandle);
 
   if (!mRegisteredHandles.Length()) {
@@ -488,17 +607,17 @@ MediaEngineWebRTCMicrophoneSource::Start
     mPrincipalHandles.AppendElement(aPrincipalHandle);
     MOZ_ASSERT(mSources.Length() == mPrincipalHandles.Length());
   }
 
   AudioSegment* segment = new AudioSegment();
   if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) {
     mSampleFrequency = aStream->GraphRate();
   }
-  aStream->AddAudioTrack(aID, mSampleFrequency, 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
+  aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
 
   // XXX Make this based on the pref.
   aStream->RegisterForAudioMixing();
   LOG(("Start audio for stream %p", aStream));
 
   if (!mListener) {
     mListener = new mozilla::WebRTCAudioDataListener(this);
   }
@@ -509,34 +628,20 @@ MediaEngineWebRTCMicrophoneSource::Start
     return NS_OK;
   }
   mState = kStarted;
   mTrackID = aID;
 
   // Make sure logger starts before capture
   AsyncLatencyLogger::Get(true);
 
-  if (mAudioOutputObserver) {
-    mAudioOutputObserver->Clear();
-  }
-
-  if (mVoEBase->StartReceive(mChannel)) {
-    return NS_ERROR_FAILURE;
-  }
+  mAudioOutputObserver->Clear();
 
-  // Must be *before* StartSend() so it will notice we selected external input (full_duplex)
   mAudioInput->StartRecording(aStream, mListener);
 
-  if (mVoEBase->StartSend(mChannel)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // Attach external media processor, so this::Process will be called.
-  mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
-
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
   AssertIsOnOwningThread();
   {
@@ -555,38 +660,27 @@ MediaEngineWebRTCMicrophoneSource::Stop(
 
     if (!mSources.IsEmpty()) {
       mAudioInput->StopRecording(aSource);
       return NS_OK;
     }
     if (mState != kStarted) {
       return NS_ERROR_FAILURE;
     }
-    if (!mVoEBase) {
-      return NS_ERROR_FAILURE;
-    }
 
     mState = kStopped;
   }
   if (mListener) {
     // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us
     mListener->Shutdown();
     mListener = nullptr;
   }
 
   mAudioInput->StopRecording(aSource);
 
-  mVoERender->DeRegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel);
-
-  if (mVoEBase->StopSend(mChannel)) {
-    return NS_ERROR_FAILURE;
-  }
-  if (mVoEBase->StopReceive(mChannel)) {
-    return NS_ERROR_FAILURE;
-  }
   return NS_OK;
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::NotifyPull(MediaStreamGraph *aGraph,
                                               SourceMediaStream *aSource,
                                               TrackID aID,
                                               StreamTime aDesiredTime,
@@ -598,58 +692,209 @@ MediaEngineWebRTCMicrophoneSource::Notif
 
 void
 MediaEngineWebRTCMicrophoneSource::NotifyOutputData(MediaStreamGraph* aGraph,
                                                     AudioDataValue* aBuffer,
                                                     size_t aFrames,
                                                     TrackRate aRate,
                                                     uint32_t aChannels)
 {
-  if (mAudioOutputObserver) {
+  if (!PassThrough()) {
     mAudioOutputObserver->InsertFarEnd(aBuffer, aFrames, false,
-                                  aRate, aChannels);
+                                       aRate, aChannels);
   }
 }
 
+// Only called if we're not in passthrough mode
 void
 MediaEngineWebRTCMicrophoneSource::PacketizeAndProcess(MediaStreamGraph* aGraph,
                                                        const AudioDataValue* aBuffer,
                                                        size_t aFrames,
                                                        TrackRate aRate,
                                                        uint32_t aChannels)
 {
-  // This will call Process() with data coming out of the AEC/NS/AGC/etc chain
+  MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode.");
+  size_t offset = 0;
+
   if (!mPacketizer ||
       mPacketizer->PacketSize() != aRate/100u ||
       mPacketizer->Channels() != aChannels) {
     // It's ok to drop the audio still in the packetizer here.
     mPacketizer =
-      new AudioPacketizer<AudioDataValue, int16_t>(aRate/100, aChannels);
+      new AudioPacketizer<AudioDataValue, float>(aRate/100, aChannels);
+  }
+
+  // On initial capture, throw away all far-end data except the most recent sample
+  // since it's already irrelevant and we want to keep avoid confusing the AEC far-end
+  // input code with "old" audio.
+  if (!mStarted) {
+    mStarted  = true;
+    while (mAudioOutputObserver->Size() > 1) {
+      free(mAudioOutputObserver->Pop()); // only call if size() > 0
+    }
   }
 
+  // Feed the far-end audio data (speakers) to the feedback input of the AEC.
+  while (mAudioOutputObserver->Size() > 0) {
+    // Bug 1414837: This will call `free()`, and we should remove it.
+    // Pop gives ownership.
+    nsAutoPtr<FarEndAudioChunk> buffer(mAudioOutputObserver->Pop()); // only call if size() > 0
+    if (!buffer) {
+      continue;
+    }
+    AudioDataValue* packetDataPointer = buffer->mData;
+    AutoTArray<float*, MAX_CHANNELS> deinterleavedPacketDataChannelPointers;
+    AudioDataValue* interleavedFarend = nullptr;
+    uint32_t channelCountFarend = 0;
+    uint32_t framesPerPacketFarend = 0;
+
+    // Downmix from aChannels to MAX_CHANNELS if needed
+    if (mAudioOutputObserver->PlayoutChannels() > MAX_CHANNELS) {
+      AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_DEFAULT),
+                               AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_DEFAULT));
+      framesPerPacketFarend =
+        buffer->mSamples;
+      framesPerPacketFarend =
+        converter.Process(mInputDownmixBuffer,
+                          packetDataPointer,
+                          framesPerPacketFarend);
+      interleavedFarend = mInputDownmixBuffer.Data();
+      channelCountFarend = MAX_CHANNELS;
+      deinterleavedPacketDataChannelPointers.SetLength(MAX_CHANNELS);
+    } else {
+      uint32_t outputChannels = mAudioOutputObserver->PlayoutChannels();
+      interleavedFarend = packetDataPointer;
+      channelCountFarend = outputChannels;
+      framesPerPacketFarend = buffer->mSamples;
+      deinterleavedPacketDataChannelPointers.SetLength(outputChannels);
+    }
+
+    MOZ_ASSERT(interleavedFarend &&
+               (channelCountFarend == 1 || channelCountFarend == 2) &&
+               framesPerPacketFarend);
+
+    if (mInputBuffer.Length() < framesPerPacketFarend * channelCountFarend) {
+      mInputBuffer.SetLength(framesPerPacketFarend * channelCountFarend);
+    }
+
+    offset = 0;
+    for (size_t i = 0; i < deinterleavedPacketDataChannelPointers.Length(); ++i) {
+      deinterleavedPacketDataChannelPointers[i] = mInputBuffer.Data() + offset;
+      offset += framesPerPacketFarend;
+    }
+
+    // Deinterleave, prepare a channel pointers array, with enough storage for
+    // the frames.
+    //
+    // If this is a platform that uses s16 for audio input and output,
+    // convert to floats, the APM API we use only accepts floats.
+    DeinterleaveAndConvertBuffer(interleavedFarend,
+                                 framesPerPacketFarend,
+                                 channelCountFarend,
+                                 deinterleavedPacketDataChannelPointers.Elements());
+
+    // Having the same config for input and output means we potentially save
+    // some CPU.
+    StreamConfig inputConfig(mAudioOutputObserver->PlayoutFrequency(),
+                             channelCountFarend,
+                             false /* we don't use typing detection*/);
+    StreamConfig outputConfig = inputConfig;
+
+    // Passing the same pointers here saves a copy inside this function.
+    int err =
+      mAudioProcessing->ProcessReverseStream(deinterleavedPacketDataChannelPointers.Elements(),
+                                             inputConfig,
+                                             outputConfig,
+                                             deinterleavedPacketDataChannelPointers.Elements());
+
+    if (err) {
+      MOZ_LOG(GetMediaManagerLog(), LogLevel::Error,
+          ("error in audio ProcessReverseStream(): %d", err));
+      return;
+    }
+  }
+
+  // Packetize our input data into 10ms chunks, deinterleave into planar channel
+  // buffers, process, and append to the right MediaStreamTrack.
   mPacketizer->Input(aBuffer, static_cast<uint32_t>(aFrames));
 
   while (mPacketizer->PacketsAvailable()) {
     uint32_t samplesPerPacket = mPacketizer->PacketSize() *
       mPacketizer->Channels();
     if (mInputBuffer.Length() < samplesPerPacket) {
       mInputBuffer.SetLength(samplesPerPacket);
     }
-    int16_t* packet = mInputBuffer.Elements();
+    if (mDeinterleavedBuffer.Length() < samplesPerPacket) {
+      mDeinterleavedBuffer.SetLength(samplesPerPacket);
+    }
+    float* packet = mInputBuffer.Data();
     mPacketizer->Output(packet);
 
-    if (aChannels > MAX_CHANNELS) {
-      AudioConverter converter(AudioConfig(aChannels, 0, AudioConfig::FORMAT_S16),
-                               AudioConfig(MAX_CHANNELS, 0, AudioConfig::FORMAT_S16));
-      converter.Process(mInputDownmixBuffer, packet, mPacketizer->PacketSize());
-      mVoERender->ExternalRecordingInsertData(mInputDownmixBuffer.Data(),
-                                              mPacketizer->PacketSize() * MAX_CHANNELS,
-                                              aRate, 0);
-    } else {
-      mVoERender->ExternalRecordingInsertData(packet, samplesPerPacket, aRate, 0);
+    // Deinterleave the input data
+    // Prepare an array pointing to deinterleaved channels.
+    AutoTArray<float*, 8> deinterleavedPacketizedInputDataChannelPointers;
+    deinterleavedPacketizedInputDataChannelPointers.SetLength(aChannels);
+    offset = 0;
+    for (size_t i = 0; i < deinterleavedPacketizedInputDataChannelPointers.Length(); ++i) {
+      deinterleavedPacketizedInputDataChannelPointers[i] = mDeinterleavedBuffer.Data() + offset;
+      offset += mPacketizer->PacketSize();
+    }
+
+    // Deinterleave to mInputBuffer, pointed to by inputBufferChannelPointers.
+    Deinterleave(packet, mPacketizer->PacketSize(), aChannels,
+        deinterleavedPacketizedInputDataChannelPointers.Elements());
+
+    StreamConfig inputConfig(aRate,
+                             aChannels,
+                             false /* we don't use typing detection*/);
+    StreamConfig outputConfig = inputConfig;
+
+    // Bug 1404965: Get the right delay here, it saves some work down the line.
+    mAudioProcessing->set_stream_delay_ms(0);
+
+    // Bug 1414837: find a way to not allocate here.
+    RefPtr<SharedBuffer> buffer =
+      SharedBuffer::Create(mPacketizer->PacketSize() * aChannels * sizeof(float));
+    AudioSegment segment;
+
+    // Prepare channel pointers to the SharedBuffer created above.
+    AutoTArray<float*, 8> processedOutputChannelPointers;
+    AutoTArray<const float*, 8> processedOutputChannelPointersConst;
+    processedOutputChannelPointers.SetLength(aChannels);
+    processedOutputChannelPointersConst.SetLength(aChannels);
+
+    offset = 0;
+    for (size_t i = 0; i < processedOutputChannelPointers.Length(); ++i) {
+      processedOutputChannelPointers[i] = static_cast<float*>(buffer->Data()) + offset;
+      processedOutputChannelPointersConst[i] = static_cast<float*>(buffer->Data()) + offset;
+      offset += mPacketizer->PacketSize();
+    }
+
+    mAudioProcessing->ProcessStream(deinterleavedPacketizedInputDataChannelPointers.Elements(),
+                                    inputConfig,
+                                    outputConfig,
+                                    processedOutputChannelPointers.Elements());
+    MonitorAutoLock lock(mMonitor);
+    if (mState != kStarted)
+      return;
+
+    for (size_t i = 0; i < mSources.Length(); ++i) {
+      if (!mSources[i]) { // why ?!
+        continue;
+      }
+
+      // We already have planar audio data of the right format. Insert into the
+      // MSG.
+      MOZ_ASSERT(processedOutputChannelPointers.Length() == aChannels);
+      RefPtr<SharedBuffer> other = buffer;
+      segment.AppendFrames(other.forget(),
+                           processedOutputChannelPointersConst,
+                           mPacketizer->PacketSize(),
+                           mPrincipalHandles[i]);
+      mSources[i]->AppendToTrack(mTrackID, &segment);
     }
   }
 }
 
 template<typename T>
 void
 MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
                                                  size_t aFrames,
@@ -666,33 +911,33 @@ MediaEngineWebRTCMicrophoneSource::Inser
       MOZ_LOG(AudioLogModule(), LogLevel::Debug,
               ("%p: Inserting %zu samples into graph, total frames = %" PRIu64,
                (void*)this, aFrames, mTotalFrames));
       mLastLogFrames = mTotalFrames;
     }
   }
 
   size_t len = mSources.Length();
-  for (size_t i = 0; i < len; i++) {
+  for (size_t i = 0; i < len; ++i) {
     if (!mSources[i]) {
       continue;
     }
 
     TimeStamp insertTime;
     // Make sure we include the stream and the track.
     // The 0:1 is a flag to note when we've done the final insert for a given input block.
     LogTime(AsyncLatencyLogger::AudioTrackInsertion,
             LATENCY_STREAM_ID(mSources[i].get(), mTrackID),
             (i+1 < len) ? 0 : 1, insertTime);
 
     // Bug 971528 - Support stereo capture in gUM
     MOZ_ASSERT(aChannels >= 1 && aChannels <= 8,
                "Support up to 8 channels");
 
-    nsAutoPtr<AudioSegment> segment(new AudioSegment());
+    AudioSegment segment;
     RefPtr<SharedBuffer> buffer =
       SharedBuffer::Create(aFrames * aChannels * sizeof(T));
     AutoTArray<const T*, 8> channels;
     if (aChannels == 1) {
       PodCopy(static_cast<T*>(buffer->Data()), aBuffer, aFrames);
       channels.AppendElement(static_cast<T*>(buffer->Data()));
     } else {
       channels.SetLength(aChannels);
@@ -708,21 +953,21 @@ MediaEngineWebRTCMicrophoneSource::Inser
 
       DeinterleaveAndConvertBuffer(aBuffer,
                                    aFrames,
                                    aChannels,
                                    write_channels.Elements());
     }
 
     MOZ_ASSERT(aChannels == channels.Length());
-    segment->AppendFrames(buffer.forget(), channels, aFrames,
+    segment.AppendFrames(buffer.forget(), channels, aFrames,
                          mPrincipalHandles[i]);
-    segment->GetStartTime(insertTime);
+    segment.GetStartTime(insertTime);
 
-    mSources[i]->AppendToTrack(mTrackID, segment);
+    mSources[i]->AppendToTrack(mTrackID, &segment);
   }
 }
 
 // Called back on GraphDriver thread!
 // Note this can be called back after ::Shutdown()
 void
 MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
                                                    const AudioDataValue* aBuffer,
@@ -736,174 +981,64 @@ MediaEngineWebRTCMicrophoneSource::Notif
     InsertInGraph<AudioDataValue>(aBuffer, aFrames, aChannels);
   } else {
     PacketizeAndProcess(aGraph, aBuffer, aFrames, aRate, aChannels);
   }
 }
 
 #define ResetProcessingIfNeeded(_processing)                        \
 do {                                                                \
-  webrtc::_processing##Modes mode;                                  \
-  int rv = mVoEProcessing->Get##_processing##Status(enabled, mode); \
-  if (rv) {                                                         \
-    NS_WARNING("Could not get the status of the "                   \
-     #_processing " on device change.");                            \
-    return;                                                         \
-  }                                                                 \
+  bool enabled = mAudioProcessing->_processing()->is_enabled();     \
                                                                     \
   if (enabled) {                                                    \
-    rv = mVoEProcessing->Set##_processing##Status(!enabled);        \
+    int rv = mAudioProcessing->_processing()->Enable(!enabled);     \
+    if (rv) {                                                       \
+      NS_WARNING("Could not reset the status of the "               \
+      #_processing " on device change.");                           \
+      return;                                                       \
+    }                                                               \
+    rv = mAudioProcessing->_processing()->Enable(enabled);          \
     if (rv) {                                                       \
       NS_WARNING("Could not reset the status of the "               \
       #_processing " on device change.");                           \
       return;                                                       \
     }                                                               \
                                                                     \
-    rv = mVoEProcessing->Set##_processing##Status(enabled);         \
-    if (rv) {                                                       \
-      NS_WARNING("Could not reset the status of the "               \
-      #_processing " on device change.");                           \
-      return;                                                       \
-    }                                                               \
   }                                                                 \
 }  while(0)
 
 void
 MediaEngineWebRTCMicrophoneSource::DeviceChanged() {
   // Reset some processing
-  bool enabled;
-  ResetProcessingIfNeeded(Agc);
-  ResetProcessingIfNeeded(Ec);
-  ResetProcessingIfNeeded(Ns);
+  ResetProcessingIfNeeded(gain_control);
+  ResetProcessingIfNeeded(echo_cancellation);
+  ResetProcessingIfNeeded(noise_suppression);
 }
 
-bool
-MediaEngineWebRTCMicrophoneSource::InitEngine()
-{
-  MOZ_ASSERT(!mVoEBase);
-  mVoEBase = webrtc::VoEBase::GetInterface(mVoiceEngine);
-
-  mVoEBase->Init();
-  webrtc::Config config;
-  config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(mExtendedFilter));
-  config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(mDelayAgnostic));
-  mVoEBase->audio_processing()->SetExtraOptions(config);
-
-  mVoERender = webrtc::VoEExternalMedia::GetInterface(mVoiceEngine);
-  if (mVoERender) {
-    mVoENetwork = webrtc::VoENetwork::GetInterface(mVoiceEngine);
-    if (mVoENetwork) {
-      mVoEProcessing = webrtc::VoEAudioProcessing::GetInterface(mVoiceEngine);
-      if (mVoEProcessing) {
-        mNullTransport = new NullTransport();
-        return true;
-      }
-    }
-  }
-  DeInitEngine();
-  return false;
-}
-
-// This shuts down the engine when no channel is open
-void
-MediaEngineWebRTCMicrophoneSource::DeInitEngine()
-{
-  if (mVoEBase) {
-    mVoEBase->Terminate();
-    delete mNullTransport;
-    mNullTransport = nullptr;
-
-    mVoEProcessing = nullptr;
-    mVoENetwork = nullptr;
-    mVoERender = nullptr;
-    mVoEBase = nullptr;
-  }
-}
-
-// This shuts down the engine when no channel is open.
 // mState records if a channel is allocated (slightly redundantly to mChannel)
 void
 MediaEngineWebRTCMicrophoneSource::FreeChannel()
 {
   if (mState != kReleased) {
-    if (mChannel != -1) {
-      MOZ_ASSERT(mVoENetwork && mVoEBase);
-      if (mVoENetwork) {
-        mVoENetwork->DeRegisterExternalTransport(mChannel);
-      }
-      if (mVoEBase) {
-        mVoEBase->DeleteChannel(mChannel);
-      }
-      mChannel = -1;
-    }
     mState = kReleased;
 
     MOZ_ASSERT(sChannelsOpen > 0);
-    if (--sChannelsOpen == 0) {
-      DeInitEngine();
-    }
+    --sChannelsOpen;
   }
 }
 
 bool
 MediaEngineWebRTCMicrophoneSource::AllocChannel()
 {
-  MOZ_ASSERT(mVoEBase);
-
-  mChannel = mVoEBase->CreateChannel();
-  if (mChannel >= 0) {
-    if (!mVoENetwork->RegisterExternalTransport(mChannel, *mNullTransport)) {
-      mSampleFrequency = MediaEngine::DEFAULT_SAMPLE_RATE;
-      LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
-
-      // Check for availability.
-      if (!mAudioInput->SetRecordingDevice(mCapIndex)) {
-        // Because of the permission mechanism of B2G, we need to skip the status
-        // check here.
-        bool avail = false;
-        mAudioInput->GetRecordingDeviceStatus(avail);
-        if (!avail) {
-          if (sChannelsOpen == 0) {
-            DeInitEngine();
-          }
-          return false;
-        }
+  mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
+  LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
 
-        // Set "codec" to PCM, 32kHz on device's channels
-        ScopedCustomReleasePtr<webrtc::VoECodec> ptrVoECodec(webrtc::VoECodec::GetInterface(mVoiceEngine));
-        if (ptrVoECodec) {
-          webrtc::CodecInst codec;
-          strcpy(codec.plname, ENCODING);
-          codec.channels = CHANNELS;
-          uint32_t maxChannels = 0;
-          if (mAudioInput->GetMaxAvailableChannels(maxChannels) == 0) {
-            MOZ_ASSERT(maxChannels);
-            codec.channels = std::min<uint32_t>(maxChannels, MAX_CHANNELS);
-          }
-          MOZ_ASSERT(mSampleFrequency == 16000 || mSampleFrequency == 32000);
-          codec.rate = SAMPLE_RATE(mSampleFrequency);
-          codec.plfreq = mSampleFrequency;
-          codec.pacsize = SAMPLE_LENGTH(mSampleFrequency);
-          codec.pltype = 0; // Default payload type
-
-          if (!ptrVoECodec->SetSendCodec(mChannel, codec)) {
-            mState = kAllocated;
-            sChannelsOpen++;
-            return true;
-          }
-        }
-      }
-    }
-  }
-  mVoEBase->DeleteChannel(mChannel);
-  mChannel = -1;
-  if (sChannelsOpen == 0) {
-    DeInitEngine();
-  }
-  return false;
+  mState = kAllocated;
+  sChannelsOpen++;
+  return true;
 }
 
 void
 MediaEngineWebRTCMicrophoneSource::Shutdown()
 {
   Super::Shutdown();
   if (mListener) {
     // breaks a cycle, since the WebRTCAudioDataListener has a RefPtr to us
@@ -931,56 +1066,16 @@ MediaEngineWebRTCMicrophoneSource::Shutd
   }
 
   while (mRegisteredHandles.Length()) {
     MOZ_ASSERT(mState == kAllocated || mState == kStopped);
     // on last Deallocate(), FreeChannel()s and DeInit()s if all channels are released
     Deallocate(mRegisteredHandles[0].get());
   }
   MOZ_ASSERT(mState == kReleased);
-
-  mAudioInput = nullptr;
-}
-
-typedef int16_t sample;
-
-void
-MediaEngineWebRTCMicrophoneSource::Process(int channel,
-                                           webrtc::ProcessingTypes type,
-                                           sample *audio10ms, size_t length,
-                                           int samplingFreq, bool isStereo)
-{
-  MOZ_ASSERT(!PassThrough(), "This should be bypassed when in PassThrough mode.");
-  // On initial capture, throw away all far-end data except the most recent sample
-  // since it's already irrelevant and we want to keep avoid confusing the AEC far-end
-  // input code with "old" audio.
-  if (!mStarted) {
-    mStarted  = true;
-    while (mAudioOutputObserver->Size() > 1) {
-      free(mAudioOutputObserver->Pop()); // only call if size() > 0
-    }
-  }
-
-  while (mAudioOutputObserver->Size() > 0) {
-    FarEndAudioChunk *buffer = mAudioOutputObserver->Pop(); // only call if size() > 0
-    if (buffer) {
-      int length = buffer->mSamples;
-      int res = mVoERender->ExternalPlayoutData(buffer->mData,
-                                                mAudioOutputObserver->PlayoutFrequency(),
-                                                mAudioOutputObserver->PlayoutChannels(),
-                                                length);
-      free(buffer);
-      if (res == -1) {
-        return;
-      }
-    }
-  }
-
-  uint32_t channels = isStereo ? 2 : 1;
-  InsertInGraph<int16_t>(audio10ms, length, channels);
 }
 
 void
 MediaEngineWebRTCAudioCaptureSource::GetName(nsAString &aName) const
 {
   aName.AssignLiteral("AudioCapture");
 }
 
--- a/dom/media/webrtc/moz.build
+++ b/dom/media/webrtc/moz.build
@@ -34,21 +34,22 @@ if CONFIG['MOZ_WEBRTC']:
         'RTCCertificate.cpp',
         'RTCIdentityProviderRegistrar.cpp',
     ]
     # MediaEngineWebRTC.cpp needs to be built separately.
     SOURCES += [
         'MediaEngineWebRTC.cpp',
     ]
     LOCAL_INCLUDES += [
+        '..',
         '/dom/base',
         '/media/libyuv/libyuv/include',
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/signaling/src/common/browser_logging',
-        '/media/webrtc/trunk',
+        '/media/webrtc/trunk'
     ]
 
 XPIDL_SOURCES += [
     'nsITabSource.idl'
 ]
 
 UNIFIED_SOURCES += [
     'MediaEngineDefault.cpp',
--- a/dom/network/TCPSocket.cpp
+++ b/dom/network/TCPSocket.cpp
@@ -1009,20 +1009,20 @@ NS_IMETHODIMP
 TCPSocket::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
                              int64_t aProgress, int64_t aProgressMax)
 {
   if (static_cast<uint32_t>(aStatus) != nsISocketTransport::STATUS_CONNECTED_TO) {
     return NS_OK;
   }
 
   mReadyState = TCPReadyState::Open;
+  nsresult rv = CreateInputStreamPump();
+  NS_ENSURE_SUCCESS(rv, rv);
   FireEvent(NS_LITERAL_STRING("open"));
 
-  nsresult rv = CreateInputStreamPump();
-  NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TCPSocket::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
   // Only used for detecting if the connection was refused.
 
--- a/editor/libeditor/InternetCiter.cpp
+++ b/editor/libeditor/InternetCiter.cpp
@@ -6,18 +6,17 @@
 #include "InternetCiter.h"
 
 #include "nsAString.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsDebug.h"
 #include "nsDependentSubstring.h"
 #include "nsError.h"
-#include "nsILineBreaker.h"
-#include "nsLWBrkCIID.h"
+#include "mozilla/intl/LineBreaker.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsStringIterator.h"
 
 namespace mozilla {
 
 const char16_t gt ('>');
 const char16_t space (' ');
@@ -162,18 +161,20 @@ InternetCiter::Rewrap(const nsAString& a
 #ifdef DEBUG
   int32_t cr = aInString.FindChar(char16_t('\r'));
   NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n");
 #endif /* DEBUG */
 
   aOutString.Truncate();
 
   nsresult rv;
-  nsCOMPtr<nsILineBreaker> lineBreaker = do_GetService(NS_LBRK_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+
+  RefPtr<mozilla::intl::LineBreaker> lineBreaker =
+    mozilla::intl::LineBreaker::Create();
+  MOZ_ASSERT(lineBreaker);
 
   // Loop over lines in the input string, rewrapping each one.
   uint32_t length;
   uint32_t posInString = 0;
   uint32_t outStringCol = 0;
   uint32_t citeLevel = 0;
   const nsPromiseFlatString &tString = PromiseFlatString(aInString);
   length = tString.Length();
--- a/editor/txtsvc/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/nsTextServicesDocument.cpp
@@ -25,17 +25,17 @@
 #include "nsIEditor.h"                  // for nsIEditor, etc
 #include "nsINode.h"                    // for nsINode
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor
 #include "nsISelection.h"               // for nsISelection
 #include "nsISelectionController.h"     // for nsISelectionController, etc
 #include "nsISupportsBase.h"            // for nsISupports
 #include "nsISupportsUtils.h"           // for NS_IF_ADDREF, NS_ADDREF, etc
 #include "nsITextServicesFilter.h"      // for nsITextServicesFilter
-#include "nsIWordBreaker.h"             // for nsWordRange, nsIWordBreaker
+#include "mozilla/intl/WordBreaker.h"   // for WordRange, WordBreaker
 #include "nsRange.h"                    // for nsRange
 #include "nsStaticAtom.h"               // for NS_STATIC_ATOM_SETUP, etc
 #include "nsString.h"                   // for nsString, nsAutoString
 #include "nsTextServicesDocument.h"
 #include "nscore.h"                     // for nsresult, NS_IMETHODIMP, etc
 
 #define LOCK_DOC(doc)
 #define UNLOCK_DOC(doc)
@@ -3149,18 +3149,18 @@ nsTextServicesDocument::FindWordBounds(n
   uint32_t strOffset = entry->mStrOffset + aNodeOffset - entry->mNodeOffset;
 
   // Now we use the word breaker to find the beginning and end
   // of the word from our calculated string offset.
 
   const char16_t *str = aBlockStr->get();
   uint32_t strLen = aBlockStr->Length();
 
-  nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
-  nsWordRange res = wordBreaker->FindWord(str, strLen, strOffset);
+  mozilla::intl::WordBreaker* wordBreaker = nsContentUtils::WordBreaker();
+  mozilla::intl::WordRange res = wordBreaker->FindWord(str, strLen, strOffset);
   if (res.mBegin > strLen) {
     return str ? NS_ERROR_ILLEGAL_VALUE : NS_ERROR_NULL_POINTER;
   }
 
   // Strip out the NBSPs at the ends
   while (res.mBegin <= res.mEnd && IS_NBSP_CHAR(str[res.mBegin])) {
     res.mBegin++;
   }
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -238,28 +238,30 @@ ContentClient::BeginPaint(PaintedLayer* 
   // types, or we failed to unrotate the buffer when requested. In any case,
   // we need to allocate a new one and prepare it for drawing.
   if (!dest.mCanReuseBuffer) {
     uint32_t bufferFlags = 0;
     if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
       bufferFlags |= BUFFER_COMPONENT_ALPHA;
     }
 
-    RefPtr<RotatedBuffer> newBuffer = CreateBuffer(result.mContentType,
-                                                   dest.mBufferRect,
-                                                   bufferFlags);
+    RefPtr<RotatedBuffer> newBuffer;
+    if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
+      newBuffer = CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags);
 
-    if (!newBuffer) {
-      if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
+      if (!newBuffer) {
         gfxCriticalNote << "Failed buffer for "
                         << dest.mBufferRect.x << ", "
                         << dest.mBufferRect.y << ", "
                         << dest.mBufferRect.Width() << ", "
                         << dest.mBufferRect.Height();
       }
+    }
+
+    if (!newBuffer) {
       Clear();
       return result;
     }
 
     if (!newBuffer->Lock(lockMode)) {
       gfxCriticalNote << "Failed to lock new back buffer.";
       Clear();
       return result;
--- a/intl/build/nsI18nModule.cpp
+++ b/intl/build/nsI18nModule.cpp
@@ -1,61 +1,47 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/ModuleUtils.h"
 
-// lwbrk
-#include "nsLWBrkCIID.h"
-#include "nsJISx4051LineBreaker.h"
-#include "nsSampleWordBreaker.h"
-
 // string bundles (intl)
 #include "nsStringBundleService.h"
 #include "nsStringBundleTextOverride.h"
 
 // locale
 #include "nsLocaleConstructors.h"
 
 // uconv
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsJISx4051LineBreaker)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsSampleWordBreaker)
-
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStringBundleTextOverride, Init)
 
 NS_DEFINE_NAMED_CID(MOZ_LOCALESERVICE_CID);
 NS_DEFINE_NAMED_CID(MOZ_OSPREFERENCES_CID);
-NS_DEFINE_NAMED_CID(NS_LBRK_CID);
-NS_DEFINE_NAMED_CID(NS_WBRK_CID);
 NS_DEFINE_NAMED_CID(NS_STRINGBUNDLESERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STRINGBUNDLETEXTOVERRIDE_CID);
 NS_DEFINE_NAMED_CID(NS_COLLATIONFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
 
 static const mozilla::Module::CIDEntry kIntlCIDs[] = {
     { &kMOZ_LOCALESERVICE_CID, false, nullptr, mozilla::intl::LocaleServiceConstructor },
     { &kMOZ_OSPREFERENCES_CID, false, nullptr, mozilla::intl::OSPreferencesConstructor },
-    { &kNS_LBRK_CID, false, nullptr, nsJISx4051LineBreakerConstructor },
-    { &kNS_WBRK_CID, false, nullptr, nsSampleWordBreakerConstructor },
     { &kNS_STRINGBUNDLESERVICE_CID, false, nullptr, nsStringBundleServiceConstructor },
     { &kNS_STRINGBUNDLETEXTOVERRIDE_CID, false, nullptr, nsStringBundleTextOverrideConstructor },
     { &kNS_COLLATIONFACTORY_CID, false, nullptr, nsCollationFactoryConstructor },
     { &kNS_COLLATION_CID, false, nullptr, nsCollationConstructor },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kIntlContracts[] = {
     { MOZ_LOCALESERVICE_CONTRACTID, &kMOZ_LOCALESERVICE_CID },
     { MOZ_OSPREFERENCES_CONTRACTID, &kMOZ_OSPREFERENCES_CID },
-    { NS_LBRK_CONTRACTID, &kNS_LBRK_CID },
-    { NS_WBRK_CONTRACTID, &kNS_WBRK_CID },
     { NS_STRINGBUNDLE_CONTRACTID, &kNS_STRINGBUNDLESERVICE_CID },
     { NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID, &kNS_STRINGBUNDLETEXTOVERRIDE_CID },
     { NS_COLLATIONFACTORY_CONTRACTID, &kNS_COLLATIONFACTORY_CID },
     { NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
     { nullptr }
 };
 
 static const mozilla::Module kIntlModule = {
rename from intl/lwbrk/nsJISx4051LineBreaker.cpp
rename to intl/lwbrk/LineBreaker.cpp
--- a/intl/lwbrk/nsJISx4051LineBreaker.cpp
+++ b/intl/lwbrk/LineBreaker.cpp
@@ -1,23 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
-
-#include "nsJISx4051LineBreaker.h"
+#include "mozilla/intl/LineBreaker.h"
 
 #include "jisx4051class.h"
 #include "nsComplexBreaker.h"
 #include "nsTArray.h"
 #include "nsUnicodeProperties.h"
 
 using namespace mozilla::unicode;
+using namespace mozilla::intl;
+
+/*static*/
+already_AddRefed<LineBreaker>
+LineBreaker::Create()
+{
+  return RefPtr<LineBreaker>(new LineBreaker()).forget();
+}
 
 /*
 
    Simplification of Pair Table in JIS X 4051
 
    1. The Origion Table - in 4.1.3
 
    In JIS x 4051. The pair table is defined as below
@@ -577,26 +583,16 @@ static bool
 GetPairConservative(int8_t c1, int8_t c2)
 {
   NS_ASSERTION(c1 < MAX_CLASSES ,"illegal classes 1");
   NS_ASSERTION(c2 < MAX_CLASSES ,"illegal classes 2");
 
   return (0 == ((gPairConservative[c1] >> c2) & 0x0001));
 }
 
-nsJISx4051LineBreaker::nsJISx4051LineBreaker()
-{
-}
-
-nsJISx4051LineBreaker::~nsJISx4051LineBreaker()
-{
-}
-
-NS_IMPL_ISUPPORTS(nsJISx4051LineBreaker, nsILineBreaker)
-
 class ContextState {
 public:
   ContextState(const char16_t* aText, uint32_t aLength)
     : mUniText(aText)
     , mText(nullptr)
     , mLength(aLength)
   {
     Init();
@@ -873,18 +869,18 @@ ContextualAnalysis(char32_t prev, char32
       NS_ERROR("Forgot to handle the current character!");
     }
   }
   return GetClass(cur);
 }
 
 
 int32_t
-nsJISx4051LineBreaker::WordMove(const char16_t* aText, uint32_t aLen,
-                                uint32_t aPos, int8_t aDirection)
+LineBreaker::WordMove(const char16_t* aText, uint32_t aLen,
+                      uint32_t aPos, int8_t aDirection)
 {
   bool    textNeedsJISx4051 = false;
   int32_t begin, end;
 
   for (begin = aPos; begin > 0 && !NS_IsSpace(aText[begin - 1]); --begin) {
     if (IS_CJK_CHAR(aText[begin]) || NS_NeedsPlatformNativeHandling(aText[begin])) {
       textNeedsJISx4051 = true;
     }
@@ -902,55 +898,55 @@ nsJISx4051LineBreaker::WordMove(const ch
     // (This is required for serializers. See Bug #344816.)
     // Also fall back to this when out of memory.
     if (aDirection < 0) {
       ret = (begin == int32_t(aPos)) ? begin - 1 : begin;
     } else {
       ret = end;
     }
   } else {
-    GetJISx4051Breaks(aText + begin, end - begin, nsILineBreaker::kWordBreak_Normal,
+    GetJISx4051Breaks(aText + begin, end - begin, LineBreaker::kWordBreak_Normal,
                       breakState.Elements());
 
     ret = aPos;
     do {
       ret += aDirection;
     } while (begin < ret && ret < end && !breakState[ret - begin]);
   }
 
   return ret;
 }
 
 int32_t
-nsJISx4051LineBreaker::Next(const char16_t* aText, uint32_t aLen,
-                            uint32_t aPos)
+LineBreaker::Next(const char16_t* aText, uint32_t aLen,
+                  uint32_t aPos)
 {
   NS_ASSERTION(aText, "aText shouldn't be null");
   NS_ASSERTION(aLen > aPos, "Bad position passed to nsJISx4051LineBreaker::Next");
 
   int32_t nextPos = WordMove(aText, aLen, aPos, 1);
   return nextPos < int32_t(aLen) ? nextPos : NS_LINEBREAKER_NEED_MORE_TEXT;
 }
 
 int32_t
-nsJISx4051LineBreaker::Prev(const char16_t* aText, uint32_t aLen,
-                            uint32_t aPos)
+LineBreaker::Prev(const char16_t* aText, uint32_t aLen,
+                  uint32_t aPos)
 {
   NS_ASSERTION(aText, "aText shouldn't be null");
   NS_ASSERTION(aLen >= aPos && aPos > 0,
                "Bad position passed to nsJISx4051LineBreaker::Prev");
 
   int32_t prevPos = WordMove(aText, aLen, aPos, -1);
   return prevPos > 0 ? prevPos : NS_LINEBREAKER_NEED_MORE_TEXT;
 }
 
 void
-nsJISx4051LineBreaker::GetJISx4051Breaks(const char16_t* aChars, uint32_t aLength,
-                                         uint8_t aWordBreak,
-                                         uint8_t* aBreakBefore)
+LineBreaker::GetJISx4051Breaks(const char16_t* aChars, uint32_t aLength,
+                               uint8_t aWordBreak,
+                               uint8_t* aBreakBefore)
 {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
     char32_t ch = state.GetUnicodeCharAt(cur);
     uint32_t chLen = ch > 0xFFFFu ? 2 : 1;
@@ -981,20 +977,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
       state.NotifyNonHyphenCharacter(ch);
       cl = GetClass(ch);
     }
 
     bool allowBreak = false;
     if (cur > 0) {
       NS_ASSERTION(CLASS_COMPLEX != lastClass || CLASS_COMPLEX != cl,
                    "Loop should have prevented adjacent complex chars here");
-      if (aWordBreak == nsILineBreaker::kWordBreak_Normal) {
+      if (aWordBreak == LineBreaker::kWordBreak_Normal) {
         allowBreak = (state.UseConservativeBreaking()) ?
           GetPairConservative(lastClass, cl) : GetPair(lastClass, cl);
-      } else if (aWordBreak == nsILineBreaker::kWordBreak_BreakAll) {
+      } else if (aWordBreak == LineBreaker::kWordBreak_BreakAll) {
         allowBreak = true;
       }
     }
     aBreakBefore[cur] = allowBreak;
     if (allowBreak)
       state.NotifyBreakBefore();
     lastClass = cl;
     if (CLASS_COMPLEX == cl) {
@@ -1009,20 +1005,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
         if (c > 0xFFFFU) { // it was a surrogate pair
           ++end;
         }
       }
 
       NS_GetComplexLineBreaks(aChars + cur, end - cur, aBreakBefore + cur);
 
       // We have to consider word-break value again for complex characters
-      if (aWordBreak != nsILineBreaker::kWordBreak_Normal) {
+      if (aWordBreak != LineBreaker::kWordBreak_Normal) {
         // Respect word-break property
         for (uint32_t i = cur; i < end; i++)
-          aBreakBefore[i] = (aWordBreak == nsILineBreaker::kWordBreak_BreakAll);
+          aBreakBefore[i] = (aWordBreak == LineBreaker::kWordBreak_BreakAll);
       }
 
       // restore breakability at chunk begin, which was always set to false
       // by the complex line breaker
       aBreakBefore[cur] = allowBreak;
 
       cur = end - 1;
     }
@@ -1033,19 +1029,19 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
       ++cur;
       aBreakBefore[cur] = false;
       state.AdvanceIndex();
     }
   }
 }
 
 void
-nsJISx4051LineBreaker::GetJISx4051Breaks(const uint8_t* aChars, uint32_t aLength,
-                                         uint8_t aWordBreak,
-                                         uint8_t* aBreakBefore)
+LineBreaker::GetJISx4051Breaks(const uint8_t* aChars, uint32_t aLength,
+                               uint8_t aWordBreak,
+                               uint8_t* aBreakBefore)
 {
   uint32_t cur;
   int8_t lastClass = CLASS_NONE;
   ContextState state(aChars, aLength);
 
   for (cur = 0; cur < aLength; ++cur, state.AdvanceIndex()) {
     char32_t ch = aChars[cur];
     int8_t cl;
@@ -1059,20 +1055,20 @@ nsJISx4051LineBreaker::GetJISx4051Breaks
       if (ch == U_EQUAL)
         state.NotifySeenEqualsSign();
       state.NotifyNonHyphenCharacter(ch);
       cl = GetClass(ch);
     }
 
     bool allowBreak = false;
     if (cur > 0) {
-      if (aWordBreak == nsILineBreaker::kWordBreak_Normal) {
+      if (aWordBreak == LineBreaker::kWordBreak_Normal) {
         allowBreak = (state.UseConservativeBreaking()) ?
           GetPairConservative(lastClass, cl) : GetPair(lastClass, cl);
-      } else if (aWordBreak == nsILineBreaker::kWordBreak_BreakAll) {
+      } else if (aWordBreak == LineBreaker::kWordBreak_BreakAll) {
         allowBreak = true;
       }
     }
     aBreakBefore[cur] = allowBreak;
     if (allowBreak)
       state.NotifyBreakBefore();
     lastClass = cl;
   }
rename from intl/lwbrk/nsILineBreaker.h
rename to intl/lwbrk/LineBreaker.h
--- a/intl/lwbrk/nsILineBreaker.h
+++ b/intl/lwbrk/LineBreaker.h
@@ -1,59 +1,60 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsILineBreaker_h__
-#define nsILineBreaker_h__
-
-#include "nsISupports.h"
+#ifndef mozilla_intl_LineBreaker_h__
+#define mozilla_intl_LineBreaker_h__
 
 #include "nscore.h"
+#include "nsISupports.h"
 
 #define NS_LINEBREAKER_NEED_MORE_TEXT -1
 
-// 	{0x4b0b9e04-6ffb-4647-aa5f-2fa2ebd883e8}
-#define NS_ILINEBREAKER_IID \
-{0x4b0b9e04, 0x6ffb, 0x4647, \
-    {0xaa, 0x5f, 0x2f, 0xa2, 0xeb, 0xd8, 0x83, 0xe8}}
+namespace mozilla {
+namespace intl {
 
-class nsILineBreaker : public nsISupports
+class LineBreaker
 {
 public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILINEBREAKER_IID)
+  NS_INLINE_DECL_REFCOUNTING(LineBreaker)
 
   enum {
     kWordBreak_Normal   = 0, // default
     kWordBreak_BreakAll = 1, // break all
     kWordBreak_KeepAll  = 2  // always keep
   };
 
-  virtual int32_t Next( const char16_t* aText, uint32_t aLen,
-                        uint32_t aPos) = 0;
+  static already_AddRefed<LineBreaker> Create();
 
-  virtual int32_t Prev( const char16_t* aText, uint32_t aLen,
-                        uint32_t aPos) = 0;
+  int32_t Next(const char16_t* aText, uint32_t aLen, uint32_t aPos);
+
+  int32_t Prev( const char16_t* aText, uint32_t aLen, uint32_t aPos);
 
   // Call this on a word with whitespace at either end. We will apply JISx4051
   // rules to find breaks inside the word. aBreakBefore is set to the break-
   // before status of each character; aBreakBefore[0] will always be false
   // because we never return a break before the first character.
   // aLength is the length of the aText array and also the length of the aBreakBefore
   // output array.
-  virtual void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength,
-                                 uint8_t aWordBreak,
-                                 uint8_t* aBreakBefore) = 0;
-  virtual void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength,
-                                 uint8_t aWordBreak,
-                                 uint8_t* aBreakBefore) = 0;
+  void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength,
+                         uint8_t aWordBreak,
+                         uint8_t* aBreakBefore);
+  void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength,
+                         uint8_t aWordBreak,
+                         uint8_t* aBreakBefore);
+
+private:
+  ~LineBreaker() { }
+
+  int32_t WordMove(const char16_t* aText, uint32_t aLen, uint32_t aPos,
+                   int8_t aDirection);
 };
 
-NS_DEFINE_STATIC_IID_ACCESSOR(nsILineBreaker, NS_ILINEBREAKER_IID)
-
 static inline bool
 NS_IsSpace(char16_t u)
 {
   return u == 0x0020 ||                  // SPACE
          u == 0x0009 ||                  // CHARACTER TABULATION
          u == 0x000D ||                  // CARRIAGE RETURN
          (0x2000 <= u && u <= 0x2006) || // EN QUAD, EM QUAD, EN SPACE,
                                          // EM SPACE, THREE-PER-EM SPACE,
@@ -67,9 +68,12 @@ NS_IsSpace(char16_t u)
 
 static inline bool
 NS_NeedsPlatformNativeHandling(char16_t aChar)
 {
   return (0x0e01 <= aChar && aChar <= 0x0fff) || // Thai, Lao, Tibetan
          (0x1780 <= aChar && aChar <= 0x17ff);   // Khmer
 }
 
-#endif  /* nsILineBreaker_h__ */
+} // namespace intl
+} // namespace mozilla
+
+#endif  /* mozilla_intl_LineBreaker_h__ */
rename from intl/lwbrk/nsSampleWordBreaker.cpp
rename to intl/lwbrk/WordBreaker.cpp
--- a/intl/lwbrk/nsSampleWordBreaker.cpp
+++ b/intl/lwbrk/WordBreaker.cpp
@@ -1,26 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/intl/WordBreaker.h"
 
-#include "nsSampleWordBreaker.h"
+using mozilla::intl::WordBreaker;
+using mozilla::intl::WordBreakClass;
+using mozilla::intl::WordRange;
 
-nsSampleWordBreaker::nsSampleWordBreaker()
+/*static*/
+already_AddRefed<WordBreaker>
+WordBreaker::Create()
 {
-}
-nsSampleWordBreaker::~nsSampleWordBreaker()
-{
+  return RefPtr<WordBreaker>(new WordBreaker()).forget();
 }
 
-NS_IMPL_ISUPPORTS(nsSampleWordBreaker, nsIWordBreaker)
 
-bool nsSampleWordBreaker::BreakInBetween(
+bool WordBreaker::BreakInBetween(
   const char16_t* aText1 , uint32_t aTextLen1,
   const char16_t* aText2 , uint32_t aTextLen2)
 {
   NS_PRECONDITION( nullptr != aText1, "null ptr");
   NS_PRECONDITION( nullptr != aText2, "null ptr");
 
   if(!aText1 || !aText2 || (0 == aTextLen1) || (0 == aTextLen2))
     return false;
@@ -37,18 +39,18 @@ bool nsSampleWordBreaker::BreakInBetween
 
 // we change the beginning of IS_HAN from 0x4e00 to 0x3400 to relfect Unicode 3.0
 #define IS_HAN(c)              (( 0x3400 <= (c)) && ((c) <= 0x9fff))||(( 0xf900 <= (c)) && ((c) <= 0xfaff))
 #define IS_KATAKANA(c)         (( 0x30A0 <= (c)) && ((c) <= 0x30FF))
 #define IS_HIRAGANA(c)         (( 0x3040 <= (c)) && ((c) <= 0x309F))
 #define IS_HALFWIDTHKATAKANA(c)         (( 0xFF60 <= (c)) && ((c) <= 0xFF9F))
 #define IS_THAI(c)         (0x0E00 == (0xFF80 & (c) )) // Look at the higest 9 bits
 
-/* static */ nsWordBreakClass
-nsIWordBreaker::GetClass(char16_t c)
+/* static */ WordBreakClass
+WordBreaker::GetClass(char16_t c)
 {
   // begin of the hack
 
   if (IS_ALPHABETICAL_SCRIPT(c))  {
 	  if(IS_ASCII(c))  {
 		  if(ASCII_IS_SPACE(c)) {
 			  return kWbClassSpace;
 		  } else if(ASCII_IS_ALPHA(c) || ASCII_IS_DIGIT(c)) {
@@ -71,35 +73,35 @@ nsIWordBreaker::GetClass(char16_t c)
 	  } else if(IS_HIRAGANA(c))   {
 		  return kWbClassHiraganaLetter;
 	  } else if(IS_HALFWIDTHKATAKANA(c))  {
 		  return kWbClassHWKatakanaLetter;
 	  } else  {
 		  return kWbClassAlphaLetter;
 	  }
   }
-  return static_cast<nsWordBreakClass>(0);
+  return static_cast<WordBreakClass>(0);
 }
 
-nsWordRange nsSampleWordBreaker::FindWord(
+WordRange WordBreaker::FindWord(
   const char16_t* aText , uint32_t aTextLen,
   uint32_t aOffset)
 {
-  nsWordRange range;
+  WordRange range;
   NS_PRECONDITION( nullptr != aText, "null ptr");
   NS_PRECONDITION( 0 != aTextLen, "len = 0");
   NS_PRECONDITION( aOffset <= aTextLen, "aOffset > aTextLen");
 
   range.mBegin = aTextLen + 1;
   range.mEnd = aTextLen + 1;
 
   if(!aText || aOffset > aTextLen)
     return range;
 
-  nsWordBreakClass c = GetClass(aText[aOffset]);
+  WordBreakClass c = GetClass(aText[aOffset]);
   uint32_t i;
   // Scan forward
   range.mEnd--;
   for(i = aOffset +1;i <= aTextLen; i++)
   {
      if( c != GetClass(aText[i]))
      {
        range.mEnd = i;
@@ -120,20 +122,20 @@ nsWordRange nsSampleWordBreaker::FindWor
   if(kWbClassThaiLetter == c)
   {
 	// need to call Thai word breaker from here
 	// we should pass the whole Thai segment to the thai word breaker to find a shorter answer
   }
   return range;
 }
 
-int32_t nsSampleWordBreaker::NextWord(
+int32_t WordBreaker::NextWord(
   const char16_t* aText, uint32_t aLen, uint32_t aPos)
 {
-  nsWordBreakClass c1, c2;
+  WordBreakClass c1, c2;
   uint32_t cur = aPos;
   if (cur == aLen)
     return NS_WORDBREAKER_NEED_MORE_TEXT;
   c1 = GetClass(aText[cur]);
 
   for(cur++; cur <aLen; cur++)
   {
      c2 = GetClass(aText[cur]);
rename from intl/lwbrk/nsIWordBreaker.h
rename to intl/lwbrk/WordBreaker.h
--- a/intl/lwbrk/nsIWordBreaker.h
+++ b/intl/lwbrk/WordBreaker.h
@@ -1,53 +1,54 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsIWordBreaker_h__
-#define nsIWordBreaker_h__
-
-#include "nsISupports.h"
+#ifndef mozilla_intl_WordBreaker_h__
+#define mozilla_intl_WordBreaker_h__
 
 #include "nscore.h"
+#include "nsISupports.h"
 
 #define NS_WORDBREAKER_NEED_MORE_TEXT -1
 
-// {E86B3379-BF89-11d2-B3AF-00805F8A6670}
-#define NS_IWORDBREAKER_IID \
-{ 0xe86b3379, 0xbf89, 0x11d2, \
-   { 0xb3, 0xaf, 0x0, 0x80, 0x5f, 0x8a, 0x66, 0x70 } }
+namespace mozilla {
+namespace intl {
 
 typedef struct {
   uint32_t mBegin;
   uint32_t mEnd;
-} nsWordRange;
+} WordRange;
 
-enum nsWordBreakClass : uint8_t {
+enum WordBreakClass : uint8_t {
   kWbClassSpace = 0,
   kWbClassAlphaLetter,
   kWbClassPunct,
   kWbClassHanLetter,
   kWbClassKatakanaLetter,
   kWbClassHiraganaLetter,
   kWbClassHWKatakanaLetter,
   kWbClassThaiLetter
 };
 
-class nsIWordBreaker : public nsISupports
+class WordBreaker
 {
 public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWORDBREAKER_IID)
+  NS_INLINE_DECL_REFCOUNTING(WordBreaker)
+
+  static already_AddRefed<WordBreaker> Create();
 
-  virtual bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1,
-                                const char16_t* aText2 ,
-                                uint32_t aTextLen2) = 0;
-  virtual nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1,
-                               uint32_t aOffset) = 0;
-  virtual int32_t NextWord(const char16_t* aText, uint32_t aLen,
-                           uint32_t aPos) = 0;
+  bool BreakInBetween(const char16_t* aText1, uint32_t aTextLen1,
+                      const char16_t* aText2, uint32_t aTextLen2);
+  WordRange FindWord(const char16_t* aText1, uint32_t aTextLen1,
+                     uint32_t aOffset);
+  int32_t NextWord(const char16_t* aText, uint32_t aLen, uint32_t aPos);
 
-  static nsWordBreakClass GetClass(char16_t aChar);
+  static WordBreakClass GetClass(char16_t aChar);
+
+private:
+  ~WordBreaker() { }
 };
 
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIWordBreaker, NS_IWORDBREAKER_IID)
+} // namespace intl
+} // namespace mozilla
 
-#endif  /* nsIWordBreaker_h__ */
+#endif  /* mozilla_intl_WordBreaker_h__ */
--- a/intl/lwbrk/gtest/TestLineBreak.cpp
+++ b/intl/lwbrk/gtest/TestLineBreak.cpp
@@ -4,24 +4,21 @@
  * 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 <stdio.h>
 #include "nsXPCOM.h"
 #include "nsIComponentManager.h"
 #include "nsISupports.h"
 #include "nsServiceManagerUtils.h"
-#include "nsILineBreaker.h"
-#include "nsIWordBreaker.h"
-#include "nsLWBrkCIID.h"
 #include "nsString.h"
 #include "gtest/gtest.h"
 
-NS_DEFINE_CID(kLBrkCID, NS_LBRK_CID);
-NS_DEFINE_CID(kWBrkCID, NS_WBRK_CID);
+#include "mozilla/intl/LineBreaker.h"
+#include "mozilla/intl/WordBreaker.h"
 
 static char teng1[] =
 //          1         2         3         4         5         6         7
 //01234567890123456789012345678901234567890123456789012345678901234567890123456789
  "This is a test to test(reasonable) line    break. This 0.01123 = 45 x 48.";
 
 static uint32_t lexp1[] = {
   4,7,9,14,17,34,39,40,41,42,49,54,62,64,67,69,73
@@ -103,17 +100,17 @@ Check(const char* in, const uint32_t* ou
     }
     printf("\n");
   }
 
   return ok;
 }
 
 bool
-TestASCIILB(nsILineBreaker *lb,
+TestASCIILB(mozilla::intl::LineBreaker *lb,
             const char* in,
             const uint32_t* out, uint32_t outlen)
 {
   NS_ConvertASCIItoUTF16 eng1(in);
   uint32_t i;
   uint32_t res[256];
   int32_t curr;
 
@@ -123,17 +120,17 @@ TestASCIILB(nsILineBreaker *lb,
     curr = lb->Next(eng1.get(), eng1.Length(), curr);
     res[i] = curr != NS_LINEBREAKER_NEED_MORE_TEXT ? curr : eng1.Length();
   }
 
   return Check(in, out, outlen, i, res);
 }
 
 bool
-TestASCIIWB(nsIWordBreaker *lb,
+TestASCIIWB(mozilla::intl::WordBreaker *lb,
             const char* in,
             const uint32_t* out, uint32_t outlen)
 {
   NS_ConvertASCIItoUTF16 eng1(in);
 
   uint32_t i;
   uint32_t res[256];
   int32_t curr = 0;
@@ -144,64 +141,49 @@ TestASCIIWB(nsIWordBreaker *lb,
     res [i] = curr != NS_WORDBREAKER_NEED_MORE_TEXT ? curr : eng1.Length();
   }
 
   return Check(in, out, outlen, i, res);
 }
 
 TEST(LineBreak, LineBreaker)
 {
-  nsILineBreaker *t = nullptr;
-  nsresult res = CallGetService(kLBrkCID, &t);
-  ASSERT_TRUE(NS_SUCCEEDED(res) && t);
-  NS_IF_RELEASE(t);
+  RefPtr<mozilla::intl::LineBreaker> t = mozilla::intl::LineBreaker::Create();
 
-  res = CallGetService(kLBrkCID, &t);
-  ASSERT_TRUE(NS_SUCCEEDED(res) && t);
+  ASSERT_TRUE(t);
 
   ASSERT_TRUE(TestASCIILB(t, teng1, lexp1, sizeof(lexp1) / sizeof(uint32_t)));
   ASSERT_TRUE(TestASCIILB(t, teng2, lexp2, sizeof(lexp2) / sizeof(uint32_t)));
   ASSERT_TRUE(TestASCIILB(t, teng3, lexp3, sizeof(lexp3) / sizeof(uint32_t)));
-
-  NS_RELEASE(t);
 }
 
 TEST(LineBreak, WordBreaker)
 {
-  nsIWordBreaker *t = nullptr;
-  nsresult res = CallGetService(kWBrkCID, &t);
-  ASSERT_TRUE(NS_SUCCEEDED(res) && t);
-  NS_IF_RELEASE(t);
-
-  res = CallGetService(kWBrkCID, &t);
-  ASSERT_TRUE(NS_SUCCEEDED(res) && t);
+  RefPtr<mozilla::intl::WordBreaker> t = mozilla::intl::WordBreaker::Create();
+  ASSERT_TRUE(t);
 
   ASSERT_TRUE(TestASCIIWB(t, teng1, wexp1, sizeof(wexp1) / sizeof(uint32_t)));
   ASSERT_TRUE(TestASCIIWB(t, teng2, wexp2, sizeof(wexp2) / sizeof(uint32_t)));
   ASSERT_TRUE(TestASCIIWB(t, teng3, wexp3, sizeof(wexp3) / sizeof(uint32_t)));
-
-  NS_RELEASE(t);
 }
 
 //                         012345678901234
 static const char wb0[] = "T";
 static const char wb1[] = "h";
 static const char wb2[] = "is   is a int";
 static const char wb3[] = "ernationali";
 static const char wb4[] = "zation work.";
 
 static const char* wb[] = { wb0, wb1, wb2, wb3, wb4 };
 
 void
 TestPrintWordWithBreak()
 {
   uint32_t numOfFragment = sizeof(wb) / sizeof(char*);
-  nsIWordBreaker* wbk = nullptr;
-
-  CallGetService(kWBrkCID, &wbk);
+  RefPtr<mozilla::intl::WordBreaker> wbk = mozilla::intl::WordBreaker::Create();
 
   nsAutoString result;
 
   for (uint32_t i = 0; i < numOfFragment; i++) {
     NS_ConvertASCIItoUTF16 fragText(wb[i]);
 
     int32_t cur = 0;
     cur = wbk->NextWord(fragText.get(), fragText.Length(), cur);
@@ -226,32 +208,28 @@ TestPrintWordWithBreak()
       if (canBreak) {
         result.Append('^');
       }
       fragText.Assign(nextFragText);
     }
   }
   ASSERT_STREQ("is^   ^is^ ^a^ ^  is a intzation^ ^work^ation work.",
                NS_ConvertUTF16toUTF8(result).get());
-
-  NS_IF_RELEASE(wbk);
 }
 
 void
 TestFindWordBreakFromPosition(uint32_t fragN, uint32_t offset,
                               const char* expected)
 {
   uint32_t numOfFragment = sizeof(wb) / sizeof(char*);
-  nsIWordBreaker* wbk = nullptr;
-
-  CallGetService(kWBrkCID, &wbk);
+  RefPtr<mozilla::intl::WordBreaker> wbk = mozilla::intl::WordBreaker::Create();
 
   NS_ConvertASCIItoUTF16 fragText(wb[fragN]);
 
-  nsWordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset);
+  mozilla::intl::WordRange res = wbk->FindWord(fragText.get(), fragText.Length(), offset);
 
   bool canBreak;
   nsAutoString result(Substring(fragText, res.mBegin, res.mEnd-res.mBegin));
 
   if ((uint32_t)fragText.Length() == res.mEnd) {
     // if we hit the end of the fragment
     nsAutoString curFragText = fragText;
     for(uint32_t  p = fragN +1; p < numOfFragment ;p++)
@@ -259,17 +237,17 @@ TestFindWordBreakFromPosition(uint32_t f
       NS_ConvertASCIItoUTF16 nextFragText(wb[p]);
       canBreak = wbk->BreakInBetween(curFragText.get(),
                                      curFragText.Length(),
                                      nextFragText.get(),
                                      nextFragText.Length());
       if (canBreak) {
         break;
       }
-      nsWordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(),
+      mozilla::intl::WordRange r = wbk->FindWord(nextFragText.get(), nextFragText.Length(),
                                     0);
 
       result.Append(Substring(nextFragText, r.mBegin, r.mEnd - r.mBegin));
 
       if ((uint32_t)nextFragText.Length() != r.mEnd) {
         break;
       }
       nextFragText.Assign(curFragText);
@@ -283,32 +261,30 @@ TestFindWordBreakFromPosition(uint32_t f
       NS_ConvertASCIItoUTF16 prevFragText(wb[p-1]);
       canBreak = wbk->BreakInBetween(prevFragText.get(),
                                      prevFragText.Length(),
                                      curFragText.get(),
                                      curFragText.Length());
       if (canBreak) {
         break;
       }
-      nsWordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(),
+      mozilla::intl::WordRange r = wbk->FindWord(prevFragText.get(), prevFragText.Length(),
                                     prevFragText.Length());
 
       result.Insert(Substring(prevFragText, r.mBegin, r.mEnd - r.mBegin), 0);
 
       if (0 != r.mBegin) {
         break;
       }
       prevFragText.Assign(curFragText);
     }
   }
 
   ASSERT_STREQ(expected, NS_ConvertUTF16toUTF8(result).get())
     << "FindWordBreakFromPosition(" << fragN << ", " << offset << ")";
-
-  NS_IF_RELEASE(wbk);
 }
 
 TEST(LineBreak, WordBreakUsage)
 {
   TestPrintWordWithBreak();
   TestFindWordBreakFromPosition(0, 0, "This");
   TestFindWordBreakFromPosition(1, 0, "his");
   TestFindWordBreakFromPosition(2, 0, "is");
--- a/intl/lwbrk/moz.build
+++ b/intl/lwbrk/moz.build
@@ -1,25 +1,24 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['gtest']
 
-EXPORTS += [
-    'nsILineBreaker.h',
-    'nsIWordBreaker.h',
-    'nsLWBrkCIID.h',
+EXPORTS.mozilla.intl += [
+    'LineBreaker.h',
+    'WordBreaker.h',
 ]
 
 UNIFIED_SOURCES += [
-    'nsJISx4051LineBreaker.cpp',
-    'nsSampleWordBreaker.cpp',
+    'LineBreaker.cpp',
+    'WordBreaker.cpp',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     SOURCES += [
         'nsPangoBreaker.cpp',
     ]
     CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
deleted file mode 100644
--- a/intl/lwbrk/nsJISx4051LineBreaker.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsJISx4051LineBreaker_h__
-#define nsJISx4051LineBreaker_h__
-
-
-#include "nsILineBreaker.h"
-
-class nsJISx4051LineBreaker : public nsILineBreaker
-{
-  NS_DECL_ISUPPORTS
-
-private:
-  virtual ~nsJISx4051LineBreaker();
-
-public:
-  nsJISx4051LineBreaker();
-
-  int32_t Next( const char16_t* aText, uint32_t aLen, uint32_t aPos) override;
-
-  int32_t Prev( const char16_t* aText, uint32_t aLen, uint32_t aPos) override;
-
-  virtual void GetJISx4051Breaks(const char16_t* aText, uint32_t aLength,
-                                 uint8_t aBreakMode,
-                                 uint8_t* aBreakBefore) override;
-  virtual void GetJISx4051Breaks(const uint8_t* aText, uint32_t aLength,
-                                 uint8_t aBreakMode,
-                                 uint8_t* aBreakBefore) override;
-
-private:
-  int32_t WordMove(const char16_t* aText, uint32_t aLen, uint32_t aPos,
-                   int8_t aDirection);
-};
-
-#endif  /* nsJISx4051LineBreaker_h__ */
deleted file mode 100644
--- a/intl/lwbrk/nsSampleWordBreaker.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef nsSampleWordBreaker_h__
-#define nsSampleWordBreaker_h__
-
-
-#include "nsIWordBreaker.h"
-
-class nsSampleWordBreaker : public nsIWordBreaker
-{
-  NS_DECL_ISUPPORTS
-public:
-
-  nsSampleWordBreaker() ;
-
-  bool BreakInBetween(const char16_t* aText1 , uint32_t aTextLen1,
-                        const char16_t* aText2 , uint32_t aTextLen2) override;
-  nsWordRange FindWord(const char16_t* aText1 , uint32_t aTextLen1,
-                       uint32_t aOffset) override;
-
-  int32_t NextWord(const char16_t* aText, uint32_t aLen, uint32_t aPos) override;
-
-protected:
-  virtual ~nsSampleWordBreaker();
-};
-
-#endif  /* nsSampleWordBreaker_h__ */
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1199,17 +1199,17 @@ GlobalObject::initModuleProto(JSContext*
 #undef DEFINE_GETTER_FUNCTIONS
 #undef DEFINE_STRING_ACCESSOR_METHOD
 #undef DEFINE_ARRAY_SLOT_ACCESSOR
 
 ///////////////////////////////////////////////////////////////////////////
 // ModuleBuilder
 
 ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module,
-                             const frontend::TokenStream& tokenStream)
+                             const frontend::TokenStreamAnyChars& tokenStream)
   : cx_(cx),
     module_(cx, module),
     tokenStream_(tokenStream),
     requestedModuleSpecifiers_(cx, AtomSet(cx)),
     requestedModules_(cx, RequestedModuleVector(cx)),
     importedBoundNames_(cx, AtomVector(cx)),
     importEntries_(cx, ImportEntryVector(cx)),
     exportEntries_(cx, ExportEntryVector(cx)),
@@ -1320,17 +1320,17 @@ ModuleBuilder::processImport(frontend::P
         RootedAtom importName(cx_, spec->pn_left->pn_atom);
         RootedAtom localName(cx_, spec->pn_right->pn_atom);
 
         if (!importedBoundNames_.append(localName))
             return false;
 
         uint32_t line;
         uint32_t column;
-        tokenStream_.lineNumAndColumnIndex(spec->pn_left->pn_pos.begin, &line, &column);
+        tokenStream_.lineAndColumnAt(spec->pn_left->pn_pos.begin, &line, &column);
 
         RootedImportEntryObject importEntry(cx_);
         importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column);
         if (!importEntry || !importEntries_.append(importEntry))
             return false;
     }
 
     return true;
@@ -1458,47 +1458,47 @@ ModuleBuilder::hasExportedName(JSAtom* n
 }
 
 bool
 ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName, ParseNode* node)
 {
     uint32_t line = 0;
     uint32_t column = 0;
     if (node)
-        tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
+        tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
 
     Rooted<ExportEntryObject*> exportEntry(cx_);
     exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr, localName,
                                             line, column);
     return exportEntry && exportEntries_.append(exportEntry);
 }
 
 bool
 ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
                                      HandleAtom importName, ParseNode* node)
 {
     uint32_t line;
     uint32_t column;
-    tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
+    tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
 
     Rooted<ExportEntryObject*> exportEntry(cx_);
     exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr,
                                             line, column);
     return exportEntry && exportEntries_.append(exportEntry);
 }
 
 bool
 ModuleBuilder::maybeAppendRequestedModule(HandleAtom specifier, ParseNode* node)
 {
     if (requestedModuleSpecifiers_.has(specifier))
         return true;
 
     uint32_t line;
     uint32_t column;
-    tokenStream_.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
+    tokenStream_.lineAndColumnAt(node->pn_pos.begin, &line, &column);
 
     JSContext* cx = cx_;
     RootedRequestedModuleObject req(cx, RequestedModuleObject::create(cx, specifier, line, column));
     if (!req)
         return false;
 
     return FreezeObject(cx, req) &&
            requestedModules_.append(req) &&
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -21,17 +21,17 @@
 
 namespace js {
 
 class ModuleEnvironmentObject;
 class ModuleObject;
 
 namespace frontend {
 class ParseNode;
-class TokenStream;
+class TokenStreamAnyChars;
 } /* namespace frontend */
 
 typedef Rooted<ModuleObject*> RootedModuleObject;
 typedef Handle<ModuleObject*> HandleModuleObject;
 typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
 typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
 
 class ImportEntryObject : public NativeObject
@@ -345,17 +345,17 @@ class ModuleObject : public NativeObject
 };
 
 // Process a module's parse tree to collate the import and export data used when
 // creating a ModuleObject.
 class MOZ_STACK_CLASS ModuleBuilder
 {
   public:
     explicit ModuleBuilder(JSContext* cx, HandleModuleObject module,
-                           const frontend::TokenStream& tokenStream);
+                           const frontend::TokenStreamAnyChars& tokenStream);
     bool init();
 
     bool processImport(frontend::ParseNode* pn);
     bool processExport(frontend::ParseNode* pn);
     bool processExportFrom(frontend::ParseNode* pn);
 
     bool hasExportedName(JSAtom* name) const;
 
@@ -375,17 +375,17 @@ class MOZ_STACK_CLASS ModuleBuilder
     using RootedAtomVector = JS::Rooted<AtomVector>;
     using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
     using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
     using RootedRequestedModuleVector = JS::Rooted<RequestedModuleVector>;
     using RootedAtomSet = JS::Rooted<AtomSet>;
 
     JSContext* cx_;
     RootedModuleObject module_;
-    const frontend::TokenStream& tokenStream_;
+    const frontend::TokenStreamAnyChars& tokenStream_;
     RootedAtomSet requestedModuleSpecifiers_;
     RootedRequestedModuleVector requestedModules_;
     RootedAtomVector importedBoundNames_;
     RootedImportEntryVector importEntries_;
     RootedExportEntryVector exportEntries_;
     RootedExportEntryVector localExportEntries_;
     RootedExportEntryVector indirectExportEntries_;
     RootedExportEntryVector starExportEntries_;
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -1732,17 +1732,17 @@ class ASTSerializer
     {}
 
     bool init(HandleObject userobj) {
         return builder.init(userobj);
     }
 
     void setParser(Parser<FullParseHandler, char16_t>* p) {
         parser = p;
-        builder.setTokenStream(&p->tokenStream);
+        builder.setTokenStream(&p->anyChars);
     }
 
     bool program(ParseNode* pn, MutableHandleValue dst);
 };
 
 } /* anonymous namespace */
 
 AssignmentOperator
@@ -1909,17 +1909,17 @@ ASTSerializer::blockStatement(ParseNode*
     NodeVector stmts(cx);
     return statements(pn, stmts) &&
            builder.blockStatement(stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::program(ParseNode* pn, MutableHandleValue dst)
 {
-    MOZ_ASSERT(parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) == lineno);
+    MOZ_ASSERT(parser->anyChars.srcCoords.lineNum(pn->pn_pos.begin) == lineno);
 
     NodeVector stmts(cx);
     return statements(pn, stmts) &&
            builder.program(stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::sourceElement(ParseNode* pn, MutableHandleValue dst)
@@ -3486,17 +3486,17 @@ reflect_parse(JSContext* cx, uint32_t ar
     } else {
         if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
             return false;
 
         Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
         if (!module)
             return false;
 
-        ModuleBuilder builder(cx, module, parser.tokenStream);
+        ModuleBuilder builder(cx, module, parser.anyChars);
         if (!builder.init())
             return false;
 
         ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
         pn = parser.moduleBody(&modulesc);
         if (!pn)
             return false;
 
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -327,17 +327,17 @@ function extraRootedPointers()
     return [
         'ModuleValidator',
         'JSErrorResult',
         'WrappableJSErrorResult',
 
         // These are not actually rooted, but are only used in the context of
         // AutoKeepAtoms.
         'js::frontend::TokenStream',
-        'js::frontend::TokenStream::Position',
+        'js::frontend::TokenStreamAnyChars',
 
         'mozilla::ErrorResult',
         'mozilla::IgnoredErrorResult',
         'mozilla::dom::binding_detail::FastErrorResult',
     ];
 }
 
 function isRootedGCPointerTypeName(name)
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -100,18 +100,17 @@ AutoFrontendTraceLog::AutoFrontendTraceL
 {
     // If the tokenizer hasn't yet gotten any tokens, use the line and column
     // numbers from CompileOptions.
     uint32_t line, column;
     if (errorReporter.hasTokenizationStarted()) {
         line = errorReporter.options().lineno;
         column = errorReporter.options().column;
     } else {
-        uint32_t offset = errorReporter.offset();
-        errorReporter.lineNumAndColumnIndex(offset, &line, &column);
+        errorReporter.currentLineAndColumn(&line, &column);
     }
     frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column);
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
@@ -132,17 +131,17 @@ AutoFrontendTraceLog::AutoFrontendTraceL
 #endif
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                                            const ErrorReporter& errorReporter, ParseNode* pn)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     uint32_t line, column;
-    errorReporter.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
+    errorReporter.lineAndColumnAt(pn->pn_pos.begin, &line, &column);
     frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column);
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
 
@@ -274,17 +273,17 @@ BytecodeCompiler::emplaceEmitter(Maybe<B
 bool
 BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
 {
     if (parser->hadAbortedSyntaxParse()) {
         // Hit some unrecoverable ambiguity during an inner syntax parse.
         // Syntax parsing has now been disabled in the parser, so retry
         // the parse.
         parser->clearAbortedSyntaxParse();
-    } else if (parser->tokenStream.hadError() || directives == newDirectives) {
+    } else if (parser->anyChars.hadError() || directives == newDirectives) {
         return false;
     }
 
     parser->tokenStream.seek(startPosition);
 
     // Assignment must be monotonic to prevent reparsing iloops
     MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
     MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
@@ -398,17 +397,17 @@ BytecodeCompiler::compileModule()
     if (!module)
         return nullptr;
 
     if (!createScript())
         return nullptr;
 
     module->init(script);
 
-    ModuleBuilder builder(cx, module, parser->tokenStream);
+    ModuleBuilder builder(cx, module, parser->anyChars);
     if (!builder.init())
         return nullptr;
 
     ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
     ParseNode* pn = parser->moduleBody(&modulesc);
     if (!pn)
         return nullptr;
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3519,18 +3519,18 @@ BytecodeEmitter::maybeSetSourceMap()
 
     /*
      * Source map URLs passed as a compile option (usually via a HTTP source map
      * header) override any source map urls passed as comment pragmas.
      */
     if (parser.options().sourceMapURL()) {
         // Warn about the replacement, but use the new one.
         if (parser.ss()->hasSourceMapURL()) {
-            if (!parser.reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA,
-                                       parser.ss()->filename(), "//# sourceMappingURL"))
+            if (!parser.warningNoOffset(JSMSG_ALREADY_HAS_PRAGMA,
+                                        parser.ss()->filename(), "//# sourceMappingURL"))
             {
                 return false;
             }
         }
 
         if (!parser.ss()->setSourceMapURL(cx, parser.options().sourceMapURL()))
             return false;
     }
@@ -3576,20 +3576,17 @@ BytecodeEmitter::reportError(ParseNode* 
 bool
 BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
 {
     TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
 
     va_list args;
     va_start(args, errorNumber);
 
-    // FIXME: parser.tokenStream() should be a TokenStreamAnyChars for bug 1351107,
-    // but caused problems, cf. bug 1363116.
-    bool result = parser.tokenStream()
-                        .reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, args);
+    bool result = parser.reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::emitNewInit(JSProtoKey key)
 {
--- a/js/src/frontend/EitherParser.h
+++ b/js/src/frontend/EitherParser.h
@@ -11,223 +11,263 @@
 
 #ifndef frontend_EitherParser_h
 #define frontend_EitherParser_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/IndexSequence.h"
 #include "mozilla/Move.h"
 #include "mozilla/Tuple.h"
+#include "mozilla/TypeTraits.h"
 #include "mozilla/Variant.h"
 
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
+
+namespace detail {
+
+template<template <class Parser> class GetThis,
+         template <class This> class MemberFunction,
+         typename... Args>
+struct InvokeMemberFunction
+{
+    mozilla::Tuple<typename mozilla::Decay<Args>::Type...> args;
+
+    template<class This, size_t... Indices>
+    auto
+    matchInternal(This* obj, mozilla::IndexSequence<Indices...>)
+      -> decltype(((*obj).*(MemberFunction<This>::get()))(mozilla::Get<Indices>(args)...))
+    {
+        return ((*obj).*(MemberFunction<This>::get()))(mozilla::Get<Indices>(args)...);
+    }
+
+  public:
+    template<typename... ActualArgs>
+    explicit InvokeMemberFunction(ActualArgs&&... actualArgs)
+      : args { mozilla::Forward<ActualArgs>(actualArgs)... }
+    {}
+
+    template<class Parser>
+    auto
+    match(Parser* parser)
+      -> decltype(this->matchInternal(GetThis<Parser>::get(parser),
+                                      typename mozilla::IndexSequenceFor<Args...>::Type()))
+    {
+        return this->matchInternal(GetThis<Parser>::get(parser),
+                                   typename mozilla::IndexSequenceFor<Args...>::Type());
+    }
+};
+
+// |this|-computing templates.
+
+template<class Parser>
+struct GetParser
+{
+    static Parser* get(Parser* parser) { return parser; }
+};
+
+template<class Parser>
+struct GetParseHandler
+{
+    static auto get(Parser* parser) -> decltype(&parser->handler) {
+        return &parser->handler;
+    }
+};
+
+template<class Parser>
+struct GetTokenStream
+{
+    static auto get(Parser* parser) -> decltype(&parser->tokenStream) {
+        return &parser->tokenStream;
+    }
+};
+
+// Member function-computing templates.
+
+template<class Parser>
+struct ParserOptions
+{
+    static constexpr auto get() -> decltype(&Parser::options) {
+        return &Parser::options;
+    }
+};
+
+template<class TokenStream>
+struct TokenStreamComputeErrorMetadata
+{
+    static constexpr auto get() -> decltype(&TokenStream::computeErrorMetadata) {
+        return &TokenStream::computeErrorMetadata;
+    }
+};
+
+template<class Parser>
+struct ParserNewObjectBox
+{
+    static constexpr auto get() -> decltype(&Parser::newObjectBox) {
+        return &Parser::newObjectBox;
+    }
+};
+
+template<class Handler>
+struct HandlerSingleBindingFromDeclaration
+{
+    static constexpr auto get() -> decltype(&Handler::singleBindingFromDeclaration) {
+        return &Handler::singleBindingFromDeclaration;
+    }
+};
+
+template<class Handler>
+struct HandlerIsDeclarationList
+{
+    static constexpr auto get() -> decltype(&Handler::isDeclarationList) {
+        return &Handler::isDeclarationList;
+    }
+};
+
+template<class Handler>
+struct HandlerIsSuperBase
+{
+    static constexpr auto get() -> decltype(&Handler::isSuperBase) {
+        return &Handler::isSuperBase;
+    }
+};
+
+template<class TokenStream>
+struct TokenStreamReportError
+{
+    static constexpr auto get() -> decltype(&TokenStream::reportError) {
+        return &TokenStream::reportError;
+    }
+};
+
+template<class TokenStream>
+struct TokenStreamReportExtraWarning
+{
+    static constexpr auto get() -> decltype(&TokenStream::reportExtraWarningErrorNumberVA) {
+        return &TokenStream::reportExtraWarningErrorNumberVA;
+    }
+};
+
+// Generic matchers.
+
+struct TokenStreamMatcher
+{
+    template<class Parser>
+    frontend::TokenStreamAnyChars& match(Parser* parser) {
+        return parser->anyChars;
+    }
+};
+
+struct ScriptSourceMatcher
+{
+    template<class Parser>
+    ScriptSource* match(Parser* parser) {
+        return parser->ss;
+    }
+};
+
+struct ParserBaseMatcher
+{
+    template<class Parser>
+    frontend::ParserBase& match(Parser* parser) {
+        return *static_cast<frontend::ParserBase*>(parser);
+    }
+};
+
+} // namespace detail
+
 namespace frontend {
 
 template<class ParseHandler>
 class EitherParser
 {
-    const mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser;
+    mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser;
 
     using Node = typename ParseHandler::Node;
 
+    template<template <class Parser> class GetThis,
+             template <class This> class GetMemberFunction,
+             typename... StoredArgs>
+    using InvokeMemberFunction =
+        detail::InvokeMemberFunction<GetThis, GetMemberFunction, StoredArgs...>;
+
   public:
     template<class Parser>
     explicit EitherParser(Parser* parser) : parser(parser) {}
 
-  private:
-    struct TokenStreamMatcher
-    {
-        template<class Parser>
-        TokenStream& match(Parser* parser) {
-            return parser->tokenStream;
-        }
-    };
-
-  public:
-    TokenStream& tokenStream() {
-        return parser.match(TokenStreamMatcher());
+    TokenStreamAnyChars& tokenStream() {
+        return parser.match(detail::TokenStreamMatcher());
     }
 
-    const TokenStream& tokenStream() const {
-        return parser.match(TokenStreamMatcher());
-    }
-
-  private:
-    struct ScriptSourceMatcher
-    {
-        template<class Parser>
-        ScriptSource* match(Parser* parser) {
-            return parser->ss;
-        }
-    };
-
-  public:
-    ScriptSource* ss() {
-        return parser.match(ScriptSourceMatcher());
+    const TokenStreamAnyChars& tokenStream() const {
+        return parser.match(detail::TokenStreamMatcher());
     }
 
-  private:
-    struct OptionsMatcher
-    {
-        template<class Parser>
-        const JS::ReadOnlyCompileOptions& match(Parser* parser) {
-            return parser->options();
-        }
-    };
-
-  public:
-    const JS::ReadOnlyCompileOptions& options() {
-        return parser.match(OptionsMatcher());
+    ScriptSource* ss() {
+        return parser.match(detail::ScriptSourceMatcher());
     }
 
-  private:
-    struct ComputeErrorMetadataMatcher
-    {
-        ErrorMetadata* metadata;
-        uint32_t offset;
-
-        ComputeErrorMetadataMatcher(ErrorMetadata* metadata, uint32_t offset)
-          : metadata(metadata), offset(offset)
-        {}
-
-        template<class Parser>
-        MOZ_MUST_USE bool match(Parser* parser) {
-            return parser->tokenStream.computeErrorMetadata(metadata, offset);
-        }
-    };
-
-  public:
-    MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* metadata, uint32_t offset) {
-        return parser.match(ComputeErrorMetadataMatcher(metadata, offset));
+    const JS::ReadOnlyCompileOptions& options() {
+        InvokeMemberFunction<detail::GetParser, detail::ParserOptions> optionsMatcher;
+        return parser.match(mozilla::Move(optionsMatcher));
     }
 
-  private:
-    struct NewObjectBoxMatcher
-    {
-        JSObject* obj;
-
-        explicit NewObjectBoxMatcher(JSObject* obj) : obj(obj) {}
-
-        template<class Parser>
-        ObjectBox* match(Parser* parser) {
-            return parser->newObjectBox(obj);
-        }
-    };
-
-  public:
-    ObjectBox* newObjectBox(JSObject* obj) {
-        return parser.match(NewObjectBoxMatcher(obj));
+    MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* metadata, uint32_t offset) {
+        InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamComputeErrorMetadata,
+                             ErrorMetadata*, uint32_t>
+            matcher { metadata, offset };
+        return parser.match(mozilla::Move(matcher));
     }
 
-  private:
-    struct SingleBindingFromDeclarationMatcher
-    {
-        Node decl;
-
-        explicit SingleBindingFromDeclarationMatcher(Node decl) : decl(decl) {}
-
-        template<class Parser>
-        Node match(Parser* parser) {
-            return parser->handler.singleBindingFromDeclaration(decl);
-        }
-    };
-
-  public:
-    Node singleBindingFromDeclaration(Node decl) {
-        return parser.match(SingleBindingFromDeclarationMatcher(decl));
+    ObjectBox* newObjectBox(JSObject* obj) {
+        InvokeMemberFunction<detail::GetParser, detail::ParserNewObjectBox,
+                             JSObject*>
+            matcher { obj };
+        return parser.match(mozilla::Move(matcher));
     }
 
-  private:
-    struct IsDeclarationListMatcher
-    {
-        Node node;
-
-        explicit IsDeclarationListMatcher(Node node) : node(node) {}
-
-        template<class Parser>
-        bool match(Parser* parser) {
-            return parser->handler.isDeclarationList(node);
-        }
-    };
-
-  public:
-    bool isDeclarationList(Node node) {
-        return parser.match(IsDeclarationListMatcher(node));
+    Node singleBindingFromDeclaration(Node decl) {
+        InvokeMemberFunction<detail::GetParseHandler, detail::HandlerSingleBindingFromDeclaration,
+                             Node>
+            matcher { decl };
+        return parser.match(mozilla::Move(matcher));
     }
 
-  private:
-    struct IsSuperBaseMatcher
-    {
-        Node node;
-
-        explicit IsSuperBaseMatcher(Node node) : node(node) {}
-
-        template<class Parser>
-        bool match(Parser* parser) {
-            return parser->handler.isSuperBase(node);
-        }
-    };
-
-  public:
-    bool isSuperBase(Node node) {
-        return parser.match(IsSuperBaseMatcher(node));
+    bool isDeclarationList(Node node) {
+        InvokeMemberFunction<detail::GetParseHandler, detail::HandlerIsDeclarationList,
+                             Node>
+            matcher { node };
+        return parser.match(mozilla::Move(matcher));
     }
 
-  private:
-    template<typename Function, class This, typename... Args, size_t... Indices>
-    static auto
-    CallGenericFunction(Function func, This* obj,
-                        mozilla::Tuple<Args...>& args, mozilla::IndexSequence<Indices...>)
-      -> decltype(((*obj).*func)(mozilla::Get<Indices>(args)...))
-    {
-        return ((*obj).*func)(mozilla::Get<Indices>(args)...);
+    bool isSuperBase(Node node) {
+        InvokeMemberFunction<detail::GetParseHandler, detail::HandlerIsSuperBase,
+                             Node>
+            matcher { node };
+        return parser.match(mozilla::Move(matcher));
     }
 
-    template<typename... StoredArgs>
-    struct ReportErrorMatcher
-    {
-        mozilla::Tuple<StoredArgs...> args;
-
-        template<typename... Args>
-        explicit ReportErrorMatcher(Args&&... actualArgs)
-          : args { mozilla::Forward<Args>(actualArgs)... }
-        {}
-
-        template<class Parser>
-        void match(Parser* parser) {
-            return CallGenericFunction(&TokenStream::reportError,
-                                       &parser->tokenStream,
-                                       args,
-                                       typename mozilla::IndexSequenceFor<StoredArgs...>::Type());
-        }
-    };
-
-  public:
     template<typename... Args>
     void reportError(Args&&... args) {
-        ReportErrorMatcher<typename mozilla::Decay<Args>::Type...>
+        InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamReportError, Args...>
             matcher { mozilla::Forward<Args>(args)... };
         return parser.match(mozilla::Move(matcher));
     }
 
-  private:
-    struct ParserBaseMatcher
-    {
-        template<class Parser>
-        ParserBase& match(Parser* parser) {
-            return *parser;
-        }
-    };
+    template<typename... Args>
+    MOZ_MUST_USE bool warningNoOffset(Args&&... args) {
+        return parser.match(detail::ParserBaseMatcher()).warningNoOffset(mozilla::Forward<Args>(args)...);
+    }
 
-  public:
     template<typename... Args>
-    MOZ_MUST_USE bool reportNoOffset(Args&&... args) {
-        return parser.match(ParserBaseMatcher()).reportNoOffset(mozilla::Forward<Args>(args)...);
+    MOZ_MUST_USE bool reportExtraWarningErrorNumberVA(Args&&... args) {
+        InvokeMemberFunction<detail::GetTokenStream, detail::TokenStreamReportExtraWarning, Args...>
+            matcher { mozilla::Forward<Args>(args)... };
+        return parser.match(mozilla::Move(matcher));
     }
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_EitherParser_h */
--- a/js/src/frontend/ErrorReporter.h
+++ b/js/src/frontend/ErrorReporter.h
@@ -1,21 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 frontend_ErrorReporter_h
 #define frontend_ErrorReporter_h
 
+#include <stdarg.h> // for va_list
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t
+
+#include "jsapi.h" // for JS::ReadOnlyCompileOptions
+
 namespace js {
 namespace frontend {
 
 class ErrorReporter
 {
   public:
-    virtual const ReadOnlyCompileOptions& options() const = 0;
-    virtual void lineNumAndColumnIndex(size_t offset, uint32_t* line, uint32_t* column) const = 0;
-    virtual size_t offset() const = 0;
+    virtual const JS::ReadOnlyCompileOptions& options() const = 0;
+
+    virtual void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const = 0;
+    virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const = 0;
+
     virtual bool hasTokenizationStarted() const = 0;
-    virtual void reportErrorNoOffset(unsigned errorNumber, ...) = 0;
+    virtual void reportErrorNoOffsetVA(unsigned errorNumber, va_list args) = 0;
     virtual const char* getFilename() const = 0;
+
+    void reportErrorNoOffset(unsigned errorNumber, ...) {
+        va_list args;
+        va_start(args, errorNumber);
+
+        reportErrorNoOffsetVA(errorNumber, args);
+
+        va_end(args);
+    }
 };
 
 } // namespace frontend
 } // namespace js
 
 #endif // frontend_ErrorReporter_h
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -492,20 +492,21 @@ Boolish(ParseNode* pn)
       }
 
       default:
         return Unknown;
     }
 }
 
 static bool
-Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser);
+Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser);
 
 static bool
-FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldCondition(JSContext* cx, ParseNode** nodePtr,
+              GeneralParser<FullParseHandler, char16_t>& parser)
 {
     // Conditions fold like any other expression...
     if (!Fold(cx, nodePtr, parser))
         return false;
 
     // ...but then they sometimes can be further folded to constants.
     ParseNode* node = *nodePtr;
     Truthiness t = Boolish(node);
@@ -525,17 +526,17 @@ FoldCondition(JSContext* cx, ParseNode**
         }
         node->setArity(PN_NULLARY);
     }
 
     return true;
 }
 
 static bool
-FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldTypeOfExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -560,17 +561,17 @@ FoldTypeOfExpr(JSContext* cx, ParseNode*
         node->setOp(JSOP_NOP);
         node->pn_atom = result;
     }
 
     return true;
 }
 
 static bool
-FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteExpr(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
 
@@ -582,17 +583,18 @@ FoldDeleteExpr(JSContext* cx, ParseNode*
         node->setArity(PN_NULLARY);
         node->setOp(JSOP_TRUE);
     }
 
     return true;
 }
 
 static bool
-FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteElement(JSContext* cx, ParseNode* node,
+                  GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
@@ -606,17 +608,18 @@ FoldDeleteElement(JSContext* cx, ParseNo
     MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT));
     if (expr->isKind(PNK_DOT))
         node->setKind(PNK_DELETEPROP);
 
     return true;
 }
 
 static bool
-FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDeleteProperty(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
     MOZ_ASSERT(node->isArity(PN_UNARY));
     MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT));
 
     ParseNode*& expr = node->pn_kid;
 #ifdef DEBUG
     ParseNodeKind oldKind = expr->getKind();
@@ -627,17 +630,17 @@ FoldDeleteProperty(JSContext* cx, ParseN
 
     MOZ_ASSERT(expr->isKind(oldKind),
                "kind should have remained invariant under folding");
 
     return true;
 }
 
 static bool
-FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldNot(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_NOT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!FoldCondition(cx, &expr, parser))
         return false;
 
@@ -661,17 +664,18 @@ FoldNot(JSContext* cx, ParseNode* node, 
         node->setArity(PN_NULLARY);
         node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
     }
 
     return true;
 }
 
 static bool
-FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldUnaryArithmetic(JSContext* cx, ParseNode* node,
+                    GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
                "need a different method for this node kind");
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& expr = node->pn_kid;
     if (!Fold(cx, &expr, parser))
         return false;
@@ -694,37 +698,38 @@ FoldUnaryArithmetic(JSContext* cx, Parse
         node->setArity(PN_NULLARY);
         node->pn_dval = d;
     }
 
     return true;
 }
 
 static bool
-FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldIncrementDecrement(JSContext* cx, ParseNode* node,
+                       GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
                node->isKind(PNK_POSTINCREMENT) ||
                node->isKind(PNK_PREDECREMENT) ||
                node->isKind(PNK_POSTDECREMENT));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     ParseNode*& target = node->pn_kid;
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
 
     if (!Fold(cx, &target, parser))
         return false;
 
-    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
+    MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, PermitAssignmentToFunctionCalls));
 
     return true;
 }
 
 static bool
-FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldAndOr(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     bool isOrNode = node->isKind(PNK_OR);
     ParseNode** elem = &node->pn_head;
@@ -796,17 +801,18 @@ FoldAndOr(JSContext* cx, ParseNode** nod
         node->setArity(PN_NULLARY);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldConditional(JSContext* cx, ParseNode** nodePtr,
+                GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode** nextNode = nodePtr;
 
     do {
         // |nextNode| on entry points to the C?T:F expression to be folded.
         // Reset it to exit the loop in the common case where F isn't another
         // ?: expression.
         nodePtr = nextNode;
@@ -865,17 +871,17 @@ FoldConditional(JSContext* cx, ParseNode
 
         parser.freeTree(discarded);
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldIf(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode** nextNode = nodePtr;
 
     do {
         // |nextNode| on entry points to the initial |if| to be folded.  Reset
         // it to exit the loop when the |else| arm isn't another |if|.
         nodePtr = nextNode;
         nextNode = nullptr;
@@ -966,17 +972,17 @@ FoldIf(JSContext* cx, ParseNode** nodePt
             parser.freeTree(node);
         }
     } while (nextNode);
 
     return true;
 }
 
 static bool
-FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldFunction(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_FUNCTION));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (node->pn_funbox->useAsmOrInsideUseAsm())
         return true;
@@ -1028,28 +1034,29 @@ ComputeBinary(ParseNodeKind kind, double
     MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
 
     int32_t i = ToInt32(left);
     uint32_t j = ToUint32(right) & 31;
     return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
 }
 
 static bool
-FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldModule(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_MODULE));
     MOZ_ASSERT(node->isArity(PN_CODE));
 
     ParseNode*& moduleBody = node->pn_body;
     MOZ_ASSERT(moduleBody);
     return Fold(cx, &moduleBody, parser);
 }
 
 static bool
-FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldBinaryArithmetic(JSContext* cx, ParseNode* node,
+                     GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_SUB) ||
                node->isKind(PNK_STAR) ||
                node->isKind(PNK_LSH) ||
                node->isKind(PNK_RSH) ||
                node->isKind(PNK_URSH) ||
                node->isKind(PNK_DIV) ||
                node->isKind(PNK_MOD));
@@ -1110,17 +1117,18 @@ FoldBinaryArithmetic(JSContext* cx, Pars
             parser.freeTree(elem);
         }
     }
 
     return true;
 }
 
 static bool
-FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldExponentiation(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_POW));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Fold each operand, ideally into a number.
     ParseNode** listp = &node->pn_head;
     for (; *listp; listp = &(*listp)->pn_next) {
@@ -1153,17 +1161,17 @@ FoldExponentiation(JSContext* cx, ParseN
     node->setKind(PNK_NUMBER);
     node->setArity(PN_NULLARY);
     node->setOp(JSOP_DOUBLE);
     node->pn_dval = ecmaPow(d1, d2);
     return true;
 }
 
 static bool
-FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser)
+FoldList(JSContext* cx, ParseNode* list, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(list->isArity(PN_LIST));
 
     ParseNode** elem = &list->pn_head;
     for (; *elem; elem = &(*elem)->pn_next) {
         if (!Fold(cx, elem, parser))
             return false;
     }
@@ -1172,31 +1180,31 @@ FoldList(JSContext* cx, ParseNode* list,
     list->pn_tail = elem;
 
     list->checkListConsistency();
 
     return true;
 }
 
 static bool
-FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldReturn(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_RETURN));
     MOZ_ASSERT(node->isArity(PN_UNARY));
 
     if (ParseNode*& expr = node->pn_kid) {
         if (!Fold(cx, &expr, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldTry(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& statements = node->pn_kid1;
     if (!Fold(cx, &statements, parser))
         return false;
 
@@ -1209,17 +1217,17 @@ FoldTry(JSContext* cx, ParseNode* node, 
         if (!Fold(cx, &finally, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldCatch(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CATCH));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     if (ParseNode*& declPattern = node->pn_left) {
         if (!Fold(cx, &declPattern, parser))
             return false;
     }
@@ -1228,17 +1236,17 @@ FoldCatch(JSContext* cx, ParseNode* node
         if (!Fold(cx, &statements, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldClass(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CLASS));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& classNames = node->pn_kid1) {
         if (!Fold(cx, &classNames, parser))
             return false;
     }
@@ -1248,17 +1256,17 @@ FoldClass(JSContext* cx, ParseNode* node
             return false;
     }
 
     ParseNode*& body = node->pn_kid3;
     return Fold(cx, &body, parser);
 }
 
 static bool
-FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldElement(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ELEM));
     MOZ_ASSERT(node->isArity(PN_BINARY));
 
     ParseNode*& expr = node->pn_left;
     if (!Fold(cx, &expr, parser))
@@ -1316,17 +1324,17 @@ FoldElement(JSContext* cx, ParseNode** n
     node->setArity(PN_UNARY);
     node->pn_kid = key;
     parser.freeTree(node);
 
     return true;
 }
 
 static bool
-FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
+FoldAdd(JSContext* cx, ParseNode** nodePtr, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     ParseNode* node = *nodePtr;
 
     MOZ_ASSERT(node->isKind(PNK_ADD));
     MOZ_ASSERT(node->isArity(PN_LIST));
     MOZ_ASSERT(node->pn_count >= 2);
 
     // Generically fold all operands first.
@@ -1466,17 +1474,17 @@ FoldAdd(JSContext* cx, ParseNode** nodeP
         node->setOp(JSOP_TRUE);
         parser.freeTree(node);
     }
 
     return true;
 }
 
 static bool
-FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldCall(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
                node->isKind(PNK_TAGGED_TEMPLATE));
     MOZ_ASSERT(node->isArity(PN_LIST));
 
     // Don't fold a parenthesized callable component in an invocation, as this
     // might cause a different |this| value to be used, changing semantics:
     //
@@ -1500,28 +1508,28 @@ FoldCall(JSContext* cx, ParseNode* node,
     // If the last node in the list was replaced, pn_tail points into the wrong node.
     node->pn_tail = listp;
 
     node->checkListConsistency();
     return true;
 }
 
 static bool
-FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldForInOrOf(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
     MOZ_ASSERT(!node->pn_kid2);
 
     return Fold(cx, &node->pn_kid1, parser) &&
            Fold(cx, &node->pn_kid3, parser);
 }
 
 static bool
-FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldForHead(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_FORHEAD));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     if (ParseNode*& init = node->pn_kid1) {
         if (!Fold(cx, &init, parser))
             return false;
     }
@@ -1540,46 +1548,47 @@ FoldForHead(JSContext* cx, ParseNode* no
         if (!Fold(cx, &update, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
-FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldDottedProperty(JSContext* cx, ParseNode* node,
+                   GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_DOT));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     // Iterate through a long chain of dotted property accesses to find the
     // most-nested non-dotted property node, then fold that.
     ParseNode** nested = &node->pn_expr;
     while ((*nested)->isKind(PNK_DOT)) {
         MOZ_ASSERT((*nested)->isArity(PN_NAME));
         nested = &(*nested)->pn_expr;
     }
 
     return Fold(cx, nested, parser);
 }
 
 static bool
-FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
+FoldName(JSContext* cx, ParseNode* node, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_NAME));
     MOZ_ASSERT(node->isArity(PN_NAME));
 
     if (!node->pn_expr)
         return true;
 
     return Fold(cx, &node->pn_expr, parser);
 }
 
 bool
-Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser)
+Fold(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, char16_t>& parser)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     ParseNode* pn = *pnp;
 
     switch (pn->getKind()) {
       case PNK_NOP:
@@ -1859,22 +1868,23 @@ Fold(JSContext* cx, ParseNode** pnp, Par
     }
 
     MOZ_CRASH("shouldn't reach here");
     return false;
 }
 
 template<typename CharT>
 bool
-frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser)
+frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
+                        GeneralParser<FullParseHandler, CharT>* parser)
 {
     // Don't constant-fold inside "use asm" code, as this could create a parse
     // tree that doesn't type-check as asm.js.
     if (parser->pc->useAsmOrInsideUseAsm())
         return true;
 
     AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants);
     return Fold(cx, pnp, *parser);
 }
 
 template bool
 frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
-                        Parser<FullParseHandler, char16_t>* parser);
+                        GeneralParser<FullParseHandler, char16_t>* parser);
--- a/js/src/frontend/FoldConstants.h
+++ b/js/src/frontend/FoldConstants.h
@@ -8,39 +8,39 @@
 #define frontend_FoldConstants_h
 
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 namespace frontend {
 
 class FullParseHandler;
-template <class ParseHandler, typename CharT> class Parser;
+template <class ParseHandler, typename CharT> class GeneralParser;
 
 // Perform constant folding on the given AST. For example, the program
 // `print(2 + 2)` would become `print(4)`.
 //
 // pnp is the address of a pointer variable that points to the root node of the
 // AST. On success, *pnp points to the root node of the new tree, which may be
 // the same node (unchanged or modified in place) or a new node.
 //
 // Usage:
 //    pn = parser->statement();
 //    if (!pn)
 //        return false;
 //    if (!FoldConstants(cx, &pn, parser))
 //        return false;
 template<typename CharT>
 MOZ_MUST_USE bool
-FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser);
+FoldConstants(JSContext* cx, ParseNode** pnp, GeneralParser<FullParseHandler, CharT>* parser);
 
 template<typename CharT>
 inline MOZ_MUST_USE bool
 FoldConstants(JSContext* cx, typename SyntaxParseHandler::Node* pnp,
-              Parser<SyntaxParseHandler, CharT>* parser)
+              GeneralParser<SyntaxParseHandler, CharT>* parser)
 {
     return true;
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_FoldConstants_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -11,16 +11,19 @@
 #include "mozilla/PodOperations.h"
 
 #include <string.h>
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
+
+class RegExpObject;
+
 namespace frontend {
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
 class FullParseHandler
 {
     ParseNodeAllocator allocator;
 
@@ -216,36 +219,47 @@ class FullParseHandler
 
     // Expressions
 
     ParseNode* newArrayLiteral(uint32_t begin) {
         return new_<ListNode>(PNK_ARRAY, TokenPos(begin, begin + 1));
     }
 
     MOZ_MUST_USE bool addElision(ParseNode* literal, const TokenPos& pos) {
+        MOZ_ASSERT(literal->isKind(PNK_ARRAY));
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
         ParseNode* elision = new_<NullaryNode>(PNK_ELISION, pos);
         if (!elision)
             return false;
+
         literal->append(elision);
         literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
         return true;
     }
 
     MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) {
+        MOZ_ASSERT(literal->isKind(PNK_ARRAY));
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
         ParseNode* spread = newSpread(begin, inner);
         if (!spread)
             return false;
+
         literal->append(spread);
         literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST;
         return true;
     }
 
     void addArrayElement(ParseNode* literal, ParseNode* element) {
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
         if (!element->isConstant())
             literal->pn_xflags |= PNX_NONCONST;
+
         literal->append(element);
     }
 
     ParseNode* newCall(const TokenPos& pos) {
         return new_<ListNode>(PNK_CALL, JSOP_CALL, pos);
     }
 
     ParseNode* newSuperCall(ParseNode* callee) {
@@ -277,16 +291,19 @@ class FullParseHandler
     ParseNode* newPosHolder(const TokenPos& pos) {
         return new_<NullaryNode>(PNK_POSHOLDER, pos);
     }
     ParseNode* newSuperBase(ParseNode* thisName, const TokenPos& pos) {
         return new_<UnaryNode>(PNK_SUPERBASE, pos, thisName);
     }
 
     MOZ_MUST_USE bool addPrototypeMutation(ParseNode* literal, uint32_t begin, ParseNode* expr) {
+        MOZ_ASSERT(literal->isKind(PNK_OBJECT));
+        MOZ_ASSERT(literal->isArity(PN_LIST));
+
         // Object literals with mutated [[Prototype]] are non-constant so that
         // singleton objects will have Object.prototype as their [[Prototype]].
         setListFlag(literal, PNX_NONCONST);
 
         ParseNode* mutation = newUnary(PNK_MUTATEPROTO, begin, expr);
         if (!mutation)
             return false;
         literal->append(mutation);
@@ -333,27 +350,29 @@ class FullParseHandler
             return false;
         literal->append(spread);
         return true;
     }
 
     MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn,
                                                 AccessorType atype)
     {
+        MOZ_ASSERT(literal->isKind(PNK_OBJECT));
         MOZ_ASSERT(literal->isArity(PN_LIST));
         MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
                    key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
                    key->isKind(PNK_STRING) ||
                    key->isKind(PNK_COMPUTED_NAME));
-        literal->pn_xflags |= PNX_NONCONST;
 
         ParseNode* propdef = newBinary(PNK_COLON, key, fn, AccessorTypeToJSOp(atype));
         if (!propdef)
             return false;
+
         literal->append(propdef);
+        literal->pn_xflags |= PNX_NONCONST;
         return true;
     }
 
     MOZ_MUST_USE bool addClassMethodDefinition(ParseNode* methodList, ParseNode* key, ParseNode* fn,
                                                AccessorType atype, bool isStatic)
     {
         MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
         MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
@@ -426,24 +445,26 @@ class FullParseHandler
         list->append(casepn);
 
         if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
             list->pn_xflags |= PNX_FUNCDEFS;
     }
 
     MOZ_MUST_USE bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
         MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
+        MOZ_ASSERT(stmtList->isArity(PN_LIST));
 
         TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
         ParseNode* makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos);
         if (!makeGen)
             return false;
 
         MOZ_ASSERT(genName->getOp() == JSOP_GETNAME);
         genName->setOp(JSOP_SETNAME);
+
         ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen);
         if (!genInit)
             return false;
 
         ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit);
         if (!initialYield)
             return false;
 
@@ -645,19 +666,19 @@ class FullParseHandler
     }
 
     bool isExpressionClosure(ParseNode* node) const {
         return node->isKind(PNK_FUNCTION) &&
                node->pn_funbox->isExprBody() &&
                !node->pn_funbox->isArrow();
     }
 
-    void setFunctionFormalParametersAndBody(ParseNode* pn, ParseNode* kid) {
+    void setFunctionFormalParametersAndBody(ParseNode* funcNode, ParseNode* kid) {
         MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY));
-        pn->pn_body = kid;
+        funcNode->pn_body = kid;
     }
     void setFunctionBox(ParseNode* pn, FunctionBox* funbox) {
         MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
         pn->pn_funbox = funbox;
         funbox->functionNode = pn;
     }
     void addFunctionFormalParameter(ParseNode* pn, ParseNode* argpn) {
         pn->pn_body->append(argpn);
@@ -857,16 +878,19 @@ class FullParseHandler
         return lazyOuterFunction_->closedOverBindings()[lazyClosedOverBindingIndex++];
     }
 };
 
 inline bool
 FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
                                                         ParseNode* defaultValue)
 {
+    MOZ_ASSERT(funcpn->isKind(PNK_FUNCTION));
+    MOZ_ASSERT(funcpn->isArity(PN_CODE));
+
     ParseNode* arg = funcpn->pn_body->last();
     ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue);
     if (!pn)
         return false;
 
     checkAndSetIsDirectRHSAnonFunction(defaultValue);
 
     funcpn->pn_body->pn_pos.end = pn->pn_pos.end;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -16,16 +16,17 @@
  *
  * This parser attempts no error recovery.
  */
 
 #include "frontend/Parser.h"
 
 #include "mozilla/Range.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/TypeTraits.h"
 
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jstypes.h"
@@ -583,267 +584,235 @@ FunctionBox::initWithEnclosingScope(Scop
     } else {
         computeAllowSyntax(enclosingScope);
         computeThisBinding(enclosingScope);
     }
 
     computeInWith(enclosingScope);
 }
 
+template <class ParseHandler, typename CharT>
+inline typename GeneralParser<ParseHandler, CharT>::FinalParser*
+GeneralParser<ParseHandler, CharT>::asFinalParser()
+{
+    static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<FinalParser*>(this);
+}
+
+template <class ParseHandler, typename CharT>
+inline const typename GeneralParser<ParseHandler, CharT>::FinalParser*
+GeneralParser<ParseHandler, CharT>::asFinalParser() const
+{
+    static_assert(mozilla::IsBaseOf<GeneralParser<ParseHandler, CharT>, FinalParser>::value,
+                  "inheritance relationship required by the static_cast<> below");
+
+    return static_cast<const FinalParser*>(this);
+}
+
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::abortIfSyntaxParser()
+{
+    return asFinalParser()->abortIfSyntaxParser();
+}
+
+template <class ParseHandler, typename CharT>
 void
-ParserBase::error(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::error(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, pos().begin))
         ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
+template <class ParseHandler, typename CharT>
 void
-ParserBase::errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::errorWithNotes(UniquePtr<JSErrorNotes> notes,
+                                                   unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, pos().begin)) {
         ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber,
                            args);
     }
 
     va_end(args);
 }
 
+template <class ParseHandler, typename CharT>
 void
-ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::errorAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, offset))
         ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
+template <class ParseHandler, typename CharT>
 void
-ParserBase::errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
-                             unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::errorWithNotesAt(UniquePtr<JSErrorNotes> notes,
+                                                     uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     if (tokenStream.computeErrorMetadata(&metadata, offset)) {
         ReportCompileError(context, Move(metadata), Move(notes), JSREPORT_ERROR, errorNumber,
                            args);
     }
 
     va_end(args);
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::warning(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::warning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result =
         tokenStream.computeErrorMetadata(&metadata, pos().begin) &&
-        tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
+        anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::warningAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     bool result = tokenStream.computeErrorMetadata(&metadata, offset);
     if (result) {
         result =
-            tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber,
-                                       args);
+            anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
     }
 
     va_end(args);
     return result;
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::extraWarning(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::extraWarning(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool result =
-        tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, args);
+        tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::extraWarningAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool result =
-        tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args);
+        tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::strictModeError(unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::strictModeError(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool res =
         tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(),
-                                                  errorNumber, args);
+                                                  errorNumber, &args);
 
     va_end(args);
     return res;
 }
 
+template <class ParseHandler, typename CharT>
 bool
-ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
+GeneralParser<ParseHandler, CharT>::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     bool res =
         tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(),
-                                                  errorNumber, args);
+                                                  errorNumber, &args);
 
     va_end(args);
     return res;
 }
 
 bool
-ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...)
+ParserBase::warningNoOffset(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
-    bool result = false;
-    switch (kind) {
-      case ParseError:
-      case ParseWarning: {
-        ErrorMetadata metadata;
-        tokenStream.computeErrorMetadataNoOffset(&metadata);
-
-        if (kind == ParseError) {
-            ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber,
-                               args);
-            MOZ_ASSERT(!result);
-        } else {
-            result =
-                tokenStream.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber,
-                                           args);
-        }
-
-        break;
-      }
-      case ParseExtraWarning:
-        result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, TokenStream::NoOffset,
-                                                             errorNumber, args);
-        break;
-      case ParseStrictError:
-        result = tokenStream.reportStrictModeErrorNumberVA(nullptr, TokenStream::NoOffset, strict,
-                                                           errorNumber, args);
-        break;
-    }
+    ErrorMetadata metadata;
+    anyChars.computeErrorMetadataNoOffset(&metadata);
+
+    bool result =
+        anyChars.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
-#define ABORTED_SYNTAX_PARSE_SENTINEL (SyntaxParser*)(0x1)
-
-template <>
-bool
-Parser<FullParseHandler, char16_t>::hadAbortedSyntaxParse()
-{
-    return false;
-}
-
-template <>
-bool
-Parser<SyntaxParseHandler, char16_t>::hadAbortedSyntaxParse()
-{
-    return syntaxParser_ == ABORTED_SYNTAX_PARSE_SENTINEL;
-}
-
-template <>
 void
-Parser<FullParseHandler, char16_t>::clearAbortedSyntaxParse()
-{
-}
-
-template <>
-void
-Parser<SyntaxParseHandler, char16_t>::clearAbortedSyntaxParse()
-{
-    syntaxParser_ = nullptr;
-}
-
-template <>
-void
-Parser<FullParseHandler, char16_t>::disableSyntaxParser()
-{
-    syntaxParser_ = nullptr;
-}
-
-template <>
-void
-Parser<SyntaxParseHandler, char16_t>::disableSyntaxParser()
-{
-}
-
-template <>
-inline bool
-Parser<FullParseHandler, char16_t>::abortIfSyntaxParser()
-{
-    disableSyntaxParser();
-    return true;
-}
-
-template <>
-inline bool
-Parser<SyntaxParseHandler, char16_t>::abortIfSyntaxParser()
-{
-    syntaxParser_ = ABORTED_SYNTAX_PARSE_SENTINEL;
-    return false;
-}
-
-#undef ABORTED_SYNTAX_PARSE_SENTINEL
+ParserBase::errorNoOffset(unsigned errorNumber, ...)
+{
+    va_list args;
+    va_start(args, errorNumber);
+
+    ErrorMetadata metadata;
+    anyChars.computeErrorMetadataNoOffset(&metadata);
+
+    ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
+
+    va_end(args);
+}
 
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        const char16_t* chars, size_t length,
                        bool foldConstants,
                        UsedNameTracker& usedNames)
   : context(cx),
     alloc(alloc),
-    tokenStream(cx, options, chars, length, thisForCtor()),
+    anyChars(cx, options, thisForCtor()),
     traceListHead(nullptr),
     pc(nullptr),
     usedNames(usedNames),
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
@@ -865,68 +834,70 @@ ParserBase::~ParserBase()
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
 
     context->frontendCollectionPool().removeActiveCompilation();
 }
 
 template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::Parser(JSContext* cx, LifoAlloc& alloc,
-                                    const ReadOnlyCompileOptions& options,
-                                    const CharT* chars, size_t length,
-                                    bool foldConstants,
-                                    UsedNameTracker& usedNames,
-                                    SyntaxParser* syntaxParser,
-                                    LazyScript* lazyOuterFunction)
+GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& alloc,
+                                                  const ReadOnlyCompileOptions& options,
+                                                  const CharT* chars, size_t length,
+                                                  bool foldConstants,
+                                                  UsedNameTracker& usedNames,
+                                                  SyntaxParser* syntaxParser,
+                                                  LazyScript* lazyOuterFunction)
   : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames),
     AutoGCRooter(cx, PARSER),
     syntaxParser_(syntaxParser),
+    tokenStream(cx, options, chars, length),
     handler(cx, alloc, lazyOuterFunction)
-{
-    // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
-    // which are not generated if functions are parsed lazily. Note that the
-    // standard "use strict" does not inhibit lazy parsing.
-    if (options.extraWarningsOption)
-        disableSyntaxParser();
-}
+{}
 
 template<class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkOptions()
+GeneralParser<ParseHandler, CharT>::checkOptions()
 {
 #ifdef DEBUG
     checkOptionsCalled = true;
 #endif
 
-    return tokenStream.checkOptions();
-}
-
-template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::~Parser()
+    return anyChars.checkOptions();
+}
+
+template <class ParseHandler, typename CharT>
+GeneralParser<ParseHandler, CharT>::~GeneralParser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
-template <>
+template <typename CharT>
 void
-Parser<SyntaxParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
-{
-    awaitHandling_ = awaitHandling;
-}
-
-template <>
+Parser<SyntaxParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    this->awaitHandling_ = awaitHandling;
+}
+
+template <typename CharT>
 void
-Parser<FullParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
-{
-    awaitHandling_ = awaitHandling;
+Parser<FullParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    this->awaitHandling_ = awaitHandling;
     if (syntaxParser_)
         syntaxParser_->setAwaitHandling(awaitHandling);
 }
 
+template <class ParseHandler, typename CharT>
+inline void
+GeneralParser<ParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    asFinalParser()->setAwaitHandling(awaitHandling);
+}
+
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
@@ -943,20 +914,21 @@ ParserBase::newObjectBox(JSObject* obj)
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 template <class ParseHandler, typename CharT>
 FunctionBox*
-Parser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart,
-                                            Directives inheritedDirectives,
-                                            GeneratorKind generatorKind,
-                                            FunctionAsyncKind asyncKind)
+GeneralParser<ParseHandler, CharT>::newFunctionBox(Node fn, JSFunction* fun,
+                                                   uint32_t toStringStart,
+                                                   Directives inheritedDirectives,
+                                                   GeneratorKind generatorKind,
+                                                   FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(fun);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
@@ -986,33 +958,33 @@ ModuleSharedContext::ModuleSharedContext
     bindings(cx),
     builder(builder)
 {
     thisBinding_ = ThisBinding::Module;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::trace(JSTracer* trc)
+GeneralParser<ParseHandler, CharT>::trace(JSTracer* trc)
 {
     ObjectBox::TraceList(trc, traceListHead);
 }
 
 void
 TraceParser(JSTracer* trc, AutoGCRooter* parser)
 {
-    static_cast<Parser<FullParseHandler, char16_t>*>(parser)->trace(trc);
+    static_cast<GeneralParser<FullParseHandler, char16_t>*>(parser)->trace(trc);
 }
 
 /*
  * Parse a top-level JS script.
  */
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::parse()
+GeneralParser<ParseHandler, CharT>::parse()
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Directives directives(options().strictOption);
     GlobalSharedContext globalsc(context, ScopeKind::Global,
                                  directives, options().extraWarningsOption);
     SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
@@ -1076,27 +1048,27 @@ ParserBase::hasValidSimpleStrictParamete
         if (!isValidStrictBinding(name->asPropertyName()))
             return false;
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
-                                                  uint32_t openedPos)
+GeneralParser<ParseHandler, CharT>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
+                                                         uint32_t openedPos)
 {
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
-    tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
+    anyChars.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
 
     const size_t MaxWidth = sizeof("4294967295");
     char columnNumber[MaxWidth];
     SprintfLiteral(columnNumber, "%" PRIu32, column);
     char lineNumber[MaxWidth];
     SprintfLiteral(lineNumber, "%" PRIu32, line);
 
     if (!notes->addNoteASCII(pc->sc()->context,
@@ -1107,18 +1079,19 @@ Parser<ParseHandler, CharT>::reportMissi
         return;
     }
 
     errorWithNotes(Move(notes), errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
-                                                 TokenPos pos, uint32_t prevPos)
+GeneralParser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name,
+                                                        DeclarationKind prevKind,
+                                                        TokenPos pos, uint32_t prevPos)
 {
     JSAutoByteString bytes;
     if (!AtomToPrintableString(context, name, &bytes))
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
         errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
         return;
@@ -1126,17 +1099,17 @@ Parser<ParseHandler, CharT>::reportRedec
 
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
 
     uint32_t line, column;
-    tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
+    anyChars.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
 
     const size_t MaxWidth = sizeof("4294967295");
     char columnNumber[MaxWidth];
     SprintfLiteral(columnNumber, "%" PRIu32, column);
     char lineNumber[MaxWidth];
     SprintfLiteral(lineNumber, "%" PRIu32, line);
 
     if (!notes->addNoteASCII(pc->sc()->context,
@@ -1158,20 +1131,20 @@ Parser<ParseHandler, CharT>::reportRedec
 //
 // The 'disallowDuplicateParams' bool indicates whether the use of another
 // feature (destructuring or default arguments) disables duplicate arguments.
 // (ECMA-262 requires us to support duplicate parameter names, but, for newer
 // features, we consider the code to have "opted in" to higher standards and
 // forbid duplicates.)
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
-                                                           uint32_t beginPos,
-                                                           bool disallowDuplicateParams,
-                                                           bool* duplicatedParam)
+GeneralParser<ParseHandler, CharT>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+                                                                  uint32_t beginPos,
+                                                                  bool disallowDuplicateParams,
+                                                                  bool* duplicatedParam)
 {
     if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
         if (disallowDuplicateParams) {
             error(JSMSG_BAD_DUP_ARGS);
             return false;
         }
 
         // Strict-mode disallows duplicate args. We may not know whether we are
@@ -1203,17 +1176,18 @@ Parser<ParseHandler, CharT>::notePositio
         return false;
 
     handler.addFunctionFormalParameter(fn, paramNode);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn, Node destruct)
+GeneralParser<ParseHandler, CharT>::noteDestructuredPositionalFormalParameter(Node fn,
+                                                                              Node destruct)
 {
     // Append an empty name to the positional formals vector to keep track of
     // argument slots when making FunctionScope::Data.
     if (!pc->positionalFormalParameterNames().append(nullptr)) {
         ReportOutOfMemory(context);
         return false;
     }
 
@@ -1449,19 +1423,19 @@ ParseContext::annexBAppliesToLexicalFunc
 
     // If an early error would have occurred already, this function should not
     // exhibit Annex B.3.3 semantics.
     return !redeclaredKind;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
-                                                                        DeclarationKind kind,
-                                                                        TokenPos pos)
+GeneralParser<ParseHandler, CharT>::checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
+                                                                               DeclarationKind kind,
+                                                                               TokenPos pos)
 {
     MOZ_ASSERT(DeclarationKindIsLexical(kind));
 
     // It is an early error to declare a lexical binding not directly
     // within a block.
     if (!StatementKindIsBraced(stmt.kind()) &&
         stmt.kind() != StatementKind::ForLoopLexicalHead)
     {
@@ -1473,18 +1447,18 @@ Parser<ParseHandler, CharT>::checkLexica
         return false;
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
-                                              TokenPos pos)
+GeneralParser<ParseHandler, CharT>::noteDeclaredName(HandlePropertyName name, DeclarationKind kind,
+                                                     TokenPos pos)
 {
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
     if (pc->useAsmOrInsideUseAsm())
         return true;
 
     switch (kind) {
       case DeclarationKind::Var:
@@ -1644,17 +1618,17 @@ Parser<ParseHandler, CharT>::noteDeclare
         break;
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::noteUsedName(HandlePropertyName name)
 {
     // If the we are delazifying, the LazyScript already has all the
     // closed-over info for bindings and there's no need to track used names.
     if (handler.canSkipLazyClosedOverBindings())
         return true;
 
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
@@ -1669,26 +1643,26 @@ Parser<ParseHandler, CharT>::noteUsedNam
     if (pc->sc()->isGlobalContext() && scope == &pc->varScope())
         return true;
 
     return usedNames.noteUse(context, name, pc->scriptId(), scope->id());
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::hasUsedName(HandlePropertyName name)
 {
     if (UsedNamePtr p = usedNames.lookup(name))
         return p->value().isUsedInScript(pc->scriptId());
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
+GeneralParser<ParseHandler, CharT>::propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope)
 {
     // Now that we have all the declared names in the scope, check which
     // functions should exhibit Annex B semantics.
     if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc))
         return false;
 
     if (handler.canSkipLazyClosedOverBindings()) {
         // Scopes are nullptr-delimited in the LazyScript closed over bindings
@@ -1720,19 +1694,19 @@ Parser<ParseHandler, CharT>::propagateFr
     if (isSyntaxParser && !pc->closedOverBindingsForLazy().append(nullptr)) {
         ReportOutOfMemory(context);
         return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkStatementsEOF()
+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
     // reached EOF here.
     TokenKind tt;
     if (!tokenStream.peekToken(&tt, TokenStream::Operand))
@@ -2105,40 +2079,50 @@ ParserBase::newLexicalScopeData(ParseCon
         bindings->constStart = cursor - start;
         PodCopy(cursor, consts.begin(), consts.length());
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
-template <>
+template <typename CharT>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+Parser<SyntaxParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return null();
+
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
+Parser<FullParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, ParseNode* body)
 {
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return nullptr;
+
     Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
     if (!bindings)
         return nullptr;
+
     return handler.newLexicalScope(*bindings, body);
 }
 
-template <>
+template <class ParseHandler, typename CharT>
+inline typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::finishLexicalScope(ParseContext::Scope& scope, Node body)
+{
+    return asFinalParser()->finishLexicalScope(scope, body);
+}
+
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
+Parser<FullParseHandler, CharT>::evalBody(EvalSharedContext* evalsc)
 {
     SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
     if (!evalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2196,19 +2180,19 @@ Parser<FullParseHandler, char16_t>::eval
     Maybe<EvalScope::Data*> bindings = newEvalScopeData(pc->varScope());
     if (!bindings)
         return nullptr;
     evalsc->bindings = *bindings;
 
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::globalBody(GlobalSharedContext* globalsc)
+Parser<FullParseHandler, CharT>::globalBody(GlobalSharedContext* globalsc)
 {
     SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
     if (!globalpc.init())
         return nullptr;
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
@@ -2232,35 +2216,35 @@ Parser<FullParseHandler, char16_t>::glob
     Maybe<GlobalScope::Data*> bindings = newGlobalScopeData(pc->varScope());
     if (!bindings)
         return nullptr;
     globalsc->bindings = *bindings;
 
     return body;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
+Parser<FullParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     SourceParseContext modulepc(this, modulesc, nullptr);
     if (!modulepc.init())
         return null();
 
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule(pos());
     if (!mn)
         return null();
 
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, AwaitIsModuleKeyword);
+    AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, AwaitIsModuleKeyword);
     ParseNode* pn = statementList(YieldIsName);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2301,35 +2285,35 @@ Parser<FullParseHandler, char16_t>::modu
     Maybe<ModuleScope::Data*> bindings = newModuleScopeData(modulepc.varScope());
     if (!bindings)
         return nullptr;
 
     modulesc->bindings = *bindings;
     return mn;
 }
 
-template <>
+template <typename CharT>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
+Parser<SyntaxParseHandler, CharT>::moduleBody(ModuleSharedContext* modulesc)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::hasUsedFunctionSpecialName(HandlePropertyName name)
 {
     MOZ_ASSERT(name == context->names().arguments || name == context->names().dotThis);
     return hasUsedName(name) || pc->functionBox()->bindingsAccessedDynamically();
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareFunctionThis()
+GeneralParser<ParseHandler, CharT>::declareFunctionThis()
 {
     // The asm.js validator does all its own symbol-table management so, as an
     // optimization, avoid doing any work here.
     if (pc->useAsmOrInsideUseAsm())
         return true;
 
     // Derived class constructors emit JSOP_CHECKRETURN, which requires
     // '.this' to be bound.
@@ -2354,79 +2338,79 @@ Parser<ParseHandler, CharT>::declareFunc
         funbox->setHasThisBinding();
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
+GeneralParser<ParseHandler, CharT>::newInternalDotName(HandlePropertyName name)
 {
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
     if (!noteUsedName(name))
         return null();
     return nameNode;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newThisName()
+GeneralParser<ParseHandler, CharT>::newThisName()
 {
     return newInternalDotName(context->names().dotThis);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::newDotGeneratorName()
+GeneralParser<ParseHandler, CharT>::newDotGeneratorName()
 {
     return newInternalDotName(context->names().dotGenerator);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareDotGeneratorName()
+GeneralParser<ParseHandler, CharT>::declareDotGeneratorName()
 {
     // The special '.generator' binding must be on the function scope, as
     // generators expect to find it on the CallObject.
     ParseContext::Scope& funScope = pc->functionScope();
     HandlePropertyName dotGenerator = context->names().dotGenerator;
     AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
     if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var,
                                         DeclaredNameInfo::npos))
     {
         return false;
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
+GeneralParser<ParseHandler, CharT>::finishFunctionScopes(bool isStandaloneFunction)
 {
     FunctionBox* funbox = pc->functionBox();
 
     if (funbox->hasParameterExprs) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
             return false;
     }
 
     if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
             return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<FullParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
 
     FunctionBox* funbox = pc->functionBox();
     bool hasParameterExprs = funbox->hasParameterExprs;
 
     if (hasParameterExprs) {
@@ -2449,19 +2433,19 @@ Parser<FullParseHandler, char16_t>::fini
         if (!bindings)
             return false;
         funbox->namedLambdaBindings().set(*bindings);
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::finishFunction(bool isStandaloneFunction /* = false */)
+Parser<SyntaxParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     // The LazyScript for a lazily parsed function needs to know its set of
     // free variables and inner functions so that when it is fully parsed, we
     // can skip over any already syntax parsed inner functions and still
     // retain correct scope information.
 
     if (!finishFunctionScopes(isStandaloneFunction))
         return false;
@@ -2509,41 +2493,48 @@ Parser<SyntaxParseHandler, char16_t>::fi
     // Flags that need to copied back into the parser when we do the full
     // parse.
     PropagateTransitiveParseFlags(funbox, lazy);
 
     fun->initLazyScript(lazy);
     return true;
 }
 
+template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::finishFunction(bool isStandaloneFunction /* = false */)
+{
+    return asFinalParser()->finishFunction(isStandaloneFunction);
+}
+
 static YieldHandling
 GetYieldHandling(GeneratorKind generatorKind)
 {
     if (generatorKind == GeneratorKind::NotGenerator)
         return YieldIsName;
     return YieldIsKeyword;
 }
 
 static AwaitHandling
 GetAwaitHandling(FunctionAsyncKind asyncKind)
 {
     if (asyncKind == FunctionAsyncKind::SyncFunction)
         return AwaitIsName;
     return AwaitIsKeyword;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
-                                                       HandleScope enclosingScope,
-                                                       const Maybe<uint32_t>& parameterListEnd,
-                                                       GeneratorKind generatorKind,
-                                                       FunctionAsyncKind asyncKind,
-                                                       Directives inheritedDirectives,
-                                                       Directives* newDirectives)
+Parser<FullParseHandler, CharT>::standaloneFunction(HandleFunction fun,
+                                                    HandleScope enclosingScope,
+                                                    const Maybe<uint32_t>& parameterListEnd,
+                                                    GeneratorKind generatorKind,
+                                                    FunctionAsyncKind asyncKind,
+                                                    Directives inheritedDirectives,
+                                                    Directives* newDirectives)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     // Skip prelude.
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (asyncKind == FunctionAsyncKind::AsyncFunction) {
@@ -2558,20 +2549,20 @@ Parser<FullParseHandler, char16_t>::stan
     if (generatorKind == GeneratorKind::Generator) {
         MOZ_ASSERT(tt == TOK_MUL);
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     // Skip function name, if present.
     if (TokenKindIsPossibleIdentifierName(tt)) {
-        MOZ_ASSERT(tokenStream.currentName() == fun->explicitName());
+        MOZ_ASSERT(anyChars.currentName() == fun->explicitName());
     } else {
         MOZ_ASSERT(fun->explicitName() == nullptr);
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     Node fn = handler.newFunctionStatement(pos());
     if (!fn)
         return null();
 
     ParseNode* argsbody = handler.newList(PNK_PARAMSBODY, pos());
     if (!argsbody)
@@ -2586,17 +2577,17 @@ Parser<FullParseHandler, char16_t>::stan
 
     SourceParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
+    AutoAwaitIsKeyword<FullParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -2608,17 +2599,17 @@ Parser<FullParseHandler, char16_t>::stan
     if (!FoldConstants(context, &fn, this))
         return null();
 
     return fn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::declareFunctionArgumentsObject()
+GeneralParser<ParseHandler, CharT>::declareFunctionArgumentsObject()
 {
     FunctionBox* funbox = pc->functionBox();
     ParseContext::Scope& funScope = pc->functionScope();
     ParseContext::Scope& varScope = pc->varScope();
 
     bool hasExtraBodyVarScope = &funScope != &varScope;
 
     // Time to implement the odd semantics of 'arguments'.
@@ -2685,18 +2676,19 @@ Parser<ParseHandler, CharT>::declareFunc
             funbox->setDefinitelyNeedsArgsObj();
     }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionBody(InHandling inHandling, YieldHandling yieldHandling,
-                                          FunctionSyntaxKind kind, FunctionBodyType type)
+GeneralParser<ParseHandler, CharT>::functionBody(InHandling inHandling,
+                                                 YieldHandling yieldHandling,
+                                                 FunctionSyntaxKind kind, FunctionBodyType type)
 {
     MOZ_ASSERT(pc->isFunctionBox());
 
 #ifdef DEBUG
     uint32_t startYieldOffset = pc->lastYieldOffset;
 #endif
 
     Node pn;
@@ -2849,17 +2841,17 @@ ParserBase::newFunction(HandleAtom atom,
             fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
 #endif
     }
     return fun;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchOrInsertSemicolon()
+GeneralParser<ParseHandler, CharT>::matchOrInsertSemicolon()
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
@@ -2867,37 +2859,37 @@ Parser<ParseHandler, CharT>::matchOrInse
          *   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() && tokenStream.currentToken().type == TOK_AWAIT) {
+        if (!pc->isAsync() && anyChars.currentToken().type == TOK_AWAIT) {
             error(JSMSG_AWAIT_OUTSIDE_ASYNC);
             return false;
         }
-        if (!yieldExpressionsSupported() && tokenStream.currentToken().type == TOK_YIELD) {
+        if (!yieldExpressionsSupported() && anyChars.currentToken().type == 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);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc)
+GeneralParser<ParseHandler, CharT>::leaveInnerFunction(ParseContext* outerpc)
 {
     MOZ_ASSERT(pc != outerpc);
 
     // If the current function allows super.property but cannot have a home
     // object, i.e., it is an arrow function, we need to propagate the flag to
     // the outer ParseContext.
     if (pc->superScopeNeedsHomeObject()) {
         if (!pc->isArrowFunction())
@@ -2907,28 +2899,28 @@ Parser<ParseHandler, CharT>::leaveInnerF
     }
 
     // Lazy functions inner to another lazy function need to be remembered by
     // the inner function so that if the outer function is eventually parsed
     // we do not need any further parsing or processing of the inner function.
     //
     // Append the inner function here unconditionally; the vector is only used
     // if the Parser using outerpc is a syntax parsing. See
-    // Parser<SyntaxParseHandler>::finishFunction.
+    // GeneralParser<SyntaxParseHandler>::finishFunction.
     if (!outerpc->innerFunctionsForLazy.append(pc->functionBox()->function()))
         return false;
 
     PropagateTransitiveParseFlags(pc->functionBox(), outerpc->sc());
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 JSAtom*
-Parser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
+GeneralParser<ParseHandler, CharT>::prefixAccessorName(PropertyType propType, HandleAtom propAtom)
 {
     RootedAtom prefix(context);
     if (propType == PropertyType::Setter || propType == PropertyType::SetterNoExpressionClosure) {
         prefix = context->names().setPrefix;
     } else {
         MOZ_ASSERT(propType == PropertyType::Getter || propType == PropertyType::GetterNoExpressionClosure);
         prefix = context->names().getPrefix;
     }
@@ -2937,19 +2929,19 @@ Parser<ParseHandler, CharT>::prefixAcces
     if (!str)
         return nullptr;
 
     return AtomizeString(context, str);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
-                                               FunctionSyntaxKind kind,
-                                               Node funcpn)
+GeneralParser<ParseHandler, CharT>::functionArguments(YieldHandling yieldHandling,
+                                                      FunctionSyntaxKind kind,
+                                                      Node funcpn)
 {
     FunctionBox* funbox = pc->functionBox();
 
     bool parenFreeArrow = false;
     // Modifier for the following tokens.
     // TokenStream::None for the following cases:
     //   async a => 1
     //         ^
@@ -2961,23 +2953,23 @@ Parser<ParseHandler, CharT>::functionArg
     //         ^
     //
     //   function f(a) {}
     //             ^
     //
     // TokenStream::Operand for the following case:
     //   a => 1
     //   ^
-    TokenStream::Modifier firstTokenModifier = TokenStream::None;
+    Modifier firstTokenModifier = TokenStream::None;
 
     // Modifier for the the first token in each argument.
     // can be changed to TokenStream::None for the following case:
     //   async a => 1
     //         ^
-    TokenStream::Modifier argModifier = TokenStream::Operand;
+    Modifier argModifier = TokenStream::Operand;
     if (kind == Arrow) {
         TokenKind tt;
         // In async function, the first token after `async` is already gotten
         // with TokenStream::None.
         // In sync function, the first token is already gotten with
         // TokenStream::Operand.
         firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
         if (!tokenStream.peekToken(&tt, firstTokenModifier))
@@ -2997,17 +2989,17 @@ Parser<ParseHandler, CharT>::functionArg
             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.
-        funbox->setStart(tokenStream);
+        funbox->setStart(anyChars);
     } else {
         // When delazifying, we may not have a current token and pos() is
         // garbage. In that case, substitute the first token's position.
         if (!tokenStream.peekTokenPos(&firstTokenPos, firstTokenModifier))
             return false;
     }
 
     Node argsbody = handler.newList(PNK_PARAMSBODY, firstTokenPos);
@@ -3100,17 +3092,17 @@ Parser<ParseHandler, CharT>::functionArg
 
               default: {
                 if (!TokenKindIsPossibleIdentifier(tt)) {
                     error(JSMSG_MISSING_FORMAL);
                     return false;
                 }
 
                 if (parenFreeArrow)
-                    funbox->setStart(tokenStream);
+                    funbox->setStart(anyChars);
 
                 RootedPropertyName name(context, bindingIdentifier(yieldHandling));
                 if (!name)
                     return false;
 
                 if (!notePositionalFormalParameter(funcpn, name, pos().begin,
                                                    disallowDuplicateParams, &duplicatedParam))
                 {
@@ -3215,29 +3207,30 @@ Parser<ParseHandler, CharT>::functionArg
     } else if (IsSetterKind(kind)) {
         error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
         return false;
     }
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart,
-                                                          FunctionSyntaxKind kind, bool tryAnnexB)
+Parser<FullParseHandler, CharT>::skipLazyInnerFunction(ParseNode* funcNode, uint32_t toStringStart,
+                                                       FunctionSyntaxKind kind, bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
-    FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
-                                         fun->generatorKind(), fun->asyncKind());
+    FunctionBox* funbox =
+        newFunctionBox(funcNode, fun, toStringStart, Directives(/* strict = */ false),
+                       fun->generatorKind(), fun->asyncKind());
     if (!funbox)
         return false;
 
     LazyScript* lazy = fun->lazyScript();
     if (lazy->needsHomeObject())
         funbox->setNeedsHomeObject();
     if (lazy->isExprBody())
         funbox->setIsExprBody();
@@ -3259,30 +3252,39 @@ Parser<FullParseHandler, char16_t>::skip
 
     // Append possible Annex B function box only upon successfully parsing.
     if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
         return false;
 
     return true;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::skipLazyInnerFunction(Node pn, uint32_t toStringStart,
-                                                            FunctionSyntaxKind kind,
-                                                            bool tryAnnexB)
+Parser<SyntaxParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
+                                                         FunctionSyntaxKind kind,
+                                                         bool tryAnnexB)
 {
     MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing");
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
-                                                            Node nodeList,
-                                                            TokenKind* ttp)
+GeneralParser<ParseHandler, CharT>::skipLazyInnerFunction(Node funcNode, uint32_t toStringStart,
+                                                          FunctionSyntaxKind kind,
+                                                          bool tryAnnexB)
+{
+    return asFinalParser()->skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB);
+}
+
+template <class ParseHandler, typename CharT>
+bool
+GeneralParser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling,
+                                                                   Node nodeList,
+                                                                   TokenKind* ttp)
 {
     Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -3292,18 +3294,18 @@ Parser<ParseHandler, CharT>::addExprAndG
         return false;
     }
 
     return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
-                                            TokenKind tt)
+GeneralParser<ParseHandler, CharT>::taggedTemplate(YieldHandling yieldHandling, Node nodeList,
+                                                   TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin);
     if (!callSiteObjNode)
         return false;
     handler.addList(nodeList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
@@ -3315,17 +3317,17 @@ Parser<ParseHandler, CharT>::taggedTempl
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::templateLiteral(YieldHandling yieldHandling)
 {
     Node pn = noSubstitutionUntaggedTemplate();
     if (!pn)
         return null();
 
     Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
     if (!nodeList)
         return null();
@@ -3341,32 +3343,33 @@ Parser<ParseHandler, CharT>::templateLit
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionDefinition(Node pn, uint32_t toStringStart,
-                                                InHandling inHandling, YieldHandling yieldHandling,
-                                                HandleAtom funName, FunctionSyntaxKind kind,
-                                                GeneratorKind generatorKind,
-                                                FunctionAsyncKind asyncKind,
-                                                bool tryAnnexB /* = false */)
+GeneralParser<ParseHandler, CharT>::functionDefinition(Node funcNode, uint32_t toStringStart,
+                                                       InHandling inHandling, YieldHandling yieldHandling,
+                                                       HandleAtom funName, FunctionSyntaxKind kind,
+                                                       GeneratorKind generatorKind,
+                                                       FunctionAsyncKind asyncKind,
+                                                       bool tryAnnexB /* = false */)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
-        if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB))
-            return null();
-        return pn;
+        if (!skipLazyInnerFunction(funcNode, toStringStart, kind, tryAnnexB))
+            return null();
+
+        return funcNode;
     }
 
     RootedObject proto(context);
     if (generatorKind == GeneratorKind::Generator ||
         asyncKind == FunctionAsyncKind::AsyncFunction)
     {
         // If we are off thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseTask, so cx will not
@@ -3382,230 +3385,260 @@ Parser<ParseHandler, CharT>::functionDef
 
     // Speculatively parse using the directives of the parent parsing context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
     Directives directives(pc);
     Directives newDirectives = directives;
 
-    TokenStream::Position start(keepAtoms);
+    typename TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     // Parse the inner function. The following is a loop as we may attempt to
     // reparse a function due to failed syntax parsing and encountering new
     // "use foo" directives.
     while (true) {
-        if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind,
-                                        generatorKind, asyncKind, tryAnnexB, directives,
+        if (trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling, yieldHandling,
+                                        kind, generatorKind, asyncKind, tryAnnexB, directives,
                                         &newDirectives))
         {
             break;
         }
 
         // Return on error.
-        if (tokenStream.hadError() || directives == newDirectives)
+        if (anyChars.hadError() || directives == newDirectives)
             return null();
 
         // Assignment must be monotonic to prevent infinitely attempting to
         // reparse.
         MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
         MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
         directives = newDirectives;
 
         tokenStream.seek(start);
 
         // functionFormalParametersAndBody may have already set pn->pn_body before failing.
-        handler.setFunctionFormalParametersAndBody(pn, null());
-    }
-
-    return pn;
-}
-
-template <>
+        handler.setFunctionFormalParametersAndBody(funcNode, null());
+    }
+
+    return funcNode;
+}
+
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun,
-                                                                uint32_t toStringStart,
-                                                                InHandling inHandling,
-                                                                YieldHandling yieldHandling,
-                                                                FunctionSyntaxKind kind,
-                                                                GeneratorKind generatorKind,
-                                                                FunctionAsyncKind asyncKind,
-                                                                bool tryAnnexB,
-                                                                Directives inheritedDirectives,
-                                                                Directives* newDirectives)
+Parser<FullParseHandler, CharT>::trySyntaxParseInnerFunction(ParseNode* funcNode,
+                                                             HandleFunction fun,
+                                                             uint32_t toStringStart,
+                                                             InHandling inHandling,
+                                                             YieldHandling yieldHandling,
+                                                             FunctionSyntaxKind kind,
+                                                             GeneratorKind generatorKind,
+                                                             FunctionAsyncKind asyncKind,
+                                                             bool tryAnnexB,
+                                                             Directives inheritedDirectives,
+                                                             Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
         // the prediction may be incorrect, IIFEs are common enough that it
         // pays off for lots of code.
-        if (pn->isLikelyIIFE() &&
+        if (funcNode->isLikelyIIFE() &&
             generatorKind == GeneratorKind::NotGenerator &&
             asyncKind == FunctionAsyncKind::SyncFunction)
         {
             break;
         }
 
         if (!syntaxParser_)
             break;
 
         UsedNameTracker::RewindToken token = usedNames.getRewindToken();
 
         // Move the syntax parser to the current position in the stream.
-        TokenStream::Position position(keepAtoms);
+        typename TokenStream::Position position(keepAtoms);
         tokenStream.tell(&position);
-        if (!syntaxParser_->tokenStream.seek(position, tokenStream))
+        if (!syntaxParser_->tokenStream.seek(position, anyChars))
             return false;
 
         // Make a FunctionBox before we enter the syntax parser, because |pn|
         // still expects a FunctionBox to be attached to it during BCE, and
         // the syntax parser cannot attach one to it.
-        FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
+        FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives,
                                              generatorKind, asyncKind);
         if (!funbox)
             return false;
         funbox->initWithEnclosingParseContext(pc, kind);
 
-        if (!syntaxParser_->innerFunction(SyntaxParseHandler::NodeGeneric,
-                                          pc, funbox, inHandling, yieldHandling, kind,
-                                          newDirectives))
+        if (!syntaxParser_->innerFunctionForFunctionBox(SyntaxParseHandler::NodeGeneric,
+                                                        pc, funbox, inHandling, yieldHandling,
+                                                        kind, newDirectives))
         {
             if (syntaxParser_->hadAbortedSyntaxParse()) {
                 // Try again with a full parse. UsedNameTracker needs to be
                 // rewound to just before we tried the syntax parse for
                 // correctness.
                 syntaxParser_->clearAbortedSyntaxParse();
                 usedNames.rewind(token);
                 MOZ_ASSERT_IF(!syntaxParser_->context->helperThread(),
                               !syntaxParser_->context->isExceptionPending());
                 break;
             }
             return false;
         }
 
         // Advance this parser over tokens processed by the syntax parser.
         syntaxParser_->tokenStream.tell(&position);
-        if (!tokenStream.seek(position, syntaxParser_->tokenStream))
+        if (!tokenStream.seek(position, syntaxParser_->anyChars))
             return false;
 
         // Update the end position of the parse node.
-        pn->pn_pos.end = tokenStream.currentToken().pos.end;
+        funcNode->pn_pos.end = anyChars.currentToken().pos.end;
 
         // Append possible Annex B function box only upon successfully parsing.
-        if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
-            return false;
+        if (tryAnnexB) {
+            if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
+                return false;
+        }
 
         return true;
     } while (false);
 
     // We failed to do a syntax parse above, so do the full parse.
-    return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+    return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
                          generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::trySyntaxParseInnerFunction(Node pn, HandleFunction fun,
-                                                                  uint32_t toStringStart,
-                                                                  InHandling inHandling,
-                                                                  YieldHandling yieldHandling,
-                                                                  FunctionSyntaxKind kind,
-                                                                  GeneratorKind generatorKind,
-                                                                  FunctionAsyncKind asyncKind,
-                                                                  bool tryAnnexB,
-                                                                  Directives inheritedDirectives,
-                                                                  Directives* newDirectives)
+Parser<SyntaxParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun,
+                                                               uint32_t toStringStart,
+                                                               InHandling inHandling,
+                                                               YieldHandling yieldHandling,
+                                                               FunctionSyntaxKind kind,
+                                                               GeneratorKind generatorKind,
+                                                               FunctionAsyncKind asyncKind,
+                                                               bool tryAnnexB,
+                                                               Directives inheritedDirectives,
+                                                               Directives* newDirectives)
 {
     // This is already a syntax parser, so just parse the inner function.
-    return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind,
+    return innerFunction(funcNode, pc, fun, toStringStart, inHandling, yieldHandling, kind,
                          generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives);
 }
 
 template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::trySyntaxParseInnerFunction(Node funcNode, HandleFunction fun,
+                                                                uint32_t toStringStart,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                GeneratorKind generatorKind,
+                                                                FunctionAsyncKind asyncKind,
+                                                                bool tryAnnexB,
+                                                                Directives inheritedDirectives,
+                                                                Directives* newDirectives)
+{
+    return asFinalParser()->trySyntaxParseInnerFunction(funcNode, fun, toStringStart, inHandling,
+                                                        yieldHandling, kind, generatorKind,
+                                                        asyncKind, tryAnnexB, inheritedDirectives,
+                                                        newDirectives);
+}
+
+template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox,
-                                           InHandling inHandling,
-                                           YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                           Directives* newDirectives)
+GeneralParser<ParseHandler, CharT>::innerFunctionForFunctionBox(Node funcNode,
+                                                                ParseContext* outerpc,
+                                                                FunctionBox* funbox,
+                                                                InHandling inHandling,
+                                                                YieldHandling yieldHandling,
+                                                                FunctionSyntaxKind kind,
+                                                                Directives* newDirectives)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
     // Push a new ParseContext.
     SourceParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return false;
 
-    if (!functionFormalParametersAndBody(inHandling, yieldHandling, pn, kind))
+    if (!functionFormalParametersAndBody(inHandling, yieldHandling, funcNode, kind))
         return false;
 
     return leaveInnerFunction(outerpc);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun,
-                                           uint32_t toStringStart, InHandling inHandling,
-                                           YieldHandling yieldHandling, FunctionSyntaxKind kind,
-                                           GeneratorKind generatorKind,
-                                           FunctionAsyncKind asyncKind, bool tryAnnexB,
-                                           Directives inheritedDirectives,
-                                           Directives* newDirectives)
+GeneralParser<ParseHandler, CharT>::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)
 {
     // Note that it is possible for outerpc != this->pc, as we may be
     // attempting to syntax parse an inner function from an outer full
     // parser. In that case, outerpc is a SourceParseContext from the full parser
     // instead of the current top of the stack of the syntax parser.
 
-    FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives,
+    FunctionBox* funbox = newFunctionBox(funcNode, fun, toStringStart, inheritedDirectives,
                                          generatorKind, asyncKind);
     if (!funbox)
         return false;
     funbox->initWithEnclosingParseContext(outerpc, kind);
 
-    if (!innerFunction(pn, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives))
+    if (!innerFunctionForFunctionBox(funcNode, outerpc, funbox, inHandling, yieldHandling, kind,
+                                     newDirectives))
+    {
         return false;
+    }
 
     // Append possible Annex B function box only upon successfully parsing.
-    if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
-        return false;
+    if (tryAnnexB) {
+        if (!pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
+            return false;
+    }
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
+GeneralParser<ParseHandler, CharT>::appendToCallSiteObj(Node callSiteObj)
 {
     Node cookedNode = noSubstitutionTaggedTemplate();
     if (!cookedNode)
         return false;
 
     JSAtom* atom = tokenStream.getRawTemplateStringAtom();
     if (!atom)
         return false;
     Node rawNode = handler.newTemplateStringLiteral(atom, pos());
     if (!rawNode)
         return false;
 
     handler.addToCallSiteObject(callSiteObj, rawNode, cookedNode);
     return true;
 }
 
-template <>
+template <typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::standaloneLazyFunction(HandleFunction fun,
-                                                           uint32_t toStringStart,
-                                                           bool strict,
-                                                           GeneratorKind generatorKind,
-                                                           FunctionAsyncKind asyncKind)
+Parser<FullParseHandler, CharT>::standaloneLazyFunction(HandleFunction fun, uint32_t toStringStart,
+                                                        bool strict, GeneratorKind generatorKind,
+                                                        FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node pn = handler.newFunctionStatement(pos());
     if (!pn)
         return null();
 
     Directives directives(strict);
@@ -3616,23 +3649,23 @@ Parser<FullParseHandler, char16_t>::stan
     funbox->initFromLazyFunction();
 
     Directives newDirectives = directives;
     SourceParseContext funpc(this, funbox, &newDirectives);
     if (!funpc.init())
         return null();
 
     // Our tokenStream has no current token, so pn's position is garbage.
-    // Substitute the position of the first token in our source. If the function
-    // is a not-async arrow, use TokenStream::Operand to keep
+    // Substitute the position of the first token in our source.  If the
+    // function is a not-async arrow, use TokenStream::Operand to keep
     // verifyConsistentModifier from complaining (we will use
     // TokenStream::Operand in functionArguments).
-    TokenStream::Modifier modifier = (fun->isArrow() &&
-                                      asyncKind == FunctionAsyncKind::SyncFunction)
-                                     ? TokenStream::Operand : TokenStream::None;
+    Modifier modifier = (fun->isArrow() && asyncKind == FunctionAsyncKind::SyncFunction)
+                        ? TokenStream::Operand
+                        : TokenStream::None;
     if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
         return null();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     FunctionSyntaxKind syntaxKind = Statement;
     if (fun->isClassConstructor())
         syntaxKind = ClassConstructor;
     else if (fun->isMethod())
@@ -3652,36 +3685,37 @@ Parser<FullParseHandler, char16_t>::stan
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
-                                                             YieldHandling yieldHandling,
-                                                             Node pn, FunctionSyntaxKind kind,
-                                                             const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
-                                                             bool isStandaloneFunction /* = false */)
+GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling inHandling,
+                                                                    YieldHandling yieldHandling,
+                                                                    Node pn,
+                                                                    FunctionSyntaxKind kind,
+                                                                    const Maybe<uint32_t>& parameterListEnd /* = Nothing() */,
+                                                                    bool isStandaloneFunction /* = false */)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         AwaitHandling awaitHandling = funbox->isAsync() || (kind == Arrow && awaitIsKeyword())
                                       ? AwaitIsKeyword
                                       : AwaitIsName;
-        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
+        AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
         if (!functionArguments(yieldHandling, kind, pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3728,17 +3762,17 @@ Parser<ParseHandler, CharT>::functionFor
             if (!warnOnceAboutExprClosure())
                 return false;
 #else
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         bodyType = ExpressionBody;
         funbox->setIsExprBody();
     } else {
         openedPos = pos().begin;
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
@@ -3746,17 +3780,17 @@ Parser<ParseHandler, CharT>::functionFor
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
     // The same goes when parsing |await| in arrow functions.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind());
     bool inheritedStrict = pc->sc()->strict();
     Node body;
     {
-        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, bodyAwaitHandling);
+        AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, bodyAwaitHandling);
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     // Revalidate the function name when we transitioned to strict mode.
     if ((kind == Statement || IsFunctionExpression(kind)) &&
         fun->explicitName() &&
@@ -3776,33 +3810,33 @@ Parser<ParseHandler, CharT>::functionFor
             // because of different context.
             // It should already be checked before this point.
             nameYieldHandling = YieldIsName;
         }
 
         // We already use the correct await-handling at this point, therefore
         // we don't need call AutoAwaitIsKeyword here.
 
-        uint32_t nameOffset = handler.getFunctionNameOffset(pn, tokenStream);
+        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,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
                                                               JSMSG_CURLY_OPENED, openedPos));
-        funbox->setEnd(tokenStream);
+        funbox->setEnd(anyChars);
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
-        if (tokenStream.hadError())
+        if (anyChars.hadError())
             return false;
-        funbox->setEnd(tokenStream);
+        funbox->setEnd(anyChars);
         if (kind == Statement && !matchOrInsertSemicolon())
             return false;
     }
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
     if (!finishFunction(isStandaloneFunction))
@@ -3812,21 +3846,22 @@ Parser<ParseHandler, CharT>::functionFor
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling,
-                                          DefaultHandling defaultHandling,
-                                          FunctionAsyncKind asyncKind)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+GeneralParser<ParseHandler, CharT>::functionStmt(uint32_t toStringStart,
+                                                 YieldHandling yieldHandling,
+                                                 DefaultHandling defaultHandling,
+                                                 FunctionAsyncKind asyncKind)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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");
 
@@ -3855,17 +3890,17 @@ Parser<ParseHandler, CharT>::functionStm
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (defaultHandling == AllowDefaultName) {
         name = context->names().default_;
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         error(JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
 
     // Note the declared name and check for early errors.
     DeclarationKind kind;
@@ -3902,24 +3937,24 @@ Parser<ParseHandler, CharT>::functionStm
 
     YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
     return functionDefinition(pn, toStringStart, InAllowed, newYieldHandling, name, Statement,
                               generatorKind, asyncKind, tryAnnexB);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
-                                          ExpressionClosure expressionClosureHandling,
-                                          InvokedPrediction invoked,
-                                          FunctionAsyncKind asyncKind)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
-
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
+GeneralParser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart,
+                                                 ExpressionClosure expressionClosureHandling,
+                                                 InvokedPrediction invoked,
+                                                 FunctionAsyncKind asyncKind)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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) {
         generatorKind = GeneratorKind::Generator;
         if (!tokenStream.getToken(&tt))
@@ -3929,17 +3964,17 @@ Parser<ParseHandler, CharT>::functionExp
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     Node pn = handler.newFunctionExpression(pos());
     if (!pn)
         return null();
 
     if (invoked)
         pn = handler.setLikelyIIFE(pn);
@@ -3965,19 +4000,19 @@ IsEscapeFreeStringLiteral(const TokenPos
     /*
      * If the string's length in the source code is its length as a value,
      * accounting for the quotes, then it must not contain any escape
      * sequences or line continuations.
      */
     return pos.begin + str->length() + 2 == pos.end;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::asmJS(Node list)
+Parser<SyntaxParseHandler, CharT>::asmJS(Node list)
 {
     // While asm.js could technically be validated and compiled during syntax
     // parsing, we have no guarantee that some later JS wouldn't abort the
     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
     // For simplicity, unconditionally abort the syntax parse when "use asm" is
     // encountered so that asm.js is always validated/compiled exactly once
     // during a full parse.
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
@@ -3985,19 +4020,19 @@ Parser<SyntaxParseHandler, char16_t>::as
     // Record that the current script source constains some AsmJS, to disable
     // any incremental encoder, as AsmJS cannot be encoded with XDR at the
     // moment.
     if (ss)
         ss->setContainsAsmJS();
     return false;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::asmJS(Node list)
+Parser<FullParseHandler, CharT>::asmJS(Node list)
 {
     // Disable syntax parsing in anything nested inside the asm.js module.
     disableSyntaxParser();
 
     // We should be encountering the "use asm" directive for the first time; if
     // the directive is already, we must have failed asm.js validation and we're
     // reparsing. In that case, don't try to validate again. A non-null
     // newDirectives means we're not in a normal function.
@@ -4023,16 +4058,23 @@ Parser<FullParseHandler, char16_t>::asmJ
     if (!validated) {
         pc->newDirectives->setAsmJS();
         return false;
     }
 
     return true;
 }
 
+template <class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::asmJS(Node list)
+{
+    return asFinalParser()->asmJS(list);
+}
+
 /*
  * Recognize Directive Prologue members and directives. Assuming |pn| is a
  * candidate for membership in a directive prologue, recognize directives and
  * set |pc|'s flags accordingly. If |pn| is indeed part of a prologue, set its
  * |pn_prologue| flag.
  *
  * Note that the following is a strict mode function:
  *
@@ -4044,17 +4086,18 @@ Parser<FullParseHandler, char16_t>::asmJ
  * }
  *
  * That is, even though "use\x20loose" can never be a directive, now or in the
  * future (because of the hex escape), the Directive Prologue extends through it
  * to the "use strict" statement, which is indeed a directive.
  */
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective, bool* cont)
+GeneralParser<ParseHandler, CharT>::maybeParseDirective(Node list, Node possibleDirective,
+                                                        bool* cont)
 {
     TokenPos directivePos;
     JSAtom* directive = handler.isStringExprStatement(possibleDirective, &directivePos);
 
     *cont = !!directive;
     if (!*cont)
         return true;
 
@@ -4090,52 +4133,52 @@ Parser<ParseHandler, CharT>::maybeParseD
 
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc()->setExplicitUseStrict();
             if (!pc->sc()->strict()) {
                 // We keep track of the one possible strict violation that could
                 // occur in the directive prologue -- octal escapes -- and
                 // complain now.
-                if (tokenStream.sawOctalEscape()) {
+                if (anyChars.sawOctalEscape()) {
                     error(JSMSG_DEPRECATED_OCTAL);
                     return false;
                 }
                 pc->sc()->strictScript = true;
             }
         } else if (directive == context->names().useAsm) {
             if (pc->isFunctionBox())
                 return asmJS(list);
             return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL);
         }
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::statementList(YieldHandling yieldHandling)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
     bool canHaveDirectives = pc->atBodyLevel();
     if (canHaveDirectives)
-        tokenStream.clearSawOctalEscape();
+        anyChars.clearSawOctalEscape();
     bool afterReturn = false;
     bool warnedAboutStatementsAfterReturn = false;
     uint32_t statementBegin = 0;
     for (;;) {
         TokenKind tt = TOK_EOF;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
-            if (tokenStream.isEOF())
+            if (anyChars.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (tt == TOK_EOF || tt == TOK_RC) {
             TokenPos pos;
             if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand)) {
                 return null();
             }
@@ -4143,17 +4186,17 @@ Parser<ParseHandler, CharT>::statementLi
             break;
         }
         if (afterReturn) {
             if (!tokenStream.peekOffset(&statementBegin, TokenStream::Operand))
                 return null();
         }
         Node next = statementListItem(yieldHandling, canHaveDirectives);
         if (!next) {
-            if (tokenStream.isEOF())
+            if (anyChars.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (!warnedAboutStatementsAfterReturn) {
             if (afterReturn) {
                 if (!handler.isStatementPermittedAfterReturnStatement(next)) {
                     if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN))
                         return null();
@@ -4173,17 +4216,17 @@ Parser<ParseHandler, CharT>::statementLi
         handler.addStatementToList(pn, next);
     }
 
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
 {
     MUST_MATCH_TOKEN(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);
@@ -4193,18 +4236,18 @@ Parser<ParseHandler, CharT>::condition(I
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
-                                        MutableHandle<PropertyName*> label)
+GeneralParser<ParseHandler, CharT>::matchLabel(YieldHandling yieldHandling,
+                                               MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
     if (TokenKindIsPossibleIdentifier(tt)) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
@@ -4213,175 +4256,176 @@ Parser<ParseHandler, CharT>::matchLabel(
             return false;
     } else {
         label.set(nullptr);
     }
     return true;
 }
 
 template <class ParseHandler, typename CharT>
-Parser<ParseHandler, CharT>::PossibleError::PossibleError(Parser<ParseHandler, CharT>& parser)
+GeneralParser<ParseHandler, CharT>::PossibleError::PossibleError(GeneralParser<ParseHandler, CharT>& parser)
   : parser_(parser)
 {}
 
 template <class ParseHandler, typename CharT>
-typename Parser<ParseHandler, CharT>::PossibleError::Error&
-Parser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
+typename GeneralParser<ParseHandler, CharT>::PossibleError::Error&
+GeneralParser<ParseHandler, CharT>::PossibleError::error(ErrorKind kind)
 {
     if (kind == ErrorKind::Expression)
         return exprError_;
     if (kind == ErrorKind::Destructuring)
         return destructuringError_;
     MOZ_ASSERT(kind == ErrorKind::DestructuringWarning);
     return destructuringWarning_;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::setResolved(ErrorKind kind)
 {
     error(kind).state_ = ErrorState::None;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::hasError(ErrorKind kind)
 {
     return error(kind).state_ == ErrorState::Pending;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
+GeneralParser<ParseHandler, CharT>::PossibleError::hasPendingDestructuringError()
 {
     return hasError(ErrorKind::Destructuring);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
-                                                       unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPending(ErrorKind kind, const TokenPos& pos,
+                                                              unsigned errorNumber)
 {
     // Don't overwrite a previously recorded error.
     if (hasError(kind))
         return;
 
     // If we report an error later, we'll do it from the position where we set
     // the state to pending.
     Error& err = error(kind);
     err.offset_ = pos.begin;
     err.errorNumber_ = errorNumber;
     err.state_ = ErrorState::Pending;
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
-                                                                           unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringErrorAt(const TokenPos& pos,
+                                                                                  unsigned errorNumber)
 {
     setPending(ErrorKind::Destructuring, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
-                                                                             unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos,
+                                                                                    unsigned errorNumber)
 {
     setPending(ErrorKind::DestructuringWarning, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
-                                                                        unsigned errorNumber)
+GeneralParser<ParseHandler, CharT>::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos,
+                                                                               unsigned errorNumber)
 {
     setPending(ErrorKind::Expression, pos, errorNumber);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForError(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     parser_.errorAt(err.offset_, err.errorNumber_);
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForWarning(ErrorKind kind)
 {
     if (!hasError(kind))
         return true;
 
     Error& err = error(kind);
     return parser_.extraWarningAt(err.offset_, err.errorNumber_);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning()
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForDestructuringErrorOrWarning()
 {
     // Clear pending expression error, because we're definitely not in an
     // expression context.
     setResolved(ErrorKind::Expression);
 
     // Report any pending destructuring error or warning.
     return checkForError(ErrorKind::Destructuring) &&
            checkForWarning(ErrorKind::DestructuringWarning);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
+GeneralParser<ParseHandler, CharT>::PossibleError::checkForExpressionError()
 {
     // Clear pending destructuring error or warning, because we're definitely
     // not in a destructuring context.
     setResolved(ErrorKind::Destructuring);
     setResolved(ErrorKind::DestructuringWarning);
 
     // Report any pending expression error.
     return checkForError(ErrorKind::Expression);
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind, PossibleError* other)
+GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorTo(ErrorKind kind,
+                                                                   PossibleError* other)
 {
     if (hasError(kind) && !other->hasError(kind)) {
         Error& err = error(kind);
         Error& otherErr = other->error(kind);
         otherErr.offset_ = err.offset_;
         otherErr.errorNumber_ = err.errorNumber_;
         otherErr.state_ = err.state_;
     }
 }
 
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other)
+GeneralParser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* other)
 {
     MOZ_ASSERT(other);
     MOZ_ASSERT(this != other);
     MOZ_ASSERT(&parser_ == &other->parser_,
                "Can't transfer fields to an instance which belongs to a different parser");
 
     transferErrorTo(ErrorKind::Destructuring, other);
     transferErrorTo(ErrorKind::Expression, other);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
-                                                YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+GeneralParser<ParseHandler, CharT>::bindingInitializer(Node lhs, DeclarationKind kind,
+                                                       YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     if (kind == DeclarationKind::FormalParameter)
         pc->functionBox()->hasParameterExprs = true;
 
     Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!rhs)
         return null();
 
@@ -4394,33 +4438,35 @@ Parser<ParseHandler, CharT>::bindingInit
     if (foldConstants && !FoldConstants(context, &assign, this))
         return null();
 
     return assign;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::bindingIdentifier(DeclarationKind kind,
+                                                      YieldHandling yieldHandling)
 {
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
     if (!name)
         return null();
 
     Node binding = newName(name);
     if (!binding || !noteDeclaredName(name, kind, pos()))
         return null();
 
     return binding;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind,
-                                                        YieldHandling yieldHandling, TokenKind tt)
+GeneralParser<ParseHandler, CharT>::bindingIdentifierOrPattern(DeclarationKind kind,
+                                                               YieldHandling yieldHandling,
+                                                               TokenKind tt)
 {
     if (tt == TOK_LB)
         return arrayBindingPattern(kind, yieldHandling);
 
     if (tt == TOK_LC)
         return objectBindingPattern(kind, yieldHandling);
 
     if (!TokenKindIsPossibleIdentifierName(tt)) {
@@ -4428,37 +4474,37 @@ Parser<ParseHandler, CharT>::bindingIden
         return null();
     }
 
     return bindingIdentifier(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
-                                                  YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+GeneralParser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
+                                                         YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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) {
-            tokenStream.addModifierException(TokenStream::OperandIsNone);
+            anyChars.addModifierException(TokenStream::OperandIsNone);
             break;
         }
 
         if (tt == TOK_TRIPLEDOT) {
             tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenKind tt;
@@ -4472,17 +4518,17 @@ Parser<ParseHandler, CharT>::objectBindi
 
             Node inner = bindingIdentifier(kind, yieldHandling);
             if (!inner)
                 return null();
 
             if (!handler.addSpreadProperty(literal, begin, inner))
                 return null();
         } else {
-            TokenPos namePos = tokenStream.nextToken().pos;
+            TokenPos namePos = anyChars.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
             if (propType == PropertyType::Normal) {
                 // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|.
@@ -4556,19 +4602,20 @@ Parser<ParseHandler, CharT>::objectBindi
                                                           JSMSG_CURLY_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
+GeneralParser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind,
+                                                        YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LB));
 
     if (!CheckRecursionLimit(context))
         return null();
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin);
     if (!literal)
         return null();
@@ -4580,18 +4627,18 @@ Parser<ParseHandler, CharT>::arrayBindin
              return null();
          }
 
          TokenKind tt;
          if (!tokenStream.getToken(&tt))
              return null();
 
          if (tt == TOK_RB) {
-             tokenStream.ungetToken();
-             tokenStream.addModifierException(TokenStream::OperandIsNone);
+             anyChars.ungetToken();
+             anyChars.addModifierException(TokenStream::OperandIsNone);
              break;
          }
 
          if (tt == TOK_COMMA) {
              if (!handler.addElision(literal, pos()))
                  return null();
          } else if (tt == TOK_TRIPLEDOT) {
              uint32_t begin = pos().begin;
@@ -4644,33 +4691,33 @@ Parser<ParseHandler, CharT>::arrayBindin
                                                            JSMSG_BRACKET_OPENED, begin));
 
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
-                                                      YieldHandling yieldHandling,
-                                                      TokenKind tt)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+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
            ? arrayBindingPattern(kind, yieldHandling)
            : objectBindingPattern(kind, yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
-                                                                         YieldHandling yieldHandling,
-                                                                         TokenKind tt)
+GeneralParser<ParseHandler, CharT>::destructuringDeclarationWithoutYieldOrAwait(DeclarationKind kind,
+                                                                                YieldHandling yieldHandling,
+                                                                                TokenKind tt)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     uint32_t startAwaitOffset = pc->lastAwaitOffset;
     Node res = destructuringDeclaration(kind, yieldHandling, tt);
     if (res) {
         if (pc->lastYieldOffset != startYieldOffset) {
             errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
             return null();
@@ -4680,19 +4727,20 @@ Parser<ParseHandler, CharT>::destructuri
             return null();
         }
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+GeneralParser<ParseHandler, CharT>::blockStatement(YieldHandling yieldHandling,
+                                                   unsigned errorNumber)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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);
@@ -4703,36 +4751,36 @@ Parser<ParseHandler, CharT>::blockStatem
                                      reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
                                                           openedPos));
 
     return finishLexicalScope(scope, list);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
-                                                      YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
+                                                             YieldHandling yieldHandling)
 {
     MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF);
     Node pn = forHeadKind == PNK_FOROF
            ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
            : expr(InAllowed, yieldHandling, TripledotProhibited);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationPattern(DeclarationKind declKind, TokenKind tt,
-                                                bool initialDeclaration,
-                                                YieldHandling yieldHandling,
-                                                ParseNodeKind* forHeadKind,
-                                                Node* forInOrOfExpression)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
-               tokenStream.isCurrentTokenType(TOK_LC));
+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));
 
     Node pattern = destructuringDeclaration(declKind, yieldHandling, tt);
     if (!pattern)
         return null();
 
     if (initialDeclaration && forHeadKind) {
         bool isForIn, isForOf;
         if (!matchInOrOf(&isForIn, &isForOf))
@@ -4768,24 +4816,24 @@ Parser<ParseHandler, CharT>::declaration
 
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
     return handler.newAssignment(PNK_ASSIGN, pattern, init);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
-                                                          DeclarationKind declKind,
-                                                          bool initialDeclaration,
-                                                          YieldHandling yieldHandling,
-                                                          ParseNodeKind* forHeadKind,
-                                                          Node* forInOrOfExpression)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN));
+GeneralParser<ParseHandler, CharT>::initializerInNameDeclaration(Node binding,
+                                                                 DeclarationKind declKind,
+                                                                 bool initialDeclaration,
+                                                                 YieldHandling yieldHandling,
+                                                                 ParseNodeKind* forHeadKind,
+                                                                 Node* forInOrOfExpression)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_ASSIGN));
 
     uint32_t initializerOffset;
     if (!tokenStream.peekOffset(&initializerOffset, TokenStream::Operand))
         return false;
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                   yieldHandling, TripledotProhibited);
     if (!initializer)
@@ -4829,19 +4877,21 @@ Parser<ParseHandler, CharT>::initializer
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt,
-                                             bool initialDeclaration, YieldHandling yieldHandling,
-                                             ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
+GeneralParser<ParseHandler, CharT>::declarationName(DeclarationKind declKind, TokenKind tt,
+                                                    bool initialDeclaration,
+                                                    YieldHandling yieldHandling,
+                                                    ParseNodeKind* forHeadKind,
+                                                    Node* forInOrOfExpression)
 {
     // Anything other than possible identifier is an error.
     if (!TokenKindIsPossibleIdentifier(tt)) {
         error(JSMSG_NO_VARIABLE_NAME);
         return null();
     }
 
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
@@ -4909,20 +4959,20 @@ Parser<ParseHandler, CharT>::declaration
     if (!noteDeclaredName(name, declKind, namePos))
         return null();
 
     return binding;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
-                                             ParseNodeKind kind,
-                                             ParseNodeKind* forHeadKind /* = nullptr */,
-                                             Node* forInOrOfExpression /* = nullptr */)
+GeneralParser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
+                                                    ParseNodeKind kind,
+                                                    ParseNodeKind* forHeadKind /* = nullptr */,
+                                                    Node* forInOrOfExpression /* = nullptr */)
 {
     MOZ_ASSERT(kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST);
 
     DeclarationKind declKind;
     switch (kind) {
       case PNK_VAR:
         declKind = DeclarationKind::Var;
         break;
@@ -4971,17 +5021,18 @@ Parser<ParseHandler, CharT>::declaration
             return null();
     } while (moreDeclarations);
 
     return decl;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind)
+GeneralParser<ParseHandler, CharT>::lexicalDeclaration(YieldHandling yieldHandling,
+                                                       DeclarationKind kind)
 {
     MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let);
 
     /*
      * Parse body-level lets without a new block object. ES6 specs
      * that an execution environment's initial lexical environment
      * is the VariableEnvironment, i.e., body-level lets are in
      * the same environment record as vars.
@@ -4994,19 +5045,19 @@ Parser<ParseHandler, CharT>::lexicalDecl
     Node decl = declarationList(yieldHandling,
                                 kind == DeclarationKind::Const ? PNK_CONST : PNK_LET);
     if (!decl || !matchOrInsertSemicolon())
         return null();
 
     return decl;
 }
 
-template <>
+template <typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
+Parser<FullParseHandler, CharT>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
 {
     if (tt == 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;
@@ -5014,17 +5065,17 @@ Parser<FullParseHandler, char16_t>::name
             if (tt == TOK_RC)
                 break;
 
             if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_IMPORT_NAME);
                 return false;
             }
 
-            Rooted<PropertyName*> importName(context, tokenStream.currentName());
+            Rooted<PropertyName*> importName(context, anyChars.currentName());
             TokenPos importNamePos = pos();
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_AS))
                 return null();
 
             if (matched) {
                 TokenKind afterAs;
@@ -5111,30 +5162,21 @@ Parser<FullParseHandler, char16_t>::name
             return false;
 
         handler.addList(importSpecSet, importSpec);
     }
 
     return true;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::namedImportsOrNamespaceImport(TokenKind tt,
-                                                                    Node importSpecSet)
-{
-    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return false;
-}
-
-template<>
+template<typename CharT>
 ParseNode*
-Parser<FullParseHandler, char16_t>::importDeclaration()
-{
-    MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
+Parser<FullParseHandler, CharT>::importDeclaration()
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_IMPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
     TokenKind tt;
@@ -5215,156 +5257,211 @@ Parser<FullParseHandler, char16_t>::impo
     ParseNode* node =
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
 }
 
-template<>
-SyntaxParseHandler::Node
-Parser<SyntaxParseHandler, char16_t>::importDeclaration()
+template<typename CharT>
+inline SyntaxParseHandler::Node
+Parser<SyntaxParseHandler, CharT>::importDeclaration()
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
-template<>
+template <class ParseHandler, typename CharT>
+inline typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::importDeclaration()
+{
+    return asFinalParser()->importDeclaration();
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
+Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
     JSAutoByteString str;
     if (!AtomToPrintableString(context, exportName, &str))
         return false;
 
     error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
     return false;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedName(JSAtom* exportName)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedName(JSAtom* exportName)
+{
+    return asFinalParser()->checkExportedName(exportName);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNamesForDeclaration(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNamesForDeclaration(ParseNode* node)
 {
     MOZ_ASSERT(node->isArity(PN_LIST));
     for (ParseNode* binding = node->pn_head; binding; binding = binding->pn_next) {
         if (binding->isKind(PNK_ASSIGN))
             binding = binding->pn_left;
         MOZ_ASSERT(binding->isKind(PNK_NAME));
         if (!checkExportedName(binding->pn_atom))
             return false;
     }
 
     return true;
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNamesForDeclaration(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNamesForDeclaration(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
-bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForClause(ParseNode* node)
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNamesForDeclaration(Node node)
+{
+    return asFinalParser()->checkExportedNamesForDeclaration(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<FullParseHandler, CharT>::checkExportedNameForClause(ParseNode* node)
 {
     return checkExportedName(node->pn_atom);
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClause(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForClause(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForClause(Node node)
+{
+    return asFinalParser()->checkExportedNameForClause(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForFunction(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNameForFunction(ParseNode* node)
 {
     return checkExportedName(node->pn_funbox->function()->explicitName());
 }
 
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForFunction(Node node)
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForFunction(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForFunction(Node node)
+{
+    return asFinalParser()->checkExportedNameForFunction(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkExportedNameForClass(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkExportedNameForClass(ParseNode* node)
 {
     const ClassNode& cls = node->as<ClassNode>();
     MOZ_ASSERT(cls.names());
     return checkExportedName(cls.names()->innerBinding()->pn_atom);
 }
 
-template<>
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::checkExportedNameForClass(Node node)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return false;
+}
+
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkExportedNameForClass(Node node)
+{
+    return asFinalParser()->checkExportedNameForClass(node);
+}
+template<typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::checkExportedNameForClass(Node node)
+Parser<FullParseHandler, CharT>::processExport(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExport(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::processExport(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::processExport(Node node)
+{
+    return asFinalParser()->processExport(node);
+}
+
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::processExport(ParseNode* node)
-{
-    return pc->sc()->asModuleContext()->builder.processExport(node);
-}
-
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::processExport(Node node)
+Parser<FullParseHandler, CharT>::processExportFrom(ParseNode* node)
+{
+    return pc->sc()->asModuleContext()->builder.processExportFrom(node);
+}
+
+template<typename CharT>
+inline bool
+Parser<SyntaxParseHandler, CharT>::processExportFrom(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
-template<>
-bool
-Parser<FullParseHandler, char16_t>::processExportFrom(ParseNode* node)
-{
-    return pc->sc()->asModuleContext()->builder.processExportFrom(node);
-}
-
-template<>
-bool
-Parser<SyntaxParseHandler, char16_t>::processExportFrom(Node node)
-{
-    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
-    return false;
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::processExportFrom(Node node)
+{
+    return asFinalParser()->processExportFrom(node);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
+GeneralParser<ParseHandler, CharT>::exportFrom(uint32_t begin, Node specList)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FROM));
 
     if (!abortIfSyntaxParser())
         return null();
 
     MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
@@ -5380,22 +5477,22 @@ Parser<ParseHandler, CharT>::exportFrom(
     if (!processExportFrom(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportBatch(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportBatch(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_MUL));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
         return null();
 
     // Handle the form |export *| by adding a special export batch
     // specifier to the list.
     Node exportSpec = handler.newExportBatchSpec(pos());
@@ -5404,49 +5501,56 @@ Parser<ParseHandler, CharT>::exportBatch
 
     handler.addList(kid, exportSpec);
 
     MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR);
 
     return exportFrom(begin, kid);
 }
 
-template<>
+template<typename CharT>
 bool
-Parser<FullParseHandler, char16_t>::checkLocalExportNames(ParseNode* node)
+Parser<FullParseHandler, CharT>::checkLocalExportNames(ParseNode* node)
 {
     // ES 2017 draft 15.2.3.1.
     for (ParseNode* next = node->pn_head; next; next = next->pn_next) {
         ParseNode* name = next->pn_left;
         MOZ_ASSERT(name->isKind(PNK_NAME));
 
         RootedPropertyName ident(context, name->pn_atom->asPropertyName());
         if (!checkLocalExportName(ident, name->pn_pos.begin))
             return false;
     }
 
     return true;
 }
 
-template<>
+template<typename CharT>
 bool
-Parser<SyntaxParseHandler, char16_t>::checkLocalExportNames(Node node)
+Parser<SyntaxParseHandler, CharT>::checkLocalExportNames(Node node)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
     return false;
 }
 
+template<class ParseHandler, typename CharT>
+inline bool
+GeneralParser<ParseHandler, CharT>::checkLocalExportNames(Node node)
+{
+    return asFinalParser()->checkLocalExportNames(node);
+}
+
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportClause(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportClause(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LC));
 
     Node kid = handler.newList(PNK_EXPORT_SPEC_LIST, pos());
     if (!kid)
         return null();
 
     TokenKind tt;
     while (true) {
         // Handle the forms |export {}| and |export { ..., }| (where ... is non
@@ -5457,27 +5561,27 @@ Parser<ParseHandler, CharT>::exportClaus
         if (tt == TOK_RC)
             break;
 
         if (!TokenKindIsPossibleIdentifierName(tt)) {
             error(JSMSG_NO_BINDING_NAME);
             return null();
         }
 
-        Node bindingName = newName(tokenStream.currentName());
+        Node bindingName = newName(anyChars.currentName());
         if (!bindingName)
             return null();
 
         bool foundAs;
         if (!tokenStream.matchToken(&foundAs, TOK_AS))
             return null();
         if (foundAs)
             MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
 
-        Node exportName = newName(tokenStream.currentName());
+        Node exportName = newName(anyChars.currentName());
         if (!exportName)
             return null();
 
         if (!checkExportedNameForClause(exportName))
             return null();
 
         Node exportSpec = handler.newExportSpec(bindingName, exportName);
         if (!exportSpec)
@@ -5531,22 +5635,22 @@ Parser<ParseHandler, CharT>::exportClaus
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportVariableStatement(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_VAR));
 
     Node kid = declarationList(YieldIsName, PNK_VAR);
     if (!kid)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     if (!checkExportedNamesForDeclaration(kid))
         return null();
@@ -5558,23 +5662,24 @@ Parser<ParseHandler, CharT>::exportVaria
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
-                                                       FunctionAsyncKind asyncKind /* = SyncFunction */)
+GeneralParser<ParseHandler, CharT>::exportFunctionDeclaration(uint32_t begin,
+                                                              uint32_t toStringStart,
+                                                              FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FUNCTION));
 
     Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind);
     if (!kid)
         return null();
 
     if (!checkExportedNameForFunction(kid))
         return null();
 
@@ -5585,22 +5690,22 @@ Parser<ParseHandler, CharT>::exportFunct
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     Node kid = classDefinition(YieldIsName, ClassStatement, NameRequired);
     if (!kid)
         return null();
 
     if (!checkExportedNameForClass(kid))
         return null();
 
@@ -5611,24 +5716,24 @@ Parser<ParseHandler, CharT>::exportClass
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind)
+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, tokenStream.isCurrentTokenType(TOK_CONST));
-    MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TOK_CONST));
+    MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(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));
@@ -5638,24 +5743,24 @@ Parser<ParseHandler, CharT>::exportLexic
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
-                                                              uint32_t toStringStart,
-                                                              FunctionAsyncKind asyncKind /* = SyncFunction */)
+GeneralParser<ParseHandler, CharT>::exportDefaultFunctionDeclaration(uint32_t begin,
+                                                                     uint32_t toStringStart,
+                                                                     FunctionAsyncKind asyncKind /* = SyncFunction */)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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();
@@ -5663,22 +5768,22 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefaultClassDeclaration(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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();
@@ -5686,17 +5791,17 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefaultAssignExpr(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
     HandlePropertyName name = context->names().default_;
     Node nameNode = newName(name);
     if (!nameNode)
         return null();
@@ -5719,22 +5824,22 @@ Parser<ParseHandler, CharT>::exportDefau
     if (!processExport(node))
         return null();
 
     return node;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDefault(uint32_t begin)
+GeneralParser<ParseHandler, CharT>::exportDefault(uint32_t begin)
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT));
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_DEFAULT));
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (!checkExportedName(context->names().default_))
         return null();
 
@@ -5749,37 +5854,37 @@ Parser<ParseHandler, CharT>::exportDefau
 
         if (nextSameLine == TOK_FUNCTION) {
             uint32_t toStringStart = pos().begin;
             tokenStream.consumeKnownToken(TOK_FUNCTION);
             return exportDefaultFunctionDeclaration(begin, toStringStart,
                                                     FunctionAsyncKind::AsyncFunction);
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
       }
 
       case TOK_CLASS:
         return exportDefaultClassDeclaration(begin);
 
       default:
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return exportDefaultAssignExpr(begin);
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::exportDeclaration()
+GeneralParser<ParseHandler, CharT>::exportDeclaration()
 {
     if (!abortIfSyntaxParser())
         return null();
 
-    MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT);
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_EXPORT));
 
     if (!pc->atModuleLevel()) {
         error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
@@ -5830,32 +5935,32 @@ Parser<ParseHandler, CharT>::exportDecla
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
-                                                 InvokedPrediction invoked)
-{
-    tokenStream.ungetToken();
+GeneralParser<ParseHandler, CharT>::expressionStatement(YieldHandling yieldHandling,
+                                                        InvokedPrediction invoked)
+{
+    anyChars.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::consequentOrAlternative(YieldHandling yieldHandling)
 {
     TokenKind next;
     if (!tokenStream.peekToken(&next, TokenStream::Operand))
         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() {} }|.
@@ -5899,17 +6004,17 @@ Parser<ParseHandler, CharT>::consequentO
         return finishLexicalScope(scope, block);
     }
 
     return statement(yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::ifStatement(YieldHandling yieldHandling)
 {
     Vector<Node, 4> condList(context), thenList(context);
     Vector<uint32_t, 4> posList(context);
     Node elseBranch;
 
     ParseContext::Statement stmt(pc, StatementKind::If);
 
     while (true) {
@@ -5958,17 +6063,17 @@ Parser<ParseHandler, CharT>::ifStatement
             return null();
     }
 
     return elseBranch;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::doWhileStatement(YieldHandling yieldHandling)
+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);
     Node cond = condition(InAllowed, yieldHandling);
@@ -5984,55 +6089,54 @@ Parser<ParseHandler, CharT>::doWhileStat
     bool ignored;
     if (!tokenStream.matchToken(&ignored, TOK_SEMI, TokenStream::Operand))
         return null();
     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::whileStatement(YieldHandling yieldHandling)
 {
     uint32_t begin = pos().begin;
     ParseContext::Statement stmt(pc, StatementKind::WhileLoop);
     Node cond = condition(InAllowed, yieldHandling);
     if (!cond)
         return null();
     Node body = statement(yieldHandling);
     if (!body)
         return null();
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
+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;
     if (!*isForInp && !*isForOfp)
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
-                                          ParseNodeKind* forHeadKind,
-                                          Node* forInitialPart,
-                                          Maybe<ParseContext::Scope>& forLoopLexicalScope,
-                                          Node* forInOrOfExpression)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+GeneralParser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
+                                                 ParseNodeKind* forHeadKind, Node* forInitialPart,
+                                                 Maybe<ParseContext::Scope>& forLoopLexicalScope,
+                                                 Node* forInOrOfExpression)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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) {
@@ -6071,17 +6175,17 @@ Parser<ParseHandler, CharT>::forHeadStar
         tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return false;
 
         parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next);
         if (!parsingLexicalDeclaration) {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
             letIsIdentifier = true;
         }
     }
 
     if (parsingLexicalDeclaration) {
         forLoopLexicalScope.emplace(this);
         if (!forLoopLexicalScope->init(pc))
             return null();
@@ -6169,19 +6273,20 @@ Parser<ParseHandler, CharT>::forHeadStar
     // Finally, parse the iterated expression, making the for-loop's closing
     // ')' the next token.
     *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
     return *forInOrOfExpression != null();
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
+GeneralParser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_FOR));
+
     uint32_t begin = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     IteratorKind iterKind = IteratorKind::Sync;
     unsigned iflags = 0;
 
     if (pc->isAsync()) {
@@ -6322,19 +6427,19 @@ Parser<ParseHandler, CharT>::forStatemen
     if (forLoopLexicalScope)
         return finishLexicalScope(*forLoopLexicalScope, forLoop);
 
     return forLoop;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_SWITCH));
+GeneralParser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_SWITCH));
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
 
     Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
     if (!discriminant)
         return null();
 
@@ -6429,19 +6534,19 @@ Parser<ParseHandler, CharT>::switchState
 
     handler.setEndPosition(caseList, pos().end);
 
     return handler.newSwitchStatement(begin, discriminant, caseList);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CONTINUE));
+GeneralParser<ParseHandler, CharT>::continueStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CONTINUE));
     uint32_t begin = pos().begin;
 
     RootedPropertyName label(context);
     if (!matchLabel(yieldHandling, &label))
         return null();
 
     // Labeled 'continue' statements target the nearest labeled loop
     // statements with the same label. Unlabeled 'continue' statements target
@@ -6487,19 +6592,19 @@ Parser<ParseHandler, CharT>::continueSta
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_BREAK));
+GeneralParser<ParseHandler, CharT>::breakStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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
@@ -6527,19 +6632,19 @@ Parser<ParseHandler, CharT>::breakStatem
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
+GeneralParser<ParseHandler, CharT>::returnStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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.
@@ -6564,19 +6669,19 @@ Parser<ParseHandler, CharT>::returnState
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
+GeneralParser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     MOZ_ASSERT(pc->isGenerator());
     MOZ_ASSERT(pc->isFunctionBox());
 
     pc->lastYieldOffset = begin;
 
     Node exprNode;
@@ -6597,17 +6702,17 @@ Parser<ParseHandler, CharT>::yieldExpres
       case TOK_RC:
       case TOK_RB:
       case TOK_RP:
       case TOK_COLON:
       case TOK_COMMA:
       case TOK_IN:
         // No value.
         exprNode = null();
-        tokenStream.addModifierException(TokenStream::NoneIsOperand);
+        anyChars.addModifierException(TokenStream::NoneIsOperand);
         break;
       case TOK_MUL:
         kind = PNK_YIELD_STAR;
         tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
         MOZ_FALLTHROUGH;
       default:
         exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
         if (!exprNode)
@@ -6615,19 +6720,19 @@ Parser<ParseHandler, CharT>::yieldExpres
     }
     if (kind == PNK_YIELD_STAR)
         return handler.newYieldStarExpression(begin, exprNode);
     return handler.newYieldExpression(begin, exprNode);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
+GeneralParser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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()) {
@@ -6653,17 +6758,17 @@ Parser<ParseHandler, CharT>::withStateme
 
     pc->sc()->setBindingsAccessedDynamically();
 
     return handler.newWithStatement(begin, objectExpr, innerBlock);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::labeledItem(YieldHandling yieldHandling)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     if (tt == TOK_FUNCTION) {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
@@ -6682,23 +6787,23 @@ Parser<ParseHandler, CharT>::labeledItem
         if (pc->sc()->strict()) {
             error(JSMSG_FUNCTION_LABEL);
             return null();
         }
 
         return functionStmt(pos().begin, yieldHandling, NameRequired);
     }
 
-    tokenStream.ungetToken();
+    anyChars.ungetToken();
     return statement(yieldHandling);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::labeledStatement(YieldHandling yieldHandling)
 {
     RootedPropertyName label(context, labelIdentifier(yieldHandling));
     if (!label)
         return null();
 
     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
         return stmt->label() == label;
     };
@@ -6718,19 +6823,19 @@ Parser<ParseHandler, CharT>::labeledStat
     if (!pn)
         return null();
 
     return handler.newLabeledStatement(label, pn, begin);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
+GeneralParser<ParseHandler, CharT>::throwStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
         error(JSMSG_MISSING_EXPR_AFTER_THROW);
@@ -6748,19 +6853,19 @@ Parser<ParseHandler, CharT>::throwStatem
     if (!matchOrInsertSemicolon())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_TRY));
+GeneralParser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(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
      *
@@ -6894,30 +6999,30 @@ Parser<ParseHandler, CharT>::tryStatemen
         finallyBlock = finishLexicalScope(scope, finallyBlock);
         if (!finallyBlock)
             return null();
 
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     } else {
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
     if (!catchScope && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
     return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
-                                                 ParseContext::Scope& catchParamScope)
+GeneralParser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
+                                                        ParseContext::Scope& catchParamScope)
 {
     uint32_t openedPos = pos().begin;
 
     ParseContext::Statement stmt(pc, StatementKind::Block);
 
     // ES 13.15.7 CatchClauseEvaluation
     //
     // Step 8 means that the body of a catch block always has an additional
@@ -6942,17 +7047,17 @@ Parser<ParseHandler, CharT>::catchBlockS
     // 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);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::debuggerStatement()
+GeneralParser<ParseHandler, CharT>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
     if (!matchOrInsertSemicolon())
         return null();
     p.end = pos().end;
 
     pc->sc()->setBindingsAccessedDynamically();
@@ -6981,46 +7086,46 @@ ToAccessorType(PropertyType propType)
         return AccessorType::None;
       default:
         MOZ_CRASH("unexpected property type");
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
-                                             ClassContext classContext,
-                                             DefaultHandling defaultHandling)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
+GeneralParser<ParseHandler, CharT>::classDefinition(YieldHandling yieldHandling,
+                                                    ClassContext classContext,
+                                                    DefaultHandling defaultHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_CLASS));
 
     uint32_t classStartOffset = pos().begin;
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (classContext == ClassStatement) {
         if (defaultHandling == AllowDefaultName) {
             name = context->names().default_;
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
         } else {
             // Class statements must have a bound name
             error(JSMSG_UNNAMED_CLASS_STMT);
             return null();
         }
     } else {
         // Make sure to put it back, whatever it was
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
     }
 
     // Push a ParseContext::ClassStatement to keep track of the constructor
     // funbox.
     ParseContext::ClassStatement classStmt(pc);
 
     RootedAtom propAtom(context);
 
@@ -7078,19 +7183,19 @@ Parser<ParseHandler, CharT>::classDefini
                 tokenStream.consumeKnownToken(tt);
                 error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
                 return null();
             }
 
             if (tt != TOK_LP)
                 isStatic = true;
             else
-                tokenStream.ungetToken();
+                anyChars.ungetToken();
         } else {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
         }
 
         uint32_t nameOffset;
         if (!tokenStream.peekOffset(&nameOffset))
             return null();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom);
@@ -7126,28 +7231,28 @@ Parser<ParseHandler, CharT>::classDefini
             errorAt(nameOffset, JSMSG_BAD_METHOD_DEF);
             return null();
         }
 
         RootedAtom funName(context);
         switch (propType) {
           case PropertyType::GetterNoExpressionClosure:
           case PropertyType::SetterNoExpressionClosure:
-            if (!tokenStream.isCurrentTokenType(TOK_RB)) {
+            if (!anyChars.isCurrentTokenType(TOK_RB)) {
                 funName = prefixAccessorName(propType, propAtom);
                 if (!funName)
                     return null();
             }
             break;
           case PropertyType::Constructor:
           case PropertyType::DerivedConstructor:
             funName = name;
             break;
           default:
-            if (!tokenStream.isCurrentTokenType(TOK_RB))
+            if (!anyChars.isCurrentTokenType(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);
@@ -7210,19 +7315,19 @@ Parser<ParseHandler, CharT>::classDefini
     MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock,
                             TokenPos(classStartOffset, classEndOffset));
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
+GeneralParser<ParseHandler, CharT>::nextTokenContinuesLetDeclaration(TokenKind next)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
 
     // Destructuring continues a let declaration.
@@ -7241,29 +7346,29 @@ Parser<ParseHandler, CharT>::nextTokenCo
     // parse tree with ASI applied.  No backsies!
 
     // Otherwise a let declaration must have a name.
     return TokenKindIsPossibleIdentifier(next);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
     if (!matchOrInsertSemicolon())
         return null();
     return vars;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7282,17 +7387,17 @@ Parser<ParseHandler, CharT>::statement(Y
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
 
       // ExpressionStatement[?Yield].
 
       case TOK_YIELD: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
-        TokenStream::Modifier modifier;
+        Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
@@ -7470,18 +7575,18 @@ Parser<ParseHandler, CharT>::statement(Y
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
-                                               bool canHaveDirectives /* = false */)
+GeneralParser<ParseHandler, CharT>::statementListItem(YieldHandling yieldHandling,
+                                                      bool canHaveDirectives /* = false */)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -7500,28 +7605,28 @@ Parser<ParseHandler, CharT>::statementLi
       case 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:
-        if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
+        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: {
         // Don't use a ternary operator here due to obscure linker issues
         // around using static consts in the arms of a ternary.
-        TokenStream::Modifier modifier;
+        Modifier modifier;
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
@@ -7663,20 +7768,20 @@ Parser<ParseHandler, CharT>::statementLi
         return null();
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                                  TripledotHandling tripledotHandling,
-                                  PossibleError* possibleError /* = nullptr */,
-                                  InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHandling,
+                                         TripledotHandling tripledotHandling,
+                                         PossibleError* possibleError /* = nullptr */,
+                                         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))
@@ -7703,17 +7808,17 @@ Parser<ParseHandler, CharT>::expr(InHand
 
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt != TOK_ARROW) {
                     error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
                     return null();
                 }
 
-                tokenStream.ungetToken();  // put back right paren
+                anyChars.ungetToken();  // put back right paren
                 break;
             }
         }
 
         // Additional calls to assignExpr should not reuse the possibleError
         // which had been passed into the function. Otherwise we would lose
         // information needed to determine whether or not we're dealing with
         // a non-recoverable situation.
@@ -7788,21 +7893,21 @@ Precedence(ParseNodeKind pnk) {
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                    TripledotHandling tripledotHandling,
-                                    ExpressionClosure expressionClosureHandling,
-                                    PossibleError* possibleError,
-                                    InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                           TripledotHandling tripledotHandling,
+                                           ExpressionClosure expressionClosureHandling,
+                                           PossibleError* possibleError,
+                                           InvokedPrediction invoked /* = PredictUninvoked */)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
@@ -7867,30 +7972,30 @@ Parser<ParseHandler, CharT>::orExpr(InHa
         kindStack[depth] = pnk;
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
     // When the next token is no longer a binary operator, it's potentially the
     // start of an expression.  Add a modifier exception so that the next token
     // modifier can be Operand.
-    tokenStream.ungetToken();
-    tokenStream.addModifierException(TokenStream::OperandIsNone);
+    anyChars.ungetToken();
+    anyChars.addModifierException(TokenStream::OperandIsNone);
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <class ParseHandler, typename CharT>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                      TripledotHandling tripledotHandling,
-                                      ExpressionClosure expressionClosureHandling,
-                                      PossibleError* possibleError,
-                                      InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                             TripledotHandling tripledotHandling,
+                                             ExpressionClosure expressionClosureHandling,
+                                             PossibleError* possibleError,
+                                             InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node condition = orExpr(inHandling, yieldHandling, tripledotHandling,
                             expressionClosureHandling, possibleError, invoked);
     if (!condition)
         return null();
 
     if (handler.isExpressionClosure(condition))
         return condition;
@@ -7911,20 +8016,20 @@ Parser<ParseHandler, CharT>::condExpr(In
     if (!elseExpr)
         return null();
 
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                        TripledotHandling tripledotHandling,
-                                        PossibleError* possibleError /* = nullptr */,
-                                        InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                                               TripledotHandling tripledotHandling,
+                                               PossibleError* possibleError /* = nullptr */,
+                                               InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
@@ -7957,17 +8062,17 @@ Parser<ParseHandler, CharT>::assignExpr(
             return identifierReference(name);
         }
     }
 
     if (firstToken == TOK_NUMBER) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
-            return newNumber(tokenStream.currentToken());
+            return newNumber(anyChars.currentToken());
     }
 
     if (firstToken == TOK_STRING) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return stringLiteral();
     }
@@ -7980,21 +8085,21 @@ Parser<ParseHandler, CharT>::assignExpr(
         TokenKind nextSameLine = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
         if (TokenKindIsPossibleIdentifier(nextSameLine))
             maybeAsyncArrow = true;
     }
 
-    tokenStream.ungetToken();
+    anyChars.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
-    TokenStream::Position start(keepAtoms);
+    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);
@@ -8035,34 +8140,34 @@ Parser<ParseHandler, CharT>::assignExpr(
 
     if (isArrow) {
         tokenStream.seek(start);
 
         TokenKind next;
         if (!tokenStream.getToken(&next, TokenStream::Operand))
             return null();
         uint32_t toStringStart = pos().begin;
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
 
         FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction;
 
         if (next == TOK_ASYNC) {
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
             TokenKind nextSameLine = 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)
                 asyncKind = FunctionAsyncKind::AsyncFunction;
             else
-                tokenStream.ungetToken();
+                anyChars.ungetToken();
         }
 
         Node pn = handler.newArrowFunction(pos());
         if (!pn)
             return null();
 
         return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr, Arrow,
                                   GeneratorKind::NotGenerator, asyncKind);
@@ -8082,25 +8187,25 @@ Parser<ParseHandler, CharT>::assignExpr(
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    break;
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    break;
 
       default:
-        MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
+        MOZ_ASSERT(!anyChars.isCurrentTokenAssignment());
         if (!possibleError) {
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
 
-        tokenStream.ungetToken();
+        anyChars.ungetToken();
         return lhs;
     }
 
     // Verify the left-hand side expression doesn't have a forbidden form.
     if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
         if (kind != PNK_ASSIGN) {
             error(JSMSG_BAD_DESTRUCT_ASS);
             return null();
@@ -8139,18 +8244,18 @@ Parser<ParseHandler, CharT>::assignExpr(
     if (kind == PNK_ASSIGN)
         handler.checkAndSetIsDirectRHSAnonFunction(rhs);
 
     return handler.newAssignment(kind, lhs, rhs);
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node,
-                                                           FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
+GeneralParser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node,
+                                                                  FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
 {
     // Note that this method implements *only* a boolean test.  Reporting an
     // error for the various syntaxes that fail this, and warning for the
     // various syntaxes that "pass" this but should not, occurs elsewhere.
 
     if (handler.isName(node)) {
         if (!pc->sc()->strict())
             return true;
@@ -8166,30 +8271,30 @@ Parser<ParseHandler, CharT>::isValidSimp
             return true;
     }
 
     return false;
 }
 
 template <class ParseHandler, typename CharT>
 const char*
-Parser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node)
+GeneralParser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node)
 {
     MOZ_ASSERT(handler.isName(node), "must only call this function on known names");
 
     if (handler.isEvalName(node, context))
         return js_eval_str;
     if (handler.isArgumentsName(node, context))
         return js_arguments_str;
     return nullptr;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
+GeneralParser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
 {
     if (handler.isName(operand)) {
         if (const char* chars = nameIsArgumentsOrEval(operand)) {
             if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
     } else if (handler.isPropertyAccess(operand)) {
         // Permitted: no additional testing/fixup needed.
@@ -8207,32 +8312,32 @@ Parser<ParseHandler, CharT>::checkIncDec
 
     MOZ_ASSERT(isValidSimpleAssignmentTarget(operand, PermitAssignmentToFunctionCalls),
                "inconsistent increment/decrement operand validation");
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind,
-                                         uint32_t begin)
+GeneralParser<ParseHandler, CharT>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind,
+                                                uint32_t begin)
 {
     Node kid = unaryExpr(yieldHandling, TripledotProhibited, ExpressionClosure::Forbidden);
     if (!kid)
         return null();
     return handler.newUnary(kind, begin, kid);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
-                                       TripledotHandling tripledotHandling,
-                                       ExpressionClosure expressionClosureHandling,
-                                       PossibleError* possibleError /* = nullptr */,
-                                       InvokedPrediction invoked /* = PredictUninvoked */)
+GeneralParser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
+                                              TripledotHandling tripledotHandling,
+                                              ExpressionClosure expressionClosureHandling,
+                                              PossibleError* possibleError /* = nullptr */,
+                                              InvokedPrediction invoked /* = PredictUninvoked */)
 {
     if (!CheckRecursionLimit(context))
         return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
@@ -8341,17 +8446,17 @@ Parser<ParseHandler, CharT>::unaryExpr(Y
                                  begin, expr);
       }
     }
 }
 
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
+GeneralParser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
     uint32_t startAwaitOffset = pc->lastAwaitOffset;
     Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (res) {
         if (pc->lastYieldOffset != startYieldOffset) {
             errorAt(pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
             return null();
@@ -8361,19 +8466,19 @@ Parser<ParseHandler, CharT>::assignExprW
             return null();
         }
     }
     return res;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
-                                          bool* isSpread,
-                                          PossibleError* possibleError /* = nullptr */)
+GeneralParser<ParseHandler, CharT>::argumentList(YieldHandling yieldHandling, Node listNode,
+                                                 bool* isSpread,
+                                                 PossibleError* possibleError /* = nullptr */)
 {
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
         return false;
     if (matched) {
         handler.setEndPosition(listNode, pos().end);
         return true;
     }
@@ -8416,34 +8521,34 @@ Parser<ParseHandler, CharT>::argumentLis
     MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_ARGS);
 
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 bool
-Parser<ParseHandler, CharT>::checkAndMarkSuperScope()
+GeneralParser<ParseHandler, CharT>::checkAndMarkSuperScope()
 {
     if (!pc->sc()->allowSuperProperty())
         return false;
     pc->setSuperScopeNeedsHomeObject();
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
-Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
-                                        TripledotHandling tripledotHandling,
-                                        ExpressionClosure expressionClosureHandling,
-                                        TokenKind tt, bool allowCallSyntax /* = true */,
-                                        PossibleError* possibleError /* = nullptr */,
-                                        InvokedPrediction invoked /* = PredictUninvoked */)
-{
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
+GeneralParser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
+                                               TripledotHandling tripledotHandling,
+                                               ExpressionClosure expressionClosureHandling,
+                                               TokenKind tt, bool allowCallSyntax /* = true */,
+                                               PossibleError* possibleError /* = nullptr */,
+                                               InvokedPrediction invoked /* = PredictUninvoked */)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
 
     Node lhs;
 
     if (!CheckRecursionLimit(context))
         return null();
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
@@ -8451,17 +8556,17 @@ Parser<ParseHandler, CharT>::memberExpr(
         // Make sure this wasn't a |new.target| in disguise.
         Node newTarget;
         if (!tryNewTarget(newTarget))
             return null();
         if (newTarget) {
             lhs = newTarget;
         } else {
             // Gotten by tryNewTarget
-            tt = tokenStream.currentToken().type;
+            tt = anyChars.currentToken().type;
             Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited,
                                        ExpressionClosure::Forbidden, tt,
                                        /* allowCallSyntax = */ false,
                                        /* possibleError = */ nullptr, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
             lhs = handler.newNewExpression(newBegin, ctorExpr);
@@ -8491,30 +8596,30 @@ Parser<ParseHandler, CharT>::memberExpr(
                           possibleError, invoked);
         if (!lhs)
             return null();
 
         if (handler.isExpressionClosure(lhs))
             return lhs;
     }
 
-    MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
+    MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_EOF)
             break;
 
         Node nextMember;
         if (tt == TOK_DOT) {
             if (!tokenStream.getToken(&tt))
                 return null();
             if (TokenKindIsPossibleIdentifierName(tt)) {
-                PropertyName* field = tokenStream.currentName();
+                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 {
@@ -8643,17 +8748,17 @@ Parser<ParseHandler, CharT>::memberExpr(
                     }
                 } else {
                     if (!taggedTemplate(yieldHandling, nextMember, tt))
                         return null();
                 }
                 handler.setOp(nextMember, op);
             }
         } else {
-            tokenStream.ungetToken();
+            anyChars.ungetToken();
             if (handler.isSuperBase(lhs))
                 break;
             return lhs;
         }<