Merge mozilla-central to autoland
authorIris Hsiao <ihsiao@mozilla.com>
Thu, 04 May 2017 13:22:44 +0800
changeset 572568 e10fdc12c2414653c37c0a504e24bb8f013418fa
parent 572567 581dcfa1f86f5f17c1fbb565761fdd725fa60ad3 (current diff)
parent 572425 4a6a71f4aa22e4dc3961884ce505ce34bdd799a2 (diff)
child 572569 a8d597ee6dd58306e62e55a07c4ab20958726d6a
push id57112
push userbmo:jacheng@mozilla.com
push dateThu, 04 May 2017 09:50:00 +0000
milestone55.0a1
Merge mozilla-central to autoland
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/test/general/browser.ini
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -678,16 +678,20 @@ pref("plugin.default.state", 1);
 // Plugins bundled in XPIs are enabled by default.
 pref("plugin.defaultXpi.state", 2);
 
 // Flash is enabled by default, and Java is click-to-activate by default on
 // all channels.
 pref("plugin.state.flash", 2);
 pref("plugin.state.java", 1);
 
+#ifdef NIGHTLY_BUILD
+pref("plugins.flashBlock.enabled", true);
+#endif
+
 #ifdef XP_WIN
 pref("browser.preferences.instantApply", false);
 #else
 pref("browser.preferences.instantApply", true);
 #endif
 
 // Toggling Search bar on and off in about:preferences
 pref("browser.preferences.search", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1534,23 +1534,23 @@ var gBrowserInit = {
     // tabs and to postpone saving the pref to disk.
     try {
       const startupCrashEndDelay = 30 * 1000;
       setTimeout(Services.startup.trackStartupCrashEnd, startupCrashEndDelay);
     } catch (ex) {
       Cu.reportError("Could not end startup crash tracking: " + ex);
     }
 
-    // Delay this a minute because there's no rush
-    setTimeout(() => {
+    // Delay this a minute into the idle time because there's no rush.
+    requestIdleCallback(() => {
       this.gmpInstallManager = new GMPInstallManager();
       // We don't really care about the results, if someone is interested they
       // can check the log.
       this.gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
-    }, 1000 * 60);
+    }, {timeout: 1000 * 60});
 
     // Report via telemetry whether we're able to play MP4/H.264/AAC video.
     // We suspect that some Windows users have a broken or have not installed
     // Windows Media Foundation, and we'd like to know how many. We'd also like
     // to know how good our coverage is on other platforms.
     // Note: we delay by 90 seconds reporting this, as calling canPlayType()
     // on Windows will cause DLLs to load, i.e. cause disk I/O.
     setTimeout(() => {
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -15,16 +15,17 @@ support-files =
   audio.ogg
   browser_bug479408_sample.html
   browser_bug678392-1.html
   browser_bug678392-2.html
   browser_bug970746.xhtml
   browser_registerProtocolHandler_notification.html
   browser_star_hsts.sjs
   browser_tab_dragdrop2_frame1.xul
+  browser_tab_dragdrop_embed.html
   browser_web_channel.html
   browser_web_channel_iframe.html
   bug592338.html
   bug792517-2.html
   bug792517.html
   bug792517.sjs
   bug839103.css
   clipboard_pastefile.html
@@ -490,17 +491,17 @@ support-files =
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tabReorder.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tab_detach_restore.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tab_drag_drop_perwindow.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tab_dragdrop.js]
-skip-if = buildapp == 'mulet' || (e10s && (debug || os == 'linux')) # Bug 1312436
+skip-if = buildapp == 'mulet' || (e10s && debug) # Bug 1312436
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tab_dragdrop2.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tabbar_big_widgets.js]
 skip-if = os == "linux" || os == "mac" # No tabs in titlebar on linux
                                        # Disabled on OS X because of bug 967917
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_tabfocus.js]
--- a/browser/base/content/test/general/browser_tab_dragdrop.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop.js
@@ -33,17 +33,17 @@ function loadURI(tab, url) {
 }
 
 // Creates a framescript which caches the current object value from the plugin
 // in the page. checkObjectValue below verifies that the framescript is still
 // active for the browser and that the cached value matches that from the plugin
 // in the page which tells us the plugin hasn't been reinitialized.
 function* cacheObjectValue(browser) {
   yield ContentTask.spawn(browser, null, function*() {
-    let plugin = content.document.wrappedJSObject.body.firstChild;
+    let plugin = content.document.getElementById("p").wrappedJSObject;
     info(`plugin is ${plugin}`);
     let win = content.document.defaultView;
     info(`win is ${win}`);
     win.objectValue = plugin.getObjectValue();
     info(`got objectValue: ${win.objectValue}`);
     win.checkObjectValueListener = () => {
       let result;
       let exception;
@@ -96,33 +96,32 @@ function checkObjectValue(browser) {
     };
 
     mm.addMessageListener("Test:CheckObjectValueResult", listener);
     mm.sendAsyncMessage("Test:CheckObjectValue");
   });
 }
 
 add_task(function*() {
-  let embed = '<embed type="application/x-test" allowscriptaccess="always" allowfullscreen="true" wmode="window" width="640" height="480"></embed>'
   setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
 
   // create a few tabs
   let tabs = [
     gBrowser.tabs[0],
     gBrowser.addTab("about:blank", {skipAnimation: true}),
     gBrowser.addTab("about:blank", {skipAnimation: true}),
     gBrowser.addTab("about:blank", {skipAnimation: true}),
     gBrowser.addTab("about:blank", {skipAnimation: true})
   ];
 
   // Initially 0 1 2 3 4
   yield loadURI(tabs[1], "data:text/html;charset=utf-8,<title>tab1</title><body>tab1<iframe>");
   yield loadURI(tabs[2], "data:text/plain;charset=utf-8,tab2");
   yield loadURI(tabs[3], "data:text/html;charset=utf-8,<title>tab3</title><body>tab3<iframe>");
-  yield loadURI(tabs[4], "data:text/html;charset=utf-8,<body onload='clicks=0' onclick='++clicks'>" + embed);
+  yield loadURI(tabs[4], "http://example.com/browser/browser/base/content/test/general/browser_tab_dragdrop_embed.html");
   yield BrowserTestUtils.switchTab(gBrowser, tabs[3]);
 
   swapTabsAndCloseOther(2, 3); // now: 0 1 2 4
   is(gBrowser.tabs[1], tabs[1], "tab1");
   is(gBrowser.tabs[2], tabs[3], "tab3");
   is(gBrowser.tabs[3], tabs[4], "tab4");
   delete tabs[2];
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_tab_dragdrop_embed.html
@@ -0,0 +1,2 @@
+<body onload="clicks=0" onclick="++clicks">
+  <embed type="application/x-test" allowscriptaccess="always" allowfullscreen="true" wmode="window" width="640" height="480" id="p"></embed>
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -12,18 +12,28 @@ const {
   createNode,
   createSVGNode,
   moveInfobar,
 } = require("./utils/markup");
 const {
   getCurrentZoom,
   getDisplayPixelRatio,
   setIgnoreLayoutChanges,
+  getNodeBounds,
   getViewportDimensions,
 } = require("devtools/shared/layout/utils");
+const {
+  identity,
+  apply,
+  translate,
+  multiply,
+  scale,
+  getNodeTransformationMatrix,
+  getNodeTransformOrigin
+} = require("devtools/shared/layout/dom-matrix-2d");
 const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
 
 const DEFAULT_GRID_COLOR = "#4B0082";
 
 const COLUMNS = "cols";
 const ROWS = "rows";
@@ -68,46 +78,109 @@ const gCachedGridPattern = new Map();
 //
 // Note:
 // Once bug 1232491 lands, we could try to refactor this code to use the values from
 // the displayport API instead.
 //
 // Using a fixed value should also solve bug 1348293.
 const CANVAS_SIZE = 4096;
 
+// This constant is used as value to draw infinite lines on canvas; since we cannot use
+// the canvas boundaries as coordinates to draw the lines, and then applying
+// transformations on top of them (the resulting coordinates might ending before reaching
+// the viewport's edges, therefore the lines won't looks as "infinite").
+const CANVAS_INFINITY = CANVAS_SIZE << 8;
+
+/**
+ * Draws a line to the context given, applying a transformation matrix if passed.
+ *
+ * @param {CanvasRenderingContext2D} ctx
+ *        The 2d canvas context.
+ * @param {Number} x1
+ *        The x-axis of the coordinate for the begin of the line.
+ * @param {Number} y1
+ *        The y-axis of the coordinate for the begin of the line.
+ * @param {Number} x2
+ *        The x-axis of the coordinate for the end of the line.
+ * @param {Number} y2
+ *        The y-axis of the coordinate for the end of the line.
+ * @param {Array} [matrix=identity()]
+ *        The transformation matrix to apply.
+ */
+function drawLine(ctx, x1, y1, x2, y2, matrix = identity()) {
+  let fromPoint = apply(matrix, [x1, y1]);
+  let toPoint = apply(matrix, [x2, y2]);
+
+  ctx.moveTo(Math.round(fromPoint[0]), Math.round(fromPoint[1]));
+  ctx.lineTo(Math.round(toPoint[0]), Math.round(toPoint[1]));
+}
+
+/**
+ * Draws a rect to the context given, applying a transformation matrix if passed.
+ * The coordinates are the start and end points of the rectangle's diagonal.
+ *
+ * @param {CanvasRenderingContext2D} ctx
+ *        The 2d canvas context.
+ * @param {Number} x1
+ *        The x-axis coordinate of the rectangle's diagonal start point.
+ * @param {Number} y1
+ *        The y-axis coordinate of the rectangle's diagonal start point.
+ * @param {Number} x2
+ *        The x-axis coordinate of the rectangle's diagonal end point.
+ * @param {Number} y2
+ *        The y-axis coordinate of the rectangle's diagonal end point.
+ * @param {Array} [matrix=identity()]
+ *        The transformation matrix to apply.
+ */
+function drawRect(ctx, x1, y1, x2, y2, matrix = identity()) {
+  let p = [
+    [x1, y1],
+    [x2, y1],
+    [x2, y2],
+    [x1, y2]
+  ].map(point => apply(matrix, point).map(Math.round));
+
+  ctx.beginPath();
+  ctx.moveTo(p[0][0], p[0][1]);
+  ctx.lineTo(p[1][0], p[1][1]);
+  ctx.lineTo(p[2][0], p[2][1]);
+  ctx.lineTo(p[3][0], p[3][1]);
+  ctx.closePath();
+}
+
 /**
  * Utility method to draw a rounded rectangle in the provided canvas context.
  *
  * @param  {CanvasRenderingContext2D} ctx
  *         The 2d canvas context.
  * @param  {Number} x
  *         The x-axis origin of the rectangle.
  * @param  {Number} y
  *         The y-axis origin of the rectangle.
  * @param  {Number} width
  *         The width of the rectangle.
  * @param  {Number} height
  *         The height of the rectangle.
  * @param  {Number} radius
  *         The radius of the rounding.
  */
-const roundedRect = function (ctx, x, y, width, height, radius) {
+function drawRoundedRect(ctx, x, y, width, height, radius) {
   ctx.beginPath();
   ctx.moveTo(x, y + radius);
   ctx.lineTo(x, y + height - radius);
   ctx.arcTo(x, y + height, x + radius, y + height, radius);
   ctx.lineTo(x + width - radius, y + height);
   ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
   ctx.lineTo(x + width, y + radius);
   ctx.arcTo(x + width, y, x + width - radius, y, radius);
   ctx.lineTo(x + radius, y);
   ctx.arcTo(x, y, x, y + radius, radius);
   ctx.stroke();
   ctx.fill();
-};
+}
 
 /**
  * The CssGridHighlighter is the class that overlays a visual grid on top of
  * display:[inline-]grid elements.
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
@@ -657,21 +730,23 @@ CssGridHighlighter.prototype = extend(Au
     // Updates the <canvas> element's position and size.
     // It also clear the <canvas>'s drawing context.
     this.updateCanvasElement();
 
     // Clear the grid area highlights.
     this.clearGridAreas();
     this.clearGridCell();
 
+    // Update the current matrix used in our canvas' rendering
+    this.updateCurrentMatrix();
+
     // Start drawing the grid fragments.
     for (let i = 0; i < this.gridData.length; i++) {
       let fragment = this.gridData[i];
-      let quad = this.currentQuads.content[i];
-      this.renderFragment(fragment, quad);
+      this.renderFragment(fragment);
     }
 
     // Display the grid area highlights if needed.
     if (this.options.showAllGridAreas) {
       this.showAllGridAreas();
     } else if (this.options.showGridArea) {
       this.showGridArea(this.options.showGridArea);
     }
@@ -871,16 +946,59 @@ CssGridHighlighter.prototype = extend(Au
     // Resize the canvas taking the dpr into account so as to have crisp lines, and
     // translating it to give the perception that it always covers the viewport.
     this.canvas.setAttribute("style",
       `width:${size}px;height:${size}px; transform: translate(${x}px, ${y}px);`);
 
     this.ctx.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
   },
 
+  /**
+   * Updates the current matrix taking in account the following transformations, in this
+   * order:
+   *   1. The scale given by the display pixel ratio.
+   *   2. The translation to the top left corner of the element.
+   *   3. The scale given by the current zoom.
+   *   4. Any CSS transformation applied directly to the element (only 2D
+   *      transformation; the 3D transformation are flattened, see `dom-matrix-2d` module
+   *      for further details.)
+   *
+   *  The transformations of the element's ancestors are not currently computed (see
+   *  bug 1355675).
+   */
+  updateCurrentMatrix() {
+    let origin = getNodeTransformOrigin(this.currentNode);
+    let bounds = getNodeBounds(this.win, this.currentNode);
+    let nodeMatrix = getNodeTransformationMatrix(this.currentNode);
+
+    let ox = origin[0];
+    let oy = origin[1];
+
+    let m = identity();
+
+    // First, we scale based on the display's current pixel ratio.
+    m = multiply(m, scale(getDisplayPixelRatio(this.win)));
+    // Then we translate the origin to the node's top left corner.
+    m = multiply(m, translate(bounds.p1.x, bounds.p1.y));
+    // And scale based on the current zoom factor.
+    m = multiply(m, scale(getCurrentZoom(this.win)));
+    // Finally, we can apply the current node's transformation matrix, taking in account
+    // the `transform-origin` property.
+    if (nodeMatrix) {
+      m = multiply(m, translate(ox, oy));
+      m = multiply(m, nodeMatrix);
+      m = multiply(m, translate(-ox, -oy));
+      this.hasNodeTransformations = true;
+    } else {
+      this.hasNodeTransformations = false;
+    }
+
+    this.currentMatrix = m;
+  },
+
   getFirstRowLinePos(fragment) {
     return fragment.rows.lines[0].start;
   },
 
   getLastRowLinePos(fragment) {
     return fragment.rows.lines[fragment.rows.lines.length - 1].start;
   },
 
@@ -906,29 +1024,29 @@ CssGridHighlighter.prototype = extend(Au
     while (trackIndex >= 0 && tracks[trackIndex].type != "explicit") {
       trackIndex--;
     }
 
     // The grid line index is the grid track index + 1.
     return trackIndex + 1;
   },
 
-  renderFragment(fragment, quad) {
-    this.renderLines(fragment.cols, quad, COLUMNS, "left", "top", "height",
+  renderFragment(fragment) {
+    this.renderLines(fragment.cols, COLUMNS, "left", "top", "height",
                      this.getFirstRowLinePos(fragment),
                      this.getLastRowLinePos(fragment));
-    this.renderLines(fragment.rows, quad, ROWS, "top", "left", "width",
+    this.renderLines(fragment.rows, ROWS, "top", "left", "width",
                      this.getFirstColLinePos(fragment),
                      this.getLastColLinePos(fragment));
 
     // Line numbers are rendered in a 2nd step to avoid overlapping with existing lines.
     if (this.options.showGridLineNumbers) {
-      this.renderLineNumbers(fragment.cols, quad, COLUMNS, "left", "top",
+      this.renderLineNumbers(fragment.cols, COLUMNS, "left", "top",
                        this.getFirstRowLinePos(fragment));
-      this.renderLineNumbers(fragment.rows, quad, ROWS, "top", "left",
+      this.renderLineNumbers(fragment.rows, ROWS, "top", "left",
                        this.getFirstColLinePos(fragment));
     }
   },
 
   /**
    * Render the grid lines given the grid dimension information of the
    * column or row lines.
    *
@@ -947,32 +1065,31 @@ CssGridHighlighter.prototype = extend(Au
    * @param  {String} mainSize
    *         The main size of the given grid dimension - "width" for rows and
    *         "height" for columns.
    * @param  {Number} startPos
    *         The start position of the cross side of the grid dimension.
    * @param  {Number} endPos
    *         The end position of the cross side of the grid dimension.
    */
-  renderLines(gridDimension, {bounds}, dimensionType, mainSide, crossSide,
+  renderLines(gridDimension, dimensionType, mainSide, crossSide,
               mainSize, startPos, endPos) {
-    let currentZoom = getCurrentZoom(this.win);
-    let lineStartPos = (bounds[crossSide] / currentZoom) + startPos;
-    let lineEndPos = (bounds[crossSide] / currentZoom) + endPos;
+    let lineStartPos = startPos;
+    let lineEndPos = endPos;
 
     if (this.options.showInfiniteLines) {
       lineStartPos = 0;
       lineEndPos = Infinity;
     }
 
     let lastEdgeLineIndex = this.getLastEdgeLineIndex(gridDimension.tracks);
 
     for (let i = 0; i < gridDimension.lines.length; i++) {
       let line = gridDimension.lines[i];
-      let linePos = (bounds[mainSide] / currentZoom) + line.start;
+      let linePos = line.start;
 
       if (i == 0 || i == lastEdgeLineIndex) {
         this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType, "edge");
       } else {
         this.renderLine(linePos, lineStartPos, lineEndPos, dimensionType,
                         gridDimension.tracks[i - 1].type);
       }
 
@@ -987,27 +1104,23 @@ CssGridHighlighter.prototype = extend(Au
   },
 
   /**
    * Render the grid lines given the grid dimension information of the
    * column or row lines.
    *
    * see @param for renderLines.
    */
-  renderLineNumbers(gridDimension, {bounds}, dimensionType, mainSide, crossSide,
+  renderLineNumbers(gridDimension, dimensionType, mainSide, crossSide,
               startPos) {
-    let zoom = getCurrentZoom(this.win);
-    let lineStartPos = (bounds[crossSide] / zoom) + startPos;
-    if (this.options.showInfiniteLines) {
-      lineStartPos = 0;
-    }
+    let lineStartPos = startPos;
 
     for (let i = 0; i < gridDimension.lines.length; i++) {
       let line = gridDimension.lines[i];
-      let linePos = (bounds[mainSide] / zoom) + line.start;
+      let linePos = line.start;
       this.renderGridLineNumber(line.number, linePos, lineStartPos, line.breadth,
         dimensionType);
     }
   },
 
   /**
    * Render the grid line on the css grid highlighter canvas.
    *
@@ -1026,33 +1139,41 @@ CssGridHighlighter.prototype = extend(Au
   renderLine(linePos, startPos, endPos, dimensionType, lineType) {
     let { devicePixelRatio } = this.win;
     let lineWidth = getDisplayPixelRatio(this.win);
     let offset = (lineWidth / 2) % 1;
 
     let x = Math.round(this._canvasPosition.x * devicePixelRatio);
     let y = Math.round(this._canvasPosition.y * devicePixelRatio);
 
-    linePos = Math.round(linePos * devicePixelRatio);
-    startPos = Math.round(startPos * devicePixelRatio);
+    linePos = Math.round(linePos);
+    startPos = Math.round(startPos);
 
     this.ctx.save();
     this.ctx.setLineDash(GRID_LINES_PROPERTIES[lineType].lineDash);
     this.ctx.beginPath();
     this.ctx.translate(offset - x, offset - y);
     this.ctx.lineWidth = lineWidth;
 
     if (dimensionType === COLUMNS) {
-      endPos = isFinite(endPos) ? endPos * devicePixelRatio : CANVAS_SIZE + y;
-      this.ctx.moveTo(linePos, startPos);
-      this.ctx.lineTo(linePos, endPos);
+      if (isFinite(endPos)) {
+        endPos = Math.round(endPos);
+      } else {
+        endPos = CANVAS_INFINITY;
+        startPos = -endPos;
+      }
+      drawLine(this.ctx, linePos, startPos, linePos, endPos, this.currentMatrix);
     } else {
-      endPos = isFinite(endPos) ? endPos * devicePixelRatio : CANVAS_SIZE + x;
-      this.ctx.moveTo(startPos, linePos);
-      this.ctx.lineTo(endPos, linePos);
+      if (isFinite(endPos)) {
+        endPos = Math.round(endPos);
+      } else {
+        endPos = CANVAS_INFINITY;
+        startPos = -endPos;
+      }
+      drawLine(this.ctx, startPos, linePos, endPos, linePos, this.currentMatrix);
     }
 
     this.ctx.strokeStyle = this.color;
     this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;
 
     this.ctx.stroke();
     this.ctx.restore();
   },
@@ -1068,32 +1189,33 @@ CssGridHighlighter.prototype = extend(Au
    * @param  {Number} startPos
    *         The start position of the cross side of the grid line.
    * @param  {Number} breadth
    *         The grid line breadth value.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
    */
   renderGridLineNumber(lineNumber, linePos, startPos, breadth, dimensionType) {
+    let displayPixelRatio = getDisplayPixelRatio(this.win);
     let { devicePixelRatio } = this.win;
-    let displayPixelRatio = getDisplayPixelRatio(this.win);
+    let offset = (displayPixelRatio / 2) % 1;
 
-    linePos = Math.round(linePos * devicePixelRatio);
-    startPos = Math.round(startPos * devicePixelRatio);
-    breadth = Math.round(breadth * devicePixelRatio);
+    linePos = Math.round(linePos);
+    startPos = Math.round(startPos);
+    breadth = Math.round(breadth);
 
     if (linePos + breadth < 0) {
       // The line is not visible on screen, don't render the line number
       return;
     }
 
     this.ctx.save();
     let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
     let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
-    this.ctx.translate(.5 - canvasX, .5 - canvasY);
+    this.ctx.translate(offset - canvasX, offset - canvasY);
 
     let fontSize = (GRID_FONT_SIZE * displayPixelRatio);
     this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY;
 
     let textWidth = this.ctx.measureText(lineNumber).width;
 
     // The width of the character 'm' approximates the height of the text.
     let textHeight = this.ctx.measureText("m").width;
@@ -1102,36 +1224,42 @@ CssGridHighlighter.prototype = extend(Au
     let padding = 3 * displayPixelRatio;
 
     let boxWidth = textWidth + 2 * padding;
     let boxHeight = textHeight + 2 * padding;
 
     // Calculate the x & y coordinates for the line number container, so that it is
     // centered on the line, and in the middle of the gap if there is any.
     let x, y;
+
     if (dimensionType === COLUMNS) {
-      x = linePos - boxWidth / 2;
-      y = startPos - boxHeight / 2;
-      x += breadth / 2;
+      x = linePos + breadth / 2;
+      y = startPos;
     } else {
-      x = startPos - boxWidth / 2;
-      y = linePos - boxHeight / 2;
-      y += breadth / 2;
+      x = startPos;
+      y = linePos + breadth / 2;
     }
 
-    x = Math.max(x, padding);
-    y = Math.max(y, padding);
+    [x, y] = apply(this.currentMatrix, [x, y]);
+
+    x -= boxWidth / 2;
+    y -= boxHeight / 2;
 
-    // Draw a rounded rectangle with a border width of 4 pixels, a border color matching
+    if (!this.hasNodeTransformations) {
+      x = Math.max(x, padding);
+      y = Math.max(y, padding);
+    }
+
+    // Draw a rounded rectangle with a border width of 2 pixels, a border color matching
     // the grid color and a white background (the line number will be written in black).
     this.ctx.lineWidth = 2 * displayPixelRatio;
     this.ctx.strokeStyle = this.color;
     this.ctx.fillStyle = "white";
     let radius = 2 * displayPixelRatio;
-    roundedRect(this.ctx, x, y, boxWidth, boxHeight, radius);
+    drawRoundedRect(this.ctx, x, y, boxWidth, boxHeight, radius);
 
     // Write the line number inside of the rectangle.
     this.ctx.fillStyle = "black";
     this.ctx.fillText(lineNumber, x + padding, y + textHeight + padding);
 
     this.ctx.restore();
   },
 
@@ -1147,34 +1275,50 @@ CssGridHighlighter.prototype = extend(Au
    *         The end position of the cross side of the grid line.
    * @param  {Number} breadth
    *         The grid line breadth value.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
    */
   renderGridGap(linePos, startPos, endPos, breadth, dimensionType) {
     let { devicePixelRatio } = this.win;
-    let x = Math.round(this._canvasPosition.x * devicePixelRatio);
-    let y = Math.round(this._canvasPosition.y * devicePixelRatio);
+    let displayPixelRatio = getDisplayPixelRatio(this.win);
+    let offset = (displayPixelRatio / 2) % 1;
 
-    linePos = Math.round(linePos * devicePixelRatio);
-    startPos = Math.round(startPos * devicePixelRatio);
-    breadth = Math.round(breadth * devicePixelRatio);
+    let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
+    let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
+
+    linePos = Math.round(linePos);
+    startPos = Math.round(startPos);
+    breadth = Math.round(breadth);
 
     this.ctx.save();
     this.ctx.fillStyle = this.getGridGapPattern(devicePixelRatio, dimensionType);
-    this.ctx.translate(.5 - x, .5 - y);
+    this.ctx.translate(offset - canvasX, offset - canvasY);
 
     if (dimensionType === COLUMNS) {
-      endPos = isFinite(endPos) ? Math.round(endPos * devicePixelRatio) : CANVAS_SIZE + y;
-      this.ctx.fillRect(linePos, startPos, breadth, endPos - startPos);
+      if (isFinite(endPos)) {
+        endPos = Math.round(endPos);
+      } else {
+        endPos = this._winDimensions.height;
+        startPos = -endPos;
+      }
+      drawRect(this.ctx, linePos, startPos, linePos + breadth, endPos,
+        this.currentMatrix);
     } else {
-      endPos = isFinite(endPos) ? Math.round(endPos * devicePixelRatio) : CANVAS_SIZE + x;
-      this.ctx.fillRect(startPos, linePos, endPos - startPos, breadth);
+      if (isFinite(endPos)) {
+        endPos = Math.round(endPos);
+      } else {
+        endPos = this._winDimensions.width;
+        startPos = -endPos;
+      }
+      drawRect(this.ctx, startPos, linePos, endPos, linePos + breadth,
+        this.currentMatrix);
     }
+    this.ctx.fill();
     this.ctx.restore();
   },
 
   /**
    * Render the grid area highlight for the given area name or for all the grid areas.
    *
    * @param  {String} areaName
    *         Name of the grid area to be highlighted. If no area name is provided, all
new file mode 100644
--- /dev/null
+++ b/devtools/shared/layout/dom-matrix-2d.js
@@ -0,0 +1,158 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/**
+ * Returns a matrix for the scaling given.
+ * Calling `scale()` or `scale(1) returns a new identity matrix.
+ *
+ * @param {Number} [sx = 1]
+ *        the abscissa of the scaling vector.
+ *        If unspecified, it will equal to `1`.
+ * @param {Number} [sy = sx]
+ *        The ordinate of the scaling vector.
+ *        If not present, its default value is `sx`, leading to a uniform scaling.
+ * @return {Array}
+ *         The new matrix.
+ */
+const scale = (sx = 1, sy = sx) => [
+  sx, 0, 0,
+  0, sy, 0,
+  0, 0, 1
+];
+exports.scale = scale;
+
+/**
+ * Returns a matrix for the translation given.
+ * Calling `translate()` or `translate(0) returns a new identity matrix.
+ *
+ * @param {Number} [tx = 0]
+ *        The abscissa of the translating vector.
+ *        If unspecified, it will equal to `0`.
+ * @param {Number} [ty = tx]
+ *        The ordinate of the translating vector.
+ *        If unspecified, it will equal to `tx`.
+ * @return {Array}
+ *         The new matrix.
+ */
+const translate = (tx = 0, ty = tx) => [
+  1, 0, tx,
+  0, 1, ty,
+  0, 0, 1
+];
+exports.translate = translate;
+
+/**
+ * Returns a new identity matrix.
+ *
+ * @return {Array}
+ *         The new matrix.
+ */
+const identity = () => [
+  1, 0, 0,
+  0, 1, 0,
+  0, 0, 1
+];
+exports.identity = identity;
+
+/**
+ * Multiplies two matrices and returns a new matrix with the result.
+ *
+ * @param {Array} M1
+ *        The first operand.
+ * @param {Array} M2
+ *        The second operand.
+ * @return {Array}
+ *        The resulting matrix.
+ */
+const multiply = (M1, M2) => {
+  let c11 = M1[0] * M2[0] + M1[1] * M2[3] + M1[2] * M2[6];
+  let c12 = M1[0] * M2[1] + M1[1] * M2[4] + M1[2] * M2[7];
+  let c13 = M1[0] * M2[2] + M1[1] * M2[5] + M1[2] * M2[8];
+
+  let c21 = M1[3] * M2[0] + M1[4] * M2[3] + M1[5] * M2[6];
+  let c22 = M1[3] * M2[1] + M1[4] * M2[4] + M1[5] * M2[7];
+  let c23 = M1[3] * M2[2] + M1[4] * M2[5] + M1[5] * M2[8];
+
+  let c31 = M1[6] * M2[0] + M1[7] * M2[3] + M1[8] * M2[6];
+  let c32 = M1[6] * M2[1] + M1[7] * M2[4] + M1[8] * M2[7];
+  let c33 = M1[6] * M2[2] + M1[7] * M2[5] + M1[8] * M2[8];
+
+  return [
+    c11, c12, c13,
+    c21, c22, c23,
+    c31, c32, c33
+  ];
+};
+exports.multiply = multiply;
+
+/**
+ * Applies the given matrix to a point.
+ *
+ * @param {Array} M
+ *        The matrix to apply.
+ * @param {Array} P
+ *        The point's vector.
+ * @return {Array}
+ *        The resulting point's vector.
+ */
+const apply = (M, P) => [
+  M[0] * P[0] + M[1] * P[1] + M[2],
+  M[3] * P[0] + M[4] * P[1] + M[5],
+];
+exports.apply = apply;
+
+/**
+ * Returns the transformation origin point for the given node.
+ *
+ * @param {DOMNode} node
+ *        The node.
+ * @return {Array}
+ *        The transformation origin point.
+ */
+function getNodeTransformOrigin(node) {
+  let origin = node.ownerGlobal.getComputedStyle(node).transformOrigin;
+
+  return origin.split(/ /).map(parseFloat);
+}
+exports.getNodeTransformOrigin = getNodeTransformOrigin;
+
+/**
+ * Returns the transformation matrix for the given node.
+ *
+ * @param {DOMNode} node
+ *        The node.
+ * @return {Array}
+ *        The transformation matrix.
+ */
+function getNodeTransformationMatrix(node) {
+  let t = node.ownerGlobal.getComputedStyle(node).transform;
+
+  if (t === "none") {
+    return null;
+  }
+
+  // We're assuming is a 2d matrix.
+  let m = t.substring(t.indexOf("(") + 1, t.length - 1).split(/,\s*/).map(Number);
+  let [a, b, c, d, e, f] = m;
+
+  // If the length is 16, it's a 3d matrix: in that case we'll extrapolate only the values
+  // we need for the 2D transformation; this cover the scenario where 3D CSS properties
+  // are used only for HW acceleration on 2D transformation.
+  if (m.length === 16) {
+    c = m[4];
+    d = m[5];
+    e = m[12];
+    f = m[13];
+  }
+
+  return [
+    a, c, e,
+    b, d, f,
+    0, 0, 1
+  ];
+}
+
+exports.getNodeTransformationMatrix = getNodeTransformationMatrix;
--- a/devtools/shared/layout/moz.build
+++ b/devtools/shared/layout/moz.build
@@ -1,9 +1,10 @@
 # -*- 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/.
 
 DevToolsModules(
+    'dom-matrix-2d.js',
     'utils.js'
 )
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -352,31 +352,34 @@ function getNodeBounds(boundaryWindow, n
     if (el.scrollLeft) {
       offsetLeft -= el.scrollLeft;
     }
     el = el.parentNode;
   }
 
   // And add the potential frame offset if the node is nested
   let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
-  xOffset += offsetLeft + scrollX;
-  yOffset += offsetTop + scrollY;
-
-  xOffset *= scale;
-  yOffset *= scale;
+  xOffset += (offsetLeft + scrollX) * scale;
+  yOffset += (offsetTop + scrollY) * scale;
 
   // Get the width and height
   let width = node.offsetWidth * scale;
   let height = node.offsetHeight * scale;
 
   return {
     p1: {x: xOffset, y: yOffset},
     p2: {x: xOffset + width, y: yOffset},
     p3: {x: xOffset + width, y: yOffset + height},
-    p4: {x: xOffset, y: yOffset + height}
+    p4: {x: xOffset, y: yOffset + height},
+    top: yOffset,
+    right: xOffset + width,
+    bottom: yOffset + height,
+    left: xOffset,
+    width,
+    height
   };
 }
 exports.getNodeBounds = getNodeBounds;
 
 /**
  * Same as doing iframe.contentWindow but works with all types of container
  * elements that act like frames (e.g. <embed>), where 'contentWindow' isn't a
  * property that can be accessed.
--- a/devtools/shared/tests/mochitest/chrome.ini
+++ b/devtools/shared/tests/mochitest/chrome.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
 tags = devtools
 skip-if = os == 'android'
 
 [test_css-logic-getCssPath.html]
 [test_css-logic.html]
 [test_devtools_extensions.html]
+[test_dom_matrix_2d.html]
 [test_eventemitter_basic.html]
 skip-if = os == 'linux' && debug # Bug 1205739
new file mode 100644
--- /dev/null
+++ b/devtools/shared/tests/mochitest/test_dom_matrix_2d.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<html>
+  <!--
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1297072
+  -->
+  <head>
+    <meta charset="utf8">
+    <title>Testing 2d matrix utility functions for DOM</title>
+
+    <script type="application/javascript"
+            src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css"
+          href="chrome://mochikit/content/tests/SimpleTest/test.css">
+    <style>
+      #element {
+        position: absolute;
+        top: 20px;
+        left: 10px;
+        width: 320px;
+        height: 200px;
+        background: salmon;
+      }
+    </style>
+  </head>
+
+  <body>
+    <div id="element"></div>
+
+    <script type="application/javascript">
+      "use strict";
+
+      const { utils: Cu } = Components;
+      const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+      const {
+        getNodeTransformationMatrix,
+        getNodeTransformOrigin
+      } = require("devtools/shared/layout/dom-matrix-2d");
+
+      let element = document.getElementById("element");
+
+      testNodeTransformOrigin(element);
+      testNodeTransformationMatrix(element);
+
+      function testNodeTransformOrigin(node) {
+        isDeeply(getNodeTransformOrigin(node), [160, 100],
+          "Default Transform Origin is correct.");
+
+        node.style.transformOrigin = "left 10%";
+        isDeeply(getNodeTransformOrigin(node), [0, 20],
+          "Transform Origin is properly computed.");
+
+        node.style.transformOrigin = "invalid";
+        isDeeply(getNodeTransformOrigin(node), [0, 20],
+          "Invalid values are ignored.");
+
+        node.style.transformOrigin = "left 5px -3px";
+        isDeeply(getNodeTransformOrigin(node), [0, 5, -3],
+          "3D coordinates and negative numbers are properly computed.");
+      }
+
+      function testNodeTransformationMatrix(node) {
+        is(getNodeTransformationMatrix(node), null,
+          "Default Transformation Matrix is `null`");
+
+        node.style.transform = "translate(10%, 20px)";
+        isDeeply(getNodeTransformationMatrix(node), [ 1, 0, 32, 0, 1, 20, 0, 0, 1 ],
+          "Transformation Matrix properly computed with translation.");
+
+        node.style.transform = "translate(10%, 20px) scale(2)";
+        isDeeply(getNodeTransformationMatrix(node), [ 2, 0, 32, 0, 2, 20, 0, 0, 1 ],
+          "Transformation Matrix properly translated and scaled.");
+
+        node.style.transform = "scale(2) translate(10%, 20px)";
+        isDeeply(getNodeTransformationMatrix(node), [ 2, 0, 64, 0, 2, 40, 0, 0, 1 ],
+          "Transformation Matrix properly scaled and translated.");
+
+        node.style.transform = "translate3d(12px, 50%, 3em)";
+        isDeeply(getNodeTransformationMatrix(node), [ 1, 0, 12, 0, 1, 100, 0, 0, 1 ],
+          "3D Transformation Matrix are handled for 2D values.");
+      }
+    </script>
+  </body>
+</html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3142,25 +3142,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       mCreatingInnerWindow = false;
       createdInnerWindow = true;
 
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (currentInner && currentInner->GetWrapperPreserveColor()) {
       if (oldDoc == aDocument) {
-        // Move the navigator from the old inner window to the new one since
-        // this is a document.write. This is safe from a same-origin point of
-        // view because document.write can only be used by the same origin.
-        newInnerWindow->mNavigator = currentInner->mNavigator;
-        currentInner->mNavigator = nullptr;
-        if (newInnerWindow->mNavigator) {
-          newInnerWindow->mNavigator->SetWindow(newInnerWindow->AsInner());
-        }
-
         // Make a copy of the old window's performance object on document.open.
         // Note that we have to force eager creation of it here, because we need
         // to grab the current document channel and whatnot before that changes.
         currentInner->AsInner()->CreatePerformanceObjectIfNeeded();
         if (currentInner->mPerformance) {
           newInnerWindow->mPerformance =
             Performance::CreateForMainThread(newInnerWindow->AsInner(),
                                              currentInner->mPerformance->GetDOMTiming(),
--- a/dom/plugins/test/mochitest/chrome.ini
+++ b/dom/plugins/test/mochitest/chrome.ini
@@ -22,13 +22,12 @@ skip-if = !crashreporter
 [test_hangui.xul]
 skip-if = (!crashreporter) || (os != "win")
 support-files = hangui_subpage.html hangui_common.js hangui_iface.js dialog_watcher.js
 [test_idle_hang.xul]
 skip-if = (!crashreporter) || (os != "win")
 [test_npruntime.xul]
 [test_plugin_tag_clicktoplay.html]
 [test_privatemode_perwindowpb.xul]
-[test_refresh_navigator_plugins.html]
 [test_xulbrowser_plugin_visibility.xul]
 skip-if = (toolkit == "cocoa") || (os == "win")
 support-files = xulbrowser_plugin_visibility.xul plugin_visibility_loader.html
 [test_wmode.xul]
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -137,16 +137,18 @@ skip-if = true # Bug 1267432
 skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
 [test_propertyAndMethod.html]
 [test_queryCSSZoomFactor.html]
 [test_queryContentsScaleFactor.html]
 skip-if = (toolkit != "cocoa") || (os != "win")
 [test_queryContentsScaleFactorWindowed.html]
 skip-if = (toolkit != "cocoa") || (os != "win")
 [test_redirect_handling.html]
+[test_refresh_navigator_plugins.html]
+skip-if = e10s # Bug 1090576
 [test_secondPlugin.html]
 [test_src_url_change.html]
 [test_streamatclose.html]
 [test_streamNotify.html]
 [test_stringHandling.html]
 [test_twostreams.html]
 [test_visibility.html]
 skip-if = toolkit == "cocoa"
--- a/dom/plugins/test/mochitest/test_refresh_navigator_plugins.html
+++ b/dom/plugins/test/mochitest/test_refresh_navigator_plugins.html
@@ -1,68 +1,34 @@
 <!DOCTYPE html>
 <!-- bug 820708 -->
 <html>
   <head>
     <meta><charset="utf-8"/>
     <title>Test Refreshing navigator.plugins (bug 820708)</title>
-    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript"
+            src="/tests/SimpleTest/SimpleTest.js"></script>
     <script type="application/javascript" src="plugin-utils.js"></script>
+    <link rel="stylesheet" type="text/css"
+          href="/tests/SimpleTest/test.css">
   </head>
   <body>
+    <p id="display"></p>
     <script class="testbody" type="application/javascript">
       "use strict";
 
       SimpleTest.waitForExplicitFinish();
       setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
-      var pluginHost = Components.classes["@mozilla.org/plugin/host;1"]
-                       .getService(Components.interfaces.nsIPluginHost);
-      var pluginTags = pluginHost.getPluginTags();
-      var nextTest = null;
-      var obsService = Components.classes["@mozilla.org/observer-service;1"]
-                       .getService(Components.interfaces.nsIObserverService);
-      var observer = {
-        observe: function(aSubject, aTopic, aData) {
-          if (aTopic == "plugin-info-updated") {
-            SimpleTest.executeSoon(nextTest);
-          }
-        }
-      };
-      obsService.addObserver(observer, "plugin-info-updated");
+      ok("Test Plug-in" in navigator.plugins, "testplugin should be present");
+      ok("application/x-test" in navigator.mimeTypes, "testplugin MIME should be present");
 
-      var navTestPlugin1 = navigator.plugins.namedItem("Test Plug-in");
-      ok(navTestPlugin1, "navigator.plugins should have Test Plug-in");
-      var tagTestPlugin = null;
-      for (var plugin of pluginTags) {
-        if (plugin.name == navTestPlugin1.name) {
-          tagTestPlugin = plugin;
-          break;
-        }
-      }
-      ok(tagTestPlugin, "plugin tags should have Test Plug-in");
-      var mimeType = tagTestPlugin.getMimeTypes()[0];
-      ok(mimeType, "should have a MIME type for Test Plug-in");
-      ok(navigator.mimeTypes[mimeType], "navigator.mimeTypes should have an entry for '" + mimeType + "'");
-      ok(!tagTestPlugin.disabled, "test plugin should not be disabled");
+      setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_DISABLED);
+      ok(!("Test Plug-in" in navigator.plugins), "testplugin should not be present");
+      ok(!("application/x-test" in navigator.mimeTypes), "testplugin MIME should not be present");
 
-      nextTest = testPart2;
-      tagTestPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_DISABLED;
-
-      function testPart2() {
-        var navTestPlugin2 = navigator.plugins.namedItem("Test Plug-in");
-        ok(!navTestPlugin2, "now navigator.plugins should not have Test Plug-in");
-        ok(!navigator.mimeTypes[mimeType], "now navigator.mimeTypes should not have an entry for '" + mimeType + "'");
-
-        nextTest = testPart3;
-        tagTestPlugin.enabledState = Components.interfaces.nsIPluginTag.STATE_ENABLED;
-      }
-
-      function testPart3() {
-        var navTestPlugin3 = navigator.plugins.namedItem("Test Plug-in");
-        ok(navTestPlugin3, "now navigator.plugins should have Test Plug-in again");
-        ok(navigator.mimeTypes[mimeType], "now navigator.mimeTypes should have an entry for '" + mimeType + "' again");
-        obsService.removeObserver(observer, "plugin-info-updated");
-        SimpleTest.finish();
-      }
+      setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+      ok("Test Plug-in" in navigator.plugins, "testplugin should be present again");
+      ok("application/x-test" in navigator.mimeTypes, "testplugin MIME should be present again");
+      SimpleTest.finish();
     </script>
   </body>
 </html>
--- a/dom/tests/mochitest/fetch/test_formdataparsing.js
+++ b/dom/tests/mochitest/fetch/test_formdataparsing.js
@@ -25,18 +25,16 @@ function testFormDataParsing(fn) {
     [
       [ true,
 
         boundary +
         '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' +
         boundary + '-',
 
         function(fd) {
-          document.write("0xDEADBEEF");
-          document.write(fd.get("greeting"));
           is(fd.get("greeting"), '"hello"');
         }
       ],
       [ false,
 
         // Invalid disposition.
         boundary +
         '\r\nContent-Disposition: form-datafoobar; name="greeting"\r\n\r\n"hello"\r\n' +
new file mode 100644
--- /dev/null
+++ b/image/SVGDrawingParameters.h
@@ -0,0 +1,67 @@
+/* -*- 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 mozilla_image_SVGDrawingParameters_h
+#define mozilla_image_SVGDrawingParameters_h
+
+#include "gfxContext.h"
+#include "gfxTypes.h"
+#include "ImageRegion.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/Maybe.h"
+#include "nsSize.h"
+#include "SVGImageContext.h"
+
+namespace mozilla {
+namespace image {
+
+struct SVGDrawingParameters
+{
+  typedef mozilla::gfx::IntSize IntSize;
+  typedef mozilla::gfx::SamplingFilter SamplingFilter;
+
+  SVGDrawingParameters(gfxContext* aContext,
+                       const nsIntSize& aSize,
+                       const ImageRegion& aRegion,
+                       SamplingFilter aSamplingFilter,
+                       const Maybe<SVGImageContext>& aSVGContext,
+                       float aAnimationTime,
+                       uint32_t aFlags,
+                       float aOpacity)
+    : context(aContext)
+    , size(aSize.width, aSize.height)
+    , region(aRegion)
+    , samplingFilter(aSamplingFilter)
+    , svgContext(aSVGContext)
+    , viewportSize(aSize)
+    , animationTime(aAnimationTime)
+    , flags(aFlags)
+    , opacity(aOpacity)
+  {
+    if (aSVGContext) {
+      auto sz = aSVGContext->GetViewportSize();
+      if (sz) {
+        viewportSize = nsIntSize(sz->width, sz->height); // XXX losing unit
+      }
+    }
+  }
+
+  gfxContext*                   context;
+  IntSize                       size;
+  ImageRegion                   region;
+  SamplingFilter                samplingFilter;
+  const Maybe<SVGImageContext>& svgContext;
+  nsIntSize                     viewportSize;
+  float                         animationTime;
+  uint32_t                      flags;
+  gfxFloat                      opacity;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_SVGDrawingParameters_h
+
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -26,16 +26,17 @@
 #include "nsStubDocumentObserver.h"
 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
 #include "nsWindowMemoryReporter.h"
 #include "ImageRegion.h"
 #include "ISurfaceProvider.h"
 #include "LookupResult.h"
 #include "Orientation.h"
 #include "SVGDocumentWrapper.h"
+#include "SVGDrawingParameters.h"
 #include "nsIDOMEventListener.h"
 #include "SurfaceCache.h"
 #include "nsDocument.h"
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 #undef GetCurrentTime
 
 namespace mozilla {
@@ -770,55 +771,16 @@ VectorImage::IsImageContainerAvailable(L
 
 //******************************************************************************
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 VectorImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 {
   return nullptr;
 }
 
-struct SVGDrawingParameters
-{
-  SVGDrawingParameters(gfxContext* aContext,
-                       const nsIntSize& aSize,
-                       const ImageRegion& aRegion,
-                       SamplingFilter aSamplingFilter,
-                       const Maybe<SVGImageContext>& aSVGContext,
-                       float aAnimationTime,
-                       uint32_t aFlags,
-                       float aOpacity)
-    : context(aContext)
-    , size(aSize.width, aSize.height)
-    , region(aRegion)
-    , samplingFilter(aSamplingFilter)
-    , svgContext(aSVGContext)
-    , viewportSize(aSize)
-    , animationTime(aAnimationTime)
-    , flags(aFlags)
-    , opacity(aOpacity)
-  {
-    if (aSVGContext) {
-      auto sz = aSVGContext->GetViewportSize();
-      if (sz) {
-        viewportSize = nsIntSize(sz->width, sz->height); // XXX losing unit
-      }
-    }
-  }
-
-  gfxContext*                   context;
-  IntSize                       size;
-  ImageRegion                   region;
-  SamplingFilter                samplingFilter;
-  const Maybe<SVGImageContext>& svgContext;
-  nsIntSize                     viewportSize;
-  float                         animationTime;
-  uint32_t                      flags;
-  gfxFloat                      opacity;
-};
-
 //******************************************************************************
 NS_IMETHODIMP_(DrawResult)
 VectorImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -238,17 +238,17 @@ enum class GeneratorStyle
  *
  * Bug 569487: generalize builder interface
  */
 class NodeBuilder
 {
     typedef AutoValueArray<AST_LIMIT> CallbackArray;
 
     JSContext*  cx;
-    TokenStream* tokenStream;
+    TokenStreamAnyChars* tokenStream;
     bool        saveLoc;               /* save source location information?     */
     char const* src;                  /* source filename or null               */
     RootedValue srcval;                /* source filename JS value or null      */
     CallbackArray callbacks;           /* user-specified callbacks              */
     RootedValue userv;                 /* user-specified builder object or null */
 
   public:
     NodeBuilder(JSContext* c, bool l, char const* s)
@@ -297,17 +297,17 @@ class NodeBuilder
             }
 
             callbacks[i].set(funv);
         }
 
         return true;
     }
 
-    void setTokenStream(TokenStream* ts) {
+    void setTokenStream(TokenStreamAnyChars* ts) {
         tokenStream = ts;
     }
 
   private:
     MOZ_MUST_USE bool callbackHelper(HandleValue fun, const InvokeArgs& args, size_t i,
                                      TokenPos* pos, MutableHandleValue dst)
     {
         // The end of the implementation of callback(). All arguments except
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -6,16 +6,17 @@
 
 #include "builtin/RegExp.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jscntxt.h"
 
+#include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
 #include "jsobjinlines.h"
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -102,17 +102,17 @@ AutoFrontendTraceLog::AutoFrontendTraceL
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                                           const TokenStream& tokenStream)
+                                           const TokenStreamAnyChars& tokenStream)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     // If the tokenizer hasn't yet gotten any tokens, use the line and column
     // numbers from CompileOptions.
     uint32_t line, column;
     if (tokenStream.isCurrentTokenType(TOK_EOF) && !tokenStream.isEOF()) {
         line = tokenStream.options().lineno;
@@ -125,31 +125,32 @@ AutoFrontendTraceLog::AutoFrontendTraceL
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                                           const TokenStream& tokenStream, FunctionBox* funbox)
+                                           const TokenStreamAnyChars& tokenStream,
+                                           FunctionBox* funbox)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(),
                            funbox->startLine, funbox->startColumn);
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                                           const TokenStream& tokenStream, ParseNode* pn)
+                                           const TokenStreamAnyChars& tokenStream, ParseNode* pn)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     uint32_t line, column;
     tokenStream.srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
     frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -21,17 +21,17 @@ namespace js {
 
 class LazyScript;
 class LifoAlloc;
 class ModuleObject;
 class ScriptSourceObject;
 
 namespace frontend {
 
-class TokenStream;
+class TokenStreamAnyChars;
 class FunctionBox;
 class ParseNode;
 
 JSScript*
 CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
                     const ReadOnlyCompileOptions& options,
                     SourceBufferHolder& srcBuf,
                     ScriptSourceObject** sourceObjectOut = nullptr);
@@ -138,21 +138,21 @@ class MOZ_STACK_CLASS AutoFrontendTraceL
     mozilla::Maybe<AutoTraceLog> typeLog_;
 #endif
 
   public:
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                          const char* filename, size_t line, size_t column);
 
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                         const TokenStream& tokenStream);
+                         const TokenStreamAnyChars& tokenStream);
 
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                         const TokenStream& tokenStream, FunctionBox* funbox);
+                         const TokenStreamAnyChars& tokenStream, FunctionBox* funbox);
 
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                         const TokenStream& tokenStream, ParseNode* pn);
+                         const TokenStreamAnyChars& tokenStream, ParseNode* pn);
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_BytecodeCompiler_h */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2172,17 +2172,17 @@ class ForOfLoopControl : public LoopCont
                 return false;
         }
 
         return true;
     }
 };
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
+                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
     lazyScript(cx, lazyScript),
     prologue(cx, lineNum),
@@ -2212,21 +2212,21 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     emittingRunOnceLambda(false),
     emitterMode(emitterMode),
     functionBodyEndPosSet(false)
 {
     MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
+                                 const EitherParser<FullParseHandler>& parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  TokenPos bodyPosition, EmitterMode emitterMode)
     : BytecodeEmitter(parent, parser, sc, script, lazyScript,
-                      parser->tokenStream.srcCoords.lineNum(bodyPosition.begin),
+                      parser.tokenStream().srcCoords.lineNum(bodyPosition.begin),
                       emitterMode)
 {
     setFunctionBodyEndPos(bodyPosition);
 }
 
 bool
 BytecodeEmitter::init()
 {
@@ -2560,20 +2560,20 @@ LengthOfSetLine(unsigned line)
 {
     return 1 /* SN_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
 }
 
 /* Updates line number notes, not column notes. */
 bool
 BytecodeEmitter::updateLineNumberNotes(uint32_t offset)
 {
-    TokenStream* ts = &parser->tokenStream;
+    TokenStreamAnyChars* ts = &parser.tokenStream();
     bool onThisLine;
     if (!ts->srcCoords.isOnThisLine(offset, currentLine(), &onThisLine)) {
-        ts->reportError(JSMSG_OUT_OF_MEMORY);
+        ts->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
         return false;
     }
 
     if (!onThisLine) {
         unsigned line = ts->srcCoords.lineNum(offset);
         unsigned delta = line - currentLine();
 
         /*
@@ -2604,17 +2604,17 @@ BytecodeEmitter::updateLineNumberNotes(u
 
 /* Updates the line number and column number information in the source notes. */
 bool
 BytecodeEmitter::updateSourceCoordNotes(uint32_t offset)
 {
     if (!updateLineNumberNotes(offset))
         return false;
 
-    uint32_t columnIndex = parser->tokenStream.srcCoords.columnIndex(offset);
+    uint32_t columnIndex = parser.tokenStream().srcCoords.columnIndex(offset);
     ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(current->lastColumn);
     if (colspan != 0) {
         // If the column span is so large that we can't store it, then just
         // discard this information. This can happen with minimized or otherwise
         // machine-generated code. Even gigantic column numbers are still
         // valuable if you have a source map to relate them to something real;
         // but it's better to fail soft here.
         if (!SN_REPRESENTABLE_COLSPAN(colspan))
@@ -3571,46 +3571,46 @@ BytecodeEmitter::needsImplicitThis()
 
     return false;
 }
 
 bool
 BytecodeEmitter::maybeSetDisplayURL()
 {
     if (tokenStream().hasDisplayURL()) {
-        if (!parser->ss->setDisplayURL(cx, tokenStream().displayURL()))
+        if (!parser.ss()->setDisplayURL(cx, tokenStream().displayURL()))
             return false;
     }
     return true;
 }
 
 bool
 BytecodeEmitter::maybeSetSourceMap()
 {
     if (tokenStream().hasSourceMapURL()) {
-        MOZ_ASSERT(!parser->ss->hasSourceMapURL());
-        if (!parser->ss->setSourceMapURL(cx, tokenStream().sourceMapURL()))
+        MOZ_ASSERT(!parser.ss()->hasSourceMapURL());
+        if (!parser.ss()->setSourceMapURL(cx, tokenStream().sourceMapURL()))
             return false;
     }
 
     /*
      * 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()) {
+    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.ss()->hasSourceMapURL()) {
+            if (!parser.reportNoOffset(ParseWarning, false, JSMSG_ALREADY_HAS_PRAGMA,
+                                       parser.ss()->filename(), "//# sourceMappingURL"))
             {
                 return false;
             }
         }
 
-        if (!parser->ss->setSourceMapURL(cx, parser->options().sourceMapURL()))
+        if (!parser.ss()->setSourceMapURL(cx, parser.options().sourceMapURL()))
             return false;
     }
 
     return true;
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext* cx)
@@ -3621,60 +3621,60 @@ BytecodeEmitter::tellDebuggerAboutCompil
         return;
 
     // Lazy scripts are never top level (despite always being invoked with a
     // nullptr parent), and so the hook should never be fired.
     if (emitterMode != LazyFunction && !parent)
         Debugger::onNewScript(cx, script);
 }
 
-inline TokenStream&
+inline TokenStreamAnyChars&
 BytecodeEmitter::tokenStream()
 {
-    return parser->tokenStream;
+    return parser.tokenStream();
 }
 
 void
 BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
 {
     TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
 
     va_list args;
     va_start(args, errorNumber);
 
-    TokenStream& ts = tokenStream();
     ErrorMetadata metadata;
-    if (ts.computeErrorMetadata(&metadata, pos.begin))
+    if (parser.computeErrorMetadata(&metadata, pos.begin))
         ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 bool
 BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
 {
     TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
 
     va_list args;
     va_start(args, errorNumber);
-    bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin,
-                                                                errorNumber, args);
+
+    bool result = parser.reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, args);
+
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...)
 {
     TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
 
     va_list args;
     va_start(args, errorNumber);
-    bool result = tokenStream().reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
-                                                              errorNumber, args);
+    bool result = parser.reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
+                                                       errorNumber, args);
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::emitNewInit(JSProtoKey key)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
@@ -3711,17 +3711,17 @@ BytecodeEmitter::iteratorResultShape(uns
         return false;
     }
     if (!NativeDefineProperty(cx, obj, done_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
     {
         return false;
     }
 
-    ObjectBox* objbox = parser->newObjectBox(obj);
+    ObjectBox* objbox = parser.newObjectBox(obj);
     if (!objbox)
         return false;
 
     *shape = objectList.add(objbox);
 
     return true;
 }
 
@@ -4543,17 +4543,17 @@ BytecodeEmitter::emitSwitch(ParseNode* p
     // After entering the scope, push the switch control.
     BreakableControl controlInfo(this, StatementKind::Switch);
 
     ptrdiff_t top = offset();
 
     // Switch bytecodes run from here till end of final case.
     uint32_t caseCount = cases->pn_count;
     if (caseCount > JS_BIT(16)) {
-        parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
+        parser.reportError(JSMSG_TOO_MANY_CASES);
         return false;
     }
 
     // Try for most optimal, fall back if not dense ints.
     JSOp switchOp = JSOP_TABLESWITCH;
     uint32_t tableLength = 0;
     int32_t low, high;
     bool hasDefault = false;
@@ -6426,42 +6426,42 @@ BytecodeEmitter::emitSingletonInitialise
     NewObjectKind newKind = (pn->getKind() == PNK_OBJECT) ? SingletonObject : TenuredObject;
 
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind))
         return false;
 
     MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton());
 
-    ObjectBox* objbox = parser->newObjectBox(&value.toObject());
+    ObjectBox* objbox = parser.newObjectBox(&value.toObject());
     if (!objbox)
         return false;
 
     return emitObjectOp(objbox, JSOP_OBJECT);
 }
 
 bool
 BytecodeEmitter::emitCallSiteObject(ParseNode* pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox1 = parser->newObjectBox(&value.toObject());
+    ObjectBox* objbox1 = parser.newObjectBox(&value.toObject());
     if (!objbox1)
         return false;
 
     if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox2 = parser->newObjectBox(&value.toObject());
+    ObjectBox* objbox2 = parser.newObjectBox(&value.toObject());
     if (!objbox2)
         return false;
 
     return emitObjectPairOp(objbox1, objbox2, JSOP_CALLSITEOBJ);
 }
 
 /* See the SRC_FOR source note offsetBias comments later in this file. */
 JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
@@ -7001,30 +7001,30 @@ BytecodeEmitter::emitInitializeForInOrOf
                "must have a per-iteration value for initializing");
 
     ParseNode* target = forHead->pn_kid1;
     MOZ_ASSERT(!forHead->pn_kid2);
 
     // If the for-in/of loop didn't have a variable declaration, per-loop
     // initialization is just assigning the iteration value to a target
     // expression.
-    if (!parser->handler.isDeclarationList(target))
+    if (!parser.isDeclarationList(target))
         return emitAssignment(target, JSOP_NOP, nullptr); // ... ITERVAL
 
     // Otherwise, per-loop initialization is (possibly) declaration
     // initialization.  If the declaration is a lexical declaration, it must be
     // initialized.  If the declaration is a variable declaration, an
     // assignment to that name (which does *not* necessarily assign to the
     // variable!) must be generated.
 
     if (!updateSourceCoordNotes(target->pn_pos.begin))
         return false;
 
     MOZ_ASSERT(target->isForLoopDeclaration());
-    target = parser->handler.singleBindingFromDeclaration(target);
+    target = parser.singleBindingFromDeclaration(target);
 
     if (target->isKind(PNK_NAME)) {
         auto emitSwapScopeAndRhs = [](BytecodeEmitter* bce, const NameLocation&,
                                       bool emittedBindOp)
         {
             if (emittedBindOp) {
                 // Per-iteration initialization in for-in/of loops computes the
                 // iteration value *before* initializing.  Thus the
@@ -7245,18 +7245,18 @@ BytecodeEmitter::emitForIn(ParseNode* fo
 
     ParseNode* forInHead = forInLoop->pn_left;
     MOZ_ASSERT(forInHead->isKind(PNK_FORIN));
     MOZ_ASSERT(forInHead->isArity(PN_TERNARY));
 
     // Annex B: Evaluate the var-initializer expression if present.
     // |for (var i = initializer in expr) { ... }|
     ParseNode* forInTarget = forInHead->pn_kid1;
-    if (parser->handler.isDeclarationList(forInTarget)) {
-        ParseNode* decl = parser->handler.singleBindingFromDeclaration(forInTarget);
+    if (parser.isDeclarationList(forInTarget)) {
+        ParseNode* decl = parser.singleBindingFromDeclaration(forInTarget);
         if (decl->isKind(PNK_NAME)) {
             if (ParseNode* initializer = decl->expr()) {
                 MOZ_ASSERT(forInTarget->isKind(PNK_VAR),
                            "for-in initializers are only permitted for |var| declarations");
 
                 if (!updateSourceCoordNotes(decl->pn_pos.begin))
                     return false;
 
@@ -7511,17 +7511,17 @@ BytecodeEmitter::emitCStyleFor(ParseNode
         if (!updateSourceCoordNotes(update->pn_pos.begin))
             return false;
         if (!emitTree(update, ValueUsage::IgnoreValue))
             return false;
         if (!emit1(JSOP_POP))
             return false;
 
         /* Restore the absolute line number for source note readers. */
-        uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end);
+        uint32_t lineNum = parser.tokenStream().srcCoords.lineNum(pn->pn_pos.end);
         if (currentLine() != lineNum) {
             if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum)))
                 return false;
             current->currentLine = lineNum;
             current->lastColumn = 0;
         }
     }
 
@@ -7648,22 +7648,22 @@ BytecodeEmitter::emitComprehensionForOf(
 
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     TDZCheckCache tdzCache(this);
     Maybe<EmitterScope> emitterScope;
     ParseNode* loopVariableName;
     if (lexicalScope) {
-        loopVariableName = parser->handler.singleBindingFromDeclaration(loopDecl->pn_expr);
+        loopVariableName = parser.singleBindingFromDeclaration(loopDecl->pn_expr);
         emitterScope.emplace(this);
         if (!emitterScope->enterComprehensionFor(this, loopDecl->scopeBindings()))
             return false;
     } else {
-        loopVariableName = parser->handler.singleBindingFromDeclaration(loopDecl);
+        loopVariableName = parser.singleBindingFromDeclaration(loopDecl);
     }
 
     LoopControl loopInfo(this, StatementKind::ForOfLoop);
 
     // Jump down to the loop condition to minimize overhead assuming at least
     // one iteration, as the other loop forms do.  Annotate so IonMonkey can
     // find the loop-closing jump.
     unsigned noteIndex;
@@ -7991,19 +7991,19 @@ BytecodeEmitter::emitFunction(ParseNode*
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
-            MOZ_ASSERT(parent->getVersion() == parser->options().version);
-            MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
-            const TransitiveCompileOptions& transitiveOptions = parser->options();
+            MOZ_ASSERT(parent->getVersion() == parser.options().version);
+            MOZ_ASSERT(parent->mutedErrors() == parser.options().mutedErrors());
+            const TransitiveCompileOptions& transitiveOptions = parser.options();
             CompileOptions options(cx, transitiveOptions);
 
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
                                                           funbox->bufStart, funbox->bufEnd,
                                                           funbox->toStringStart,
                                                           funbox->toStringEnd));
             if (!script)
@@ -8278,20 +8278,22 @@ BytecodeEmitter::emitWhile(ParseNode* pn
     // If we have a single-line while, like "while (x) ;", we want to
     // emit the line note before the initial goto, so that the
     // debugger sees a single entry point.  This way, if there is a
     // breakpoint on the line, it will only fire once; and "next"ing
     // will skip the whole loop.  However, for the multi-line case we
     // want to emit the line note after the initial goto, so that
     // "cont" stops on each iteration -- but without a stop before the
     // first iteration.
-    if (parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) ==
-        parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end) &&
-        !updateSourceCoordNotes(pn->pn_pos.begin))
-        return false;
+    if (parser.tokenStream().srcCoords.lineNum(pn->pn_pos.begin) ==
+        parser.tokenStream().srcCoords.lineNum(pn->pn_pos.end))
+    {
+        if (!updateSourceCoordNotes(pn->pn_pos.begin))
+            return false;
+    }
 
     JumpTarget top{ -1 };
     if (!emitJumpTarget(&top))
         return false;
 
     LoopControl loopInfo(this, StatementKind::WhileLoop);
     loopInfo.continueTarget = top;
 
@@ -8960,17 +8962,17 @@ BytecodeEmitter::emitStatement(ParseNode
                 }
             }
 
             if (directive) {
                 if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
                     return false;
             }
         } else {
-            current->currentLine = parser->tokenStream.srcCoords.lineNum(pn2->pn_pos.begin);
+            current->currentLine = parser.tokenStream().srcCoords.lineNum(pn2->pn_pos.begin);
             current->lastColumn = 0;
             if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR))
                 return false;
         }
     }
 
     return true;
 }
@@ -9302,19 +9304,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
      * Then (or in a call case that has no explicit reference-base
      * object) we emit JSOP_UNDEFINED to produce the undefined |this|
      * value required for calls (which non-strict mode functions
      * will box into the global object).
      */
     uint32_t argc = pn->pn_count - 1;
 
     if (argc >= ARGC_LIMIT) {
-        parser->tokenStream.reportError(callop
-                                        ? JSMSG_TOO_MANY_FUN_ARGS
-                                        : JSMSG_TOO_MANY_CON_ARGS);
+        parser.reportError(callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
     ParseNode* pn2 = pn->pn_head;
     bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
     switch (pn2->getKind()) {
       case PNK_NAME:
         if (emitterMode == BytecodeEmitter::SelfHosting && !spread) {
@@ -9388,17 +9388,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
         } else {
             if (!emitTree(pn2))
                 return false;
         }
         callop = false;
         break;
       case PNK_SUPERBASE:
         MOZ_ASSERT(pn->isKind(PNK_SUPERCALL));
-        MOZ_ASSERT(parser->handler.isSuperBase(pn2));
+        MOZ_ASSERT(parser.isSuperBase(pn2));
         if (!emit1(JSOP_SUPERFUN))
             return false;
         break;
       default:
         if (!emitTree(pn2))
             return false;
         callop = false;             /* trigger JSOP_UNDEFINED after */
         break;
@@ -9508,17 +9508,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
             return false;
         checkTypeSet(pn->getOp());
     }
     if (pn->isOp(JSOP_EVAL) ||
         pn->isOp(JSOP_STRICTEVAL) ||
         pn->isOp(JSOP_SPREADEVAL) ||
         pn->isOp(JSOP_STRICTSPREADEVAL))
     {
-        uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
+        uint32_t lineNum = parser.tokenStream().srcCoords.lineNum(pn->pn_pos.begin);
         if (!emitUint32Operand(JSOP_LINENO, lineNum))
             return false;
     }
 
     return true;
 }
 
 bool
@@ -9892,17 +9892,17 @@ BytecodeEmitter::emitObject(ParseNode* p
     if (!emitPropertyList(pn, &obj, ObjectLiteral))
         return false;
 
     if (obj) {
         /*
          * The object survived and has a predictable shape: update the original
          * bytecode.
          */
-        ObjectBox* objbox = parser->newObjectBox(obj);
+        ObjectBox* objbox = parser.newObjectBox(obj);
         if (!objbox)
             return false;
 
         static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
                       "newinit and newobject must have equal length to edit in-place");
 
         uint32_t index = objectList.add(objbox);
         jsbytecode* code = this->code(offset);
@@ -9960,17 +9960,17 @@ BytecodeEmitter::emitArrayLiteral(ParseN
                 // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the
                 // group for the template is accurate. We don't do this here as we
                 // want to use ObjectGroup::allocationSiteGroup, which requires a
                 // finished script.
                 JSObject* obj = &value.toObject();
                 MOZ_ASSERT(obj->is<ArrayObject>() &&
                            obj->as<ArrayObject>().denseElementsAreCopyOnWrite());
 
-                ObjectBox* objbox = parser->newObjectBox(obj);
+                ObjectBox* objbox = parser.newObjectBox(obj);
                 if (!objbox)
                     return false;
 
                 return emitObjectOp(objbox, JSOP_NEWARRAY_COPYONWRITE);
             }
         }
     }
 
@@ -11121,17 +11121,17 @@ BytecodeEmitter::addToSrcNoteDelta(jssrc
     }
     return true;
 }
 
 bool
 BytecodeEmitter::setSrcNoteOffset(unsigned index, unsigned which, ptrdiff_t offset)
 {
     if (!SN_REPRESENTABLE_OFFSET(offset)) {
-        parser->tokenStream.reportError(JSMSG_NEED_DIET, js_script_str);
+        parser.reportError(JSMSG_NEED_DIET, js_script_str);
         return false;
     }
 
     SrcNotesVector& notes = this->notes();
 
     /* Find the offset numbered which (i.e., skip exactly which offsets). */
     jssrcnote* sn = &notes[index];
     MOZ_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -4,37 +4,32 @@
  * 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/. */
 
 /* JS bytecode generation. */
 
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
+#include "mozilla/Attributes.h"
+
 #include "jscntxt.h"
 #include "jsiter.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 
 #include "ds/InlineTable.h"
-#include "frontend/Parser.h"
+#include "frontend/EitherParser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "vm/Interpreter.h"
 
 namespace js {
 namespace frontend {
 
-template <typename CharT> class FullParseHandler;
-class ObjectBox;
-class ParseNode;
-template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
-class SharedContext;
-class TokenStream;
-
 class CGConstList {
     Vector<Value> list;
   public:
     explicit CGConstList(JSContext* cx) : list(cx) {}
     MOZ_MUST_USE bool append(const Value& v) {
         MOZ_ASSERT_IF(v.isString(), v.toString()->isAtom());
         return list.append(v);
     }
@@ -203,17 +198,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
         EmitSection(JSContext* cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
             lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
         {}
     };
     EmitSection prologue, main, *current;
 
-    Parser<FullParseHandler, char16_t>* const parser;
+    EitherParser<FullParseHandler> parser;
 
     PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
 
     uint32_t        maxFixedSlots;  /* maximum number of fixed frame slots so far */
     uint32_t        maxStackDepth;  /* maximum number of expression stack slots so far */
 
     int32_t         stackDepth;     /* current stack depth in script frame */
@@ -277,26 +272,42 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     bool functionBodyEndPosSet;
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
-    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser<FullParseHandler>& parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     uint32_t lineNum, EmitterMode emitterMode = Normal);
 
+    template<typename CharT>
+    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
+                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
+                    uint32_t lineNum, EmitterMode emitterMode = Normal)
+      : BytecodeEmitter(parent, EitherParser<FullParseHandler>(parser), sc, script, lazyScript,
+                        lineNum, emitterMode)
+    {}
+
     // An alternate constructor that uses a TokenPos for the starting
     // line and that sets functionBodyEndPos as well.
-    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser<FullParseHandler>& parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     TokenPos bodyPosition, EmitterMode emitterMode = Normal);
 
+    template<typename CharT>
+    BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
+                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
+                    TokenPos bodyPosition, EmitterMode emitterMode = Normal)
+      : BytecodeEmitter(parent, EitherParser<FullParseHandler>(parser), sc, script, lazyScript,
+                        bodyPosition, emitterMode)
+    {}
+
     MOZ_MUST_USE bool init();
 
     template <typename Predicate /* (NestableControl*) -> bool */>
     NestableControl* findInnermostNestableControl(Predicate predicate) const;
 
     template <typename T>
     T* findInnermostNestableControl() const;
 
@@ -355,17 +366,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool checkRunOnceContext();
 
     bool needsImplicitThis();
 
     MOZ_MUST_USE bool maybeSetDisplayURL();
     MOZ_MUST_USE bool maybeSetSourceMap();
     void tellDebuggerAboutCompiledScript(JSContext* cx);
 
-    inline TokenStream& tokenStream();
+    inline TokenStreamAnyChars& tokenStream();
 
     BytecodeVector& code() const { return current->code; }
     jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; }
     ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
     ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); }
     void switchToMain() { current = &main; }
     void switchToPrologue() { current = &prologue; }
     bool inPrologue() const { return current == &prologue; }
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/EitherParser.h
@@ -0,0 +1,289 @@
+/* -*- 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/. */
+
+/*
+ * A variant-like class abstracting operations on a Parser with a given ParseHandler but
+ * unspecified character type.
+ */
+
+#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/Variant.h"
+
+#include "frontend/Parser.h"
+#include "frontend/TokenStream.h"
+
+namespace js {
+namespace frontend {
+
+template<template<typename CharT> class ParseHandler>
+class EitherParser
+{
+    const mozilla::Variant<Parser<ParseHandler, char16_t>* const> parser;
+
+    using Node = typename ParseHandler<char16_t>::Node;
+
+  public:
+    template<class Parser>
+    explicit EitherParser(Parser* parser) : parser(parser) {}
+
+  private:
+    struct TokenStreamMatcher
+    {
+        template<class Parser>
+        TokenStreamAnyChars& match(Parser* parser) {
+            return parser->tokenStream;
+        }
+    };
+
+  public:
+    TokenStreamAnyChars& tokenStream() {
+        return parser.match(TokenStreamMatcher());
+    }
+
+    const TokenStreamAnyChars& 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());
+    }
+
+  private:
+    struct OptionsMatcher
+    {
+        template<class Parser>
+        const JS::ReadOnlyCompileOptions& match(Parser* parser) {
+            return parser->options();
+        }
+    };
+
+  public:
+    const JS::ReadOnlyCompileOptions& options() {
+        return parser.match(OptionsMatcher());
+    }
+
+  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));
+    }
+
+  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));
+    }
+
+  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));
+    }
+
+  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));
+    }
+
+  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));
+    }
+
+  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)...);
+    }
+
+    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...>
+            matcher { mozilla::Forward<Args>(args)... };
+        return parser.match(mozilla::Move(matcher));
+    }
+
+  private:
+    struct ParserBaseMatcher
+    {
+        template<class Parser>
+        ParserBase& match(Parser* parser) {
+            return *parser;
+        }
+    };
+
+  public:
+    template<typename... Args>
+    MOZ_MUST_USE bool reportNoOffset(Args&&... args) {
+        return parser.match(ParserBaseMatcher()).reportNoOffset(mozilla::Forward<Args>(args)...);
+    }
+
+  private:
+    template<typename... StoredArgs>
+    struct ReportExtraWarningMatcher
+    {
+        mozilla::Tuple<StoredArgs...> args;
+
+        template<typename... Args>
+        explicit ReportExtraWarningMatcher(Args&&... actualArgs)
+          : args { mozilla::Forward<Args>(actualArgs)... }
+        {}
+
+        template<class Parser>
+        MOZ_MUST_USE bool match(Parser* parser) {
+            return CallGenericFunction(&TokenStream::reportExtraWarningErrorNumberVA,
+                                       &parser->tokenStream,
+                                       args,
+                                       typename mozilla::IndexSequenceFor<StoredArgs...>::Type());
+        }
+    };
+
+  public:
+    template<typename... Args>
+    MOZ_MUST_USE bool reportExtraWarningErrorNumberVA(Args&&... args) {
+        ReportExtraWarningMatcher<typename mozilla::Decay<Args>::Type...>
+            matcher { mozilla::Forward<Args>(args)... };
+        return parser.match(mozilla::Move(matcher));
+    }
+
+  private:
+    template<typename... StoredArgs>
+    struct ReportStrictModeErrorMatcher
+    {
+        mozilla::Tuple<StoredArgs...> args;
+
+        template<typename... Args>
+        explicit ReportStrictModeErrorMatcher(Args&&... actualArgs)
+          : args { mozilla::Forward<Args>(actualArgs)... }
+        {}
+
+        template<class Parser>
+        MOZ_MUST_USE bool match(Parser* parser) {
+            return CallGenericFunction(&TokenStream::reportStrictModeErrorNumberVA,
+                                       &parser->tokenStream,
+                                       args,
+                                       typename mozilla::IndexSequenceFor<StoredArgs...>::Type());
+        }
+    };
+
+  public:
+    template<typename... Args>
+    MOZ_MUST_USE bool reportStrictModeErrorNumberVA(Args&&... args) {
+        ReportStrictModeErrorMatcher<typename mozilla::Decay<Args>::Type...>
+            matcher { mozilla::Forward<Args>(args)... };
+        return parser.match(mozilla::Move(matcher));
+    }
+};
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_EitherParser_h */
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -776,17 +776,17 @@ class FullParseHandlerBase
     void setEndPosition(ParseNode* pn, ParseNode* oth) {
         setEndPosition(pn, oth->pn_pos.end);
     }
     void setEndPosition(ParseNode* pn, uint32_t end) {
         pn->pn_pos.end = end;
         MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
     }
 
-    uint32_t getFunctionNameOffset(ParseNode* func, TokenStreamBase& ts) {
+    uint32_t getFunctionNameOffset(ParseNode* func, TokenStreamAnyChars& ts) {
         return func->pn_pos.begin;
     }
 
     bool isDeclarationKind(ParseNodeKind kind) {
         return kind == PNK_VAR || kind == PNK_LET || kind == PNK_CONST;
     }
 
     ParseNode* newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
new file mode 100644
--- /dev/null
+++ b/js/src/frontend/LanguageExtensions.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+/* Code related to various SpiderMonkey-specific language syntax extensions. */
+
+#ifndef frontend_LanguageExtensions_h
+#define frontend_LanguageExtensions_h
+
+namespace js {
+
+/**
+ * Numeric identifiers for various deprecated language extensions.
+ *
+ * The initializer numbers are directly used in telemetry, so while it's okay
+ * to *remove* values as language extensions are removed from SpiderMonkey,
+ * it's *not* okay to compact or reorder them.  When an initializer falls into
+ * disuse, remove it without reassigning its value to a new or existing
+ * initializer.  The *only* initializer whose value should ever change is
+ * DeprecatedLanguageExtension::Count.
+ */
+enum class DeprecatedLanguageExtension
+{
+    ForEach = 0, // JS 1.6+
+    // NO LONGER USING 1
+    LegacyGenerator = 2, // JS 1.7+
+    ExpressionClosure = 3, // Added in JS 1.8
+    // NO LONGER USING 4
+    // NO LONGER USING 5
+    // NO LONGER USING 6
+    // NO LONGER USING 7
+    // NO LONGER USING 8
+    // NO LONGER USING 9
+    BlockScopeFunRedecl = 10,
+
+    // Sentinel value.  MAY change as extension initializers are added (only to
+    // the end) above.
+    Count
+};
+
+} // namespace js
+
+#endif /* frontend_LanguageExtensions_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -29,16 +29,17 @@
 #include "jsscript.h"
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/TokenStream.h"
+#include "vm/RegExpObject.h"
 #include "wasm/AsmJS.h"
 
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 
@@ -3690,17 +3691,17 @@ Parser<ParseHandler, CharT>::functionFor
                 kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
                 IsConstructorKind(kind))
             {
                 error(JSMSG_CURLY_BEFORE_BODY);
                 return false;
             }
 
 #if JS_HAS_EXPR_CLOSURES
-            addTelemetry(JSCompartment::DeprecatedExpressionClosure);
+            addTelemetry(DeprecatedLanguageExtension::ExpressionClosure);
             if (!warnOnceAboutExprClosure())
                 return false;
 #else
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
@@ -6186,17 +6187,17 @@ Parser<ParseHandler, CharT>::forStatemen
 
     if (allowsForEachIn()) {
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_EACH))
             return null();
         if (matched) {
             iflags = JSITER_FOREACH;
             isForEach = true;
-            addTelemetry(JSCompartment::DeprecatedForEach);
+            addTelemetry(DeprecatedLanguageExtension::ForEach);
             if (!warnOnceAboutForEach())
                 return null();
         }
     }
 
     if (asyncIterationSupported()) {
         if (pc->isAsync()) {
             bool matched;
@@ -6697,17 +6698,17 @@ Parser<ParseHandler, CharT>::yieldExpres
             )
         {
             /* As in Python (see PEP-255), disallow return v; in generators. */
             errorAt(begin, JSMSG_BAD_FUNCTION_YIELD);
             return null();
         }
 
         pc->functionBox()->setGeneratorKind(LegacyGenerator);
-        addTelemetry(JSCompartment::DeprecatedLegacyGenerator);
+        addTelemetry(DeprecatedLanguageExtension::LegacyGenerator);
 
         MOZ_FALLTHROUGH;
 
       case LegacyGenerator:
       {
         // We are in a legacy generator: a function that has already seen a
         // yield.
         MOZ_ASSERT(pc->isFunctionBox());
@@ -10240,17 +10241,17 @@ Parser<ParseHandler, CharT>::exprInParen
                                           TripledotHandling tripledotHandling,
                                           PossibleError* possibleError /* = nullptr */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
 }
 
 void
-ParserBase::addTelemetry(JSCompartment::DeprecatedLanguageExtension e)
+ParserBase::addTelemetry(DeprecatedLanguageExtension e)
 {
     if (context->helperThread())
         return;
     context->compartment()->addTelemetry(getFilename(), e);
 }
 
 bool
 ParserBase::warnOnceAboutExprClosure()
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -14,16 +14,17 @@
 #include "mozilla/TypeTraits.h"
 
 #include "jsiter.h"
 #include "jspubtd.h"
 
 #include "ds/Nestable.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"
+#include "frontend/LanguageExtensions.h"
 #include "frontend/NameAnalysisTypes.h"
 #include "frontend/NameCollections.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 
 class ModuleObject;
@@ -264,17 +265,17 @@ class ParseContext : public Nestable<Par
   private:
     // Trace logging of parsing time.
     AutoFrontendTraceLog traceLog_;
 
     // Context shared between parsing and bytecode generation.
     SharedContext* sc_;
 
     // TokenStream used for error reporting.
-    TokenStreamBase& tokenStream_;
+    TokenStreamAnyChars& tokenStream_;
 
     // The innermost statement, i.e., top of the statement stack.
     Statement* innermostStatement_;
 
     // The innermost scope, i.e., top of the scope stack.
     //
     // The outermost scope in the stack is usually varScope_. In the case of
     // functions, the outermost scope is functionScope_, which may be
@@ -894,17 +895,17 @@ class ParserBase : public StrictModeGett
     /*
      * If extra warnings are enabled, report the given warning at the given
      * offset.
      */
     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
 
     bool isValidStrictBinding(PropertyName* name);
 
-    void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
+    void addTelemetry(DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
     bool warnOnceAboutForEach();
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -10,23 +10,24 @@
 #include "jsatom.h"
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "ds/InlineTable.h"
-#include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 #include "vm/EnvironmentObject.h"
 
 namespace js {
 namespace frontend {
 
+class ParseNode;
+
 enum class StatementKind : uint8_t
 {
     Label,
     Block,
     If,
     Switch,
     With,
     Catch,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -397,17 +397,17 @@ class SyntaxParseHandlerBase
     MOZ_MUST_USE bool finishInitializerAssignment(Node pn, Node init) { return true; }
 
     void setBeginPosition(Node pn, Node oth) {}
     void setBeginPosition(Node pn, uint32_t begin) {}
 
     void setEndPosition(Node pn, Node oth) {}
     void setEndPosition(Node pn, uint32_t end) {}
 
-    uint32_t getFunctionNameOffset(Node func, TokenStreamBase& ts) {
+    uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) {
         // XXX This offset isn't relevant to the offending function name.  But
         //     we may not *have* that function name around, because of how lazy
         //     parsing works -- the actual name could be outside
         //     |tokenStream.userbuf|'s observed region.  So the current offset
         //     is the best we can do.
         return ts.currentToken().pos.begin;
     }
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -26,33 +26,31 @@
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "vm/HelperThreads.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
-using namespace js;
-using namespace js::frontend;
-
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::PodAssign;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
-struct ReservedWordInfo {
+struct ReservedWordInfo
+{
     const char* chars;         // C string with reserved word text
-    TokenKind   tokentype;
+    js::frontend::TokenKind tokentype;
 };
 
 static const ReservedWordInfo reservedWords[] = {
 #define RESERVED_WORD_INFO(word, name, type) \
-    {js_##word##_str, type},
+    {js_##word##_str, js::frontend::type},
     FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO)
 #undef RESERVED_WORD_INFO
 };
 
 // Returns a ReservedWordInfo for the specified characters, or nullptr if the
 // string is not a reserved word.
 template <typename CharT>
 static const ReservedWordInfo*
@@ -100,16 +98,18 @@ FindReservedWord(JSLinearString* str)
            ? FindReservedWord(str->latin1Chars(nogc), str->length())
            : FindReservedWord(str->twoByteChars(nogc), str->length());
 }
 
 template <typename CharT>
 static bool
 IsIdentifier(const CharT* chars, size_t length)
 {
+    using namespace js;
+
     if (length == 0)
         return false;
 
     if (!unicode::IsIdentifierStart(char16_t(*chars)))
         return false;
 
     const CharT* end = chars + length;
     while (++chars != end) {
@@ -118,16 +118,18 @@ IsIdentifier(const CharT* chars, size_t 
     }
 
     return true;
 }
 
 static uint32_t
 GetSingleCodePoint(const char16_t** p, const char16_t* end)
 {
+    using namespace js;
+
     uint32_t codePoint;
     if (MOZ_UNLIKELY(unicode::IsLeadSurrogate(**p)) && *p + 1 < end) {
         char16_t lead = **p;
         char16_t maybeTrail = *(*p + 1);
         if (unicode::IsTrailSurrogate(maybeTrail)) {
             *p += 2;
             return unicode::UTF16Decode(lead, maybeTrail);
         }
@@ -136,16 +138,18 @@ GetSingleCodePoint(const char16_t** p, c
     codePoint = **p;
     (*p)++;
     return codePoint;
 }
 
 static bool
 IsIdentifierMaybeNonBMP(const char16_t* chars, size_t length)
 {
+    using namespace js;
+
     if (IsIdentifier(chars, length))
         return true;
 
     if (length == 0)
         return false;
 
     const char16_t* p = chars;
     const char16_t* end = chars + length;
@@ -159,80 +163,84 @@ IsIdentifierMaybeNonBMP(const char16_t* 
         codePoint = GetSingleCodePoint(&p, end);
         if (!unicode::IsIdentifierPart(codePoint))
             return false;
     }
 
     return true;
 }
 
+namespace js {
+
+namespace frontend {
+
 bool
-frontend::IsIdentifier(JSLinearString* str)
+IsIdentifier(JSLinearString* str)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
            ? ::IsIdentifier(str->latin1Chars(nogc), str->length())
            : ::IsIdentifierMaybeNonBMP(str->twoByteChars(nogc), str->length());
 }
 
 bool
-frontend::IsIdentifier(const char* chars, size_t length)
+IsIdentifier(const char* chars, size_t length)
 {
     return ::IsIdentifier(chars, length);
 }
 
 bool
-frontend::IsIdentifier(const char16_t* chars, size_t length)
+IsIdentifier(const char16_t* chars, size_t length)
 {
     return ::IsIdentifier(chars, length);
 }
 
 bool
-frontend::IsKeyword(JSLinearString* str)
+IsKeyword(JSLinearString* str)
 {
     if (const ReservedWordInfo* rw = FindReservedWord(str))
         return TokenKindIsKeyword(rw->tokentype);
 
     return false;
 }
 
 TokenKind
-frontend::ReservedWordTokenKind(PropertyName* str)
+ReservedWordTokenKind(PropertyName* str)
 {
     if (const ReservedWordInfo* rw = FindReservedWord(str))
         return rw->tokentype;
 
     return TOK_NAME;
 }
 
 const char*
-frontend::ReservedWordToCharZ(PropertyName* str)
+ReservedWordToCharZ(PropertyName* str)
 {
     if (const ReservedWordInfo* rw = FindReservedWord(str))
         return ReservedWordToCharZ(rw->tokentype);
 
     return nullptr;
 }
 
 const char*
-frontend::ReservedWordToCharZ(TokenKind tt)
+ReservedWordToCharZ(TokenKind tt)
 {
     MOZ_ASSERT(tt != TOK_NAME);
     switch (tt) {
 #define EMIT_CASE(word, name, type) case type: return js_##word##_str;
       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
 #undef EMIT_CASE
       default:
         MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName.");
     }
     return nullptr;
 }
 
 PropertyName*
-TokenStreamBase::reservedWordToPropertyName(TokenKind tt) const
+TokenStreamAnyChars::reservedWordToPropertyName(TokenKind tt) const
 {
     MOZ_ASSERT(tt != TOK_NAME);
     switch (tt) {
 #define EMIT_CASE(word, name, type) case type: return cx->names().name;
       FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
 #undef EMIT_CASE
       default:
         MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
@@ -288,17 +296,17 @@ TokenStream::SourceCoords::add(uint32_t 
         // than checking it hasn't mysteriously changed).
         // This path can be executed after hitting OOM, so check lineIndex.
         MOZ_ASSERT_IF(lineIndex < sentinelIndex, lineStartOffsets_[lineIndex] == lineStartOffset);
     }
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
-TokenStreamBase::SourceCoords::fill(const TokenStreamBase::SourceCoords& other)
+TokenStreamAnyChars::SourceCoords::fill(const TokenStreamAnyChars::SourceCoords& other)
 {
     MOZ_ASSERT(lineStartOffsets_.back() == MAX_PTR);
     MOZ_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
 
     if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
         return true;
 
     uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
@@ -307,17 +315,17 @@ TokenStreamBase::SourceCoords::fill(cons
     for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++) {
         if (!lineStartOffsets_.append(other.lineStartOffsets_[i]))
             return false;
     }
     return true;
 }
 
 MOZ_ALWAYS_INLINE uint32_t
-TokenStreamBase::SourceCoords::lineIndexOf(uint32_t offset) const
+TokenStreamAnyChars::SourceCoords::lineIndexOf(uint32_t offset) const
 {
     uint32_t iMin, iMax, iMid;
 
     if (lineStartOffsets_[lastLineIndex_] <= offset) {
         // If we reach here, offset is on a line the same as or higher than
         // last time.  Check first for the +0, +1, +2 cases, because they
         // typically cover 85--98% of cases.
         if (offset < lineStartOffsets_[lastLineIndex_ + 1])
@@ -358,49 +366,49 @@ TokenStreamBase::SourceCoords::lineIndex
     }
     MOZ_ASSERT(iMax == iMin);
     MOZ_ASSERT(lineStartOffsets_[iMin] <= offset && offset < lineStartOffsets_[iMin + 1]);
     lastLineIndex_ = iMin;
     return iMin;
 }
 
 uint32_t
-TokenStreamBase::SourceCoords::lineNum(uint32_t offset) const
+TokenStreamAnyChars::SourceCoords::lineNum(uint32_t offset) const
 {
     uint32_t lineIndex = lineIndexOf(offset);
     return lineIndexToNum(lineIndex);
 }
 
 uint32_t
-TokenStreamBase::SourceCoords::columnIndex(uint32_t offset) const
+TokenStreamAnyChars::SourceCoords::columnIndex(uint32_t offset) const
 {
     uint32_t lineIndex = lineIndexOf(offset);
     uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
     MOZ_ASSERT(offset >= lineStartOffset);
     return offset - lineStartOffset;
 }
 
 void
-TokenStreamBase::SourceCoords::lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
-                                                     uint32_t* columnIndex) const
+TokenStreamAnyChars::SourceCoords::lineNumAndColumnIndex(uint32_t offset, uint32_t* lineNum,
+                                                         uint32_t* columnIndex) const
 {
     uint32_t lineIndex = lineIndexOf(offset);
     *lineNum = lineIndexToNum(lineIndex);
     uint32_t lineStartOffset = lineStartOffsets_[lineIndex];
     MOZ_ASSERT(offset >= lineStartOffset);
     *columnIndex = offset - lineStartOffset;
 }
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4351)
 #endif
 
-TokenStreamBase::TokenStreamBase(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                 StrictModeGetter* smg)
+TokenStreamAnyChars::TokenStreamAnyChars(JSContext* cx, const ReadOnlyCompileOptions& options,
+                                         StrictModeGetter* smg)
   : srcCoords(cx, options.lineno),
     options_(options),
     tokens(),
     cursor(),
     lookahead(),
     lineno(options.lineno),
     flags(),
     linebase(0),
@@ -411,17 +419,17 @@ TokenStreamBase::TokenStreamBase(JSConte
     cx(cx),
     mutedErrors(options.mutedErrors()),
     strictModeGetter(smg)
 {
 }
 
 TokenStream::TokenStream(JSContext* cx, const ReadOnlyCompileOptions& options,
                          const CharT* base, size_t length, StrictModeGetter* smg)
-  : TokenStreamBase(cx, options, smg),
+  : TokenStreamAnyChars(cx, options, smg),
     userbuf(cx, base, length, options.column),
     tokenbuf(cx)
 {
     // Nb: the following tables could be static, but initializing them here is
     // much easier.  Don't worry, the time to initialize them for each
     // TokenStream is trivial.  See bug 639420.
 
     // See Parser::assignExpr() for an explanation of isExprEnding[].
@@ -434,17 +442,17 @@ TokenStream::TokenStream(JSContext* cx, 
     isExprEnding[TOK_RC]    = 1;
 }
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 bool
-TokenStreamBase::checkOptions()
+TokenStreamAnyChars::checkOptions()
 {
     // Constrain starting columns to half of the range of a signed 32-bit value,
     // to avoid overflow.
     if (options().column >= mozilla::MaxValue<int32_t>::value / 2 + 1) {
         reportErrorNoOffset(JSMSG_BAD_COLUMN_NUMBER);
         return false;
     }
 
@@ -465,17 +473,17 @@ TokenStream::updateLineInfoForEOL()
 {
     prevLinebase = linebase;
     linebase = userbuf.offset();
     lineno++;
     return srcCoords.add(lineno, linebase);
 }
 
 MOZ_ALWAYS_INLINE void
-TokenStreamBase::updateFlagsForEOL()
+TokenStreamAnyChars::updateFlagsForEOL()
 {
     flags.isDirtyLine = false;
 }
 
 // This gets the next char, normalizing all EOL sequences to '\n' as it goes.
 bool
 TokenStream::getChar(int32_t* cp)
 {
@@ -495,17 +503,17 @@ TokenStream::getChar(int32_t* cp)
         if (MOZ_UNLIKELY(c == '\r')) {
             // If it's a \r\n sequence: treat as a single EOL, skip over the \n.
             if (MOZ_LIKELY(userbuf.hasRawChars()))
                 userbuf.matchRawChar('\n');
 
             break;
         }
 
-        if (MOZ_UNLIKELY(c == LINE_SEPARATOR || c == PARA_SEPARATOR))
+        if (MOZ_UNLIKELY(c == unicode::LINE_SEPARATOR || c == unicode::PARA_SEPARATOR))
             break;
 
         *cp = c;
         return true;
     } while (false);
 
     if (!updateLineInfoForEOL())
         return false;
@@ -678,69 +686,77 @@ TokenStream::reportStrictModeErrorNumber
         return false;
     }
 
     return compileWarning(Move(metadata), Move(notes), JSREPORT_WARNING | JSREPORT_STRICT,
                           errorNumber, args);
 }
 
 bool
-TokenStreamBase::compileWarning(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
-                                unsigned flags, unsigned errorNumber, va_list args)
+TokenStreamAnyChars::compileWarning(ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
+                                    unsigned flags, unsigned errorNumber, va_list args)
 {
     if (options().werrorOption) {
         flags &= ~JSREPORT_WARNING;
         ReportCompileError(cx, Move(metadata), Move(notes), flags, errorNumber, args);
         return false;
     }
 
     return ReportCompileWarning(cx, Move(metadata), Move(notes), flags, errorNumber, args);
 }
 
 void
-TokenStreamBase::computeErrorMetadataNoOffset(ErrorMetadata* err)
+TokenStreamAnyChars::computeErrorMetadataNoOffset(ErrorMetadata* err)
 {
     err->isMuted = mutedErrors;
     err->filename = filename;
     err->lineNumber = 0;
     err->columnNumber = 0;
 
     MOZ_ASSERT(err->lineOfContext == nullptr);
 }
 
 bool
+TokenStreamAnyChars::fillExcludingContext(ErrorMetadata* err, uint32_t offset)
+{
+    err->isMuted = mutedErrors;
+
+    // If this TokenStreamAnyChars doesn't have location information, try to
+    // get it from the caller.
+    if (!filename && !cx->helperThread()) {
+        NonBuiltinFrameIter iter(cx,
+                                 FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
+                                 cx->compartment()->principals());
+        if (!iter.done() && iter.filename()) {
+            err->filename = iter.filename();
+            err->lineNumber = iter.computeLine(&err->columnNumber);
+            return false;
+        }
+    }
+
+    // Otherwise use this TokenStreamAnyChars's location information.
+    err->filename = filename;
+    srcCoords.lineNumAndColumnIndex(offset, &err->lineNumber, &err->columnNumber);
+    return true;
+}
+
+bool
 TokenStream::computeErrorMetadata(ErrorMetadata* err, uint32_t offset)
 {
     if (offset == NoOffset) {
         computeErrorMetadataNoOffset(err);
         return true;
     }
 
-    err->isMuted = mutedErrors;
-
-    // If this TokenStream doesn't have location information, try to get it
-    // from the caller.
-    if (!filename && !cx->helperThread()) {
-        NonBuiltinFrameIter iter(cx,
-                                 FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
-                                 cx->compartment()->principals());
-        if (!iter.done() && iter.filename()) {
-            err->filename = iter.filename();
-            err->lineNumber = iter.computeLine(&err->columnNumber);
-
-            // We can't get a line of context if we're using the caller's
-            // location, so we're done.
-            return true;
-        }
-    }
-
-    // Otherwise this TokenStream's location information should be used.
-    err->filename = filename;
-    srcCoords.lineNumAndColumnIndex(offset,
-                                    &err->lineNumber, &err->columnNumber);
+    // This function's return value isn't a success/failure indication: it
+    // returns true if this TokenStream's location information could be used,
+    // and it returns false when that information can't be used (and so we
+    // can't provide a line of context).
+    if (!fillExcludingContext(err, offset))
+        return true;
 
     // Add a line of context from this TokenStream to help with debugging.
     return computeLineOfContext(err, offset);
 }
 
 bool
 TokenStream::computeLineOfContext(ErrorMetadata* err, uint32_t offset)
 {
@@ -750,22 +766,17 @@ TokenStream::computeLineOfContext(ErrorM
 
     // We only have line-start information for the current line.  If the error
     // is on a different line, we can't easily provide context.  (This means
     // any error in a multi-line token, e.g. an unterminated multiline string
     // literal, won't have context.)
     if (err->lineNumber != lineno)
         return true;
 
-    // We show only a portion (a "window") of the line around the erroneous
-    // token -- the first char in the token, plus |windowRadius| chars before
-    // it and |windowRadius - 1| chars after it.  This is because for a very
-    // long line, printing the whole line is (a) not that helpful, and (b) can
-    // waste a lot of memory.  See bug 634444.
-    constexpr size_t windowRadius = 60;
+    constexpr size_t windowRadius = ErrorMetadata::lineOfContextRadius;
 
     // The window must start within the current line, no earlier than
     // |windowRadius| characters before |offset|.
     MOZ_ASSERT(offset >= linebase);
     size_t windowStart = (offset - linebase > windowRadius) ?
                          offset - windowRadius :
                          linebase;
 
@@ -818,17 +829,17 @@ TokenStream::reportError(unsigned errorN
     ErrorMetadata metadata;
     if (computeErrorMetadata(&metadata, currentToken().pos.begin))
         ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 void
-TokenStreamBase::reportErrorNoOffset(unsigned errorNumber, ...)
+TokenStreamAnyChars::reportErrorNoOffset(unsigned errorNumber, ...)
 {
     va_list args;
     va_start(args, errorNumber);
 
     ErrorMetadata metadata;
     computeErrorMetadataNoOffset(&metadata);
 
     ReportCompileError(cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
@@ -1320,17 +1331,17 @@ TokenStream::getTokenInternal(TokenKind*
 
     c = userbuf.getRawChar();
     MOZ_ASSERT(c != EOF);
 
     // Chars not in the range 0..127 are rare.  Getting them out of the way
     // early allows subsequent checking to be faster.
     if (MOZ_UNLIKELY(c >= 128)) {
         if (unicode::IsSpaceOrBOM2(c)) {
-            if (c == LINE_SEPARATOR || c == PARA_SEPARATOR) {
+            if (c == unicode::LINE_SEPARATOR || c == unicode::PARA_SEPARATOR) {
                 if (!updateLineInfoForEOL())
                     goto error;
 
                 updateFlagsForEOL();
             }
 
             goto retry;
         }
@@ -2176,16 +2187,51 @@ TokenStream::getStringOrTemplateToken(in
         else
             (*tp)->type = TOK_NO_SUBS_TEMPLATE;
     }
 
     (*tp)->setAtom(atom);
     return true;
 }
 
+const char*
+TokenKindToDesc(TokenKind tt)
+{
+    switch (tt) {
+#define EMIT_CASE(name, desc) case TOK_##name: return desc;
+      FOR_EACH_TOKEN_KIND(EMIT_CASE)
+#undef EMIT_CASE
+      case TOK_LIMIT:
+        MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
+        break;
+    }
+
+    return "<bad TokenKind>";
+}
+
+#ifdef DEBUG
+const char*
+TokenKindToString(TokenKind tt)
+{
+    switch (tt) {
+#define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
+      FOR_EACH_TOKEN_KIND(EMIT_CASE)
+#undef EMIT_CASE
+      case TOK_LIMIT: break;
+    }
+
+    return "<bad TokenKind>";
+}
+#endif
+
+} // namespace frontend
+
+} // namespace js
+
+
 JS_FRIEND_API(int)
 js_fgets(char* buf, int size, FILE* file)
 {
     int n, i, c;
     bool crflag;
 
     n = size - 1;
     if (n < 0)
@@ -2203,38 +2249,8 @@ js_fgets(char* buf, int size, FILE* file
             break;              // and overwrite c in buf with \0
         }
         crflag = (c == '\r');
     }
 
     buf[i] = '\0';
     return i;
 }
-
-const char*
-frontend::TokenKindToDesc(TokenKind tt)
-{
-    switch (tt) {
-#define EMIT_CASE(name, desc) case TOK_##name: return desc;
-      FOR_EACH_TOKEN_KIND(EMIT_CASE)
-#undef EMIT_CASE
-      case TOK_LIMIT:
-        MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
-        break;
-    }
-
-    return "<bad TokenKind>";
-}
-
-#ifdef DEBUG
-const char*
-TokenKindToString(TokenKind tt)
-{
-    switch (tt) {
-#define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
-      FOR_EACH_TOKEN_KIND(EMIT_CASE)
-#undef EMIT_CASE
-      case TOK_LIMIT: break;
-    }
-
-    return "<bad TokenKind>";
-}
-#endif
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -22,18 +22,19 @@
 
 #include "jscntxt.h"
 #include "jspubtd.h"
 
 #include "frontend/TokenKind.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "vm/ErrorReporting.h"
-#include "vm/RegExpObject.h"
+#include "vm/RegExpShared.h"
 #include "vm/String.h"
+#include "vm/Unicode.h"
 
 struct KeywordInfo;
 
 namespace js {
 namespace frontend {
 
 struct TokenPos {
     uint32_t    begin;  // Offset of the token's first char.
@@ -90,17 +91,17 @@ enum class InvalidEscapeType {
     Unicode,
     // An otherwise well-formed \u escape which represents a
     // codepoint > 10FFFF.
     UnicodeOverflow,
     // An octal escape in a template token.
     Octal
 };
 
-class TokenStreamBase;
+class TokenStreamAnyChars;
 
 struct Token
 {
   private:
     // Sometimes the parser needs to inform the tokenizer to interpret
     // subsequent text in a particular manner: for example, to tokenize a
     // keyword as an identifier, not as the actual keyword, on the right-hand
     // side of a dotted property access.  Such information is communicated to
@@ -158,17 +159,17 @@ struct Token
         // operand before try getting colon/comma/semicolon.
         // See also the comment in Parser::assignExpr().
         NoneIsOperand,
 
         // If a semicolon is inserted automatically, the next token is already
         // gotten with None, but we expect Operand.
         OperandIsNone,
     };
-    friend class TokenStreamBase;
+    friend class TokenStreamAnyChars;
 
   public:
     TokenKind           type;           // char value or above enumerator
     TokenPos            pos;            // token position in file
     union {
       private:
         friend struct Token;
         PropertyName*   name;          // non-numeric atom
@@ -194,17 +195,17 @@ struct Token
 
     void setAtom(JSAtom* atom) {
         MOZ_ASSERT(type == TOK_STRING ||
                    type == TOK_TEMPLATE_HEAD ||
                    type == TOK_NO_SUBS_TEMPLATE);
         u.atom = atom;
     }
 
-    void setRegExpFlags(js::RegExpFlag flags) {
+    void setRegExpFlags(RegExpFlag flags) {
         MOZ_ASSERT(type == TOK_REGEXP);
         MOZ_ASSERT((flags & AllFlags) == flags);
         u.reflags = flags;
     }
 
     void setNumber(double n, DecimalPoint decimalPoint) {
         MOZ_ASSERT(type == TOK_NUMBER);
         u.number.value = n;
@@ -220,17 +221,17 @@ struct Token
 
     JSAtom* atom() const {
         MOZ_ASSERT(type == TOK_STRING ||
                    type == TOK_TEMPLATE_HEAD ||
                    type == TOK_NO_SUBS_TEMPLATE);
         return u.atom;
     }
 
-    js::RegExpFlag regExpFlags() const {
+    RegExpFlag regExpFlags() const {
         MOZ_ASSERT(type == TOK_REGEXP);
         MOZ_ASSERT((u.reflags & AllFlags) == u.reflags);
         return u.reflags;
     }
 
     double number() const {
         MOZ_ASSERT(type == TOK_NUMBER);
         return u.number.value;
@@ -258,26 +259,20 @@ ReservedWordToCharZ(TokenKind tt);
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream.
 //
 class StrictModeGetter {
   public:
     virtual bool strictMode() = 0;
 };
 
-class TokenStreamBase
+class TokenStreamAnyChars
 {
   protected:
-    TokenStreamBase(JSContext* cx, const ReadOnlyCompileOptions& options, StrictModeGetter* smg);
-
-    // Unicode separators that are treated as line terminators, in addition to \n, \r.
-    enum {
-        LINE_SEPARATOR = 0x2028,
-        PARA_SEPARATOR = 0x2029
-    };
+    TokenStreamAnyChars(JSContext* cx, const ReadOnlyCompileOptions& options, StrictModeGetter* smg);
 
     static const size_t ntokens = 4;                // 1 current + 2 lookahead, rounded
                                                     // to power of 2 to avoid divmod by 3
     static const unsigned maxLookahead = 2;
     static const unsigned ntokensMask = ntokens - 1;
 
   public:
     // Accessors.
@@ -543,16 +538,23 @@ class TokenStreamBase
     JSContext* context() const {
         return cx;
     }
 
     const ReadOnlyCompileOptions& options() const {
         return options_;
     }
 
+    /**
+     * Fill in |err|, excepting line-of-context-related fields.  If the token
+     * stream has location information, use that and return true.  If it does
+     * not, use the caller's location information and return false.
+     */
+    bool fillExcludingContext(ErrorMetadata* err, uint32_t offset);
+
     void updateFlagsForEOL();
 
     const Token& nextToken() const {
         MOZ_ASSERT(hasLookahead());
         return tokens[(cursor + 1) & ntokensMask];
     }
 
     bool hasLookahead() const { return lookahead > 0; }
@@ -621,17 +623,17 @@ class TokenStreamBase
 // this turns out not to be a problem in practice. See the
 // mozilla.dev.tech.js-engine.internals thread entitled 'Bug in the scanner?'
 // for more details:
 // https://groups.google.com/forum/?fromgroups=#!topic/mozilla.dev.tech.js-engine.internals/2JLH5jRcr7E).
 //
 // The methods seek() and tell() allow to rescan from a previous visited
 // location of the buffer.
 //
-class MOZ_STACK_CLASS TokenStream final : public TokenStreamBase
+class MOZ_STACK_CLASS TokenStream final : public TokenStreamAnyChars
 {
   public:
     using CharT = char16_t;
     using CharBuffer = Vector<CharT, 32>;
 
     TokenStream(JSContext* cx, const ReadOnlyCompileOptions& options,
                 const CharT* base, size_t length, StrictModeGetter* smg);
 
@@ -999,17 +1001,20 @@ class MOZ_STACK_CLASS TokenStream final 
 #ifdef DEBUG
         // Poison the TokenBuf so it cannot be accessed again.
         void poison() {
             ptr = nullptr;
         }
 #endif
 
         static bool isRawEOLChar(int32_t c) {
-            return c == '\n' || c == '\r' || c == LINE_SEPARATOR || c == PARA_SEPARATOR;
+            return c == '\n' ||
+                   c == '\r' ||
+                   c == unicode::LINE_SEPARATOR ||
+                   c == unicode::PARA_SEPARATOR;
         }
 
         // Returns the offset of the next EOL, but stops once 'max' characters
         // have been scanned (*including* the char at startOffset_).
         size_t findEOLMax(size_t start, size_t max);
 
       private:
         const CharT* base_;          // base of buffer
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -19,16 +19,18 @@
 #include "gc/GCInternals.h"
 #include "gc/Policy.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
+#include "vm/RegExpObject.h"
+#include "vm/RegExpShared.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 #include "wasm/WasmJS.h"
 
 #include "jscompartmentinlines.h"
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -37,17 +37,17 @@ using namespace js;
 using namespace gc;
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
-static const uintptr_t CanaryMagicValue = 0xDEADB15D;
+constexpr uintptr_t CanaryMagicValue = 0xDEADB15D;
 
 struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask
 {
     explicit FreeMallocedBuffersTask(FreeOp* fop) : GCParallelTask(fop->runtime()), fop_(fop) {}
     bool init() { return buffers_.init(); }
     void transferBuffersToFree(MallocedBuffersSet& buffersToFree,
                                const AutoLockHelperThreadState& lock);
     ~FreeMallocedBuffersTask() override { join(); }
--- a/js/src/gc/NurseryAwareHashMap.h
+++ b/js/src/gc/NurseryAwareHashMap.h
@@ -2,16 +2,22 @@
  * 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 gc_NurseryAwareHashMap_h
 #define gc_NurseryAwareHashMap_h
 
+#include "gc/Barrier.h"
+#include "gc/Marking.h"
+#include "js/GCHashTable.h"
+#include "js/GCPolicyAPI.h"
+#include "js/HashTable.h"
+
 namespace js {
 
 namespace detail {
 // This class only handles the incremental case and does not deal with nursery
 // pointers. The only users should be for NurseryAwareHashMap; it is defined
 // externally because we need a GCPolicy for its use in the contained map.
 template <typename T>
 class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -25,21 +25,29 @@
 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "irregexp/RegExpParser.h"
 
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Move.h"
+
 #include "frontend/TokenStream.h"
+#include "vm/ErrorReporting.h"
+#include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::irregexp;
 
+using mozilla::Move;
+using mozilla::PointerRangeSize;
+
 // ----------------------------------------------------------------------------
 // RegExpBuilder
 
 RegExpBuilder::RegExpBuilder(LifoAlloc* alloc)
   : alloc(alloc),
     pending_empty_(false),
     characters_(nullptr)
 #ifdef DEBUG
@@ -221,37 +229,91 @@ RegExpBuilder::AddQuantifierToAtom(int m
 
 template <typename CharT>
 RegExpParser<CharT>::RegExpParser(frontend::TokenStream& ts, LifoAlloc* alloc,
                                   const CharT* chars, const CharT* end, bool multiline_mode,
                                   bool unicode, bool ignore_case)
   : ts(ts),
     alloc(alloc),
     captures_(nullptr),
-    next_pos_(chars),
+    start_(chars),
+    next_pos_(start_),
     end_(end),
     current_(kEndMarker),
     capture_count_(0),
     has_more_(true),
     multiline_(multiline_mode),
     unicode_(unicode),
     ignore_case_(ignore_case),
     simple_(false),
     contains_anchor_(false),
     is_scanned_for_captures_(false)
 {
     Advance();
 }
 
 template <typename CharT>
+void
+RegExpParser<CharT>::SyntaxError(unsigned errorNumber, ...)
+{
+    ErrorMetadata err;
+
+    ts.fillExcludingContext(&err, ts.currentToken().pos.begin);
+
+    // For most error reporting, the line of context derives from the token
+    // stream.  So when location information doesn't come from the token
+    // stream, we can't give a line of context.  But here the "line of context"
+    // can be (and is) derived from the pattern text, so we can provide it no
+    // matter if the location is derived from the caller.
+    size_t offset = PointerRangeSize(start_, next_pos_ - 1);
+    size_t end = PointerRangeSize(start_, end_);
+
+    const CharT* windowStart = (offset > ErrorMetadata::lineOfContextRadius)
+                               ? start_ + (offset - ErrorMetadata::lineOfContextRadius)
+                               : start_;
+
+    const CharT* windowEnd = (end - offset > ErrorMetadata::lineOfContextRadius)
+                             ? start_ + offset + ErrorMetadata::lineOfContextRadius
+                             : end_;
+
+    size_t windowLength = PointerRangeSize(windowStart, windowEnd);
+    MOZ_ASSERT(windowLength <= ErrorMetadata::lineOfContextRadius * 2);
+
+    // Create the windowed string, not including the potential line
+    // terminator.
+    StringBuffer windowBuf(ts.context());
+    if (!windowBuf.append(windowStart, windowEnd))
+        return;
+
+    // The line of context must be null-terminated, and StringBuffer doesn't
+    // make that happen unless we force it to.
+    if (!windowBuf.append('\0'))
+        return;
+
+    err.lineOfContext.reset(windowBuf.stealChars());
+    if (!err.lineOfContext)
+        return;
+
+    err.lineLength = windowLength;
+    err.tokenOffset = offset - (windowStart - start_);
+
+    va_list args;
+    va_start(args, errorNumber);
+
+    ReportCompileError(ts.context(), Move(err), nullptr, JSREPORT_ERROR, errorNumber, args);
+
+    va_end(args);
+}
+
+template <typename CharT>
 RegExpTree*
 RegExpParser<CharT>::ReportError(unsigned errorNumber, const char* param /* = nullptr */)
 {
     gc::AutoSuppressGC suppressGC(ts.context());
-    ts.reportError(errorNumber, param);
+    SyntaxError(errorNumber, param);
     return nullptr;
 }
 
 template <typename CharT>
 void
 RegExpParser<CharT>::Advance()
 {
     if (next_pos_ < end_) {
--- a/js/src/irregexp/RegExpParser.h
+++ b/js/src/irregexp/RegExpParser.h
@@ -26,16 +26,18 @@
 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef V8_PARSER_H_
 #define V8_PARSER_H_
 
+#include <stdarg.h>
+
 #include "irregexp/RegExpAST.h"
 
 namespace js {
 
 namespace frontend {
     class TokenStream;
 }
 
@@ -206,17 +208,23 @@ class RegExpParser
 
     // Tries to parse the input as a back reference.  If successful it
     // stores the result in the output parameter and returns true.  If
     // it fails it will push back the characters read so the same characters
     // can be reparsed.
     bool ParseBackReferenceIndex(int* index_out);
 
     bool ParseClassAtom(char16_t* char_class, widechar *value);
+
+  private:
+    void SyntaxError(unsigned errorNumber, ...);
+
+  public:
     RegExpTree* ReportError(unsigned errorNumber, const char* param = nullptr);
+
     void Advance();
     void Advance(int dist) {
         next_pos_ += dist - 1;
         Advance();
     }
 
     void Reset(const CharT* pos) {
         next_pos_ = pos;
@@ -286,16 +294,17 @@ class RegExpParser
             return *next_pos_;
         return kEndMarker;
     }
     void ScanForCaptures();
 
     frontend::TokenStream& ts;
     LifoAlloc* alloc;
     RegExpCaptureVector* captures_;
+    const CharT* const start_;
     const CharT* next_pos_;
     const CharT* end_;
     widechar current_;
     // The capture count is only valid after we have scanned for captures.
     int capture_count_;
     bool has_more_;
     bool multiline_;
     bool unicode_;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -18,16 +18,17 @@
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
+#include "builtin/RegExp.h"
 #include "builtin/TypedObject.h"
 #include "gc/Nursery.h"
 #include "irregexp/NativeRegExpMacroAssembler.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/IonBuilder.h"
 #include "jit/IonCaches.h"
 #include "jit/IonIC.h"
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include <ctype.h>
 
 #include "jslibmath.h"
 #include "jsstr.h"
 
+#include "builtin/RegExp.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 #include "js/Conversions.h"
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -22,18 +22,20 @@
 #include "jit/BaselineIC.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
+#include "js/HeapAPI.h"
 #include "vm/ArrayObject.h"
 #include "vm/EnvironmentObject.h"
+#include "vm/RegExpObject.h"
 #include "vm/SharedMem.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 namespace js {
 
 class StringObject;
 
--- a/js/src/jsapi-tests/testArrayBufferView.cpp
+++ b/js/src/jsapi-tests/testArrayBufferView.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 
 #include "jsapi-tests/tests.h"
+#include "vm/ProxyObject.h"
 
 #include "jscompartmentinlines.h"
 
 using namespace js;
 
 BEGIN_TEST(testArrayBufferView_type)
 {
     CHECK((TestViewType<uint8_t,
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -9,19 +9,19 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "jsiter.h"
 
 #include "builtin/Object.h"
 #include "jit/JitFrames.h"
+#include "proxy/Proxy.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
-#include "vm/ProxyObject.h"
 #include "vm/Symbol.h"
 
 namespace js {
 
 class CompartmentChecker
 {
     JSCompartment* compartment;
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -4,16 +4,18 @@
  * 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 "jscompartmentinlines.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
+#include <stddef.h>
+
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
@@ -1375,32 +1377,32 @@ JSCompartment::reportTelemetry()
     // Hazard analysis can't tell that the telemetry callbacks don't GC.
     JS::AutoSuppressGCAnalysis nogc;
 
     int id = creationOptions_.addonIdOrNull()
              ? JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS
              : JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT;
 
     // Call back into Firefox's Telemetry reporter.
-    for (size_t i = 0; i < DeprecatedLanguageExtensionCount; i++) {
+    for (size_t i = 0; i < size_t(DeprecatedLanguageExtension::Count); i++) {
         if (sawDeprecatedLanguageExtension[i])
             runtime_->addTelemetry(id, i);
     }
 }
 
 void
 JSCompartment::addTelemetry(const char* filename, DeprecatedLanguageExtension e)
 {
     // Only report telemetry for web content and add-ons, not chrome JS.
     if (isSystem_)
         return;
     if (!creationOptions_.addonIdOrNull() && (!filename || strncmp(filename, "http", 4) != 0))
         return;
 
-    sawDeprecatedLanguageExtension[e] = true;
+    sawDeprecatedLanguageExtension[size_t(e)] = true;
 }
 
 HashNumber
 JSCompartment::randomHashCode()
 {
     ensureRandomNumberGenerator();
     return HashNumber(randomNumberGenerator.ref().next());
 }
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -9,37 +9,40 @@
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/Variant.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
-#include "builtin/RegExp.h"
+#include <stddef.h>
+
+#include "frontend/LanguageExtensions.h"
 #include "gc/Barrier.h"
 #include "gc/NurseryAwareHashMap.h"
 #include "gc/Zone.h"
-#include "vm/GlobalObject.h"
 #include "vm/PIC.h"
+#include "vm/RegExpShared.h"
 #include "vm/SavedStacks.h"
 #include "vm/TemplateRegistry.h"
 #include "vm/Time.h"
 #include "wasm/WasmCompartment.h"
 
 namespace js {
 
 namespace jit {
 class JitCompartment;
 } // namespace jit
 
 namespace gc {
 template <typename Node, typename Derived> class ComponentFinder;
 } // namespace gc
 
+class GlobalObject;
 class LexicalEnvironmentObject;
 class ScriptSourceObject;
 struct NativeIterator;
 
 /*
  * A single-entry cache for some base-10 double-to-string conversions. This
  * helps date-format-xparb.js.  It also avoids skewing the results for
  * v8-splay.js when measured by the SunSpider harness, where the splay tree
@@ -952,43 +955,28 @@ struct JSCompartment
     js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
 
   public:
     bool ensureJitCompartmentExists(JSContext* cx);
     js::jit::JitCompartment* jitCompartment() {
         return jitCompartment_;
     }
 
-    enum DeprecatedLanguageExtension {
-        DeprecatedForEach = 0,              // JS 1.6+
-        // NO LONGER USING 1
-        DeprecatedLegacyGenerator = 2,      // JS 1.7+
-        DeprecatedExpressionClosure = 3,    // Added in JS 1.8
-        // NO LONGER USING 4
-        // NO LONGER USING 5
-        // NO LONGER USING 6
-        // NO LONGER USING 7
-        // NO LONGER USING 8
-        // NO LONGER USING 9
-        DeprecatedBlockScopeFunRedecl = 10,
-        DeprecatedLanguageExtensionCount
-    };
-
     js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
 
     js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
 
   private:
     // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
-    bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount];
+    bool sawDeprecatedLanguageExtension[size_t(js::DeprecatedLanguageExtension::Count)];
 
     void reportTelemetry();
 
   public:
-    void addTelemetry(const char* filename, DeprecatedLanguageExtension e);
+    void addTelemetry(const char* filename, js::DeprecatedLanguageExtension e);
 
   public:
     // Aggregated output used to collect JSScript hit counts when code coverage
     // is enabled.
     js::coverage::LCovCompartment lcovOutput;
 };
 
 namespace js {
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -7,16 +7,17 @@
 #ifndef jsscriptinlines_h
 #define jsscriptinlines_h
 
 #include "jsscript.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/IonAnalysis.h"
 #include "vm/EnvironmentObject.h"
+#include "vm/RegExpObject.h"
 #include "wasm/AsmJS.h"
 
 #include "jscompartmentinlines.h"
 
 #include "vm/Shape-inl.h"
 
 namespace js {
 
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -7,16 +7,17 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsexn.h"
 #include "jswrapper.h"
 
 #include "js/Proxy.h"
 #include "vm/ErrorObject.h"
 #include "vm/ProxyObject.h"
+#include "vm/RegExpObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/EnvironmentObject-inl.h
+++ b/js/src/vm/EnvironmentObject-inl.h
@@ -3,17 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_EnvironmentObject_inl_h
 #define vm_EnvironmentObject_inl_h
 
 #include "vm/EnvironmentObject.h"
-#include "frontend/SharedContext.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/TypeInference-inl.h"
 
 namespace js {
 
 inline LexicalEnvironmentObject&
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jscompartment.h"
 #include "jsiter.h"
 
 #include "builtin/ModuleObject.h"
-#include "frontend/ParseNode.h"
 #include "gc/Policy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/GlobalObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
 #include "wasm/WasmInstance.h"
--- a/js/src/vm/ErrorReporting.h
+++ b/js/src/vm/ErrorReporting.h
@@ -32,22 +32,30 @@ struct ErrorMetadata
     // The line and column numbers where the error occurred.  If the error
     // is with respect to the entire script and not with respect to a
     // particular location, these will both be zero.
     uint32_t lineNumber;
     uint32_t columnNumber;
 
     // If the error occurs at a particular location, context surrounding the
     // location of the error: the line that contained the error, or a small
-    // portion of it if the line is long.
+    // portion of it if the line is long.  (If the error occurs within a
+    // regular expression, this context is based upon its pattern characters.)
     //
     // This information is provided on a best-effort basis: code populating
     // ErrorMetadata instances isn't obligated to supply this.
     JS::UniqueTwoByteChars lineOfContext;
 
+    // If |lineOfContext| is provided, we show only a portion (a "window") of
+    // the line around the erroneous token -- the first char in the token, plus
+    // |lineOfContextRadius| chars before it and |lineOfContextRadius - 1|
+    // chars after it.  This is because for a very long line, the full line is
+    // (a) not that helpful, and (b) wastes a lot of memory.  See bug 634444.
+    static constexpr size_t lineOfContextRadius = 60;
+
     // If |lineOfContext| is non-null, its length.
     size_t lineLength;
 
     // If |lineOfContext| is non-null, the offset within it of the token that
     // triggered the error.
     size_t tokenOffset;
 
     // Whether the error is "muted" because it derives from a cross-origin
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -8,17 +8,16 @@
 #define vm_GlobalObject_h
 
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsnum.h"
 
-#include "builtin/RegExp.h"
 #include "js/Vector.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -17,17 +17,16 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Variant.h"
 
 #include "jsapi.h"
 #include "jscntxt.h"
 
-#include "frontend/TokenStream.h"
 #include "jit/Ion.h"
 #include "threading/ConditionVariable.h"
 #include "vm/MutexIDs.h"
 
 namespace JS {
 struct Zone;
 } // namespace JS
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -9,18 +9,16 @@
 
 /*
  * JS interpreter interface.
  */
 
 #include "jsiter.h"
 #include "jspubtd.h"
 
-#include "frontend/ParseNode.h"
-
 #include "vm/Stack.h"
 
 namespace js {
 
 class EnvironmentIter;
 
 /*
  * Convert null/undefined |thisv| into the current global object for the
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -12,16 +12,17 @@
 
 #include "builtin/DataViewObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/StoreBuffer.h"
 #include "gc/Zone.h"
 #include "js/CharacterEncoding.h"
 #include "vm/ArrayObject.h"
+#include "vm/RegExpObject.h"
 #include "vm/Shape.h"
 #include "vm/TaggedProto.h"
 #include "vm/UnboxedObject.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/UnboxedObject-inl.h"
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -990,17 +990,17 @@ RegExpShared::compile(JSContext* cx, Mut
 /* static */ bool
 RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
                       HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
 {
     if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
         re->canStringMatch = true;
 
     CompileOptions options(cx);
-    TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
+    frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
 
     /* Parse the pattern. */
     irregexp::RegExpCompileData data;
     if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
                                 re->multiline(), mode == MatchOnly, re->unicode(),
                                 re->ignoreCase(), re->global(), re->sticky(), &data))
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -1,358 +1,62 @@
 /* -*- 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/. */
 
+/* JavaScript RegExp objects. */
+
 #ifndef vm_RegExpObject_h
 #define vm_RegExpObject_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "gc/Marking.h"
-#include "gc/Zone.h"
+#include "js/GCHashTable.h"
 #include "proxy/Proxy.h"
 #include "vm/ArrayObject.h"
+#include "vm/RegExpShared.h"
 #include "vm/Shape.h"
 
+namespace JS { struct Zone; }
+
 /*
  * JavaScript Regular Expressions
  *
  * There are several engine concepts associated with a single logical regexp:
  *
- *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
- *
- *   RegExpShared - The compiled representation of the regexp.
- *
- *   RegExpCompartment - Owns all RegExpShared instances in a compartment.
- *
- * To save memory, a RegExpShared is not created for a RegExpObject until it is
- * needed for execution. When a RegExpShared needs to be created, it is looked
- * up in a per-compartment table to allow reuse between objects. Lastly, on GC,
- * every RegExpShared that is not in active use is discarded.
+ *   RegExpObject:
+ *     The JS-visible object whose .[[Class]] equals "RegExp".
+ *   RegExpShared:
+ *     The compiled representation of the regexp (lazily created, cleared
+ *     during some forms of GC).
+ *   RegExpCompartment:
+ *     Owns all RegExpShared instances in a compartment.
  */
 namespace js {
 
 struct MatchPair;
 class MatchPairs;
-class RegExpShared;
 class RegExpStatics;
 
-using RootedRegExpShared = JS::Rooted<RegExpShared*>;
-using HandleRegExpShared = JS::Handle<RegExpShared*>;
-using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
-
 namespace frontend { class TokenStream; }
 
-enum RegExpFlag : uint8_t
-{
-    IgnoreCaseFlag  = 0x01,
-    GlobalFlag      = 0x02,
-    MultilineFlag   = 0x04,
-    StickyFlag      = 0x08,
-    UnicodeFlag     = 0x10,
-
-    NoFlags         = 0x00,
-    AllFlags        = 0x1f
-};
-
-static_assert(IgnoreCaseFlag == REGEXP_IGNORECASE_FLAG &&
-              GlobalFlag == REGEXP_GLOBAL_FLAG &&
-              MultilineFlag == REGEXP_MULTILINE_FLAG &&
-              StickyFlag == REGEXP_STICKY_FLAG &&
-              UnicodeFlag == REGEXP_UNICODE_FLAG,
-              "Flag values should be in sync with self-hosted JS");
-
-enum RegExpRunStatus
-{
-    RegExpRunStatus_Error,
-    RegExpRunStatus_Success,
-    RegExpRunStatus_Success_NotFound
-};
-
 extern RegExpObject*
 RegExpAlloc(JSContext* cx, HandleObject proto = nullptr);
 
 // |regexp| is under-typed because this function's used in the JIT.
 extern JSObject*
 CloneRegExpObject(JSContext* cx, JSObject* regexp);
 
-/*
- * A RegExpShared is the compiled representation of a regexp. A RegExpShared is
- * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
- * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a
- * table so that they can be reused when compiling the same regex string.
- *
- * During a GC, RegExpShared instances are marked and swept like GC things.
- * Usually, RegExpObjects clear their pointers to their RegExpShareds rather
- * than explicitly tracing them, so that the RegExpShared and any jitcode can
- * be reclaimed quicker. However, the RegExpShareds are traced through by
- * objects when we are preserving jitcode in their zone, to avoid the same
- * recompilation inefficiencies as normal Ion and baseline compilation.
- */
-class RegExpShared : public gc::TenuredCell
-{
-  public:
-    enum CompilationMode {
-        Normal,
-        MatchOnly
-    };
-
-    enum ForceByteCodeEnum {
-        DontForceByteCode,
-        ForceByteCode
-    };
-
-  private:
-    friend class RegExpCompartment;
-    friend class RegExpStatics;
-
-    typedef frontend::TokenStream TokenStream;
-
-    struct RegExpCompilation
-    {
-        ReadBarriered<jit::JitCode*> jitCode;
-        uint8_t* byteCode;
-
-        RegExpCompilation() : byteCode(nullptr) {}
-
-        bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
-            return byteCode || (force == DontForceByteCode && jitCode);
-        }
-    };
-
-    /* Source to the RegExp, for lazy compilation. */
-    HeapPtr<JSAtom*>   source;
-
-    RegExpFlag         flags;
-    bool               canStringMatch;
-    size_t             parenCount;
-
-    RegExpCompilation  compilationArray[4];
-
-    static int CompilationIndex(CompilationMode mode, bool latin1) {
-        switch (mode) {
-          case Normal:    return latin1 ? 0 : 1;
-          case MatchOnly: return latin1 ? 2 : 3;
-        }
-        MOZ_CRASH();
-    }
-
-    // Tables referenced by JIT code.
-    using JitCodeTables = Vector<uint8_t*, 0, SystemAllocPolicy>;
-    JitCodeTables tables;
-
-    /* Internal functions. */
-    RegExpShared(JSAtom* source, RegExpFlag flags);
-
-    static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleLinearString input,
-                        CompilationMode mode, ForceByteCodeEnum force);
-    static bool compile(JSContext* cx, MutableHandleRegExpShared res, HandleAtom pattern,
-                        HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force);
-
-    static bool compileIfNecessary(JSContext* cx, MutableHandleRegExpShared res,
-                                   HandleLinearString input, CompilationMode mode,
-                                   ForceByteCodeEnum force);
-
-    const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
-        return compilationArray[CompilationIndex(mode, latin1)];
-    }
-
-    RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
-        return compilationArray[CompilationIndex(mode, latin1)];
-    }
-
-  public:
-    ~RegExpShared() = delete;
-
-    // Execute this RegExp on input starting from searchIndex, filling in
-    // matches if specified and otherwise only determining if there is a match.
-    static RegExpRunStatus execute(JSContext* cx, MutableHandleRegExpShared res,
-                                   HandleLinearString input, size_t searchIndex,
-                                   MatchPairs* matches, size_t* endIndex);
-
-    // Register a table with this RegExpShared, and take ownership.
-    bool addTable(uint8_t* table) {
-        return tables.append(table);
-    }
-
-    /* Accessors */
-
-    size_t getParenCount() const {
-        MOZ_ASSERT(isCompiled());
-        return parenCount;
-    }
-
-    /* Accounts for the "0" (whole match) pair. */
-    size_t pairCount() const            { return getParenCount() + 1; }
-
-    JSAtom* getSource() const           { return source; }
-    RegExpFlag getFlags() const         { return flags; }
-    bool ignoreCase() const             { return flags & IgnoreCaseFlag; }
-    bool global() const                 { return flags & GlobalFlag; }
-    bool multiline() const              { return flags & MultilineFlag; }
-    bool sticky() const                 { return flags & StickyFlag; }
-    bool unicode() const                { return flags & UnicodeFlag; }
-
-    bool isCompiled(CompilationMode mode, bool latin1,
-                    ForceByteCodeEnum force = DontForceByteCode) const {
-        return compilation(mode, latin1).compiled(force);
-    }
-    bool isCompiled() const {
-        return isCompiled(Normal, true) || isCompiled(Normal, false)
-            || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
-    }
-
-    void traceChildren(JSTracer* trc);
-    void discardJitCode();
-    void finalize(FreeOp* fop);
-
-    static size_t offsetOfSource() {
-        return offsetof(RegExpShared, source);
-    }
-
-    static size_t offsetOfFlags() {
-        return offsetof(RegExpShared, flags);
-    }
-
-    static size_t offsetOfParenCount() {
-        return offsetof(RegExpShared, parenCount);
-    }
-
-    static size_t offsetOfLatin1JitCode(CompilationMode mode) {
-        return offsetof(RegExpShared, compilationArray)
-             + (CompilationIndex(mode, true) * sizeof(RegExpCompilation))
-             + offsetof(RegExpCompilation, jitCode);
-    }
-    static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
-        return offsetof(RegExpShared, compilationArray)
-             + (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
-             + offsetof(RegExpCompilation, jitCode);
-    }
-
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
-
-#ifdef DEBUG
-    static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res, bool match_only,
-                             HandleLinearString input);
-#endif
-};
-
-class RegExpCompartment
-{
-    struct Key {
-        JSAtom* atom;
-        uint16_t flag;
-
-        Key() {}
-        Key(JSAtom* atom, RegExpFlag flag)
-          : atom(atom), flag(flag)
-        { }
-        MOZ_IMPLICIT Key(const ReadBarriered<RegExpShared*>& shared)
-          : atom(shared.unbarrieredGet()->getSource()),
-            flag(shared.unbarrieredGet()->getFlags())
-        { }
-
-        typedef Key Lookup;
-        static HashNumber hash(const Lookup& l) {
-            return DefaultHasher<JSAtom*>::hash(l.atom) ^ (l.flag << 1);
-        }
-        static bool match(Key l, Key r) {
-            return l.atom == r.atom && l.flag == r.flag;
-        }
-    };
-
-    /*
-     * The set of all RegExpShareds in the compartment. On every GC, every
-     * RegExpShared that was not marked is deleted and removed from the set.
-     */
-    using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
-    JS::WeakCache<Set> set_;
-
-    /*
-     * This is the template object where the result of re.exec() is based on,
-     * if there is a result. This is used in CreateRegExpMatchResult to set
-     * the input/index properties faster.
-     */
-    ReadBarriered<ArrayObject*> matchResultTemplateObject_;
-
-    /*
-     * The shape of RegExp.prototype object that satisfies following:
-     *   * RegExp.prototype.flags getter is not modified
-     *   * RegExp.prototype.global getter is not modified
-     *   * RegExp.prototype.ignoreCase getter is not modified
-     *   * RegExp.prototype.multiline getter is not modified
-     *   * RegExp.prototype.sticky getter is not modified
-     *   * RegExp.prototype.unicode getter is not modified
-     *   * RegExp.prototype.exec is an own data property
-     *   * RegExp.prototype[@@match] is an own data property
-     *   * RegExp.prototype[@@search] is an own data property
-     */
-    ReadBarriered<Shape*> optimizableRegExpPrototypeShape_;
-
-    /*
-     * The shape of RegExp instance that satisfies following:
-     *   * lastProperty is lastIndex
-     *   * prototype is RegExp.prototype
-     */
-    ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
-
-    ArrayObject* createMatchResultTemplateObject(JSContext* cx);
-
-  public:
-    explicit RegExpCompartment(Zone* zone);
-    ~RegExpCompartment();
-
-    bool init(JSContext* cx);
-    void sweep(JSRuntime* rt);
-
-    bool empty() { return set_.empty(); }
-
-    bool get(JSContext* cx, HandleAtom source, RegExpFlag flags, MutableHandleRegExpShared shared);
-
-    /* Like 'get', but compile 'maybeOpt' (if non-null). */
-    bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
-             MutableHandleRegExpShared shared);
-
-    /* Get or create template object used to base the result of .exec() on. */
-    ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
-        if (matchResultTemplateObject_)
-            return matchResultTemplateObject_;
-        return createMatchResultTemplateObject(cx);
-    }
-
-    Shape* getOptimizableRegExpPrototypeShape() {
-        return optimizableRegExpPrototypeShape_;
-    }
-    void setOptimizableRegExpPrototypeShape(Shape* shape) {
-        optimizableRegExpPrototypeShape_ = shape;
-    }
-    Shape* getOptimizableRegExpInstanceShape() {
-        return optimizableRegExpInstanceShape_;
-    }
-    void setOptimizableRegExpInstanceShape(Shape* shape) {
-        optimizableRegExpInstanceShape_ = shape;
-    }
-
-    static size_t offsetOfOptimizableRegExpPrototypeShape() {
-        return offsetof(RegExpCompartment, optimizableRegExpPrototypeShape_);
-    }
-    static size_t offsetOfOptimizableRegExpInstanceShape() {
-        return offsetof(RegExpCompartment, optimizableRegExpInstanceShape_);
-    }
-
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
-};
-
 class RegExpObject : public NativeObject
 {
     static const unsigned LAST_INDEX_SLOT          = 0;
     static const unsigned SOURCE_SLOT              = 1;
     static const unsigned FLAGS_SLOT               = 2;
 
     static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
                   "FLAGS_SLOT values should be in sync with self-hosted JS");
@@ -515,34 +219,9 @@ template <typename CharT>
 extern bool
 HasRegExpMetaChars(const CharT* chars, size_t length);
 
 extern bool
 StringHasRegExpMetaChars(JSLinearString* str);
 
 } /* namespace js */
 
-namespace JS {
-namespace ubi {
-
-template <>
-class Concrete<js::RegExpShared> : TracerConcrete<js::RegExpShared>
-{
-  protected:
-    explicit Concrete(js::RegExpShared* ptr) : TracerConcrete<js::RegExpShared>(ptr) { }
-
-  public:
-    static void construct(void* storage, js::RegExpShared* ptr) {
-        new (storage) Concrete(ptr);
-    }
-
-    CoarseType coarseType() const final { return CoarseType::Other; }
-
-    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
-
-    const char16_t* typeName() const override { return concreteTypeName; }
-    static const char16_t concreteTypeName[];
-};
-
-} // namespace ubi
-} // namespace JS
-
 #endif /* vm_RegExpObject_h */
copy from js/src/vm/RegExpObject.h
copy to js/src/vm/RegExpShared.h
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpShared.h
@@ -1,58 +1,48 @@
 /* -*- 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 vm_RegExpObject_h
-#define vm_RegExpObject_h
+/**
+ * The compiled representation of a RegExp, potentially shared among RegExp instances created
+ * during separate evaluations of a single RegExp literal in source code.
+ */
 
-#include "mozilla/Attributes.h"
+#ifndef vm_RegExpShared_h
+#define vm_RegExpShared_h
+
+#include "mozilla/Assertions.h"
 #include "mozilla/MemoryReporting.h"
 
-#include "jscntxt.h"
+#include "jsalloc.h"
+#include "jsatom.h"
 
 #include "builtin/SelfHostingDefines.h"
+#include "gc/Heap.h"
 #include "gc/Marking.h"
-#include "gc/Zone.h"
-#include "proxy/Proxy.h"
-#include "vm/ArrayObject.h"
-#include "vm/Shape.h"
+#include "js/UbiNode.h"
+#include "js/Vector.h"
 
-/*
- * JavaScript Regular Expressions
- *
- * There are several engine concepts associated with a single logical regexp:
- *
- *   RegExpObject - The JS-visible object whose .[[Class]] equals "RegExp"
- *
- *   RegExpShared - The compiled representation of the regexp.
- *
- *   RegExpCompartment - Owns all RegExpShared instances in a compartment.
- *
- * To save memory, a RegExpShared is not created for a RegExpObject until it is
- * needed for execution. When a RegExpShared needs to be created, it is looked
- * up in a per-compartment table to allow reuse between objects. Lastly, on GC,
- * every RegExpShared that is not in active use is discarded.
- */
+struct JSContext;
+
 namespace js {
 
-struct MatchPair;
+class ArrayObject;
 class MatchPairs;
+class RegExpCompartment;
 class RegExpShared;
 class RegExpStatics;
 
 using RootedRegExpShared = JS::Rooted<RegExpShared*>;
 using HandleRegExpShared = JS::Handle<RegExpShared*>;
 using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
 
-namespace frontend { class TokenStream; }
-
 enum RegExpFlag : uint8_t
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
     UnicodeFlag     = 0x10,
 
@@ -69,29 +59,26 @@ static_assert(IgnoreCaseFlag == REGEXP_I
 
 enum RegExpRunStatus
 {
     RegExpRunStatus_Error,
     RegExpRunStatus_Success,
     RegExpRunStatus_Success_NotFound
 };
 
-extern RegExpObject*
-RegExpAlloc(JSContext* cx, HandleObject proto = nullptr);
-
-// |regexp| is under-typed because this function's used in the JIT.
-extern JSObject*
-CloneRegExpObject(JSContext* cx, JSObject* regexp);
-
 /*
  * A RegExpShared is the compiled representation of a regexp. A RegExpShared is
  * potentially pointed to by multiple RegExpObjects. Additionally, C++ code may
  * have pointers to RegExpShareds on the stack. The RegExpShareds are kept in a
  * table so that they can be reused when compiling the same regex string.
  *
+ * To save memory, a RegExpShared is not created for a RegExpObject until it is
+ * needed for execution. When a RegExpShared needs to be created, it is looked
+ * up in a per-compartment table to allow reuse between objects.
+ *
  * During a GC, RegExpShared instances are marked and swept like GC things.
  * Usually, RegExpObjects clear their pointers to their RegExpShareds rather
  * than explicitly tracing them, so that the RegExpShared and any jitcode can
  * be reclaimed quicker. However, the RegExpShareds are traced through by
  * objects when we are preserving jitcode in their zone, to avoid the same
  * recompilation inefficiencies as normal Ion and baseline compilation.
  */
 class RegExpShared : public gc::TenuredCell
@@ -106,18 +93,16 @@ class RegExpShared : public gc::TenuredC
         DontForceByteCode,
         ForceByteCode
     };
 
   private:
     friend class RegExpCompartment;
     friend class RegExpStatics;
 
-    typedef frontend::TokenStream TokenStream;
-
     struct RegExpCompilation
     {
         ReadBarriered<jit::JitCode*> jitCode;
         uint8_t* byteCode;
 
         RegExpCompilation() : byteCode(nullptr) {}
 
         bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
@@ -265,17 +250,17 @@ class RegExpCompartment
             return l.atom == r.atom && l.flag == r.flag;
         }
     };
 
     /*
      * The set of all RegExpShareds in the compartment. On every GC, every
      * RegExpShared that was not marked is deleted and removed from the set.
      */
-    using Set = GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
+    using Set = JS::GCHashSet<ReadBarriered<RegExpShared*>, Key, RuntimeAllocPolicy>;
     JS::WeakCache<Set> set_;
 
     /*
      * This is the template object where the result of re.exec() is based on,
      * if there is a result. This is used in CreateRegExpMatchResult to set
      * the input/index properties faster.
      */
     ReadBarriered<ArrayObject*> matchResultTemplateObject_;
@@ -343,186 +328,16 @@ class RegExpCompartment
     }
     static size_t offsetOfOptimizableRegExpInstanceShape() {
         return offsetof(RegExpCompartment, optimizableRegExpInstanceShape_);
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
-class RegExpObject : public NativeObject
-{
-    static const unsigned LAST_INDEX_SLOT          = 0;
-    static const unsigned SOURCE_SLOT              = 1;
-    static const unsigned FLAGS_SLOT               = 2;
-
-    static_assert(RegExpObject::FLAGS_SLOT == REGEXP_FLAGS_SLOT,
-                  "FLAGS_SLOT values should be in sync with self-hosted JS");
-
-  public:
-    static const unsigned RESERVED_SLOTS = 3;
-    static const unsigned PRIVATE_SLOT = 3;
-
-    static const Class class_;
-    static const Class protoClass_;
-
-    // The maximum number of pairs a MatchResult can have, without having to
-    // allocate a bigger MatchResult.
-    static const size_t MaxPairCount = 14;
-
-    static RegExpObject*
-    create(JSContext* cx, const char16_t* chars, size_t length, RegExpFlag flags,
-           frontend::TokenStream* ts, LifoAlloc& alloc);
-
-    static RegExpObject*
-    create(JSContext* cx, HandleAtom atom, RegExpFlag flags,
-           frontend::TokenStream* ts, LifoAlloc& alloc);
-
-    /*
-     * Compute the initial shape to associate with fresh RegExp objects,
-     * encoding their initial properties. Return the shape after
-     * changing |obj|'s last property to it.
-     */
-    static Shape*
-    assignInitialShape(JSContext* cx, Handle<RegExpObject*> obj);
-
-    /* Accessors. */
-
-    static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
-
-    static bool isInitialShape(RegExpObject* rx) {
-        Shape* shape = rx->lastProperty();
-        if (!shape->hasSlot())
-            return false;
-        if (shape->maybeSlot() != LAST_INDEX_SLOT)
-            return false;
-        return true;
-    }
-
-    const Value& getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
-
-    void setLastIndex(double d) {
-        setSlot(LAST_INDEX_SLOT, NumberValue(d));
-    }
-
-    void zeroLastIndex(JSContext* cx) {
-        MOZ_ASSERT(lookupPure(cx->names().lastIndex)->writable(),
-                   "can't infallibly zero a non-writable lastIndex on a "
-                   "RegExp that's been exposed to script");
-        setSlot(LAST_INDEX_SLOT, Int32Value(0));
-    }
-
-    JSFlatString* toString(JSContext* cx) const;
-
-    JSAtom* getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
-
-    void setSource(JSAtom* source) {
-        setSlot(SOURCE_SLOT, StringValue(source));
-    }
-
-    /* Flags. */
-
-    static unsigned flagsSlot() { return FLAGS_SLOT; }
-
-    RegExpFlag getFlags() const {
-        return RegExpFlag(getFixedSlot(FLAGS_SLOT).toInt32());
-    }
-    void setFlags(RegExpFlag flags) {
-        setSlot(FLAGS_SLOT, Int32Value(flags));
-    }
-
-    bool ignoreCase() const { return getFlags() & IgnoreCaseFlag; }
-    bool global() const     { return getFlags() & GlobalFlag; }
-    bool multiline() const  { return getFlags() & MultilineFlag; }
-    bool sticky() const     { return getFlags() & StickyFlag; }
-    bool unicode() const    { return getFlags() & UnicodeFlag; }
-
-    static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
-
-    static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp,
-                                       MutableHandleRegExpShared shared);
-
-    bool hasShared() {
-        return !!sharedRef();
-    }
-
-    void setShared(RegExpShared& shared) {
-        MOZ_ASSERT(!hasShared());
-        sharedRef() = &shared;
-    }
-
-    static void trace(JSTracer* trc, JSObject* obj);
-    void trace(JSTracer* trc);
-
-    void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags);
-
-    // NOTE: This method is *only* safe to call on RegExps that haven't been
-    //       exposed to script, because it requires that the "lastIndex"
-    //       property be writable.
-    void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, JSContext* cx);
-
-#ifdef DEBUG
-    static MOZ_MUST_USE bool dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
-                                          bool match_only, HandleLinearString input);
-#endif
-
-  private:
-    /*
-     * Precondition: the syntax for |source| has already been validated.
-     * Side effect: sets the private field.
-     */
-    static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp,
-                                          MutableHandleRegExpShared shared);
-
-    ReadBarriered<RegExpShared*>& sharedRef() {
-        auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
-        return reinterpret_cast<ReadBarriered<RegExpShared*>&>(ref);
-    }
-
-    /* Call setShared in preference to setPrivate. */
-    void setPrivate(void* priv) = delete;
-};
-
-/*
- * Parse regexp flags. Report an error and return false if an invalid
- * sequence of flags is encountered (repeat/invalid flag).
- *
- * N.B. flagStr must be rooted.
- */
-bool
-ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
-
-/* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
-inline bool
-RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
-{
-    if (obj->is<RegExpObject>())
-        return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared);
-
-    return Proxy::regexp_toShared(cx, obj, shared);
-}
-
-template<XDRMode mode>
-bool
-XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp);
-
-extern JSObject*
-CloneScriptRegExpObject(JSContext* cx, RegExpObject& re);
-
-/* Escape all slashes and newlines in the given string. */
-extern JSAtom*
-EscapeRegExpPattern(JSContext* cx, HandleAtom src);
-
-template <typename CharT>
-extern bool
-HasRegExpMetaChars(const CharT* chars, size_t length);
-
-extern bool
-StringHasRegExpMetaChars(JSLinearString* str);
-
 } /* namespace js */
 
 namespace JS {
 namespace ubi {
 
 template <>
 class Concrete<js::RegExpShared> : TracerConcrete<js::RegExpShared>
 {
@@ -540,9 +355,9 @@ class Concrete<js::RegExpShared> : Trace
 
     const char16_t* typeName() const override { return concreteTypeName; }
     static const char16_t concreteTypeName[];
 };
 
 } // namespace ubi
 } // namespace JS
 
-#endif /* vm_RegExpObject_h */
+#endif /* vm_RegExpShared_h */
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -2,19 +2,21 @@
  * 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 vm_RegExpStatics_h
 #define vm_RegExpStatics_h
 
+#include "jscntxt.h"
+
 #include "gc/Marking.h"
 #include "vm/MatchPairs.h"
-#include "vm/RegExpObject.h"
+#include "vm/RegExpShared.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
 class GlobalObject;
 class RegExpStaticsObject;
 
 class RegExpStatics
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -25,16 +25,17 @@
 #include "selfhosted.out.h"
 
 #include "builtin/Intl.h"
 #include "builtin/MapObject.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/Promise.h"
 #include "builtin/Reflect.h"
+#include "builtin/RegExp.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -39,16 +39,17 @@
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jswrapper.h"
 
 #include "builtin/DataViewObject.h"
 #include "builtin/MapObject.h"
 #include "js/Date.h"
 #include "js/GCHashTable.h"
+#include "vm/RegExpObject.h"
 #include "vm/SavedFrame.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
--- a/js/src/vm/Unicode.h
+++ b/js/src/vm/Unicode.h
@@ -58,26 +58,28 @@ namespace unicode {
 
 namespace CharFlag {
     const uint8_t SPACE = 1 << 0;
     const uint8_t UNICODE_ID_START = 1 << 1;
     const uint8_t UNICODE_ID_CONTINUE_ONLY = 1 << 2;
     const uint8_t UNICODE_ID_CONTINUE = UNICODE_ID_START + UNICODE_ID_CONTINUE_ONLY;
 }
 
-const char16_t NO_BREAK_SPACE = 0x00A0;
-const char16_t MICRO_SIGN = 0x00B5;
-const char16_t LATIN_SMALL_LETTER_SHARP_S = 0x00DF;
-const char16_t LATIN_SMALL_LETTER_Y_WITH_DIAERESIS = 0x00FF;
-const char16_t LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE = 0x0130;
-const char16_t COMBINING_DOT_ABOVE = 0x0307;
-const char16_t GREEK_CAPITAL_LETTER_SIGMA = 0x03A3;
-const char16_t GREEK_SMALL_LETTER_FINAL_SIGMA = 0x03C2;
-const char16_t GREEK_SMALL_LETTER_SIGMA = 0x03C3;
-const char16_t BYTE_ORDER_MARK2 = 0xFFFE;
+constexpr char16_t NO_BREAK_SPACE = 0x00A0;
+constexpr char16_t MICRO_SIGN = 0x00B5;
+constexpr char16_t LATIN_SMALL_LETTER_SHARP_S = 0x00DF;
+constexpr char16_t LATIN_SMALL_LETTER_Y_WITH_DIAERESIS = 0x00FF;
+constexpr char16_t LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE = 0x0130;
+constexpr char16_t COMBINING_DOT_ABOVE = 0x0307;
+constexpr char16_t GREEK_CAPITAL_LETTER_SIGMA = 0x03A3;
+constexpr char16_t GREEK_SMALL_LETTER_FINAL_SIGMA = 0x03C2;
+constexpr char16_t GREEK_SMALL_LETTER_SIGMA = 0x03C3;
+constexpr char16_t LINE_SEPARATOR = 0x2028;
+constexpr char16_t PARA_SEPARATOR = 0x2029;
+constexpr char16_t BYTE_ORDER_MARK2 = 0xFFFE;
 
 const char16_t LeadSurrogateMin = 0xD800;
 const char16_t LeadSurrogateMax = 0xDBFF;
 const char16_t TrailSurrogateMin = 0xDC00;
 const char16_t TrailSurrogateMax = 0xDFFF;
 
 const uint32_t UTF16Max = 0xFFFF;
 const uint32_t NonBMPMin = 0x10000;
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -8,17 +8,16 @@
 
 #include "jsapi.h"
 
 #include "nsCollationCID.h"
 #include "nsJSUtils.h"
 #include "nsICollation.h"
 #include "nsIObserver.h"
 #include "nsNativeCharsetUtils.h"
-#include "nsUnicharUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/Preferences.h"
 
 #include "xpcpublic.h"
 
@@ -70,18 +69,21 @@ XPCLocaleObserver::Observe(nsISupports* 
  * XPCLocaleCallbacks are limited to the main thread.
  */
 struct XPCLocaleCallbacks : public JSLocaleCallbacks
 {
   XPCLocaleCallbacks()
   {
     MOZ_COUNT_CTOR(XPCLocaleCallbacks);
 
-    localeToUpperCase = LocaleToUpperCase;
-    localeToLowerCase = LocaleToLowerCase;
+    // Disable the toLocaleUpper/Lower case hooks to use the standard,
+    // locale-insensitive definition from String.prototype. (These hooks are
+    // only consulted when EXPOSE_INTL_API is not set.)
+    localeToUpperCase = nullptr;
+    localeToLowerCase = nullptr;
     localeCompare = LocaleCompare;
     localeToUnicode = LocaleToUnicode;
 
     // It's going to be retained by the ObserverService.
     RefPtr<XPCLocaleObserver> locObs = new XPCLocaleObserver();
     locObs->Init();
   }
 
@@ -97,73 +99,39 @@ struct XPCLocaleCallbacks : public JSLoc
    */
   static XPCLocaleCallbacks*
   This(JSContext* cx)
   {
     // Locale information for |cx| was associated using xpc_LocalizeContext;
     // assert and double-check this.
     const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
     MOZ_ASSERT(lc);
-    MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
-    MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
+    MOZ_ASSERT(lc->localeToUpperCase == nullptr);
+    MOZ_ASSERT(lc->localeToLowerCase == nullptr);
     MOZ_ASSERT(lc->localeCompare == LocaleCompare);
     MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
 
     const XPCLocaleCallbacks* ths = static_cast<const XPCLocaleCallbacks*>(lc);
     ths->AssertThreadSafety();
     return const_cast<XPCLocaleCallbacks*>(ths);
   }
 
   static bool
-  LocaleToUpperCase(JSContext* cx, HandleString src, MutableHandleValue rval)
-  {
-    return ChangeCase(cx, src, rval, ToUpperCase);
-  }
-
-  static bool
-  LocaleToLowerCase(JSContext* cx, HandleString src, MutableHandleValue rval)
-  {
-    return ChangeCase(cx, src, rval, ToLowerCase);
-  }
-
-  static bool
   LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
   {
     return This(cx)->ToUnicode(cx, src, rval);
   }
 
   static bool
   LocaleCompare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
   {
     return This(cx)->Compare(cx, src1, src2, rval);
   }
 
 private:
-  static bool
-  ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval,
-             void(*changeCaseFnc)(const nsAString&, nsAString&))
-  {
-    nsAutoJSString autoStr;
-    if (!autoStr.init(cx, src)) {
-      return false;
-    }
-
-    nsAutoString result;
-    changeCaseFnc(autoStr, result);
-
-    JSString* ucstr =
-      JS_NewUCStringCopyN(cx, result.get(), result.Length());
-    if (!ucstr) {
-      return false;
-    }
-
-    rval.setString(ucstr);
-    return true;
-  }
-
   bool
   Compare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
   {
     nsresult rv;
 
     if (!mCollation) {
       nsCOMPtr<nsICollationFactory> colFactory =
         do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
--- a/layout/base/ScrollbarStyles.h
+++ b/layout/base/ScrollbarStyles.h
@@ -47,17 +47,16 @@ struct ScrollbarStyles
     mScrollSnapDestinationX.mHasPercent = false;
     mScrollSnapDestinationY.mPercent = 0;
     mScrollSnapDestinationY.mLength = nscoord(0.0f);
     mScrollSnapDestinationY.mHasPercent = false;
   }
 
   explicit ScrollbarStyles(const nsStyleDisplay* aDisplay);
   ScrollbarStyles(uint8_t aH, uint8_t aV, const nsStyleDisplay* aDisplay);
-  ScrollbarStyles() {}
   bool operator==(const ScrollbarStyles& aStyles) const {
     return aStyles.mHorizontal == mHorizontal && aStyles.mVertical == mVertical &&
            aStyles.mScrollBehavior == mScrollBehavior &&
            aStyles.mScrollSnapTypeX == mScrollSnapTypeX &&
            aStyles.mScrollSnapTypeY == mScrollSnapTypeY &&
            aStyles.mScrollSnapPointsX == mScrollSnapPointsX &&
            aStyles.mScrollSnapPointsY == mScrollSnapPointsY &&
            aStyles.mScrollSnapDestinationX == mScrollSnapDestinationX &&
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1729,23 +1729,20 @@ nsBidiPresUtils::RepositionFrame(nsIFram
     aContainerReverseDir ? lineSize - frameStartOrEnd - icoord
                          : frameStartOrEnd;
   aFrame->SetRect(aContainerWM, rect, aContainerSize);
 
   return icoord + margin.IStartEnd(aContainerWM);
 }
 
 void
-nsBidiPresUtils::InitContinuationStates(nsIFrame*              aFrame,
-                                        nsContinuationStates*  aContinuationStates)
+nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame,
+                                        nsContinuationStates* aContinuationStates)
 {
-  nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
-  state->mFirstVisualFrame = nullptr;
-  state->mFrameCount = 0;
-
+  aContinuationStates->PutEntry(aFrame);
   if (!IsBidiLeaf(aFrame) || RubyUtils::IsRubyBox(aFrame->Type())) {
     // Continue for child frames
     for (nsIFrame* frame : aFrame->PrincipalChildList()) {
       InitContinuationStates(frame,
                              aContinuationStates);
     }
   }
 }
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -35,41 +35,43 @@ namespace mozilla {
 
 /**
  * A structure representing some continuation state for each frame on the line,
  * used to determine the first and the last continuation frame for each
  * continuation chain on the line.
  */
 struct nsFrameContinuationState : public nsVoidPtrHashKey
 {
-  explicit nsFrameContinuationState(const void *aFrame) : nsVoidPtrHashKey(aFrame) {}
+  explicit nsFrameContinuationState(const void *aFrame)
+    : nsVoidPtrHashKey(aFrame)
+  {}
 
   /**
    * The first visual frame in the continuation chain containing this frame, or
    * nullptr if this frame is the first visual frame in the chain.
    */
-  nsIFrame* mFirstVisualFrame;
+  nsIFrame* mFirstVisualFrame { nullptr };
 
   /**
    * The number of frames in the continuation chain containing this frame, if
    * this frame is the first visual frame of the chain, or 0 otherwise.
    */
-  uint32_t mFrameCount;
+  uint32_t mFrameCount { 0 };
 
   /**
    * TRUE if this frame is the first visual frame of its continuation chain on
    * this line and the chain has some frames on the previous lines.
    */
-  bool mHasContOnPrevLines;
+  bool mHasContOnPrevLines { false };
 
   /**
    * TRUE if this frame is the first visual frame of its continuation chain on
    * this line and the chain has some frames left for next lines.
    */
-  bool mHasContOnNextLines;
+  bool mHasContOnNextLines { false };
 };
 
 /*
  * Following type is used to pass needed hashtable to reordering methods
  */
 typedef nsTHashtable<nsFrameContinuationState> nsContinuationStates;
 
 /**
--- a/layout/base/tests/chrome/chrome.ini
+++ b/layout/base/tests/chrome/chrome.ini
@@ -1,17 +1,16 @@
 [DEFAULT]
 skip-if = os == 'android'
 support-files =
   animated.gif
   blue-32x32.png
   bug495648.rdf
   bug551434_childframe.html
   chrome_content_integration_window.xul
-  chrome_over_plugin_window.xul
   default_background_window.xul
   dialog_with_positioning_window.xul
   no_clip_iframe_subdoc.html
   no_clip_iframe_window.xul
   printpreview_bug396024_helper.xul
   printpreview_bug482976_helper.xul
   printpreview_helper.xul
   file_bug1018265.xul
@@ -28,16 +27,19 @@ support-files =
 [test_bug708062.html]
 [test_bug812817.xul]
 [test_bug847890_paintFlashing.html]
 [test_bug1018265.xul]
 [test_bug1041200.xul]
 support-files=bug1041200_window.html
 [test_chrome_content_integration.xul]
 [test_chrome_over_plugin.xul]
+support-files =
+  chrome_over_plugin_window.xul
+  chrome_over_plugin_window_frame.html
 [test_default_background.xul]
 [test_dialog_with_positioning.html]
 tags = openwindow
 [test_fixed_bg_scrolling_repaints.html]
 [test_leaf_layers_partition_browser_window.xul]
 skip-if = (!debug) || (toolkit == "cocoa") || (os == "linux") # Disabled on Mac and Linux because of Bug 992311
 [test_no_clip_iframe.xul]
 [test_prerendered_transforms.html]
--- a/layout/base/tests/chrome/chrome_over_plugin_window.xul
+++ b/layout/base/tests/chrome/chrome_over_plugin_window.xul
@@ -2,21 +2,17 @@
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <window title="Content/chrome integration subwindow"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="runTests()">\
   <!-- We're mainly testing that a) translucent chrome elements cause the plugin to be clipped away and
        b) translucent content elements do NOT cause the plugin to be clipped away -->
   <stack style="height:100px; width:150px;">
     <iframe type="content" style="border:none;" id="f"
-            src="data:text/html,&lt;embed id='e' type='application/x-test' wmode='window'
-                                style='position:absolute;left:0;top:0;width:100px;height:100px'&gt;&lt;/embed&gt;
-                                &lt;div style='position:absolute;left:0;top:80px;width:100px;height:10px;background:rgba(0,0,128,0.5)'&gt;&lt;/div&gt;
-                                &lt;div style='position:absolute;left:0;top:90px;width:100px;height:10px;background:blue'&gt;&lt;/div&gt;
-                                "/>
+            src="http://example.org/chrome/layout/base/tests/chrome/chrome_over_plugin_window_frame.html"/>
     <vbox>
       <vbox style="height:25px; background:yellow;"/> <!-- plugin should be covered here -->
       <vbox style="height:25px; background:rgba(0,128,0,0.5);"/> <!-- plugin should be covered here -->
       <vbox style="height:50px;"/> <!-- plugin should be visible here -->
     </vbox>
   </stack>
 
   <script type="application/javascript">
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/chrome/chrome_over_plugin_window_frame.html
@@ -0,0 +1,4 @@
+<embed id="e" type="application/x-test" wmode="window"
+       style="position:absolute;left:0;top:0;width:100px;height:100px"></embed>
+<div style="position:absolute;left:0;top:80px;width:100px;height:10px;background:rgba(0,0,128,0.5)"></div>
+<div style="position:absolute;left:0;top:90px;width:100px;height:10px;background:blue"></div>
--- a/layout/generic/BRFrame.cpp
+++ b/layout/generic/BRFrame.cpp
@@ -60,16 +60,17 @@ public:
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
 protected:
   explicit BRFrame(nsStyleContext* aContext)
     : nsFrame(aContext, LayoutFrameType::Br)
+    , mAscent(NS_INTRINSIC_WIDTH_UNKNOWN)
   {}
 
   virtual ~BRFrame();
 
   nscoord mAscent;
 };
 
 } // namespace mozilla
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -452,17 +452,16 @@ NS_NewEmptyFrame(nsIPresShell* aPresShel
   return new (aPresShell) nsFrame(aContext, LayoutFrameType::None);
 }
 
 nsFrame::nsFrame(nsStyleContext* aContext, LayoutFrameType aType)
   : nsBox(aType)
 {
   MOZ_COUNT_CTOR(nsFrame);
 
-  mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY;
   mStyleContext = aContext;
   mWritingMode = WritingMode(mStyleContext);
   mStyleContext->AddRef();
 #ifdef DEBUG
   mStyleContext->FrameAddRef();
 #endif
 }
 
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -451,13 +451,13 @@ private:
 
   // Our baselines, one per BaselineSharingGroup per axis.
   nscoord mBaseline[2/*LogicalAxis*/][2/*BaselineSharingGroup*/];
 
 #ifdef DEBUG
   // If true, NS_STATE_GRID_DID_PUSH_ITEMS may be set even though all pushed
   // frames may have been removed.  This is used to suppress an assertion
   // in case RemoveFrame removed all associated child frames.
-  bool mDidPushItemsBitMayLie;
+  bool mDidPushItemsBitMayLie { false };
 #endif
 };
 
 #endif /* nsGridContainerFrame_h___ */
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -604,16 +604,17 @@ public:
 
   explicit nsIFrame(mozilla::LayoutFrameType aType)
     : mRect()
     , mContent(nullptr)
     , mStyleContext(nullptr)
     , mParent(nullptr)
     , mNextSibling(nullptr)
     , mPrevSibling(nullptr)
+    , mState(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY)
     , mType(aType)
   {
     mozilla::PodZero(&mOverflow);
   }
 
   nsPresContext* PresContext() const {
     return StyleContext()->PresContext();
   }
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -138,16 +138,17 @@ protected:
       mLineContainer = nullptr;
       mLineLayout = nullptr;
       mSetParentPointer = false;
     }
   };
 
   nsInlineFrame(nsStyleContext* aContext, mozilla::LayoutFrameType aType)
     : nsContainerFrame(aContext, aType)
+    , mBaseline(NS_INTRINSIC_WIDTH_UNKNOWN)
   {}
 
   explicit nsInlineFrame(nsStyleContext* aContext)
     : nsInlineFrame(aContext, mozilla::LayoutFrameType::Inline)
   {}
 
   virtual LogicalSides GetLogicalSkipSides(const ReflowInput* aReflowInput = nullptr) const override;
 
--- a/layout/generic/nsPlaceholderFrame.h
+++ b/layout/generic/nsPlaceholderFrame.h
@@ -161,12 +161,12 @@ public:
                     "Must have placeholder frame as input");
     nsIFrame* outOfFlow =
       static_cast<nsPlaceholderFrame*>(aFrame)->GetOutOfFlowFrame();
     NS_ASSERTION(outOfFlow, "Null out-of-flow for placeholder?");
     return outOfFlow;
   }
 
 protected:
-  nsIFrame* mOutOfFlowFrame;
+  nsIFrame* mOutOfFlowFrame { nullptr };
 };
 
 #endif /* nsPlaceholderFrame_h___ */
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1202,17 +1202,26 @@ TextContainsLineBreakerWhiteSpace(const 
     for (uint32_t i = 0; i < aLength; ++i) {
       if (IsLineBreakingWhiteSpace(chars[i]))
         return true;
     }
     return false;
   }
 }
 
-struct FrameTextTraversal {
+struct FrameTextTraversal
+{
+  FrameTextTraversal()
+    : mFrameToScan(nullptr)
+    , mOverflowFrameToScan(nullptr)
+    , mScanSiblings(false)
+    , mLineBreakerCanCrossFrameBoundary(false)
+    , mTextRunCanCrossFrameBoundary(false)
+  {}
+
   // These fields identify which frames should be recursively scanned
   // The first normal frame to scan (or null, if no such frame should be scanned)
   nsIFrame*    mFrameToScan;
   // The first overflow frame to scan (or null, if no such frame should be scanned)
   nsIFrame*    mOverflowFrameToScan;
   // Whether to scan the siblings of mFrameToDescendInto/mOverflowFrameToDescendInto
   bool mScanSiblings;
 
@@ -1241,49 +1250,41 @@ CanTextCrossFrameBoundary(nsIFrame* aFra
 {
   FrameTextTraversal result;
 
   bool continuesTextRun = aFrame->CanContinueTextRun();
   if (aFrame->IsPlaceholderFrame()) {
     // placeholders are "invisible", so a text run should be able to span
     // across one. But don't descend into the out-of-flow.
     result.mLineBreakerCanCrossFrameBoundary = true;
-    result.mOverflowFrameToScan = nullptr;
     if (continuesTextRun) {
       // ... Except for first-letter floats, which are really in-flow
       // from the point of view of capitalization etc, so we'd better
       // descend into them. But we actually need to break the textrun for
       // first-letter floats since things look bad if, say, we try to make a
       // ligature across the float boundary.
       result.mFrameToScan =
         (static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
-      result.mScanSiblings = false;
-      result.mTextRunCanCrossFrameBoundary = false;
     } else {
-      result.mFrameToScan = nullptr;
       result.mTextRunCanCrossFrameBoundary = true;
     }
   } else {
     if (continuesTextRun) {
       result.mFrameToScan = aFrame->PrincipalChildList().FirstChild();
       result.mOverflowFrameToScan =
         aFrame->GetChildList(nsIFrame::kOverflowList).FirstChild();
       NS_WARNING_ASSERTION(
         !result.mOverflowFrameToScan,
         "Scanning overflow inline frames is something we should avoid");
       result.mScanSiblings = true;
       result.mTextRunCanCrossFrameBoundary = true;
       result.mLineBreakerCanCrossFrameBoundary = true;
     } else {
       MOZ_ASSERT(!aFrame->IsRubyTextContainerFrame(),
                  "Shouldn't call this method for ruby text container");
-      result.mFrameToScan = nullptr;
-      result.mOverflowFrameToScan = nullptr;
-      result.mTextRunCanCrossFrameBoundary = false;
-      result.mLineBreakerCanCrossFrameBoundary = false;
     }
   }
   return result;
 }
 
 BuildTextRunsScanner::FindBoundaryResult
 BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
 {
--- a/layout/reftests/svg/as-image/context-fill-02.html
+++ b/layout/reftests/svg/as-image/context-fill-02.html
@@ -10,11 +10,11 @@ img {
   -moz-context-properties: fill;
   fill: lime;
   border: 1px solid black;
 }
 
     </style>
   </head>
   <body>
-    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='context-fill'/></svg>">
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='context-fill none'/></svg>">
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-08.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill when only "-moz-context-properties: stroke" is specified</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  -moz-context-properties: stroke;
+  fill: red;
+  stroke: red;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='context-fill blue'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-08.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke when only "-moz-context-properties: fill" is specified</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  -moz-context-properties: fill;
+  fill: red;
+  stroke: red;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect x='5' y='5' width='90' height='90' fill='none' stroke='context-stroke blue' stroke-width='10'/></svg>">
+  </body>
+</html>
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -61,28 +61,30 @@ skip-if(stylo) test-pref(svg.context-pro
 skip-if(stylo) == context-fill-02.html transparent100x100-w-border-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-02.html lime100x100-w-border-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-03.html lime100x100-50pct-ref.html
 # fuzz because on win8 the r & b components are off by one
 skip-if(stylo) fuzzy-if(winWidget,1,10000) test-pref(svg.context-properties.content.enabled,true) == context-fill-04.html lime100x100-50pct-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-05.html context-fill-or-stroke-05-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-06.html lime100x100-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-07.html context-fill-07-ref.html
+skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-08.html blue100x100-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-fill-bg-image-01.html blue100x100-ref.html
 # context-stroke:
 skip-if(stylo) == context-stroke-01.html blue100x100-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-01.html lime100x100-ref.html
 skip-if(stylo) == context-stroke-02.html transparent100x100-w-border-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-02.html lime100x100-w-border-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-03.html lime100x100-50pct-ref.html
 # fuzz because on win8 the r & b components are off by one
 skip-if(stylo) fuzzy-if(winWidget,1,10000) test-pref(svg.context-properties.content.enabled,true) == context-stroke-04.html lime100x100-50pct-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-05.html context-fill-or-stroke-05-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-06.html lime100x100-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-07.html context-stroke-07-ref.html
+skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-08.html blue100x100-border-ref.html
 skip-if(stylo) test-pref(svg.context-properties.content.enabled,true) == context-stroke-bg-image-01.html blue100x100-border-ref.html
 
 # Simple <img> tests
 == img-simple-1.html  lime100x100-ref.html
 == img-simple-2.html  lime100x100-ref.html
 fuzzy-if(skiaContent,255,350) == img-simple-3.html  img-simple-3-ref.html
 == img-simple-4.html  lime100x100-ref.html
 fuzzy-if(skiaContent,255,90) == img-simple-5.html  img-simple-5-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/fallback-color-05-ref.svg
@@ -0,0 +1,12 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Reference for context-fill fallback colour</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=1352258 -->
+
+  <rect width="100%" height="100%" fill="black"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/fallback-color-05.svg
@@ -0,0 +1,12 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Testcase for context-fill fallback colour</title>
+
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=1352258 -->
+
+  <rect width="100%" height="100%" fill="context-fill"/>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -183,16 +183,17 @@ random == dynamic-use-nested-01b.svg dyn
 == dynamic-viewBox-change-03.svg pass.svg
 
 == fallback-color-01a.svg pass.svg
 == fallback-color-01b.svg pass.svg
 == fallback-color-02a.svg fallback-color-02-ref.svg
 == fallback-color-02b.svg fallback-color-02-ref.svg
 == fallback-color-03.svg pass.svg
 fuzzy-if(skiaContent,1,2) == fallback-color-04.svg pass.svg
+== fallback-color-05.svg fallback-color-05-ref.svg
 
 == filter-basic-01.svg pass.svg
 == filter-basic-02.svg pass.svg
 == filter-basic-03.svg pass.svg
 == filter-bounds-01.svg pass.svg
 == filter-bounds-02.svg pass.svg
 # This pref is normally on by default, but we turn it off in reftest runs to
 # disable an unnecessary security-check. This reftest is actually testing that
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -484,21 +484,20 @@ nsHTMLStyleSheet::Reset()
 
 nsresult
 nsHTMLStyleSheet::ImplLinkColorSetter(RefPtr<HTMLColorRule>& aRule, nscolor aColor)
 {
   if (aRule && aRule->mColor == aColor) {
     return NS_OK;
   }
 
-  aRule = new HTMLColorRule();
+  aRule = new HTMLColorRule(aColor);
   if (!aRule)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  aRule->mColor = aColor;
   // Now make sure we restyle any links that might need it.  This
   // shouldn't happen often, so just rebuilding everything is ok.
   if (mDocument && mDocument->GetShell()) {
     Element* root = mDocument->GetRootElement();
     if (root) {
       mDocument->GetShell()->GetPresContext()->RestyleManager()->
         PostRestyleEvent(root, eRestyle_Subtree, nsChangeHint(0));
     }
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -76,30 +76,32 @@ private:
   ~nsHTMLStyleSheet() {}
 
   class HTMLColorRule;
   friend class HTMLColorRule;
   class HTMLColorRule final : public nsIStyleRule {
   private:
     ~HTMLColorRule() {}
   public:
-    HTMLColorRule() {}
+    explicit HTMLColorRule(nscolor aColor)
+      : mColor(aColor)
+    {}
 
     NS_DECL_ISUPPORTS
 
     // nsIStyleRule interface
     virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
     virtual bool MightMapInheritedStyleData() override;
     virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
                                                nsCSSValue* aValue) override;
   #ifdef DEBUG
     virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
   #endif
 
-    nscolor             mColor;
+    nscolor mColor;
   };
 
   // Implementation of SetLink/VisitedLink/ActiveLinkColor
   nsresult ImplLinkColorSetter(RefPtr<HTMLColorRule>& aRule, nscolor aColor);
 
   class GenericTableRule;
   friend class GenericTableRule;
   class GenericTableRule : public nsIStyleRule {
--- a/layout/svg/SVGImageContext.cpp
+++ b/layout/svg/SVGImageContext.cpp
@@ -26,17 +26,19 @@ SVGImageContext::MaybeStoreContextPaint(
   static bool sEnabledForContentCached = false;
 
   if (!sEnabledForContentCached) {
     Preferences::AddBoolVarCache(&sEnabledForContent,
                                  "svg.context-properties.content.enabled", false);
     sEnabledForContentCached = true;
   }
 
-  if (!aFromFrame->StyleSVG()->ExposesContextProperties()) {
+  const nsStyleSVG* style = aFromFrame->StyleSVG();
+
+  if (!style->ExposesContextProperties()) {
     // Content must have '-moz-context-properties' set to the names of the
     // properties it wants to expose to images it links to.
     return;
   }
 
   if (!sEnabledForContent &&
       !nsContentUtils::IsChromeDoc(aFromFrame->PresContext()->Document())) {
     // Context paint is pref'ed off for content and this is a content doc.
@@ -47,25 +49,23 @@ SVGImageContext::MaybeStoreContextPaint(
     // Avoid this overhead for raster images.
     return;
   }
 
   bool haveContextPaint = false;
 
   RefPtr<SVGEmbeddingContextPaint> contextPaint = new SVGEmbeddingContextPaint();
 
-  const nsStyleSVG* style = aFromFrame->StyleSVG();
-
-  // XXX don't set values for properties not listed in 'context-properties'.
-
-  if (style->mFill.Type() == eStyleSVGPaintType_Color) {
+  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL) &&
+      style->mFill.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetFill(style->mFill.GetColor());
   }
-  if (style->mStroke.Type() == eStyleSVGPaintType_Color) {
+  if ((style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE) &&
+      style->mStroke.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetStroke(style->mStroke.GetColor());
   }
 
   if (haveContextPaint) {
     if (!aContext) {
       aContext.emplace();
     }
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -30,16 +30,17 @@ using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext)
   : nsSVGPaintServerFrame(aContext, LayoutFrameType::SVGPattern)
+  , mSource(nullptr)
   , mLoopFlag(false)
   , mNoHRefURI(false)
 {
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
 
 //----------------------------------------------------------------------
--- a/layout/svg/nsSVGPatternFrame.h
+++ b/layout/svg/nsSVGPatternFrame.h
@@ -130,18 +130,18 @@ protected:
                           const gfxRect &callerBBox,
                           const Matrix &callerCTM,
                           nsIFrame *aTarget);
 
 private:
   // this is a *temporary* reference to the frame of the element currently
   // referencing our pattern.  This must be temporary because different
   // referencing frames will all reference this one frame
-  mozilla::SVGGeometryFrame        *mSource;
-  nsAutoPtr<gfxMatrix>              mCTM;
+  mozilla::SVGGeometryFrame* mSource;
+  nsAutoPtr<gfxMatrix> mCTM;
 
 protected:
   // This flag is used to detect loops in xlink:href processing
   bool                              mLoopFlag;
   bool                              mNoHRefURI;
 };
 
 #endif
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1452,21 +1452,24 @@ nsSVGUtils::PathExtentsToMaxStrokeExtent
 nsSVGUtils::GetFallbackOrPaintColor(nsStyleContext *aStyleContext,
                                     nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
 {
   const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
   nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
   nscolor color;
   switch (paint.Type()) {
     case eStyleSVGPaintType_Server:
-    case eStyleSVGPaintType_ContextFill:
     case eStyleSVGPaintType_ContextStroke:
       color = paint.GetFallbackType() == eStyleSVGFallbackType_Color ?
                 paint.GetFallbackColor() : NS_RGBA(0, 0, 0, 0);
       break;
+    case eStyleSVGPaintType_ContextFill:
+      color = paint.GetFallbackType() == eStyleSVGFallbackType_Color ?
+                paint.GetFallbackColor() : NS_RGB(0, 0, 0);
+      break;
     default:
       color = paint.GetColor();
       break;
   }
   if (styleIfVisited) {
     const nsStyleSVGPaint &paintIfVisited =
       styleIfVisited->StyleSVG()->*aFillOrStroke;
     // To prevent Web content from detecting if a user has visited a URL
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -736,19 +736,17 @@ function BuildConditionSandbox(aURL) {
     // for other platforms.  The integer is formed by 100 times the
     // major version plus the minor version, so 1006 for 10.6, 1010 for
     // 10.10, etc.
     var osxmatch = /Mac OS X (\d+).(\d+)$/.exec(hh.oscpu);
     sandbox.OSX = osxmatch ? parseInt(osxmatch[1]) * 100 + parseInt(osxmatch[2]) : undefined;
 
     // see if we have the test plugin available,
     // and set a sandox prop accordingly
-    var navigator = gContainingWindow.navigator;
-    var testPlugin = navigator.plugins["Test Plug-in"];
-    sandbox.haveTestPlugin = !!testPlugin;
+    sandbox.haveTestPlugin = !!getTestPlugin("Test Plug-in");
 
     // Set a flag on sandbox if the windows default theme is active
     sandbox.windowsDefaultTheme = gContainingWindow.matchMedia("(-moz-windows-default-theme)").matches;
 
     var prefs = CC["@mozilla.org/preferences-service;1"].
                 getService(CI.nsIPrefBranch);
     try {
         sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme");
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -115,26 +115,28 @@ NS_QUERYFRAME_HEAD(nsBoxFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 #endif
 
 nsBoxFrame::nsBoxFrame(nsStyleContext* aContext,
                        LayoutFrameType aType,
                        bool aIsRoot,
                        nsBoxLayout* aLayoutManager)
   : nsContainerFrame(aContext, aType)
+  , mFlex(0)
+  , mAscent(0)
 {
   mState |= NS_STATE_IS_HORIZONTAL;
   mState |= NS_STATE_AUTO_STRETCH;
 
-  if (aIsRoot) 
+  if (aIsRoot)
      mState |= NS_STATE_IS_ROOT;
 
   mValign = vAlign_Top;
   mHalign = hAlign_Left;
-  
+
   // if no layout manager specified us the static sprocket layout
   nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
 
   if (layout == nullptr) {
     NS_NewSprocketLayout(layout);
   }
 
   SetXULLayoutManager(layout);
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -94,16 +94,18 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
 //
 // nsMenuPopupFrame ctor
 //
 nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext)
   : nsBoxFrame(aContext, LayoutFrameType::MenuPopup)
   , mCurrentMenu(nullptr)
   , mView(nullptr)
   , mPrefSize(-1, -1)
+  , mXPos(0)
+  , mYPos(0)
   , mLastClientOffset(0, 0)
   , mPopupType(ePopupTypePanel)
   , mPopupState(ePopupClosed)
   , mPopupAlignment(POPUPALIGNMENT_NONE)
   , mPopupAnchor(POPUPALIGNMENT_NONE)
   , mPosition(POPUPPOSITION_UNKNOWN)
   , mConsumeRollupEvent(PopupBoxObject::ROLLUP_DEFAULT)
   , mFlip(FlipType_Default)
--- a/layout/xul/nsScrollbarFrame.h
+++ b/layout/xul/nsScrollbarFrame.h
@@ -17,16 +17,18 @@ class nsIScrollbarMediator;
 
 nsIFrame* NS_NewScrollbarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 class nsScrollbarFrame final : public nsBoxFrame
 {
 public:
   explicit nsScrollbarFrame(nsStyleContext* aContext)
     : nsBoxFrame(aContext, mozilla::LayoutFrameType::Scrollbar)
+    , mIncrement(0)
+    , mSmoothScroll(false)
     , mScrollbarMediator(nullptr)
   {}
 
   NS_DECL_QUERYFRAME_TARGET(nsScrollbarFrame)
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override {
     return MakeFrameName(NS_LITERAL_STRING("ScrollbarFrame"), aResult);
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -79,16 +79,18 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame
 
 NS_QUERYFRAME_HEAD(nsSliderFrame)
   NS_QUERYFRAME_ENTRY(nsSliderFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
 
 nsSliderFrame::nsSliderFrame(nsStyleContext* aContext)
   : nsBoxFrame(aContext, LayoutFrameType::Slider)
   , mRatio(0.0f)
+  , mDragStart(0)
+  , mThumbStart(0)
   , mCurPos(0)
   , mChange(0)
   , mDragFinished(true)
   , mUserChanged(false)
   , mScrollingWithAPZ(false)
   , mSuppressionActive(false)
 {
 }
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -94,21 +94,24 @@ nsTextBoxFrame::AttributeChanged(int32_t
     // If the accesskey changed, register for the new value
     // The old value has been unregistered in nsXULElement::SetAttr
     if (aAttribute == nsGkAtoms::accesskey || aAttribute == nsGkAtoms::control)
         RegUnregAccessKey(true);
 
     return NS_OK;
 }
 
-nsTextBoxFrame::nsTextBoxFrame(nsStyleContext* aContext):
-  nsLeafBoxFrame(aContext), mAccessKeyInfo(nullptr), mCropType(CropRight),
-  mNeedsReflowCallback(false)
+nsTextBoxFrame::nsTextBoxFrame(nsStyleContext* aContext)
+  : nsLeafBoxFrame(aContext)
+  , mAccessKeyInfo(nullptr)
+  , mCropType(CropRight)
+  , mAscent(0)
+  , mNeedsReflowCallback(false)
 {
-    MarkIntrinsicISizesDirty();
+  MarkIntrinsicISizesDirty();
 }
 
 nsTextBoxFrame::~nsTextBoxFrame()
 {
     delete mAccessKeyInfo;
 }
 
 
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -47,16 +47,17 @@
 
 #include "mozilla/Omnijar.h"
 #include "nsZipArchive.h"
 
 #include "nsTArray.h"
 #include "nsRefPtrHashtable.h"
 #include "nsIMemoryReporter.h"
 #include "nsThreadUtils.h"
+#include "GeckoProfiler.h"
 
 #ifdef DEBUG
 #define ENSURE_MAIN_PROCESS(message, pref) do {                                \
   if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {        \
     nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref);   \
     NS_WARNING(msg.get());                                                     \
     return NS_ERROR_NOT_AVAILABLE;                                             \
   }                                                                            \
@@ -994,16 +995,19 @@ Preferences::WritePrefFile(nsIFile* aFil
   nsCOMPtr<nsIOutputStream> outStreamSink;
   nsCOMPtr<nsIOutputStream> outStream;
   uint32_t                  writeAmount;
   nsresult                  rv;
 
   if (!gHashTable)
     return NS_ERROR_NOT_INITIALIZED;
 
+  PROFILER_LABEL("Preferences", "WritePrefFile",
+                 js::ProfileEntry::Category::OTHER);
+
   // execute a "safe" save by saving through a tempfile
   rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink),
                                        aFile,
                                        -1,
                                        0600);
   if (NS_FAILED(rv)) 
       return rv;
   rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink, 4096);
--- a/mozglue/misc/StackWalk.cpp
+++ b/mozglue/misc/StackWalk.cpp
@@ -186,16 +186,21 @@ StackWalkInitCriticalAddress()
 #include <windows.h>
 #include <process.h>
 #include <stdio.h>
 #include <malloc.h>
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/StackWalk_windows.h"
 
+#ifdef MOZ_STATIC_JS // The standalone SM build lacks the interceptor headers.
+#include "nsWindowsDllInterceptor.h"
+#define STACKWALK_HAS_DLL_INTERCEPTOR
+#endif
+
 #include <imagehlp.h>
 // We need a way to know if we are building for WXP (or later), as if we are, we
 // need to use the newer 64-bit APIs. API_VERSION_NUMBER seems to fit the bill.
 // A value of 9 indicates we want to use the new APIs.
 #if API_VERSION_NUMBER < 9
 #error Too old imagehlp.h
 #endif
 
@@ -271,16 +276,32 @@ UnregisterJitCodeRegion(uint8_t* aStart,
   // Currently we can only handle one JIT code region at a time
   MOZ_RELEASE_ASSERT(sJitCodeRegionStart &&
                      sJitCodeRegionStart == aStart &&
                      sJitCodeRegionSize == aSize);
 
   sJitCodeRegionStart = nullptr;
   sJitCodeRegionSize = 0;
 }
+
+#ifdef STACKWALK_HAS_DLL_INTERCEPTOR
+static WindowsDllInterceptor NtDllInterceptor;
+
+typedef NTSTATUS (NTAPI *LdrUnloadDll_func)(HMODULE module);
+static LdrUnloadDll_func stub_LdrUnloadDll;
+
+static NTSTATUS NTAPI
+patched_LdrUnloadDll(HMODULE module)
+{
+  // Prevent the stack walker from suspending this thread when LdrUnloadDll
+  // holds the RtlLookupFunctionEntry lock.
+  AutoSuppressStackWalking suppress;
+  return stub_LdrUnloadDll(module);
+}
+#endif // STACKWALK_HAS_DLL_INTERCEPTOR
 #endif // _M_AMD64
 
 // Routine to print an error message to standard error.
 static void
 PrintError(const char* aPrefix)
 {
   LPSTR lpMsgBuf;
   DWORD lastErr = GetLastError();
@@ -357,16 +378,23 @@ EnsureWalkThreadReady()
     // have signalled the event. If that is the case, give up for now and
     // try again next time we're called.
     return false;
   }
   ::CloseHandle(readyEvent);
   stackWalkThread = nullptr;
   readyEvent = nullptr;
 
+#if defined(_M_AMD64) && defined(STACKWALK_HAS_DLL_INTERCEPTOR)
+  NtDllInterceptor.Init("ntdll.dll");
+  NtDllInterceptor.AddHook("LdrUnloadDll",
+                           reinterpret_cast<intptr_t>(patched_LdrUnloadDll),
+                           (void**)&stub_LdrUnloadDll);
+#endif
+
   InitializeDbgHelpCriticalSection();
 
   return walkThreadReady = true;
 }
 
 static void
 WalkStackMain64(struct WalkStackData* aData)
 {
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -25,16 +25,17 @@ linux32/opt:
         -  linux32-tests
         -  linux32-opt-tests
         -  awsy
 linux32-nightly/opt:
     build-platform: linux-nightly/opt
     test-sets:
         -  linux32-tests
         -  linux32-opt-tests
+        -  awsy
 
 linux64/debug:
     build-platform: linux64/debug
     test-sets:
         - common-tests
         - web-platform-tests
 linux64/opt:
     build-platform: linux64/opt
@@ -48,16 +49,17 @@ linux64/opt:
 linux64-nightly/opt:
     build-platform: linux64-nightly/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
         - desktop-screenshot-capture
         - talos
+        - awsy
 
 # TODO: use 'pgo' and 'asan' labels here, instead of -pgo/opt
 linux64-pgo/opt:
     build-platform: linux64-pgo/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - talos
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -231,19 +231,18 @@ def target_tasks_mozilla_beta(full_task_
     """Select the set of tasks required for a promotable beta or release build
     of linux, plus android CI. The candidates build process involves a pipeline
     of builds and signing, but does not include beetmover or balrog jobs."""
 
     def filter(task):
         if not standard_filter(task, parameters):
             return False
         platform = task.attributes.get('build_platform')
-        if platform in ('linux64-pgo', 'linux-pgo', 'win32-pgo', 'win64-pgo',
-                        'android-api-15-nightly', 'android-x86-nightly',
-                        'win32', 'win64'):
+        if platform in ('linux64-pgo', 'linux-pgo', 'android-api-15-nightly',
+                        'android-x86-nightly'):
             return False
         if platform in ('linux64', 'linux', 'macosx64'):
             if task.attributes['build_type'] == 'opt':
                 return False
         # skip l10n, beetmover, balrog
         if task.kind in [
             'balrog', 'beetmover', 'beetmover-checksums', 'beetmover-l10n',
             'checksums-signing', 'nightly-l10n', 'nightly-l10n-signing',
--- a/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/009.html.ini
+++ b/testing/web-platform/meta/html/dom/dynamic-markup-insertion/opening-the-input-stream/009.html.ini
@@ -1,11 +1,8 @@
 [009.html]
   type: testharness
-  [document.open replacing singleton navigator]
-    expected: FAIL
-
   [document.open replacing singleton sessionStorage]
     expected: FAIL
 
   [document.open replacing singleton localStorage]
     expected: FAIL
 
--- a/toolkit/components/tooltiptext/TooltipTextProvider.js
+++ b/toolkit/components/tooltiptext/TooltipTextProvider.js
@@ -86,16 +86,17 @@ TooltipTextProvider.prototype = {
             titleText += "\n" + andXMoreStr;
           }
         }
       } catch (e) {}
     }
 
     // Check texts against null so that title="" can be used to undefine a
     // title on a child element.
+    let usedTipElement = null;
     while (tipElement &&
            (titleText == null) && (XLinkTitleText == null) &&
            (SVGTitleText == null) && (XULtooltiptextText == null)) {
 
       if (tipElement.nodeType == defView.Node.ELEMENT_NODE) {
         if (tipElement.namespaceURI == XULNS)
           XULtooltiptextText = tipElement.getAttribute("tooltiptext");
         else if (!(tipElement instanceof defView.SVGElement))
@@ -116,27 +117,32 @@ TooltipTextProvider.prototype = {
           for (let childNode of tipElement.childNodes) {
             if (childNode instanceof defView.SVGTitleElement) {
               SVGTitleText = childNode.textContent;
               break;
             }
           }
         }
 
-        direction = defView.getComputedStyle(tipElement)
-                           .getPropertyValue("direction");
+        usedTipElement = tipElement;
       }
 
       tipElement = tipElement.parentNode;
     }
 
     return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some(function(t) {
       if (t && /\S/.test(t)) {
         // Make CRLF and CR render one line break each.
         textOut.value = t.replace(/\r\n?/g, "\n");
+
+        if (usedTipElement) {
+          direction = defView.getComputedStyle(usedTipElement)
+                             .getPropertyValue("direction");
+        }
+
         directionOut.value = direction;
         return true;
       }
 
       return false;
     });
   },
 
--- a/toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp
@@ -118,17 +118,17 @@ void PopulateDuration(Duration& aDest, c
 {
   aDest.set_seconds(aSrc.mSecs);
   aDest.set_nanos(aSrc.mNanos);
 }
 
 // The expected match data.
 static MyDuration EXPECTED_MIN_WAIT_DURATION = { 12, 10 };
 static MyDuration EXPECTED_NEG_CACHE_DURATION = { 120, 9 };
-static const struct {
+static const struct ExpectedMatch {
   nsCString mCompleteHash;
   ThreatType mThreatType;
   MyDuration mPerHashCacheDuration;
 } EXPECTED_MATCH[] = {
   { nsCString("01234567890123456789012345678901"), SOCIAL_ENGINEERING_PUBLIC, { 8, 500 } },
   { nsCString("12345678901234567890123456789012"), SOCIAL_ENGINEERING_PUBLIC, { 7, 100} },
   { nsCString("23456789012345678901234567890123"), SOCIAL_ENGINEERING_PUBLIC, { 1, 20 } },
 };
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3131,17 +3131,22 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     ChaosMode::SetChaosFeature(feature);
   }
 
   if (ChaosMode::isActive(ChaosFeature::Any)) {
     printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
   }
 
   if (gfxPlatform::IsHeadless()) {
+#ifdef XP_LINUX
     Output(false, "*** You are running in headless mode.\n");
+#else
+    Output(true, "Error: headless mode is not currently supported on this platform.\n");
+    return 1;
+#endif
   }
 
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
     NS_BREAK();
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -585,17 +585,16 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
     }
 #if defined(XP_WIN)
     nsAutoString applyToDirPathW;
     rv = updatedDir->GetPath(applyToDirPathW);
     if (NS_FAILED(rv)) {
       return;
     }
     applyToDirPath = NS_ConvertUTF16toUTF8(applyToDirPathW);
-    rv = updatedDir->GetNativePath(applyToDirPath);
 #else
     rv = updatedDir->GetNativePath(applyToDirPath);
 #endif
   }
   if (NS_FAILED(rv)) {
      return;
   }
 
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -231,16 +231,23 @@ bool TestNtQueryFullAttributesFile(void*
 {
   typedef NTSTATUS(WINAPI *NtQueryFullAttributesFileType)(POBJECT_ATTRIBUTES,
                                                           PVOID);
   auto patchedNtQueryFullAttributesFile =
     reinterpret_cast<NtQueryFullAttributesFileType>(aFunc);
   return patchedNtQueryFullAttributesFile(0, 0) != 0;
 }
 
+bool TestLdrUnloadDll(void* aFunc)
+{
+  typedef NTSTATUS (NTAPI *LdrUnloadDllType)(HMODULE);
+  auto patchedLdrUnloadDll = reinterpret_cast<LdrUnloadDllType>(aFunc);
+  return patchedLdrUnloadDll(0) != 0;
+}
+
 bool TestSetUnhandledExceptionFilter(void* aFunc)
 {
   auto patchedSetUnhandledExceptionFilter =
     reinterpret_cast<decltype(&SetUnhandledExceptionFilter)>(aFunc);
   // Retrieve the current filter as we set the new filter to null, then restore the current filter.
   LPTOP_LEVEL_EXCEPTION_FILTER current = patchedSetUnhandledExceptionFilter(0);
   patchedSetUnhandledExceptionFilter(current);
   return true;
@@ -458,16 +465,17 @@ int main()
       // TestHook("imm32.dll", "ImmReleaseContext") &&    // see Bug 1316415
       TestHook(TestImmGetCompositionStringW, "imm32.dll", "ImmGetCompositionStringW") &&
       TestHook(TestImmSetCandidateWindow, "imm32.dll", "ImmSetCandidateWindow") &&
       TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") &&
       TestHook(TestGetSaveFileNameW, "comdlg32.dll", "GetSaveFileNameW") &&
       TestHook(TestGetOpenFileNameW, "comdlg32.dll", "GetOpenFileNameW") &&
 #ifdef _M_X64
       TestHook(TestGetKeyState, "user32.dll", "GetKeyState") &&    // see Bug 1316415
+      TestHook(TestLdrUnloadDll, "ntdll.dll", "LdrUnloadDll") &&
 #endif
       MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") &&
 #ifdef _M_IX86
       TestHook(TestSendMessageTimeoutW, "user32.dll", "SendMessageTimeoutW") &&
 #endif
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -1037,16 +1037,57 @@ protected:
             MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
             return;
           }
           COPY_CODES(len);
         } else {
           MOZ_ASSERT_UNREACHABLE("Unrecognized opcode sequence");
           return;
         }
+      } else if (origBytes[nOrigBytes] == 0x80 &&
+                 origBytes[nOrigBytes + 1] == 0x3d) {
+        // cmp byte ptr [rip-relative address], imm8
+        // We'll compute the absolute address and do the cmp in r11
+
+        // push r11 (to save the old value)
+        tramp[nTrampBytes] = 0x49;
+        ++nTrampBytes;
+        tramp[nTrampBytes] = 0x53;
+        ++nTrampBytes;
+
+        byteptr_t absAddr =
+          reinterpret_cast<byteptr_t>(origBytes + nOrigBytes + 7 +
+                                      *reinterpret_cast<int32_t*>(origBytes + nOrigBytes + 2));
+        nOrigBytes += 6;
+
+        // mov r11, absolute address
+        tramp[nTrampBytes] = 0x49;
+        ++nTrampBytes;
+        tramp[nTrampBytes] = 0xbb;
+        ++nTrampBytes;
+
+        *reinterpret_cast<byteptr_t*>(tramp + nTrampBytes) = absAddr;
+        nTrampBytes += 8;
+
+        // cmp byte ptr [r11],...
+        tramp[nTrampBytes] = 0x41;
+        ++nTrampBytes;
+        tramp[nTrampBytes] = 0x80;
+        ++nTrampBytes;
+        tramp[nTrampBytes] = 0x3b;
+        ++nTrampBytes;
+
+        // ...imm8
+        COPY_CODES(1);
+
+        // pop r11 (doesn't affect the flags from the cmp)
+        tramp[nTrampBytes] = 0x49;
+        ++nTrampBytes;
+        tramp[nTrampBytes] = 0x5b;
+        ++nTrampBytes;
       } else if (origBytes[nOrigBytes] == 0x90) {
         // nop
         COPY_CODES(1);
       } else if (origBytes[nOrigBytes] == 0xb8) {
         // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
         COPY_CODES(5);
       } else if (origBytes[nOrigBytes] == 0x33) {
         // xor r32, r/m32