Merge mozilla-inbound to mozilla-central r=merge a=merge
authorDorel Luca <dluca@mozilla.com>
Thu, 14 Dec 2017 11:54:15 +0200
changeset 396388 8062887ff0d9382ea84177f2c21f62dc0e613d9e
parent 396267 57a108d1c90a9979e2b79dbf138e055ee07ed97b (current diff)
parent 396387 4c6d8b6d91b1af04198a2ad6f22f69afad1be62a (diff)
child 396389 16bcfaad13e10377b1e5ba557da0545b4b3b4f75
child 396451 4e77f1dbb4aab48927499aec590136996db10fb3
child 396458 dde3cd07dc9fa7ca1de32b1d90b833c8dc5f0b68
push id56975
push userdluca@mozilla.com
push dateThu, 14 Dec 2017 09:59:07 +0000
treeherderautoland@16bcfaad13e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
8062887ff0d9 / 59.0a1 / 20171214100315 / files
nightly linux64
8062887ff0d9 / 59.0a1 / 20171214100315 / files
nightly mac
8062887ff0d9 / 59.0a1 / 20171214100315 / files
nightly win32
8062887ff0d9 / 59.0a1 / 20171214100315 / files
nightly win64
8062887ff0d9 / 59.0a1 / 20171214100315 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central r=merge a=merge
dom/workers/WorkerPrefs.h
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) {