Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 28 Mar 2017 13:30:56 -0700
changeset 398263 272ce6c2572164f5f6a9fba2a980ba9ccf50770c
parent 398200 e23cf1b38ad4b55416318d205864195d3666b4f3 (current diff)
parent 398262 47dfce832d75edb30fa3dac79a62828dba22e70b (diff)
child 398264 9ef667136bbe97e7e8e8606df7103f333b006ac2
child 398381 07bba24c4f671b46b351f6ae0c1bdc60fd419c8f
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
272ce6c25721 / 55.0a1 / 20170329100319 / files
nightly linux64
272ce6c25721 / 55.0a1 / 20170329100319 / files
nightly mac
272ce6c25721 / 55.0a1 / 20170329030240 / files
nightly win32
272ce6c25721 / 55.0a1 / 20170329030240 / files
nightly win64
272ce6c25721 / 55.0a1 / 20170329030240 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
layout/svg/SVGContextPaint.h
layout/xul/nsImageBoxFrame.cpp
toolkit/themes/osx/global/scale.css
toolkit/themes/osx/global/scale/scale-tray-horiz.gif
toolkit/themes/osx/global/scale/scale-tray-vert.gif
toolkit/themes/windows/global/scale.css
toolkit/themes/windows/global/scale/scale-tray-horiz.gif
toolkit/themes/windows/global/scale/scale-tray-vert.gif
widget/windows/nsWindow.cpp
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -204,16 +204,17 @@ skip-if = true # Needs to be rewritten a
 [browser_687710_2.js]
 [browser_694378.js]
 [browser_701377.js]
 [browser_705597.js]
 [browser_707862.js]
 [browser_739531.js]
 [browser_739805.js]
 [browser_819510_perwindowpb.js]
+skip-if = (os == 'win' && bits == 64) # Bug 1284312
 
 # Disabled for frequent intermittent failures
 [browser_464620_a.js]
 skip-if = true
 [browser_464620_b.js]
 skip-if = true
 
 # Disabled on OS X:
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -572,19 +572,26 @@ var gPermissionObject = {
     }
   },
 
   "desktop-notification": {
     exactHostMatch: true,
     labelID: "desktop-notification2",
   },
 
-  "camera": {},
-  "microphone": {},
+  "camera": {
+    exactHostMatch: true,
+  },
+
+  "microphone": {
+    exactHostMatch: true,
+  },
+
   "screen": {
+    exactHostMatch: true,
     states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
   },
 
   "popup": {
     getDefault() {
       return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
                SitePermissions.BLOCK : SitePermissions.ALLOW;
     }
--- a/browser/modules/test/unit/test_SitePermissions.js
+++ b/browser/modules/test/unit/test_SitePermissions.js
@@ -62,8 +62,40 @@ add_task(function* testGetAvailableState
                    [ SitePermissions.ALLOW,
                      SitePermissions.ALLOW_COOKIES_FOR_SESSION,
                      SitePermissions.BLOCK ]);
 
   Assert.deepEqual(SitePermissions.getAvailableStates("popup"),
                    [ SitePermissions.ALLOW,
                      SitePermissions.BLOCK ]);
 });
+
+add_task(function* testExactHostMatch() {
+  let uri = Services.io.newURI("https://example.com");
+  let subUri = Services.io.newURI("https://test1.example.com");
+
+  let exactHostMatched = ["desktop-notification", "camera", "microphone", "screen", "geo"];
+  let nonExactHostMatched = ["image", "cookie", "popup", "install", "indexedDB"];
+
+  let permissions = SitePermissions.listPermissions();
+  for (let permission of permissions) {
+    SitePermissions.set(uri, permission, SitePermissions.ALLOW);
+
+    if (exactHostMatched.includes(permission)) {
+      // Check that the sub-origin does not inherit the permission from its parent.
+      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.UNKNOWN);
+    } else if (nonExactHostMatched.includes(permission)) {
+      // Check that the sub-origin does inherit the permission from its parent.
+      Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.ALLOW);
+    } else {
+      Assert.ok(false, `Found an unknown permission ${permission} in exact host match test.` +
+                       "Please add new permissions from SitePermissions.jsm to this test.");
+    }
+
+    // Check that the permission can be made specific to the sub-origin.
+    SitePermissions.set(subUri, permission, SitePermissions.BLOCK);
+    Assert.equal(SitePermissions.get(subUri, permission).state, SitePermissions.BLOCK);
+    Assert.equal(SitePermissions.get(uri, permission).state, SitePermissions.ALLOW);
+
+    SitePermissions.remove(subUri, permission);
+    SitePermissions.remove(uri, permission);
+  }
+});
--- a/devtools/client/.eslintrc.js
+++ b/devtools/client/.eslintrc.js
@@ -1,9 +1,12 @@
 "use strict";
 
 module.exports = {
+  "globals": {
+    "define": true,
+  },
   "rules": {
     // See bug 1288147, the devtools front-end wants to be able to run in
     // content privileged windows, where ownerGlobal doesn't exist.
     "mozilla/use-ownerGlobal": "off",
   }
 };
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -75,31 +75,51 @@ module.exports = createClass({
   },
 
   getMarginValue(property, direction) {
     let { layout } = this.props.boxModel;
     let autoMargins = layout.autoMargins || {};
     let value = "-";
 
     if (direction in autoMargins) {
-      value = "auto";
+      value = autoMargins[direction];
     } else if (layout[property]) {
-      value = parseFloat(layout[property]);
+      let parsedValue = parseFloat(layout[property]);
+
+      if (Number.isNaN(parsedValue)) {
+        // Not a number. We use the raw string.
+        // Useful for pseudo-elements with auto margins since they
+        // don't appear in autoMargins.
+        value = layout[property];
+      } else {
+        value = parsedValue;
+      }
     }
 
     return value;
   },
 
   getPositionValue(property) {
     let { layout } = this.props.boxModel;
+    let value = "-";
 
-    if (layout.position === "static") {
-      return "-";
+    if (!layout[property]) {
+      return value;
     }
-    return layout[property] ? parseFloat(layout[property]) : "-";
+
+    let parsedValue = parseFloat(layout[property]);
+
+    if (Number.isNaN(parsedValue)) {
+      // Not a number. We use the raw string.
+      value = layout[property];
+    } else {
+      value = parsedValue;
+    }
+
+    return value;
   },
 
   /**
    * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
    * translate nodeFront to a grip-like object that can be used with an ElementNode rep.
    *
    * @params  {NodeFront} nodeFront
    *          The NodeFront for which we want to create a grip-like object.
--- a/devtools/client/inspector/boxmodel/test/browser.ini
+++ b/devtools/client/inspector/boxmodel/test/browser.ini
@@ -18,17 +18,19 @@ support-files =
 # Disabled for too many intermittent failures (bug 1009322)
 [browser_boxmodel_editablemodel_bluronclick.js]
 [browser_boxmodel_editablemodel_border.js]
 [browser_boxmodel_editablemodel_stylerules.js]
 [browser_boxmodel_guides.js]
 [browser_boxmodel_navigation.js]
 skip-if = true # Bug 1336198
 [browser_boxmodel_offsetparent.js]
+[browser_boxmodel_positions.js]
 [browser_boxmodel_properties.js]
+[browser_boxmodel_pseudo-element.js]
 [browser_boxmodel_rotate-labels-on-sides.js]
 [browser_boxmodel_sync.js]
 [browser_boxmodel_tooltips.js]
 skip-if = true # Bug 1336198
 [browser_boxmodel_update-after-navigation.js]
 [browser_boxmodel_update-after-reload.js]
 # [browser_boxmodel_update-in-iframes.js]
 # Bug 1020038 boxmodel-view updates for iframe elements changes
--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
@@ -17,16 +17,24 @@ var res1 = [
     selector: ".boxmodel-size > .boxmodel-width",
     value: "100"
   },
   {
     selector: ".boxmodel-size > .boxmodel-height",
     value: "100.117"
   },
   {
+    selector: ".boxmodel-position.boxmodel-top > span",
+    value: 42
+  },
+  {
+    selector: ".boxmodel-position.boxmodel-left > span",
+    value: 42
+  },
+  {
     selector: ".boxmodel-margin.boxmodel-top > span",
     value: 30
   },
   {
     selector: ".boxmodel-margin.boxmodel-left > span",
     value: "auto"
   },
   {
@@ -80,16 +88,24 @@ var res2 = [
     selector: ".boxmodel-size > .boxmodel-width",
     value: "100"
   },
   {
     selector: ".boxmodel-size > .boxmodel-height",
     value: "150"
   },
   {
+    selector: ".boxmodel-position.boxmodel-top > span",
+    value: 50
+  },
+  {
+    selector: ".boxmodel-position.boxmodel-left > span",
+    value: 42
+  },
+  {
     selector: ".boxmodel-margin.boxmodel-top > span",
     value: 30
   },
   {
     selector: ".boxmodel-margin.boxmodel-left > span",
     value: "auto"
   },
   {
@@ -160,17 +176,17 @@ function* testInitialValues(inspector, v
 }
 
 function* testChangingValues(inspector, view, testActor) {
   info("Test that changing the document updates the box model");
   let viewdoc = view.document;
 
   let onUpdated = waitForUpdate(inspector);
   yield testActor.setAttribute("div", "style",
-                               "height:150px;padding-right:50px;");
+                               "height:150px;padding-right:50px;top:50px");
   yield onUpdated;
 
   for (let i = 0; i < res2.length; i++) {
     let elt = viewdoc.querySelector(res2[i].selector);
     is(elt.textContent, res2[i].value,
        res2[i].selector + " has the right value after style update.");
   }
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_positions.js
@@ -0,0 +1,65 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the box model displays the right values for positions.
+
+const TEST_URI = `
+  <style type='text/css'>
+    div {
+      position: absolute;
+      left: 0;
+      margin: 0;
+      padding: 0;
+      display: none;
+      height: 100px;
+      width: 100px;
+      border: 10px solid black;
+    }
+  </style>
+  <div>Test Node</div>
+`;
+
+// Expected values:
+const res1 = [
+  {
+    selector: ".boxmodel-position.boxmodel-top > span",
+    value: "auto"
+  },
+  {
+    selector: ".boxmodel-position.boxmodel-right > span",
+    value: "auto"
+  },
+  {
+    selector: ".boxmodel-position.boxmodel-bottom > span",
+    value: "auto"
+  },
+  {
+    selector: ".boxmodel-position.boxmodel-left > span",
+    value: 0
+  },
+];
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openBoxModelView();
+  let node = yield getNodeFront("div", inspector);
+  let children = yield inspector.markup.walker.children(node);
+  let beforeElement = children.nodes[0];
+
+  yield selectNode(beforeElement, inspector);
+  yield testPositionValues(inspector, view);
+});
+
+function* testPositionValues(inspector, view) {
+  info("Test that the position values of the box model are correct");
+  let viewdoc = view.document;
+
+  for (let i = 0; i < res1.length; i++) {
+    let elt = viewdoc.querySelector(res1[i].selector);
+    is(elt.textContent, res1[i].value,
+       res1[i].selector + " has the right value.");
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_pseudo-element.js
@@ -0,0 +1,120 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the box model displays the right values for a pseduo-element.
+
+const TEST_URI = `
+  <style type='text/css'>
+    div {
+      box-sizing: border-box;
+      display: block;
+      float: left;
+      line-height: 20px;
+      position: relative;
+      z-index: 2;
+      height: 100px;
+      width: 100px;
+      border: 10px solid black;
+      padding: 20px;
+      margin: 30px auto;
+    }
+
+    div::before {
+      content: 'before';
+      display: block;
+      width: 32px;
+      height: 32px;
+      margin: 0 auto 6px;
+    }
+  </style>
+  <div>Test Node</div>
+`;
+
+// Expected values:
+const res1 = [
+  {
+    selector: ".boxmodel-element-size",
+    value: "32" + "\u00D7" + "32"
+  },
+  {
+    selector: ".boxmodel-size > .boxmodel-width",
+    value: "32"
+  },
+  {
+    selector: ".boxmodel-size > .boxmodel-height",
+    value: "32"
+  },
+  {
+    selector: ".boxmodel-margin.boxmodel-top > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-margin.boxmodel-left > span",
+    value: "auto"
+  },
+  {
+    selector: ".boxmodel-margin.boxmodel-bottom > span",
+    value: 6
+  },
+  {
+    selector: ".boxmodel-margin.boxmodel-right > span",
+    value: "auto"
+  },
+  {
+    selector: ".boxmodel-padding.boxmodel-top > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-padding.boxmodel-left > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-padding.boxmodel-bottom > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-padding.boxmodel-right > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-border.boxmodel-top > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-border.boxmodel-left > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-border.boxmodel-bottom > span",
+    value: 0
+  },
+  {
+    selector: ".boxmodel-border.boxmodel-right > span",
+    value: 0
+  },
+];
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openBoxModelView();
+  let node = yield getNodeFront("div", inspector);
+  let children = yield inspector.markup.walker.children(node);
+  let beforeElement = children.nodes[0];
+
+  yield selectNode(beforeElement, inspector);
+  yield testInitialValues(inspector, view);
+});
+
+function* testInitialValues(inspector, view) {
+  info("Test that the initial values of the box model are correct");
+  let viewdoc = view.document;
+
+  for (let i = 0; i < res1.length; i++) {
+    let elt = viewdoc.querySelector(res1[i].selector);
+    is(elt.textContent, res1[i].value,
+       res1[i].selector + " has the right value.");
+  }
+}
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -10,48 +10,58 @@ const { throttle } = require("devtools/c
 
 const Types = require("../types");
 
 // The delay prior to executing the grid cell highlighting.
 const GRID_CELL_MOUSEOVER_TIMEOUT = 150;
 
 // Move SVG grid to the right 100 units, so that it is not flushed against the edge of
 // layout border
-const TRANSLATE_X = -100;
+const TRANSLATE_X = 0;
 const TRANSLATE_Y = 0;
 
-const VIEWPORT_HEIGHT = 100;
-const VIEWPORT_WIDTH = 450;
+const GRID_CELL_SCALE_FACTOR = 50;
+
+const VIEWPORT_MIN_HEIGHT = 100;
+const VIEWPORT_MAX_HEIGHT = 150;
 
 module.exports = createClass({
 
   displayName: "GridOutline",
 
   propTypes: {
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     onShowGridAreaHighlight: PropTypes.func.isRequired,
     onShowGridCellHighlight: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   getInitialState() {
     return {
       selectedGrids: [],
+      height: 0,
+      width: 0,
     };
   },
 
   componentWillMount() {
     // Throttle the grid highlighting of grid cells. It makes the UX smoother by not
     // lagging the grid cell highlighting if a lot of grid cells are mouseover in a
     // quick succession.
     this.highlightCell = throttle(this.highlightCell, GRID_CELL_MOUSEOVER_TIMEOUT);
   },
 
   componentWillReceiveProps({ grids }) {
+    if (this.state.selectedGrids.length < 2) {
+      this.setState({
+        height: 0,
+        width: 0,
+      });
+    }
     this.setState({
       selectedGrids: grids.filter(grid => grid.highlighted),
     });
   },
 
   /**
    * Returns the grid area name if the given grid cell is part of a grid area, otherwise
    * null.
@@ -72,16 +82,33 @@ module.exports = createClass({
 
     if (!gridArea) {
       return null;
     }
 
     return gridArea.name;
   },
 
+  /**
+   * Returns the height of the grid outline ranging between a minimum and maximum height.
+   *
+   * @return {Number} The height of the grid outline.
+   */
+  getHeight() {
+    const { height } = this.state;
+
+    if (height >= VIEWPORT_MAX_HEIGHT) {
+      return VIEWPORT_MAX_HEIGHT;
+    } else if (height <= VIEWPORT_MIN_HEIGHT) {
+      return VIEWPORT_MIN_HEIGHT;
+    }
+
+    return height;
+  },
+
   highlightCell({ target }) {
     const {
       grids,
       onShowGridAreaHighlight,
       onShowGridCellHighlight,
     } = this.props;
     const name = target.getAttribute("data-grid-area-name");
     const id = target.getAttribute("data-grid-id");
@@ -92,61 +119,82 @@ module.exports = createClass({
 
     target.setAttribute("fill", color);
 
     if (name) {
       onShowGridAreaHighlight(grids[id].nodeFront, name, color);
     }
 
     if (fragmentIndex && rowNumber && columnNumber) {
-      onShowGridCellHighlight(grids[id].nodeFront, fragmentIndex,
+      onShowGridCellHighlight(grids[id].nodeFront, color, fragmentIndex,
         rowNumber, columnNumber);
     }
   },
 
   /**
-   * Renders the grid outline for the given grid container object.
-   *
-   * @param  {Object} grid
-   *         A single grid container in the document.
-   */
+    * Renders the grid outline for the given grid container object.
+    *
+    * @param  {Object} grid
+    *         A single grid container in the document.
+    */
   renderGrid(grid) {
     const { id, color, gridFragments } = grid;
     // TODO: We are drawing the first fragment since only one is currently being stored.
     // In the future we will need to iterate over all fragments of a grid.
     let gridFragmentIndex = 0;
     const { rows, cols, areas } = gridFragments[gridFragmentIndex];
     const numberOfColumns = cols.lines.length - 1;
     const numberOfRows = rows.lines.length - 1;
     const rectangles = [];
-
-    // Draw a rectangle that acts as a border for the grid outline.
-    const border = this.renderGridOutlineBorder(numberOfRows, numberOfColumns, color);
-    rectangles.push(border);
-
     let x = 1;
     let y = 1;
-    const width = 10;
-    const height = 10;
+    let width = 0;
+    let height = 0;
+    // The grid outline border height/width is the total height/width of grid cells drawn.
+    let totalHeight = 0;
+    let totalWidth = 0;
 
-    // Draw the cells within the grid outline border.
+      // Draw the cells contained within the grid outline border.
     for (let rowNumber = 1; rowNumber <= numberOfRows; rowNumber++) {
+      height = GRID_CELL_SCALE_FACTOR * (rows.tracks[rowNumber - 1].breadth / 100);
       for (let columnNumber = 1; columnNumber <= numberOfColumns; columnNumber++) {
+        width = GRID_CELL_SCALE_FACTOR * (cols.tracks[columnNumber - 1].breadth / 100);
+
         const gridAreaName = this.getGridAreaName(columnNumber, rowNumber, areas);
-        const gridCell = this.renderGridCell(id, gridFragmentIndex, x, y, rowNumber,
-          columnNumber, color, gridAreaName);
+        const gridCell = this.renderGridCell(id, gridFragmentIndex, x, y,
+          rowNumber, columnNumber, color, gridAreaName, width, height);
 
         rectangles.push(gridCell);
         x += width;
       }
 
       x = 1;
       y += height;
+      totalHeight += height;
     }
 
+    // Find the total width of the grid container so we can draw the border for it
+    for (let columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) {
+      totalWidth += GRID_CELL_SCALE_FACTOR * (cols.tracks[columnNumber].breadth / 100);
+    }
+
+    // Store the height of the grid container in the component state to prevent overflow
+    // issues. We want to store the width of the grid container as well so that the
+    // viewbox is only the calculated width of the grid outline.
+    if (totalHeight > this.state.height || totalWidth > this.state.width) {
+      this.setState({
+        height: totalHeight + 20,
+        width: totalWidth,
+      });
+    }
+
+    // Draw a rectangle that acts as the grid outline border.
+    const border = this.renderGridOutlineBorder(totalWidth, totalHeight, color);
+    rectangles.unshift(border);
+
     return rectangles;
   },
 
   /**
    * Renders the grid cell of a grid fragment.
    *
    * @param  {Number} id
    *         The grid id stored on the grid fragment
@@ -157,31 +205,35 @@ module.exports = createClass({
    * @param  {Number} y
    *         The y-position of the grid cell.
    * @param  {Number} rowNumber
    *         The row number of the grid cell.
    * @param  {Number} columnNumber
    *         The column number of the grid cell.
    * @param  {String|null} gridAreaName
    *         The grid area name or null if the grid cell is not part of a grid area.
+   * @param  {Number} width
+   *         The width of grid cell.
+   * @param  {Number} height
+   *         The height of the grid cell.
    */
   renderGridCell(id, gridFragmentIndex, x, y, rowNumber, columnNumber, color,
-    gridAreaName) {
+    gridAreaName, width, height) {
     return dom.rect(
       {
         className: "grid-outline-cell",
         "data-grid-area-name": gridAreaName,
         "data-grid-fragment-index": gridFragmentIndex,
         "data-grid-id": id,
         "data-grid-row": rowNumber,
         "data-grid-column": columnNumber,
         x,
         y,
-        width: 10,
-        height: 10,
+        width,
+        height,
         fill: "none",
         stroke: color,
         onMouseOver: this.onMouseOverCell,
         onMouseOut: this.onMouseLeaveCell,
       }
     );
   },
 
@@ -189,24 +241,24 @@ module.exports = createClass({
     return dom.g(
       {
         className: "grid-cell-group",
       },
       grids.map(grid => this.renderGrid(grid))
     );
   },
 
-  renderGridOutlineBorder(numberOfRows, numberOfColumns, color) {
+  renderGridOutlineBorder(borderWidth, borderHeight, color) {
     return dom.rect(
       {
         className: "grid-outline-border",
         x: 1,
         y: 1,
-        width: numberOfColumns * 10,
-        height: numberOfRows * 10,
+        width: borderWidth,
+        height: borderHeight,
         stroke: color,
       }
     );
   },
 
   onMouseLeaveCell({ target }) {
     const {
       grids,
@@ -214,32 +266,34 @@ module.exports = createClass({
       onShowGridCellHighlight,
     } = this.props;
     const id = target.getAttribute("data-grid-id");
     const color = target.getAttribute("stroke");
 
     target.setAttribute("fill", "none");
 
     onShowGridAreaHighlight(grids[id].nodeFront, null, color);
-    onShowGridCellHighlight(grids[id].nodeFront);
+    onShowGridCellHighlight(grids[id].nodeFront, color);
   },
 
   onMouseOverCell(event) {
     event.persist();
     this.highlightCell(event);
   },
 
   render() {
-    return this.state.selectedGrids.length ?
+    const { selectedGrids, height, width } = this.state;
+
+    return selectedGrids.length ?
       dom.svg(
         {
           className: "grid-outline",
           width: "100%",
-          height: 100,
-          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}`,
+          height: this.getHeight(),
+          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${width} ${height}`,
         },
-        this.renderGridOutline(this.state.selectedGrids)
+        this.renderGridOutline(selectedGrids)
       )
       :
       null;
   },
 
 });
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -340,29 +340,34 @@ GridInspector.prototype = {
   },
 
   /**
    * Highlights the grid cell in the CSS Grid Highlighter for the given grid.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is highlighted for.
+   * @param  {String} color
+   *         The color of the grid cell for which the grid highlighter
+   *         is highlighted for.
    * @param  {Number|null} gridFragmentIndex
    *         The index of the grid fragment for which the grid highlighter
    *         is highlighted for.
    * @param  {Number|null} rowNumber
    *         The row number of the grid cell for which the grid highlighter
    *         is highlighted for.
    * @param  {Number|null} columnNumber
    *         The column number of the grid cell for which the grid highlighter
    *         is highlighted for.
    */
-  onShowGridCellHighlight(node, gridFragmentIndex, rowNumber, columnNumber) {
+  onShowGridCellHighlight(node, color, gridFragmentIndex, rowNumber, columnNumber) {
     let { highlighterSettings } = this.store.getState();
+
     highlighterSettings.showGridCell = { gridFragmentIndex, rowNumber, columnNumber };
+    highlighterSettings.color = color;
 
     this.highlighters.showGridHighlighter(node, highlighterSettings);
   },
 
   /**
    * Handler for the inspector sidebar select event. Starts listening for
    * "grid-layout-changed" if the layout panel is visible. Otherwise, stop
    * listening for grid layout changes. Finally, refresh the layout view if
--- a/devtools/client/jsonview/main.js
+++ b/devtools/client/jsonview/main.js
@@ -46,17 +46,15 @@ var JsonView = {
   // Message handlers for events from child processes
 
   /**
    * Save JSON to a file needs to be implemented here
    * in the parent process.
    */
   onSave: function (message) {
     JsonViewUtils.getTargetFile().then(file => {
-      if (file) {
-        JsonViewUtils.saveToFile(file, message.data);
-      }
-    });
+      JsonViewUtils.saveToFile(file, message.data);
+    }, () => {});
   }
 };
 
 // Exports from this module
 module.exports.JsonView = JsonView;
--- a/devtools/client/jsonview/utils.js
+++ b/devtools/client/jsonview/utils.js
@@ -14,35 +14,46 @@ const OPEN_FLAGS = {
   RDONLY: parseInt("0x01", 16),
   WRONLY: parseInt("0x02", 16),
   CREATE_FILE: parseInt("0x08", 16),
   APPEND: parseInt("0x10", 16),
   TRUNCATE: parseInt("0x20", 16),
   EXCL: parseInt("0x80", 16)
 };
 
+let filePickerShown = false;
+
 /**
  * Open File Save As dialog and let the user to pick proper file location.
  */
 exports.getTargetFile = function () {
-  return new Promise(resolve => {
+  return new Promise((resolve, reject) => {
+    if (filePickerShown) {
+      reject(null);
+      return;
+    }
+
+    filePickerShown = true;
+
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
 
     let win = getMostRecentBrowserWindow();
     fp.init(win, null, Ci.nsIFilePicker.modeSave);
     fp.appendFilter("JSON Files", "*.json; *.jsonp;");
     fp.appendFilters(Ci.nsIFilePicker.filterText);
     fp.appendFilters(Ci.nsIFilePicker.filterAll);
     fp.filterIndex = 0;
 
     fp.open(rv => {
+      filePickerShown = false;
+
       if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) {
         resolve(fp.file);
       } else {
-        resolve(null);
+        reject(null);
       }
     });
   });
 };
 
 /**
  * Save JSON to a file
  */
--- a/devtools/client/shared/components/tree/label-cell.js
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -18,20 +18,22 @@ define(function (require, exports, modul
    * Render the default cell used for toggle buttons
    */
   let LabelCell = React.createClass({
     displayName: "LabelCell",
 
     // See the TreeView component for details related
     // to the 'member' object.
     propTypes: {
+      id: PropTypes.string.isRequired,
       member: PropTypes.object.isRequired
     },
 
     render: function () {
+      let id = this.props.id;
       let member = this.props.member;
       let level = member.level || 0;
 
       // Compute indentation dynamically. The deeper the item is
       // inside the hierarchy, the bigger is the left padding.
       let rowStyle = {
         "paddingInlineStart": (level * 16) + "px",
       };
@@ -45,20 +47,25 @@ define(function (require, exports, modul
       if (member.open) {
         iconClassList.push("open");
       }
 
       return (
         td({
           className: "treeLabelCell",
           key: "default",
-          style: rowStyle},
-          span({ className: iconClassList.join(" ") }),
+          style: rowStyle,
+          role: "presentation"},
+          span({
+            className: iconClassList.join(" "),
+            role: "presentation"
+          }),
           span({
             className: "treeLabel " + member.type + "Label",
+            "aria-labelledby": id,
             "data-level": level
           }, member.name)
         )
       );
     }
   });
 
   // Exports from this module
--- a/devtools/client/shared/components/tree/tree-cell.js
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -100,28 +100,33 @@ define(function (require, exports, modul
       let cellElement;
       if (enableInput && this.state.inputEnabled && type !== "object") {
         classNames.push("inputEnabled");
         cellElement = input({
           autoFocus: true,
           onBlur: this.updateInputEnabled,
           readOnly: true,
           value,
+          "aria-labelledby": id
         });
       } else {
         cellElement = span({
           onClick: (type !== "object") ? this.updateInputEnabled : null,
+          "aria-labelledby": id
         },
           renderValue(props)
         );
       }
 
       // Render me!
       return (
-        td({ className: classNames.join(" ") },
+        td({
+          className: classNames.join(" "),
+          role: "presentation"
+        },
           cellElement
         )
       );
     }
   });
 
   // Default value rendering.
   let defaultRenderValue = props => {
--- a/devtools/client/shared/components/tree/tree-header.js
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -74,27 +74,32 @@ define(function (require, exports, modul
           classNames = this.getHeaderClass(col.id);
           classNames.push("treeHeaderCell");
         }
 
         cells.push(
           td({
             className: classNames.join(" "),
             style: cellStyle,
+            role: "presentation",
+            id: col.id,
             key: col.id},
             visible ? div({ className: "treeHeaderCellBox"},
               col.title
             ) : null,
           )
         );
       });
 
       return (
-        thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
-          cells
-        ))
+        thead({
+          role: "presentation"
+        }, tr({
+          className: visible ? "treeHeaderRow" : "",
+          role: "presentation"
+        }, cells))
       );
     }
   });
 
   // Exports from this module
   module.exports = TreeHeader;
 });
--- a/devtools/client/shared/components/tree/tree-row.js
+++ b/devtools/client/shared/components/tree/tree-row.js
@@ -10,16 +10,19 @@ define(function (require, exports, modul
   // ReactJS
   const React = require("devtools/client/shared/vendor/react");
   const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
   // Tree
   const TreeCell = React.createFactory(require("./tree-cell"));
   const LabelCell = React.createFactory(require("./label-cell"));
 
+  // Scroll
+  const { scrollIntoViewIfNeeded } = require("devtools/client/shared/scroll");
+
   // Shortcuts
   const { tr } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This template represents a node in TreeView component. It's rendered
    * using <tr> element (the entire tree is one big <table>).
    */
@@ -35,23 +38,27 @@ define(function (require, exports, modul
         type: PropTypes.string.isRequired,
         rowClass: PropTypes.string.isRequired,
         level: PropTypes.number.isRequired,
         hasChildren: PropTypes.bool,
         value: PropTypes.any,
         open: PropTypes.bool.isRequired,
         path: PropTypes.string.isRequired,
         hidden: PropTypes.bool,
+        selected: PropTypes.bool,
       }),
       decorator: PropTypes.object,
       renderCell: PropTypes.object,
       renderLabelCell: PropTypes.object,
       columns: PropTypes.array.isRequired,
+      id: PropTypes.string.isRequired,
       provider: PropTypes.object.isRequired,
-      onClick: PropTypes.func.isRequired
+      onClick: PropTypes.func.isRequired,
+      onMouseOver: PropTypes.func,
+      onMouseOut: PropTypes.func
     },
 
     componentWillReceiveProps(nextProps) {
       // I don't like accessing the underlying DOM elements directly,
       // but this optimization makes the filtering so damn fast!
       // The row doesn't have to be re-rendered, all we really need
       // to do is toggling a class name.
       // The important part is that DOM elements don't need to be
@@ -62,26 +69,37 @@ define(function (require, exports, modul
       }
     },
 
     /**
      * Optimize row rendering. If props are the same do not render.
      * This makes the rendering a lot faster!
      */
     shouldComponentUpdate: function (nextProps) {
-      let props = ["name", "open", "value", "loading"];
+      let props = ["name", "open", "value", "loading", "selected", "hasChildren"];
       for (let p in props) {
         if (nextProps.member[props[p]] != this.props.member[props[p]]) {
           return true;
         }
       }
 
       return false;
     },
 
+    componentDidUpdate: function () {
+      if (this.props.member.selected) {
+        let row = ReactDOM.findDOMNode(this);
+        // Because this is called asynchronously, context window might be
+        // already gone.
+        if (row.ownerDocument.defaultView) {
+          scrollIntoViewIfNeeded(row);
+        }
+      }
+    },
+
     getRowClass: function (object) {
       let decorator = this.props.decorator;
       if (!decorator || !decorator.getRowClass) {
         return [];
       }
 
       // Decorator can return a simple string or array of strings.
       let classNames = decorator.getRowClass(object);
@@ -94,82 +112,95 @@ define(function (require, exports, modul
       }
 
       return classNames;
     },
 
     render: function () {
       let member = this.props.member;
       let decorator = this.props.decorator;
+      let props = {
+        id: this.props.id,
+        role: "treeitem",
+        "aria-level": member.level,
+        "aria-selected": !!member.selected,
+        onClick: this.props.onClick,
+        onMouseOver: this.props.onMouseOver,
+        onMouseOut: this.props.onMouseOut
+      };
 
       // Compute class name list for the <tr> element.
       let classNames = this.getRowClass(member.object) || [];
       classNames.push("treeRow");
       classNames.push(member.type + "Row");
 
       if (member.hasChildren) {
         classNames.push("hasChildren");
+        props["aria-expanded"] = false;
       }
 
       if (member.open) {
         classNames.push("opened");
+        props["aria-expanded"] = true;
       }
 
       if (member.loading) {
         classNames.push("loading");
       }
 
+      if (member.selected) {
+        classNames.push("selected");
+      }
+
       if (member.hidden) {
         classNames.push("hidden");
       }
 
+      props.className = classNames.join(" ");
+
       // The label column (with toggle buttons) is usually
       // the first one, but there might be cases (like in
       // the Memory panel) where the toggling is done
       // in the last column.
       let cells = [];
 
       // Get components for rendering cells.
       let renderCell = this.props.renderCell || RenderCell;
       let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
       if (decorator && decorator.renderLabelCell) {
         renderLabelCell = decorator.renderLabelCell(member.object) ||
           renderLabelCell;
       }
 
       // Render a cell for every column.
       this.props.columns.forEach(col => {
-        let props = Object.assign({}, this.props, {
+        let cellProps = Object.assign({}, this.props, {
           key: col.id,
           id: col.id,
           value: this.props.provider.getValue(member.object, col.id)
         });
 
         if (decorator && decorator.renderCell) {
           renderCell = decorator.renderCell(member.object, col.id);
         }
 
         let render = (col.id == "default") ? renderLabelCell : renderCell;
 
         // Some cells don't have to be rendered. This happens when some
         // other cells span more columns. Note that the label cells contains
         // toggle buttons and should be usually there unless we are rendering
         // a simple non-expandable table.
         if (render) {
-          cells.push(render(props));
+          cells.push(render(cellProps));
         }
       });
 
       // Render tree row
       return (
-        tr({
-          className: classNames.join(" "),
-          onClick: this.props.onClick},
-          cells
-        )
+        tr(props, cells)
       );
     }
   });
 
   // Helpers
 
   let RenderCell = props => {
     return TreeCell(props);
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -67,16 +67,25 @@
 }
 
 .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
   cursor: pointer;
   color: var(--tree-link-color);
   text-decoration: underline;
 }
 
+.treeTable .treeRow.selected {
+  background-color: var(--theme-selection-background);
+}
+
+.treeTable .treeRow.selected:not(:hover) *,
+.treeTable .treeRow.selected:not(:hover) .treeLabelCell::after {
+  color: var(--theme-selection-color);
+}
+
 /* Filtering */
 .treeTable .treeRow.hidden {
   display: none;
 }
 
 .treeTable .treeValueCellDivider {
   display: flex;
   flex-wrap: wrap;
--- a/devtools/client/shared/components/tree/tree-view.js
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -57,16 +57,17 @@ define(function (require, exports, modul
     displayName: "TreeView",
 
     // The only required property (not set by default) is the input data
     // object that is used to puputate the tree.
     propTypes: {
       // The input data object.
       object: PropTypes.any,
       className: PropTypes.string,
+      label: PropTypes.string,
       // Data provider (see also the interface above)
       provider: PropTypes.shape({
         getChildren: PropTypes.func,
         hasChildren: PropTypes.func,
         getLabel: PropTypes.func,
         getValue: PropTypes.func,
         getKey: PropTypes.func,
         getType: PropTypes.func,
@@ -116,27 +117,38 @@ define(function (require, exports, modul
         expandableStrings: true,
         columns: []
       };
     },
 
     getInitialState: function () {
       return {
         expandedNodes: this.props.expandedNodes,
-        columns: ensureDefaultColumn(this.props.columns)
+        columns: ensureDefaultColumn(this.props.columns),
+        selected: null
       };
     },
 
     componentWillReceiveProps: function (nextProps) {
       let { expandedNodes } = nextProps;
       this.setState(Object.assign({}, this.state, {
         expandedNodes,
       }));
     },
 
+    componentDidUpdate: function () {
+      let selected = this.getSelectedRow(this.rows);
+      if (!selected && this.rows.length > 0) {
+        // TODO: Do better than just selecting the first row again. We want to
+        // select (in order) previous, next or parent in case when selected
+        // row is removed.
+        this.selectRow(this.rows[0].props.member.path);
+      }
+    },
+
     // Node expand/collapse
 
     toggle: function (nodePath) {
       let nodes = this.state.expandedNodes;
       if (this.isExpanded(nodePath)) {
         nodes.delete(nodePath);
       } else {
         nodes.add(nodePath);
@@ -149,19 +161,82 @@ define(function (require, exports, modul
     },
 
     isExpanded: function (nodePath) {
       return this.state.expandedNodes.has(nodePath);
     },
 
     // Event Handlers
 
+    onKeyDown: function (event) {
+      if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(
+        event.key)) {
+        event.preventDefault();
+      }
+    },
+
+    onKeyUp: function (event) {
+      let row = this.getSelectedRow(this.rows);
+      if (!row) {
+        return;
+      }
+
+      let index = this.rows.indexOf(row);
+      switch (event.key) {
+        case "ArrowRight":
+          let { hasChildren, open } = row.props.member;
+          if (hasChildren && !open) {
+            this.toggle(this.state.selected);
+          }
+          break;
+        case "ArrowLeft":
+          if (row && row.props.member.open) {
+            this.toggle(this.state.selected);
+          }
+          break;
+        case "ArrowDown":
+          let nextRow = this.rows[index + 1];
+          if (nextRow) {
+            this.selectRow(nextRow.props.member.path);
+          }
+          break;
+        case "ArrowUp":
+          let previousRow = this.rows[index - 1];
+          if (previousRow) {
+            this.selectRow(previousRow.props.member.path);
+          }
+          break;
+        default:
+          return;
+      }
+
+      event.preventDefault();
+    },
+
     onClickRow: function (nodePath, event) {
       event.stopPropagation();
       this.toggle(nodePath);
+      this.selectRow(nodePath);
+    },
+
+    getSelectedRow: function (rows) {
+      if (!this.state.selected || rows.length === 0) {
+        return null;
+      }
+      return rows.find(row => this.isSelected(row.props.member.path));
+    },
+
+    selectRow: function (nodePath) {
+      this.setState(Object.assign({}, this.state, {
+        selected: nodePath
+      }));
+    },
+
+    isSelected: function (nodePath) {
+      return nodePath === this.state.selected;
     },
 
     // Filtering & Sorting
 
     /**
      * Filter out nodes that don't correspond to the current filter.
      * @return {Boolean} true if the node should be visible otherwise false.
      */
@@ -232,17 +307,19 @@ define(function (require, exports, modul
           hasChildren: hasChildren,
           // Value associated with this node (as provided by the data provider)
           value: value,
           // True if the node is expanded.
           open: this.isExpanded(nodePath),
           // Node path
           path: nodePath,
           // True if the node is hidden (used for filtering)
-          hidden: !this.onFilter(child)
+          hidden: !this.onFilter(child),
+          // True if the node is selected with keyboard
+          selected: this.isSelected(nodePath)
         };
       });
     },
 
     /**
      * Render tree rows/nodes.
      */
     renderRows: function (parent, level = 0, path = "") {
@@ -263,16 +340,18 @@ define(function (require, exports, modul
         if (decorator && decorator.renderRow) {
           renderRow = decorator.renderRow(member.object) || renderRow;
         }
 
         let props = Object.assign({}, this.props, {
           key: member.path,
           member: member,
           columns: this.state.columns,
+          id: member.path,
+          ref: row => row && this.rows.push(row),
           onClick: this.onClickRow.bind(this, member.path)
         });
 
         // Render single row.
         rows.push(renderRow(props));
 
         // If a child node is expanded render its rows too.
         if (member.hasChildren && member.open) {
@@ -293,16 +372,17 @@ define(function (require, exports, modul
       });
 
       return rows;
     },
 
     render: function () {
       let root = this.props.object;
       let classNames = ["treeTable"];
+      this.rows = [];
 
       // Use custom class name from props.
       let className = this.props.className;
       if (className) {
         classNames.push(...className.split(" "));
       }
 
       // Alright, let's render all tree rows. The tree is one big <table>.
@@ -317,22 +397,28 @@ define(function (require, exports, modul
 
       let props = Object.assign({}, this.props, {
         columns: this.state.columns
       });
 
       return (
         DOM.table({
           className: classNames.join(" "),
+          role: "tree",
+          tabIndex: 0,
+          onKeyDown: this.onKeyDown,
+          onKeyUp: this.onKeyUp,
+          "aria-label": this.props.label || "",
+          "aria-activedescendant": this.state.selected,
           cellPadding: 0,
           cellSpacing: 0},
           TreeHeader(props),
-          DOM.tbody({},
-            rows
-          )
+          DOM.tbody({
+            role: "presentation"
+          }, rows)
         )
       );
     }
   });
 
   // Helpers
 
   /**
--- a/devtools/client/shared/scroll.js
+++ b/devtools/client/shared/scroll.js
@@ -1,52 +1,56 @@
 /* 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";
 
-/**
- * Scroll the document so that the element "elem" appears in the viewport.
- *
- * @param {DOMNode} elem
- *        The element that needs to appear in the viewport.
- * @param {Boolean} centered
- *        true if you want it centered, false if you want it to appear on the
- *        top of the viewport. It is true by default, and that is usually what
- *        you want.
- */
-function scrollIntoViewIfNeeded(elem, centered = true) {
-  let win = elem.ownerDocument.defaultView;
-  let clientRect = elem.getBoundingClientRect();
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+  /**
+   * Scroll the document so that the element "elem" appears in the viewport.
+   *
+   * @param {DOMNode} elem
+   *        The element that needs to appear in the viewport.
+   * @param {Boolean} centered
+   *        true if you want it centered, false if you want it to appear on the
+   *        top of the viewport. It is true by default, and that is usually what
+   *        you want.
+   */
+  function scrollIntoViewIfNeeded(elem, centered = true) {
+    let win = elem.ownerDocument.defaultView;
+    let clientRect = elem.getBoundingClientRect();
 
-  // The following are always from the {top, bottom}
-  // of the viewport, to the {top, …} of the box.
-  // Think of them as geometrical vectors, it helps.
-  // The origin is at the top left.
+    // The following are always from the {top, bottom}
+    // of the viewport, to the {top, …} of the box.
+    // Think of them as geometrical vectors, it helps.
+    // The origin is at the top left.
 
-  let topToBottom = clientRect.bottom;
-  let bottomToTop = clientRect.top - win.innerHeight;
-  // We allow one translation on the y axis.
-  let yAllowed = true;
+    let topToBottom = clientRect.bottom;
+    let bottomToTop = clientRect.top - win.innerHeight;
+    // We allow one translation on the y axis.
+    let yAllowed = true;
 
-  // Whatever `centered` is, the behavior is the same if the box is
-  // (even partially) visible.
-  if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
-    win.scrollBy(0, topToBottom - elem.offsetHeight);
-    yAllowed = false;
-  } else if ((bottomToTop < 0 || !centered) &&
-             bottomToTop >= -elem.offsetHeight) {
-    win.scrollBy(0, bottomToTop + elem.offsetHeight);
-    yAllowed = false;
-  }
+    // Whatever `centered` is, the behavior is the same if the box is
+    // (even partially) visible.
+    if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
+      win.scrollBy(0, topToBottom - elem.offsetHeight);
+      yAllowed = false;
+    } else if ((bottomToTop < 0 || !centered) &&
+              bottomToTop >= -elem.offsetHeight) {
+      win.scrollBy(0, bottomToTop + elem.offsetHeight);
+      yAllowed = false;
+    }
 
-  // If we want it centered, and the box is completely hidden,
-  // then we center it explicitly.
-  if (centered) {
-    if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
-      win.scroll(win.scrollX,
-                 win.scrollY + clientRect.top
-                 - (win.innerHeight - elem.offsetHeight) / 2);
+    // If we want it centered, and the box is completely hidden,
+    // then we center it explicitly.
+    if (centered) {
+      if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
+        win.scroll(win.scrollX,
+                  win.scrollY + clientRect.top
+                  - (win.innerHeight - elem.offsetHeight) / 2);
+      }
     }
   }
-}
-exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
+  // Exports from this module
+  module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
+});
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -58,23 +58,25 @@
  * Grid Outline
  */
 
 #grid-outline {
   margin: 5px auto;
 }
 
 .grid-outline-border {
+  fill: none;
   stroke-width: 0.75;
-  fill: none;
+  vector-effect: non-scaling-stroke;
 }
 
 .grid-outline-cell {
   pointer-events: all;
   stroke-dasharray: 0.5, 2;
+  vector-effect: non-scaling-stroke;
 }
 
 .grid-outline-cell:hover {
   opacity: 0.45;
 }
 
 /**
  * Container when no grids are present
--- a/dom/file/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MutableBlobStorage.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TaskQueue.h"
 #include "File.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsNetCID.h"
 #include "nsProxyRelease.h"
 #include "WorkerPrivate.h"
 
 #define BLOB_MEMORY_TEMPORARY_FILE 1048576
 
@@ -357,17 +358,22 @@ MutableBlobStorage::MutableBlobStorage(M
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 MutableBlobStorage::~MutableBlobStorage()
 {
   free(mData);
 
   if (mFD) {
-    DispatchToIOThread(new CloseFileRunnable(mFD));
+    RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
+    DispatchToIOThread(runnable.forget());
+  }
+
+  if (mTaskQueue) {
+    mTaskQueue->BeginShutdown();
   }
 }
 
 uint64_t
 MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
                                      const nsACString& aContentType,
                                      MutableBlobStorageCallback* aCallback)
 {
@@ -378,32 +384,28 @@ MutableBlobStorage::GetBlobWhenReady(nsI
   MOZ_ASSERT(mStorageState != eClosed);
   StorageState previousState = mStorageState;
   mStorageState = eClosed;
 
   if (previousState == eInTemporaryFile) {
     MOZ_ASSERT(mFD);
 
     if (NS_FAILED(mErrorResult)) {
-      NS_DispatchToMainThread(
-        new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult));
+      RefPtr<Runnable> runnable =
+        new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
+      NS_DispatchToMainThread(runnable.forget());
       return 0;
     }
 
     // We want to wait until all the WriteRunnable are completed. The way we do
     // this is to go to the I/O thread and then we come back: the runnables are
     // executed in order and this LastRunnable will be... the last one.
-    nsresult rv = DispatchToIOThread(new LastRunnable(this, aParent,
-                                                      aContentType, aCallback));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      NS_DispatchToMainThread(
-        new BlobCreationDoneRunnable(this, aCallback, nullptr, rv));
-      return 0;
-    }
-
+    RefPtr<Runnable> runnable =
+      new LastRunnable(this, aParent, aContentType, aCallback);
+    DispatchToIOThread(runnable.forget());
     return mDataLen;
   }
 
   RefPtr<BlobImpl> blobImpl;
 
   if (mData) {
     blobImpl = new MemoryBlobImpl(mData, mDataLen,
                                   NS_ConvertUTF8toUTF16(aContentType));
@@ -453,20 +455,17 @@ MutableBlobStorage::Append(const void* a
     MOZ_ASSERT(mFD);
 
     RefPtr<WriteRunnable> runnable =
       WriteRunnable::CopyBuffer(this, mFD, aData, aLength);
     if (NS_WARN_IF(!runnable)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
-    nsresult rv = DispatchToIOThread(runnable);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    DispatchToIOThread(runnable.forget());
 
     mDataLen += aLength;
     return NS_OK;
   }
 
   // By default, we store in memory.
 
   uint64_t offset = mDataLen;
@@ -527,20 +526,18 @@ MutableBlobStorage::ShouldBeTemporarySto
   return bufferSize.value() >= Preferences::GetUint("dom.blob.memoryToTemporaryFile",
                                                     BLOB_MEMORY_TEMPORARY_FILE);
 }
 
 nsresult
 MutableBlobStorage::MaybeCreateTemporaryFile()
 {
   if (XRE_IsParentProcess()) {
-    nsresult rv = DispatchToIOThread(new CreateTemporaryFileRunnable(this));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+    RefPtr<Runnable> runnable = new CreateTemporaryFileRunnable(this);
+    DispatchToIOThread(runnable.forget());
   } else {
     RefPtr<MutableBlobStorage> self(this);
     ContentChild::GetSingleton()->
       AsyncOpenAnonymousTemporaryFile([self](PRFileDesc* prfile) {
         if (prfile) {
           // The ownership of the prfile is moved to the FileCreatedRunnable.
           NS_DispatchToMainThread(new FileCreatedRunnable(self, prfile));
         }
@@ -554,34 +551,31 @@ MutableBlobStorage::MaybeCreateTemporary
 void
 MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
              mStorageState == eClosed);
 
   if (mStorageState == eClosed) {
-    DispatchToIOThread(new CloseFileRunnable(aFD));
+    RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
+    DispatchToIOThread(runnable.forget());
     return;
   }
 
   mStorageState = eInTemporaryFile;
   mFD = aFD;
 
   RefPtr<WriteRunnable> runnable =
     WriteRunnable::AdoptBuffer(this, mFD, mData, mDataLen);
   MOZ_ASSERT(runnable);
 
   mData = nullptr;
 
-  nsresult rv = DispatchToIOThread(runnable);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    mErrorResult = rv;
-    return;
-  }
+  DispatchToIOThread(runnable.forget());
 }
 
 void
 MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
                                          const nsACString& aContentType,
                                          already_AddRefed<MutableBlobStorageCallback> aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -602,25 +596,25 @@ MutableBlobStorage::CreateBlobAndRespond
 
 void
 MutableBlobStorage::ErrorPropagated(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mErrorResult = aRv;
 }
 
-/* static */ nsresult
-MutableBlobStorage::DispatchToIOThread(Runnable* aRunnable)
+void
+MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
 {
-  nsCOMPtr<nsIEventTarget> target
-    = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-  MOZ_ASSERT(target);
+  if (!mTaskQueue) {
+    nsCOMPtr<nsIEventTarget> target
+      = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+    MOZ_ASSERT(target);
 
-  nsresult rv = target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+    mTaskQueue = new TaskQueue(target.forget());
   }
 
-  return NS_OK;
+  nsCOMPtr<nsIRunnable> runnable(aRunnable);
+  mTaskQueue->Dispatch(runnable.forget());
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -6,16 +6,19 @@
 
 #ifndef mozilla_dom_MutableBlobStorage_h
 #define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
 #include "prio.h"
 
 namespace mozilla {
+
+class TaskQueue;
+
 namespace dom {
 
 class Blob;
 class BlobImpl;
 class MutableBlobStorage;
 
 class MutableBlobStorageCallback
 {
@@ -62,17 +65,17 @@ private:
   ~MutableBlobStorage();
 
   bool ExpandBufferSize(uint64_t aSize);
 
   bool ShouldBeTemporaryStorage(uint64_t aSize) const;
 
   nsresult MaybeCreateTemporaryFile();
 
-  static nsresult DispatchToIOThread(Runnable* aRunnable);
+  void DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable);
 
   // All these variables are touched on the main thread only.
 
   void* mData;
   uint64_t mDataLen;
   uint64_t mDataBufferLen;
 
   enum StorageState {
@@ -83,14 +86,16 @@ private:
     eClosed
   };
 
   StorageState mStorageState;
 
   PRFileDesc* mFD;
 
   nsresult mErrorResult;
+
+  RefPtr<TaskQueue> mTaskQueue;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MutableBlobStorage_h
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -135,16 +135,17 @@ AudioContext::AudioContext(nsPIDOMWindow
   , mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
   , mAudioContextState(AudioContextState::Suspended)
   , mNumberOfChannels(aNumberOfChannels)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
   , mCloseCalled(false)
   , mSuspendCalled(false)
+  , mIsDisconnecting(false)
 {
   bool mute = aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
 
@@ -255,17 +256,19 @@ AudioContext::Constructor(const GlobalOb
 
   RegisterWeakMemoryReporter(object);
 
   return object.forget();
 }
 
 bool AudioContext::CheckClosed(ErrorResult& aRv)
 {
-  if (mAudioContextState == AudioContextState::Closed) {
+  if (mAudioContextState == AudioContextState::Closed ||
+      mIsShutDown ||
+      mIsDisconnecting) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return true;
   }
   return false;
 }
 
 already_AddRefed<AudioBufferSourceNode>
 AudioContext::CreateBufferSource(ErrorResult& aRv)
@@ -635,32 +638,42 @@ AudioContext::DestinationStream() const
 
 double
 AudioContext::CurrentTime() const
 {
   MediaStream* stream = Destination()->Stream();
   return stream->StreamTimeToSeconds(stream->GetCurrentTime());
 }
 
+void AudioContext::DisconnectFromOwner()
+{
+  mIsDisconnecting = true;
+  Shutdown();
+  DOMEventTargetHelper::DisconnectFromOwner();
+}
+
 void
 AudioContext::Shutdown()
 {
   mIsShutDown = true;
 
-  if (!mIsOffline) {
-    ErrorResult dummy;
-    RefPtr<Promise> ignored = Close(dummy);
-  }
+  // We don't want to touch promises if the global is going away soon.
+  if (!mIsDisconnecting) {
+    if (!mIsOffline) {
+      IgnoredErrorResult dummy;
+      RefPtr<Promise> ignored = Close(dummy);
+    }
 
-  for (auto p : mPromiseGripArray) {
-    p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    for (auto p : mPromiseGripArray) {
+      p->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
+    mPromiseGripArray.Clear();
   }
 
-  mPromiseGripArray.Clear();
-
   // Release references to active nodes.
   // Active AudioNodes don't unregister in destructors, at which point the
   // Node is already unregistered.
   mActiveNodes.Clear();
 
   // For offline contexts, we can destroy the MediaStreamGraph at this point.
   if (mIsOffline && mDestination) {
     mDestination->OfflineShutdown();
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -137,16 +137,18 @@ public:
                                            DOMEventTargetHelper)
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return GetOwner();
   }
 
+  virtual void DisconnectFromOwner() override;
+
   void Shutdown(); // idempotent
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
   // Constructor for regular AudioContext
   static already_AddRefed<AudioContext>
@@ -367,16 +369,17 @@ private:
   uint32_t mNumberOfChannels;
   bool mIsOffline;
   bool mIsStarted;
   bool mIsShutDown;
   // Close has been called, reject suspend and resume call.
   bool mCloseCalled;
   // Suspend has been called with no following resume.
   bool mSuspendCalled;
+  bool mIsDisconnecting;
 };
 
 static const dom::AudioContext::AudioContextId NO_AUDIO_CONTEXT = 0;
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -169,16 +169,17 @@ tags=capturestream
 [test_mediaStreamAudioSourceNodeResampling.html]
 tags=capturestream
 [test_mixingRules.html]
 skip-if = toolkit == 'android' # bug 1091965
 [test_mozaudiochannel.html]
 # Android: bug 1061675; OSX 10.6: bug 1097721
 skip-if = (toolkit == 'android') || (os == 'mac' && os_version == '10.6')
 [test_nodeToParamConnection.html]
+[test_nodeCreationDocumentGone.html]
 [test_OfflineAudioContext.html]
 [test_offlineDestinationChannelCountLess.html]
 [test_offlineDestinationChannelCountMore.html]
 [test_oscillatorNode.html]
 [test_oscillatorNode2.html]
 [test_oscillatorNodeNegativeFrequency.html]
 [test_oscillatorNodePassThrough.html]
 [test_oscillatorNodeStart.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_nodeCreationDocumentGone.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test whether we can create an AudioContext interface</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({
+  set: [
+    [ "dom.disable_open_during_load", false ]
+  ]
+}).then(function() {
+  var sub = encodeURI("data:text/html,<!DOCTYPE html>\n"+
+                      "<html><script>"+
+                      "var context = new AudioContext();"+
+                      "setTimeout(function(){window.close();},1000);\x3C/script></html>");
+  window.onload = function(){
+    var a = window.open(sub);
+    a.onbeforeunload = function(){
+      setTimeout(function(){
+        try {
+          a.context.createScriptProcessor(512, 1, 1);
+        } catch(e) {
+          ok (true,"got exception");
+        }
+        setTimeout(function() {
+        ok (true,"no crash");
+          SimpleTest.finish();
+        }, 0);
+      }, 0);
+    };
+  };
+});
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/workers/PServiceWorkerManager.ipdl
+++ b/dom/workers/PServiceWorkerManager.ipdl
@@ -1,41 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
+include protocol PServiceWorkerUpdater;
 
 include PBackgroundSharedTypes;
 include ServiceWorkerRegistrarTypes;
 
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 
 namespace mozilla {
 namespace dom {
 
 protocol PServiceWorkerManager
 {
   manager PBackground;
+  manages PServiceWorkerUpdater;
 
 parent:
   async Register(ServiceWorkerRegistrationData data);
 
   async Unregister(PrincipalInfo principalInfo, nsString scope);
 
   async PropagateSoftUpdate(OriginAttributes originAttributes,
                             nsString scope);
   async PropagateUnregister(PrincipalInfo principalInfo, nsString scope);
 
   async PropagateRemove(nsCString host);
 
   async PropagateRemoveAll();
 
   async Shutdown();
 
+  async PServiceWorkerUpdater(OriginAttributes originAttributes,
+                              nsCString scope);
+
 child:
   async NotifyRegister(ServiceWorkerRegistrationData data);
   async NotifySoftUpdate(OriginAttributes originAttributes, nsString scope);
   async NotifyUnregister(PrincipalInfo principalInfo, nsString scope);
   async NotifyRemove(nsCString host);
   async NotifyRemoveAll();
 
   async __delete__();
new file mode 100644
--- /dev/null
+++ b/dom/workers/PServiceWorkerUpdater.ipdl
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PServiceWorkerManager;
+
+namespace mozilla {
+namespace dom {
+
+protocol PServiceWorkerUpdater
+{
+  manager PServiceWorkerManager;
+
+parent:
+  // This __delete__ is safe because it's called when Proceed() is received and
+  // no other IPC messages are received nor sent.
+  async __delete__();
+
+child:
+  async Proceed(bool allowed);
+};
+
+} // namespace dom
+} // namespace mozilla
+
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -72,16 +72,17 @@
 #include "ServiceWorkerPrivate.h"
 #include "ServiceWorkerRegisterJob.h"
 #include "ServiceWorkerRegistrar.h"
 #include "ServiceWorkerRegistration.h"
 #include "ServiceWorkerScriptCache.h"
 #include "ServiceWorkerEvents.h"
 #include "ServiceWorkerUnregisterJob.h"
 #include "ServiceWorkerUpdateJob.h"
+#include "ServiceWorkerUpdaterChild.h"
 #include "SharedWorker.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #ifdef PostMessage
 #undef PostMessage
@@ -503,16 +504,248 @@ public:
     return NS_OK;
   }
 
 private:
   ~PropagateRemoveAllRunnable()
   {}
 };
 
+class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback
+{
+public:
+  PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
+                          GenericPromise::Private* aPromise)
+    : mCallback(aCallback)
+    , mPromise(aPromise)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mPromise);
+  }
+
+  void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mPromise);
+
+    if (mCallback) {
+      mCallback->UpdateSucceeded(aInfo);
+    }
+
+    MaybeResolve();
+  }
+
+  void UpdateFailed(ErrorResult& aStatus) override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mPromise);
+
+    if (mCallback) {
+      mCallback->UpdateFailed(aStatus);
+    }
+
+    MaybeResolve();
+  }
+
+private:
+  ~PromiseResolverCallback()
+  {
+    MaybeResolve();
+  }
+
+  void
+  MaybeResolve()
+  {
+    if (mPromise) {
+      mPromise->Resolve(true, __func__);
+      mPromise = nullptr;
+    }
+  }
+
+  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+  RefPtr<GenericPromise::Private> mPromise;
+};
+
+// This runnable is used for 2 different tasks:
+// - to postpone the SoftUpdate() until the IPC SWM actor is created
+//   (aInternalMethod == false)
+// - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
+//   notified by the parent (aInternalMethod == true)
+class SoftUpdateRunnable final : public CancelableRunnable
+{
+public:
+  SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
+                     const nsACString& aScope, bool aInternalMethod,
+                     GenericPromise::Private* aPromise)
+    : mAttrs(aOriginAttributes)
+    , mScope(aScope)
+    , mInternalMethod(aInternalMethod)
+    , mPromise(aPromise)
+  {}
+
+  NS_IMETHOD Run() override
+  {
+    AssertIsOnMainThread();
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (mInternalMethod) {
+      RefPtr<PromiseResolverCallback> callback =
+        new PromiseResolverCallback(nullptr, mPromise);
+      mPromise = nullptr;
+
+      swm->SoftUpdateInternal(mAttrs, mScope, callback);
+    } else {
+      swm->SoftUpdate(mAttrs, mScope);
+    }
+
+    return NS_OK;
+  }
+
+  nsresult
+  Cancel() override
+  {
+    mPromise = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~SoftUpdateRunnable()
+  {
+    if (mPromise) {
+      mPromise->Resolve(true, __func__);
+    }
+  }
+
+  const OriginAttributes mAttrs;
+  const nsCString mScope;
+  bool mInternalMethod;
+
+  RefPtr<GenericPromise::Private> mPromise;
+};
+
+// This runnable is used for 3 different tasks:
+// - to postpone the Update() until the IPC SWM actor is created
+//   (aType == ePostpone)
+// - to call the 'real' Update when the ServiceWorkerUpdaterChild is
+//   notified by the parent (aType == eSuccess)
+// - an error must be propagated (aType == eFailure)
+class UpdateRunnable final : public CancelableRunnable
+{
+public:
+  enum Type {
+    ePostpone,
+    eSuccess,
+    eFailure,
+  };
+
+  UpdateRunnable(nsIPrincipal* aPrincipal,
+                 const nsACString& aScope,
+                 ServiceWorkerUpdateFinishCallback* aCallback,
+                 Type aType,
+                 GenericPromise::Private* aPromise)
+    : mPrincipal(aPrincipal)
+    , mScope(aScope)
+    , mCallback(aCallback)
+    , mType(aType)
+    , mPromise(aPromise)
+  {}
+
+  NS_IMETHOD Run() override
+  {
+    AssertIsOnMainThread();
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (mType == ePostpone) {
+      swm->Update(mPrincipal, mScope, mCallback);
+      return NS_OK;
+    }
+
+    MOZ_ASSERT(mPromise);
+
+    RefPtr<PromiseResolverCallback> callback =
+      new PromiseResolverCallback(mCallback, mPromise);
+    mPromise = nullptr;
+
+    if (mType == eSuccess) {
+      swm->UpdateInternal(mPrincipal, mScope, callback);
+      return NS_OK;
+    }
+
+    ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
+    callback->UpdateFailed(error);
+    return NS_OK;
+  }
+
+  nsresult
+  Cancel() override
+  {
+    mPromise = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~UpdateRunnable()
+  {
+    if (mPromise) {
+      mPromise->Resolve(true, __func__);
+    }
+  }
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  const nsCString mScope;
+  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+  Type mType;
+
+  RefPtr<GenericPromise::Private> mPromise;
+};
+
+class ResolvePromiseRunnable final : public CancelableRunnable
+{
+public:
+  explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
+    : mPromise(aPromise)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    MaybeResolve();
+    return NS_OK;
+  }
+
+  nsresult
+  Cancel() override
+  {
+    mPromise = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~ResolvePromiseRunnable()
+  {
+    MaybeResolve();
+  }
+
+  void
+  MaybeResolve()
+  {
+    if (mPromise) {
+      mPromise->Resolve(true, __func__);
+      mPromise = nullptr;
+    }
+  }
+
+  RefPtr<GenericPromise::Private> mPromise;
+};
+
 } // namespace
 
 // This function implements parts of the step 3 of the following algorithm:
 // https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
 static bool
 IsFromAuthenticatedOrigin(nsIDocument* aDoc)
 {
   MOZ_ASSERT(aDoc);
@@ -2721,16 +2954,90 @@ ServiceWorkerManager::SoftUpdate(const O
                                  const nsACString& aScope)
 {
   AssertIsOnMainThread();
 
   if (mShuttingDown) {
     return;
   }
 
+  if (!mActor) {
+    RefPtr<Runnable> runnable =
+      new SoftUpdateRunnable(aOriginAttributes, aScope, false, nullptr);
+    AppendPendingOperation(runnable);
+    return;
+  }
+
+  RefPtr<GenericPromise::Private> promise =
+    new GenericPromise::Private(__func__);
+
+  RefPtr<CancelableRunnable> successRunnable =
+    new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise);
+
+  RefPtr<CancelableRunnable> failureRunnable =
+    new ResolvePromiseRunnable(promise);
+
+  ServiceWorkerUpdaterChild* actor =
+    new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
+
+  mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
+                                               nsCString(aScope));
+}
+
+namespace {
+
+class UpdateJobCallback final : public ServiceWorkerJob::Callback
+{
+  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+
+  ~UpdateJobCallback() = default;
+
+public:
+  explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
+    : mCallback(aCallback)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mCallback);
+  }
+
+  void
+  JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aJob);
+
+    if (aStatus.Failed()) {
+      mCallback->UpdateFailed(aStatus);
+      return;
+    }
+
+    MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
+    RefPtr<ServiceWorkerUpdateJob> updateJob =
+      static_cast<ServiceWorkerUpdateJob*>(aJob);
+    RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
+    mCallback->UpdateSucceeded(reg);
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback)
+};
+
+} // anonymous namespace
+
+void
+ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
+                                         const nsACString& aScope,
+                                         ServiceWorkerUpdateFinishCallback* aCallback)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aCallback);
+
+  if (mShuttingDown) {
+    return;
+  }
+
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   nsCOMPtr<nsIPrincipal> principal =
     BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
@@ -2774,64 +3081,62 @@ ServiceWorkerManager::SoftUpdate(const O
   // See: https://github.com/slightlyoff/ServiceWorker/issues/759
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
                                                             aScope);
 
   RefPtr<ServiceWorkerUpdateJob> job =
     new ServiceWorkerUpdateJob(principal, registration->mScope,
                                newest->ScriptSpec(), nullptr,
                                registration->GetLoadFlags());
+
+  RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
+  job->AppendResultCallback(cb);
+
   queue->ScheduleJob(job);
 }
 
-namespace {
-
-class UpdateJobCallback final : public ServiceWorkerJob::Callback
-{
-  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
-
-  ~UpdateJobCallback()
-  {
-  }
-
-public:
-  explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
-    : mCallback(aCallback)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(mCallback);
-  }
-
-  void
-  JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aJob);
-
-    if (aStatus.Failed()) {
-      mCallback->UpdateFailed(aStatus);
-      return;
-    }
-
-    MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
-    RefPtr<ServiceWorkerUpdateJob> updateJob =
-      static_cast<ServiceWorkerUpdateJob*>(aJob);
-    RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
-    mCallback->UpdateSucceeded(reg);
-  }
-
-  NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback)
-};
-} // anonymous namespace
-
 void
 ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
                              const nsACString& aScope,
                              ServiceWorkerUpdateFinishCallback* aCallback)
 {
+  AssertIsOnMainThread();
+
+  if (!mActor) {
+    RefPtr<Runnable> runnable =
+      new UpdateRunnable(aPrincipal, aScope, aCallback,
+                         UpdateRunnable::ePostpone, nullptr);
+    AppendPendingOperation(runnable);
+    return;
+  }
+
+  RefPtr<GenericPromise::Private> promise =
+    new GenericPromise::Private(__func__);
+
+  RefPtr<CancelableRunnable> successRunnable =
+    new UpdateRunnable(aPrincipal, aScope, aCallback,
+                       UpdateRunnable::eSuccess, promise);
+
+  RefPtr<CancelableRunnable> failureRunnable =
+    new UpdateRunnable(aPrincipal, aScope, aCallback,
+                       UpdateRunnable::eFailure, promise);
+
+  ServiceWorkerUpdaterChild* actor =
+    new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
+
+  mActor->SendPServiceWorkerUpdaterConstructor(actor,
+                                               aPrincipal->OriginAttributesRef(),
+                                               nsCString(aScope));
+}
+
+void
+ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
+                                     const nsACString& aScope,
+                                     ServiceWorkerUpdateFinishCallback* aCallback)
+{
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
   nsAutoCString scopeKey;
   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -160,20 +160,31 @@ public:
                      ErrorResult& aRv);
 
   void
   Update(nsIPrincipal* aPrincipal,
          const nsACString& aScope,
          ServiceWorkerUpdateFinishCallback* aCallback);
 
   void
+  UpdateInternal(nsIPrincipal* aPrincipal,
+                 const nsACString& aScope,
+                 ServiceWorkerUpdateFinishCallback* aCallback);
+
+  void
   SoftUpdate(const OriginAttributes& aOriginAttributes,
              const nsACString& aScope);
 
   void
+  SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
+                     const nsACString& aScope,
+                     ServiceWorkerUpdateFinishCallback* aCallback);
+
+
+  void
   PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
                       const nsAString& aScope);
 
   void
   PropagateRemove(const nsACString& aHost);
 
   void
   Remove(const nsACString& aHost);
--- a/dom/workers/ServiceWorkerManagerChild.cpp
+++ b/dom/workers/ServiceWorkerManagerChild.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerManagerChild.h"
 #include "ServiceWorkerManager.h"
+#include "ServiceWorkerUpdaterChild.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 
 using namespace ipc;
 
 namespace dom {
 namespace workers {
@@ -97,11 +98,25 @@ ServiceWorkerManagerChild::RecvNotifyRem
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (swm) {
     swm->RemoveAll();
   }
 
   return IPC_OK();
 }
 
+PServiceWorkerUpdaterChild*
+ServiceWorkerManagerChild::AllocPServiceWorkerUpdaterChild(const OriginAttributes& aOriginAttributes,
+                                                           const nsCString& aScope)
+{
+  MOZ_CRASH("Do no use ServiceWorkerUpdaterChild IPC CTOR.");
+}
+
+bool
+ServiceWorkerManagerChild::DeallocPServiceWorkerUpdaterChild(PServiceWorkerUpdaterChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/ServiceWorkerManagerChild.h
+++ b/dom/workers/ServiceWorkerManagerChild.h
@@ -41,16 +41,23 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
                                                        const nsString& aScope) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyRemove(const nsCString& aHost) override;
 
   virtual mozilla::ipc::IPCResult RecvNotifyRemoveAll() override;
 
+  virtual PServiceWorkerUpdaterChild*
+  AllocPServiceWorkerUpdaterChild(const OriginAttributes& originAttributes,
+                                  const nsCString& scope) override;
+
+  virtual bool
+  DeallocPServiceWorkerUpdaterChild(PServiceWorkerUpdaterChild* aActor) override;
+
 private:
   ServiceWorkerManagerChild()
     : mShuttingDown(false)
   {}
 
   ~ServiceWorkerManagerChild() {}
 
   bool mShuttingDown;
--- a/dom/workers/ServiceWorkerManagerParent.cpp
+++ b/dom/workers/ServiceWorkerManagerParent.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerManagerParent.h"
 #include "ServiceWorkerManagerService.h"
+#include "ServiceWorkerUpdaterParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/Unused.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
@@ -305,16 +306,48 @@ ServiceWorkerManagerParent::RecvShutdown
 
   mService->UnregisterActor(this);
   mService = nullptr;
 
   Unused << Send__delete__(this);
   return IPC_OK();
 }
 
+PServiceWorkerUpdaterParent*
+ServiceWorkerManagerParent::AllocPServiceWorkerUpdaterParent(const OriginAttributes& aOriginAttributes,
+                                                             const nsCString& aScope)
+{
+  AssertIsOnBackgroundThread();
+  return new ServiceWorkerUpdaterParent();
+}
+
+mozilla::ipc::IPCResult
+ServiceWorkerManagerParent::RecvPServiceWorkerUpdaterConstructor(PServiceWorkerUpdaterParent* aActor,
+                                                                 const OriginAttributes& aOriginAttributes,
+                                                                 const nsCString& aScope)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  mService->ProcessUpdaterActor(static_cast<ServiceWorkerUpdaterParent*>(aActor),
+                                aOriginAttributes, aScope, mID);
+  return IPC_OK();
+}
+
+bool
+ServiceWorkerManagerParent::DeallocPServiceWorkerUpdaterParent(PServiceWorkerUpdaterParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  delete aActor;
+  return true;
+}
+
 void
 ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
 
   if (mService) {
     // This object is about to be released and with it, also mService will be
     // released too.
--- a/dom/workers/ServiceWorkerManagerParent.h
+++ b/dom/workers/ServiceWorkerManagerParent.h
@@ -51,16 +51,28 @@ private:
                                                           const nsString& aScope) override;
 
   virtual mozilla::ipc::IPCResult RecvPropagateRemove(const nsCString& aHost) override;
 
   virtual mozilla::ipc::IPCResult RecvPropagateRemoveAll() override;
 
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
 
+  virtual PServiceWorkerUpdaterParent*
+  AllocPServiceWorkerUpdaterParent(const OriginAttributes& aOriginAttributes,
+                                   const nsCString& aScope) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPServiceWorkerUpdaterConstructor(PServiceWorkerUpdaterParent* aActor,
+                                       const OriginAttributes& aOriginAttributes,
+                                       const nsCString& aScope) override;
+
+  virtual bool
+  DeallocPServiceWorkerUpdaterParent(PServiceWorkerUpdaterParent* aActor) override;
+
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   RefPtr<ServiceWorkerManagerService> mService;
 
   // We use this ID in the Service in order to avoid the sending of messages to
   // ourself.
   uint64_t mID;
 };
--- a/dom/workers/ServiceWorkerManagerService.cpp
+++ b/dom/workers/ServiceWorkerManagerService.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerManagerService.h"
 #include "ServiceWorkerManagerParent.h"
 #include "ServiceWorkerRegistrar.h"
+#include "ServiceWorkerUpdaterParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/Unused.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 
 using namespace ipc;
@@ -227,11 +228,56 @@ ServiceWorkerManagerService::PropagateRe
     }
   }
 
 #ifdef DEBUG
   MOZ_ASSERT(parentFound);
 #endif
 }
 
+void
+ServiceWorkerManagerService::ProcessUpdaterActor(ServiceWorkerUpdaterParent* aActor,
+                                                 const OriginAttributes& aOriginAttributes,
+                                                 const nsACString& aScope,
+                                                 uint64_t aParentId)
+{
+  AssertIsOnBackgroundThread();
+
+  nsAutoCString suffix;
+  aOriginAttributes.CreateSuffix(suffix);
+
+  nsCString scope(aScope);
+  scope.Append(suffix);
+
+  for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
+    // We already have an actor doing this update on another process.
+    if (mPendingUpdaterActors[i].mScope.Equals(scope) &&
+        mPendingUpdaterActors[i].mParentId != aParentId) {
+      Unused << aActor->SendProceed(false);
+      return;
+    }
+  }
+
+  if (aActor->Proceed(this)) {
+    PendingUpdaterActor* pua = mPendingUpdaterActors.AppendElement();
+    pua->mActor = aActor;
+    pua->mScope = scope;
+    pua->mParentId = aParentId;
+  }
+}
+
+void
+ServiceWorkerManagerService::UpdaterActorDestroyed(ServiceWorkerUpdaterParent* aActor)
+{
+  for (uint32_t i = 0; i < mPendingUpdaterActors.Length(); ++i) {
+    // We already have an actor doing the update for this scope.
+    if (mPendingUpdaterActors[i].mActor == aActor) {
+      mPendingUpdaterActors.RemoveElementAt(i);
+      return;
+    }
+  }
+
+  MOZ_CRASH("The actor should be found");
+}
+
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/ServiceWorkerManagerService.h
+++ b/dom/workers/ServiceWorkerManagerService.h
@@ -21,16 +21,17 @@ class PrincipalInfo;
 
 namespace dom {
 
 class ServiceWorkerRegistrationData;
 
 namespace workers {
 
 class ServiceWorkerManagerParent;
+class ServiceWorkerUpdaterParent;
 
 class ServiceWorkerManagerService final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerManagerService)
 
   static already_AddRefed<ServiceWorkerManagerService> Get();
   static already_AddRefed<ServiceWorkerManagerService> GetOrCreate();
@@ -48,20 +49,36 @@ public:
   void PropagateUnregister(uint64_t aParentID,
                            const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                            const nsAString& aScope);
 
   void PropagateRemove(uint64_t aParentID, const nsACString& aHost);
 
   void PropagateRemoveAll(uint64_t aParentID);
 
+  void ProcessUpdaterActor(ServiceWorkerUpdaterParent* aActor,
+                           const OriginAttributes& aOriginAttributes,
+                           const nsACString& aScope,
+                           uint64_t aParentID);
+
+  void UpdaterActorDestroyed(ServiceWorkerUpdaterParent* aActor);
+
 private:
   ServiceWorkerManagerService();
   ~ServiceWorkerManagerService();
 
   nsTHashtable<nsPtrHashKey<ServiceWorkerManagerParent>> mAgents;
+
+  struct PendingUpdaterActor
+  {
+    nsCString mScope;
+    ServiceWorkerUpdaterParent* mActor;
+    uint64_t mParentId;
+  };
+
+  nsTArray<PendingUpdaterActor> mPendingUpdaterActors;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ServiceWorkerManagerService_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerUpdaterChild.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ServiceWorkerUpdaterChild.h"
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+ServiceWorkerUpdaterChild::ServiceWorkerUpdaterChild(GenericPromise* aPromise,
+                                                     CancelableRunnable* aSuccessRunnable,
+                                                     CancelableRunnable* aFailureRunnable)
+  : mSuccessRunnable(aSuccessRunnable)
+  , mFailureRunnable(aFailureRunnable)
+{
+  MOZ_ASSERT(aPromise);
+  MOZ_ASSERT(aSuccessRunnable);
+  MOZ_ASSERT(aFailureRunnable);
+
+  aPromise->Then(AbstractThread::GetCurrent(), __func__,
+    [this]() {
+      mPromiseHolder.Complete();
+      Unused << Send__delete__(this);
+  }).Track(mPromiseHolder);
+}
+
+mozilla::ipc::IPCResult
+ServiceWorkerUpdaterChild::RecvProceed(const bool& aAllowed)
+{
+  // If we have a callback, it will resolve the promise.
+
+  if (aAllowed) {
+    mSuccessRunnable->Run();
+    mFailureRunnable->Cancel();
+  } else {
+    mFailureRunnable->Run();
+    mSuccessRunnable->Cancel();
+  }
+
+  mSuccessRunnable = nullptr;
+  mFailureRunnable = nullptr;
+
+  return IPC_OK();
+}
+
+void
+ServiceWorkerUpdaterChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mSuccessRunnable) {
+    mSuccessRunnable->Cancel();
+  }
+
+  if (mFailureRunnable) {
+    mFailureRunnable->Cancel();
+  }
+
+  mPromiseHolder.DisconnectIfExists();
+}
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerUpdaterChild.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ServiceWorkerUpdaterChild_h
+#define mozilla_dom_ServiceWorkerUpdaterChild_h
+
+#include "mozilla/dom/PServiceWorkerUpdaterChild.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/MozPromise.h"
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+class ServiceWorkerUpdaterChild final : public PServiceWorkerUpdaterChild
+{
+public:
+  ServiceWorkerUpdaterChild(GenericPromise* aPromise,
+                            CancelableRunnable* aSuccessRunnable,
+                            CancelableRunnable* aFailureRunnable);
+
+  mozilla::ipc::IPCResult
+  RecvProceed(const bool& aAllowed) override;
+
+private:
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  MozPromiseRequestHolder<GenericPromise> mPromiseHolder;
+
+  RefPtr<CancelableRunnable> mSuccessRunnable;
+  RefPtr<CancelableRunnable> mFailureRunnable;
+};
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ServiceWorkerUpdaterChild_h
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerUpdaterParent.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ServiceWorkerUpdaterParent.h"
+#include "ServiceWorkerManagerService.h"
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+bool
+ServiceWorkerUpdaterParent::Proceed(ServiceWorkerManagerService* aService)
+{
+  if (!SendProceed(true)) {
+    return false;
+  }
+
+  mService = aService;
+  return true;
+}
+
+void
+ServiceWorkerUpdaterParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mService) {
+    mService->UpdaterActorDestroyed(this);
+  }
+}
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/workers/ServiceWorkerUpdaterParent.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ServiceWorkerUpdaterParent_h
+#define mozilla_dom_ServiceWorkerUpdaterParent_h
+
+#include "mozilla/dom/PServiceWorkerUpdaterParent.h"
+#include "mozilla/BasePrincipal.h"
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+class ServiceWorkerManagerService;
+
+class ServiceWorkerUpdaterParent final : public PServiceWorkerUpdaterParent
+{
+public:
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  bool
+  Proceed(ServiceWorkerManagerService* aService);
+
+private:
+  RefPtr<ServiceWorkerManagerService> mService;
+};
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ServiceWorkerUpdaterParent_h
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -71,30 +71,33 @@ UNIFIED_SOURCES += [
     'ServiceWorkerPrivate.cpp',
     'ServiceWorkerRegisterJob.cpp',
     'ServiceWorkerRegistrar.cpp',
     'ServiceWorkerRegistration.cpp',
     'ServiceWorkerRegistrationInfo.cpp',
     'ServiceWorkerScriptCache.cpp',
     'ServiceWorkerUnregisterJob.cpp',
     'ServiceWorkerUpdateJob.cpp',
+    'ServiceWorkerUpdaterChild.cpp',
+    'ServiceWorkerUpdaterParent.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'WorkerDebuggerManager.cpp',
     'WorkerHolder.cpp',
     'WorkerLocation.cpp',
     'WorkerNavigator.cpp',
     'WorkerPrivate.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
 ]
 
 IPDL_SOURCES += [
     'PServiceWorkerManager.ipdl',
+    'PServiceWorkerUpdater.ipdl',
     'ServiceWorkerRegistrarTypes.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/dom/base',
     '/dom/bindings',
--- a/dom/workers/test/serviceworkers/browser.ini
+++ b/dom/workers/test/serviceworkers/browser.ini
@@ -1,10 +1,14 @@
 [DEFAULT]
 support-files =
   browser_base_force_refresh.html
   browser_cached_force_refresh.html
   download/window.html
   download/worker.js
+  file_multie10s_update.html
   force_refresh_browser_worker.js
+  server_multie10s_update.sjs
 
 [browser_force_refresh.js]
 [browser_download.js]
+[browser_multie10s_update.js]
+run-if=e10s
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/browser_multie10s_update.js
@@ -0,0 +1,80 @@
+"use strict";
+
+const { classes: Cc, interfaces: Ci, results: Cr } = Components;
+
+// Testing if 2 child processes are correctly managed when they both try to do
+// an SW update.
+
+const BASE_URI = "http://mochi.test:8888/browser/dom/workers/test/serviceworkers/";
+
+add_task(function* test_update() {
+  info("Setting the prefs to having multi-e10s enabled");
+  yield SpecialPowers.pushPrefEnv({"set": [
+    ["dom.ipc.processCount", 4],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]});
+
+  let url = BASE_URI + "file_multie10s_update.html";
+
+  info("Creating the first tab...");
+  let tab1 = gBrowser.addTab(url);
+  let browser1 = gBrowser.getBrowserForTab(tab1);
+  yield BrowserTestUtils.browserLoaded(browser1);
+
+  info("Creating the second tab...");
+  let tab2 = gBrowser.addTab(url);
+  let browser2 = gBrowser.getBrowserForTab(tab2);
+  yield BrowserTestUtils.browserLoaded(browser2);
+
+  let sw = BASE_URI + "server_multie10s_update.sjs";
+
+  info("Let's start the test...");
+  let status = yield ContentTask.spawn(browser1, sw, function(url) {
+    // Registration of the SW
+    return content.navigator.serviceWorker.register(url)
+
+    // Activation
+    .then(function(r) {
+      return new content.window.Promise(resolve => {
+        let worker = r.installing;
+        worker.addEventListener('statechange', () => {
+          if (worker.state === 'installed') {
+            resolve(true);
+          }
+        });
+      });
+    })
+
+    // Waiting for the result.
+    .then(() => {
+      return new content.window.Promise(resolve => {
+        let results = [];
+        let bc = new content.window.BroadcastChannel('result');
+        bc.onmessage = function(e) {
+          results.push(e.data);
+          if (results.length != 2) {
+            return;
+          }
+
+          resolve(results[0] + results[1]);
+        }
+
+        // Let's inform the tabs.
+        bc = new content.window.BroadcastChannel('start');
+        bc.postMessage('go');
+      });
+    });
+  });
+
+  if (status == 0) {
+    ok(false, "both succeeded. This is wrong.");
+  } else if (status == 1) {
+    ok(true, "one succeded, one failed. This is good.");
+  } else {
+    ok(false, "both failed. This is definitely wrong.");
+  }
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.removeTab(tab2);
+});
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/file_multie10s_update.html
@@ -0,0 +1,34 @@
+<html>
+<body>
+<script>
+
+var bc = new BroadcastChannel('start');
+bc.onmessage = function(e) {
+  // This message is not for us.
+  if (e.data != 'go') {
+    return;
+  }
+
+  // It can happen that we don't have the registrations yet. Let's try with a
+  // timeout.
+  function proceed() {
+    return navigator.serviceWorker.getRegistrations().then(regs => {
+      if (regs.length == 0) {
+        setTimeout(proceed, 200);
+        return;
+      }
+
+      bc = new BroadcastChannel('result');
+      regs[0].update().then(() => {
+        bc.postMessage(0);
+      }, () => {
+        bc.postMessage(1);
+      });
+    });
+  }
+
+  proceed();
+}
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/server_multie10s_update.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response)
+{
+  response.processAsync();
+  response.setHeader("Content-Type", "application/javascript", false);
+
+  timer = Components.classes["@mozilla.org/timer;1"].
+          createInstance(Components.interfaces.nsITimer);
+  timer.init(function() {
+    response.write("42");
+    response.finish();
+  }, 3000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1519,20 +1519,21 @@ HTMLEditRules::WillInsertBreak(Selection
     *aCancel = true;
     return NS_OK;
   }
 
   // Identify the block
   nsCOMPtr<Element> blockParent = htmlEditor->GetBlock(node);
   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
 
-  // If the active editing host is an inline element, or if the active editing
-  // host is the block parent itself, just append a br.
+  // When there is an active editing host (the <body> if it's in designMode)
+  // and a block which becomes the parent of line breaker is in it, do the
+  // standard thing.
   nsCOMPtr<Element> host = htmlEditor->GetActiveEditingHost();
-  if (!EditorUtils::IsDescendantOf(blockParent, host)) {
+  if (host && !EditorUtils::IsDescendantOf(blockParent, host)) {
     nsresult rv = StandardBreakImpl(node, offset, aSelection);
     NS_ENSURE_SUCCESS(rv, rv);
     *aHandled = true;
     return NS_OK;
   }
 
   // If block is empty, populate with br.  (For example, imagine a div that
   // contains the word "text".  The user selects "text" and types return.
@@ -6486,20 +6487,24 @@ HTMLEditRules::SplitParagraph(nsIDOMNode
                                             address_of(selNode), aOffset);
   // XXX When it fails, why do we need to return selection node?  (Why can the
   //     caller trust the result even when it returns error?)
   *aSelNode = GetAsDOMNode(selNode);
   NS_ENSURE_SUCCESS(rv, rv);
   // split the paragraph
   NS_ENSURE_STATE(mHTMLEditor);
   NS_ENSURE_STATE(selNode->IsContent());
-  mHTMLEditor->SplitNodeDeep(*para, *selNode->AsContent(), *aOffset,
-                             HTMLEditor::EmptyContainers::yes,
-                             getter_AddRefs(leftPara),
-                             getter_AddRefs(rightPara));
+  int32_t offset =
+    mHTMLEditor->SplitNodeDeep(*para, *selNode->AsContent(), *aOffset,
+                               HTMLEditor::EmptyContainers::yes,
+                               getter_AddRefs(leftPara),
+                               getter_AddRefs(rightPara));
+  if (NS_WARN_IF(offset == -1)) {
+    return NS_ERROR_FAILURE;
+  }
   // get rid of the break, if it is visible (otherwise it may be needed to prevent an empty p)
   NS_ENSURE_STATE(mHTMLEditor);
   if (mHTMLEditor->IsVisBreak(aBRNode)) {
     NS_ENSURE_STATE(mHTMLEditor);
     rv = mHTMLEditor->DeleteNode(aBRNode);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/crashtests/1350772.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+addEventListener("DOMContentLoaded", () => {
+  try {
+    document.designMode = 'on';
+    document.removeChild(document.documentElement);
+    document.appendChild(document.createElement("p"));
+    document.execCommand("insertParagraph", false, null);
+  } catch(e) {
+  }
+});
+</script>
+</head>
+</html>
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -68,8 +68,9 @@ load 1158452.html
 load 1158651.html
 load 1244894.xhtml
 load 1264921.html
 load 1272490.html
 load 1317704.html
 load 1317718.html
 load 1324505.html
 load 1348851.html
+load 1350772.html
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -20,16 +20,17 @@ namespace gfx {
 
 class gfxVarReceiver;
 
 // Generator for graphics vars.
 #define GFX_VARS_LIST(_)                                                \
   /* C++ Name,                  Data Type,        Default Value */      \
   _(BrowserTabsRemoteAutostart, bool,             false)                \
   _(ContentBackend,             BackendType,      BackendType::NONE)    \
+  _(SoftwareBackend,            BackendType,      BackendType::NONE)    \
   _(TileSize,                   IntSize,          IntSize(-1, -1))      \
   _(UseXRender,                 bool,             false)                \
   _(OffscreenFormat,            gfxImageFormat,   mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \
   _(RequiresAcceleratedGLContextForCompositorOGL, bool, false)          \
   _(CanUseHardwareVideoDecoding, bool,            false)                \
   _(PDMWMFDisableD3D11Dlls,     nsCString,        nsCString())          \
   _(PDMWMFDisableD3D9Dlls,      nsCString,        nsCString())          \
   _(DXInterop2Blocked,          bool,             false)                \
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -52,16 +52,17 @@ ClientPaintedLayer::PaintThebes(nsTArray
   }
 #endif
   PaintState state =
     mContentClient->BeginPaintBuffer(this, flags);
   mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
   if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
     ClientManager()->SetTransactionIncomplete();
+    mContentClient->EndPaint(nullptr);
     return;
   }
 
   // The area that became invalid and is visible needs to be repainted
   // (this could be the whole visible area if our buffer switched
   // from RGB to RGBA, because we might need to repaint with
   // subpixel AA)
   state.mRegionToInvalidate.And(state.mRegionToInvalidate,
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -250,17 +250,17 @@ ContentClientRemoteBuffer::EndPaint(nsTA
   for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
     if (mOldTextures[i]->IsLocked()) {
       mOldTextures[i]->Unlock();
     }
   }
   mOldTextures.Clear();
 
   if (mTextureClient && mTextureClient->IsLocked()) {
-    if (aReadbackUpdates->Length() > 0) {
+    if (aReadbackUpdates && aReadbackUpdates->Length() > 0) {
       RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
 
       mTextureClient->SetReadbackSink(readbackSink);
     }
 
     mTextureClient->Unlock();
     mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
   }
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -199,21 +199,20 @@ TextureImageTextureSourceOGL::EnsureBuff
   }
   mTexImage->Resize(aSize);
 }
 
 void
 TextureImageTextureSourceOGL::SetTextureSourceProvider(TextureSourceProvider* aProvider)
 {
   GLContext* newGL = aProvider ? aProvider->GetGLContext() : nullptr;
-  if (!mGL) {
-    mGL = newGL;
-  } else if (mGL != newGL) {
+  if (!newGL || mGL != newGL) {
     DeallocateDeviceData();
   }
+  mGL = newGL;
 }
 
 gfx::IntSize
 TextureImageTextureSourceOGL::GetSize() const
 {
   if (mTexImage) {
     if (mIterating) {
       return mTexImage->GetTileRect().Size();
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1689,16 +1689,17 @@ gfxPlatform::InitBackendPrefs(uint32_t a
     }
 
     uint32_t swBackendBits = BackendTypeBit(BackendType::SKIA) |
                              BackendTypeBit(BackendType::CAIRO);
     mSoftwareBackend = GetContentBackendPref(swBackendBits);
 
     if (XRE_IsParentProcess()) {
         gfxVars::SetContentBackend(mContentBackend);
+        gfxVars::SetSoftwareBackend(mSoftwareBackend);
     }
 }
 
 /* static */ BackendType
 gfxPlatform::GetCanvasBackendPref(uint32_t aBackendBitmask)
 {
     return GetBackendPref("gfx.canvas.azure.backends", aBackendBitmask);
 }
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -2114,17 +2114,20 @@ MessageChannel::ShouldContinueFromTimeou
         MonitorAutoUnlock unlock(*mMonitor);
         cont = mListener->ShouldContinueFromReplyTimeout();
         mListener->ArtificialSleep();
     }
 
     static enum { UNKNOWN, NOT_DEBUGGING, DEBUGGING } sDebuggingChildren = UNKNOWN;
 
     if (sDebuggingChildren == UNKNOWN) {
-        sDebuggingChildren = getenv("MOZ_DEBUG_CHILD_PROCESS") ? DEBUGGING : NOT_DEBUGGING;
+        sDebuggingChildren = getenv("MOZ_DEBUG_CHILD_PROCESS") ||
+                             getenv("MOZ_DEBUG_CHILD_PAUSE")
+                               ? DEBUGGING
+                               : NOT_DEBUGGING;
     }
     if (sDebuggingChildren == DEBUGGING) {
         return true;
     }
 
     return cont;
 }
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -124,17 +124,18 @@ class CPOWProxyHandler : public BaseProx
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<RegExpShared*> shared) const override;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
     virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                                         MutableHandleObject protop) const override;
 
@@ -849,23 +850,24 @@ WrapperOwner::getPrototypeIfOrdinary(JSC
         return false;
 
     objp.set(fromObjectOrNullVariant(cx, val));
 
     return true;
 }
 
 bool
-CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
+                                  MutableHandle<RegExpShared*> shared) const
 {
-    FORWARD(regexp_toShared, (cx, proxy, g));
+    FORWARD(regexp_toShared, (cx, proxy, shared));
 }
 
 bool
-WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
+WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle<RegExpShared*> shared)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
     nsString source;
     unsigned flags = 0;
     if (!SendRegExpToShared(objId, &status, &source, &flags))
         return ipcfail(cx);
@@ -875,17 +877,17 @@ WrapperOwner::regexp_toShared(JSContext*
     if (!ok(cx, status))
         return false;
 
     RootedObject regexp(cx);
     regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
     if (!regexp)
         return false;
 
-    return js::RegExpToSharedNonInline(cx, regexp, g);
+    return js::RegExpToSharedNonInline(cx, regexp, shared);
 }
 
 void
 CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
 {
     AuxCPOWData* aux = AuxCPOWDataOf(proxy);
 
     OwnerOf(proxy)->drop(proxy);
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -55,17 +55,18 @@ class WrapperOwner : public virtual Java
     bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
     bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClass* cls);
     bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
     const char* className(JSContext* cx, JS::HandleObject proxy);
     bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
     bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
                                 JS::MutableHandleObject protop);
 
-    bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
+    bool regexp_toShared(JSContext* cx, JS::HandleObject proxy,
+                         js::MutableHandle<js::RegExpShared*> shared);
 
     nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
 
     bool toString(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
     bool DOMQI(JSContext* cx, JS::HandleObject callee, JS::CallArgs& args);
 
     /*
      * Check that |obj| is a DOM wrapper whose prototype chain contains
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -573,40 +573,42 @@ struct UnusedGCThingSizes
     macro(Other, GCHeapUnused, script) \
     macro(Other, GCHeapUnused, lazyScript) \
     macro(Other, GCHeapUnused, shape) \
     macro(Other, GCHeapUnused, baseShape) \
     macro(Other, GCHeapUnused, objectGroup) \
     macro(Other, GCHeapUnused, string) \
     macro(Other, GCHeapUnused, symbol) \
     macro(Other, GCHeapUnused, jitcode) \
-    macro(Other, GCHeapUnused, scope)
+    macro(Other, GCHeapUnused, scope) \
+    macro(Other, GCHeapUnused, regExpShared)
 
     UnusedGCThingSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         dummy()
     {}
 
     UnusedGCThingSizes(UnusedGCThingSizes&& other)
       : FOR_EACH_SIZE(COPY_OTHER_SIZE)
         dummy()
     {}
 
     void addToKind(JS::TraceKind kind, intptr_t n) {
         switch (kind) {
-          case JS::TraceKind::Object:       object += n;      break;
-          case JS::TraceKind::String:       string += n;      break;
-          case JS::TraceKind::Symbol:       symbol += n;      break;
-          case JS::TraceKind::Script:       script += n;      break;
-          case JS::TraceKind::Shape:        shape += n;       break;
-          case JS::TraceKind::BaseShape:    baseShape += n;   break;
-          case JS::TraceKind::JitCode:      jitcode += n;     break;
-          case JS::TraceKind::LazyScript:   lazyScript += n;  break;
-          case JS::TraceKind::ObjectGroup:  objectGroup += n; break;
-          case JS::TraceKind::Scope:        scope += n;       break;
+          case JS::TraceKind::Object:       object += n;       break;
+          case JS::TraceKind::String:       string += n;       break;
+          case JS::TraceKind::Symbol:       symbol += n;       break;
+          case JS::TraceKind::Script:       script += n;       break;
+          case JS::TraceKind::Shape:        shape += n;        break;
+          case JS::TraceKind::BaseShape:    baseShape += n;    break;
+          case JS::TraceKind::JitCode:      jitcode += n;      break;
+          case JS::TraceKind::LazyScript:   lazyScript += n;   break;
+          case JS::TraceKind::ObjectGroup:  objectGroup += n;  break;
+          case JS::TraceKind::Scope:        scope += n;        break;
+          case JS::TraceKind::RegExpShared: regExpShared += n; break;
           default:
             MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
         }
     }
 
     void addSizes(const UnusedGCThingSizes& other) {
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
     }
@@ -638,16 +640,18 @@ struct ZoneStats
     macro(Other,   GCHeapAdmin, gcHeapArenaAdmin) \
     macro(Other,   GCHeapUsed,  lazyScriptsGCHeap) \
     macro(Other,   MallocHeap,  lazyScriptsMallocHeap) \
     macro(Other,   GCHeapUsed,  jitCodesGCHeap) \
     macro(Other,   GCHeapUsed,  objectGroupsGCHeap) \
     macro(Other,   MallocHeap,  objectGroupsMallocHeap) \
     macro(Other,   GCHeapUsed,  scopesGCHeap) \
     macro(Other,   MallocHeap,  scopesMallocHeap) \
+    macro(Other,   GCHeapUsed,  regExpSharedsGCHeap) \
+    macro(Other,   MallocHeap,  regExpSharedsMallocHeap) \
     macro(Other,   MallocHeap,  typePool) \
     macro(Other,   MallocHeap,  baselineStubsOptimized) \
     macro(Other,   MallocHeap,  uniqueIdMap) \
     macro(Other,   MallocHeap,  shapeTables)
 
     ZoneStats()
       : FOR_EACH_SIZE(ZERO_SIZE)
         unusedGCThings(),
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -27,17 +27,18 @@ using JS::MutableHandle;
 using JS::MutableHandleObject;
 using JS::MutableHandleValue;
 using JS::NativeImpl;
 using JS::ObjectOpResult;
 using JS::PrivateValue;
 using JS::PropertyDescriptor;
 using JS::Value;
 
-class RegExpGuard;
+class RegExpShared;
+
 class JS_FRIEND_API(Wrapper);
 
 /*
  * A proxy is a JSObject with highly customizable behavior. ES6 specifies a
  * single kind of proxy, but the customization mechanisms we use to implement
  * ES6 Proxy objects are also useful wherever an object with weird behavior is
  * wanted. Proxies are used to implement:
  *
@@ -325,17 +326,18 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
                                  ESClass* cls) const;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<js::RegExpShared*> shared) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const;
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject*, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -11,16 +11,17 @@
 
 #include "js/TypeDecls.h"
 
 // Forward declarations of all the types a TraceKind can denote.
 namespace js {
 class BaseShape;
 class LazyScript;
 class ObjectGroup;
+class RegExpShared;
 class Shape;
 class Scope;
 namespace jit {
 class JitCode;
 } // namespace jit
 } // namespace js
 
 namespace JS {
@@ -54,23 +55,25 @@ enum class TraceKind
 
     // The kind associated with a nullptr.
     Null = 0x06,
 
     // The following kinds do not have an exposed C++ idiom.
     BaseShape = 0x0F,
     JitCode = 0x1F,
     LazyScript = 0x2F,
-    Scope = 0x3F
+    Scope = 0x3F,
+    RegExpShared = 0x4F
 };
 const static uintptr_t OutOfLineTraceKindMask = 0x07;
 static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::Scope) & OutOfLineTraceKindMask, "mask bits are set");
+static_assert(uintptr_t(JS::TraceKind::RegExpShared) & OutOfLineTraceKindMask, "mask bits are set");
 
 // When this header is imported inside SpiderMonkey, the class definitions are
 // available and we can query those definitions to find the correct kind
 // directly from the class hierarchy.
 template <typename T>
 struct MapTypeToTraceKind {
     static const JS::TraceKind kind = T::TraceKind;
 };
@@ -83,17 +86,18 @@ struct MapTypeToTraceKind {
     D(JitCode,       js::jit::JitCode,  true) \
     D(LazyScript,    js::LazyScript,    true) \
     D(Scope,         js::Scope,         true) \
     D(Object,        JSObject,          true) \
     D(ObjectGroup,   js::ObjectGroup,   true) \
     D(Script,        JSScript,          true) \
     D(Shape,         js::Shape,         true) \
     D(String,        JSString,          false) \
-    D(Symbol,        JS::Symbol,        false)
+    D(Symbol,        JS::Symbol,        false) \
+    D(RegExpShared,  js::RegExpShared,  true)
 
 // Map from all public types to their trace kind.
 #define JS_EXPAND_DEF(name, type, _) \
     template <> struct MapTypeToTraceKind<type> { \
         static const JS::TraceKind kind = JS::TraceKind::name; \
     };
 JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
 #undef JS_EXPAND_DEF
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -159,16 +159,19 @@ class JS_PUBLIC_API(CallbackTracer) : pu
         onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
     }
     virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
         onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
     }
     virtual void onScopeEdge(js::Scope** scopep) {
         onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope));
     }
+    virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) {
+        onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared));
+    }
 
     // Override this method to receive notification when a node in the GC
     // heap graph is visited.
     virtual void onChild(const JS::GCCellPtr& thing) = 0;
 
     // Access to the tracing context:
     // When tracing with a JS::CallbackTracer, we invoke the callback with the
     // edge location and the type of target. This is useful for operating on
@@ -229,16 +232,17 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
     void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
     void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
     void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
     void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
     void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
     void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
     void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
+    void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
 
   protected:
     void setTraceWeakEdges(bool value) {
         traceWeakEdges_ = value;
     }
 
   private:
     friend class AutoTracingName;
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -116,46 +116,47 @@ CreateRegExpSearchResult(JSContext* cx, 
     return position | (lastIndex << 15);
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-14, except 12.a.i, 12.c.i.1.
  */
 static RegExpRunStatus
-ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
-                  size_t searchIndex, MatchPairs* matches, size_t* endIndex)
+ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, MutableHandleRegExpShared re,
+                  HandleLinearString input, size_t searchIndex, MatchPairs* matches,
+                  size_t* endIndex)
 {
-    RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
+    RegExpRunStatus status = RegExpShared::execute(cx, re, input, searchIndex, matches, endIndex);
 
     /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
                 return RegExpRunStatus_Error;
         } else {
-            res->updateLazily(cx, input, &re, searchIndex);
+            res->updateLazily(cx, input, re, searchIndex);
         }
     }
     return status;
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*> reobj,
                         HandleLinearString input, size_t* lastIndex, bool test,
                         MutableHandleValue rval)
 {
-    RegExpGuard shared(cx);
+    RootedRegExpShared shared(cx);
     if (!RegExpObject::getShared(cx, reobj, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &shared, input, *lastIndex,
                                                &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval.setNull();
         return true;
@@ -227,17 +228,17 @@ RegExpInitializeIgnoringLastIndex(JSCont
 
         /* Step 5. */
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
     if (sharedUse == UseRegExpShared) {
         /* Steps 7-8. */
-        RegExpGuard re(cx);
+        RootedRegExpShared re(cx);
         if (!cx->compartment()->regExps.get(cx, pattern, flags, &re))
             return false;
 
         /* Steps 9-12. */
         obj->initIgnoringLastIndex(pattern, flags);
 
         obj->setShared(*re);
     } else {
@@ -333,17 +334,17 @@ regexp_compile_impl(JSContext* cx, const
         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
         // don't reuse the RegExpShared below.
         RootedObject patternObj(cx, &patternValue.toObject());
 
         RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
             // Step 3b.
-            RegExpGuard g(cx);
+            RootedRegExpShared g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
 
             sourceAtom = g->getSource();
             flags = g->getFlags();
         }
 
         // Step 5, minus lastIndex zeroing.
@@ -428,17 +429,17 @@ js::regexp_construct(JSContext* cx, unsi
         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
         // don't reuse the RegExpShared below.
         RootedObject patternObj(cx, &patternValue.toObject());
 
         RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
             // Step 4.a.
-            RegExpGuard g(cx);
+            RootedRegExpShared g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
             sourceAtom = g->getSource();
 
             // Step 4.b.
             // Get original flags in all cases, to compare with passed flags.
             flags = g->getFlags();
         }
@@ -555,17 +556,17 @@ js::regexp_clone(JSContext* cx, unsigned
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject from(cx, &args[0].toObject());
 
     RootedAtom sourceAtom(cx);
     RegExpFlag flags;
     {
-        RegExpGuard g(cx);
+        RootedRegExpShared g(cx);
         if (!RegExpToShared(cx, from, &g))
             return false;
         sourceAtom = g->getSource();
         flags = g->getFlags();
     }
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
     if (!regexp)
@@ -912,17 +913,17 @@ ExecuteRegExp(JSContext* cx, HandleObjec
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
     /* Steps 1-2 performed by the caller. */
     Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
 
-    RegExpGuard re(cx);
+    RootedRegExpShared re(cx);
     if (!RegExpObject::getShared(cx, reobj, &re))
         return RegExpRunStatus_Error;
 
     RegExpStatics* res;
     if (staticsUpdate == UpdateRegExpStatics) {
         res = GlobalObject::getRegExpStatics(cx, cx->global());
         if (!res)
             return RegExpRunStatus_Error;
@@ -963,17 +964,17 @@ ExecuteRegExp(JSContext* cx, HandleObjec
          * However, the spec will change to match our implementation's
          * behavior. See https://github.com/tc39/ecma262/issues/128.
          */
         if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
             lastIndex--;
     }
 
     /* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, &re, input, lastIndex, matches, endIndex);
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
     /* Steps 12.a.i, 12.c.i.i, 15 are done by Self-hosted function. */
 
     return status;
 }
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -246,22 +246,16 @@ IsMarkedBlack(JSObject* obj);
 #endif
 
 MOZ_ALWAYS_INLINE void
 CheckEdgeIsNotBlackToGray(JSObject* src, const Value& dst)
 {
     MOZ_ASSERT_IF(IsMarkedBlack(src), JS::ValueIsNotGray(dst));
 }
 
-MOZ_ALWAYS_INLINE void
-CheckEdgeIsNotBlackToGray(JSObject* src, gc::Cell* dst)
-{
-    MOZ_ASSERT_IF(IsMarkedBlack(src), JS::CellIsNotGray(dst));
-}
-
 template <typename T>
 struct InternalBarrierMethods {};
 
 template <typename T>
 struct InternalBarrierMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -115,23 +115,28 @@ struct MovingTracer : JS::CallbackTracer
 
     void onObjectEdge(JSObject** objp) override;
     void onShapeEdge(Shape** shapep) override;
     void onStringEdge(JSString** stringp) override;
     void onScriptEdge(JSScript** scriptp) override;
     void onLazyScriptEdge(LazyScript** lazyp) override;
     void onBaseShapeEdge(BaseShape** basep) override;
     void onScopeEdge(Scope** basep) override;
+    void onRegExpSharedEdge(RegExpShared** sharedp) override;
     void onChild(const JS::GCCellPtr& thing) override {
         MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
     }
 
 #ifdef DEBUG
     TracerKind getTracerKind() const override { return TracerKind::Moving; }
 #endif
+
+  private:
+    template <typename T>
+    void updateEdge(T** thingp);
 };
 
 // Structure for counting how many times objects in a particular group have
 // been tenured during a minor collection.
 struct TenureCount
 {
     ObjectGroup* group;
     int count;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -764,16 +764,18 @@ class GCRuntime
     bool isIncrementalGCAllowed() const { return incrementalAllowed; }
     void disallowIncrementalGC() { incrementalAllowed = false; }
 
     bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
     bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
 
     bool isCompactingGCEnabled() const;
 
+    bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
+
     void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
     MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
     void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
 
     bool triggerGCForTooMuchMalloc() { return triggerGC(JS::gcreason::TOO_MUCH_MALLOC); }
     int32_t getMallocBytes() const { return mallocCounter.bytes(); }
     size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); }
     bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); }
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -104,55 +104,57 @@ enum class AllocKind {
     FAT_INLINE_STRING,
     STRING,
     EXTERNAL_STRING,
     FAT_INLINE_ATOM,
     ATOM,
     SYMBOL,
     JITCODE,
     SCOPE,
+    REGEXP_SHARED,
     LIMIT,
     LAST = LIMIT - 1
 };
 
 // Macro to enumerate the different allocation kinds supplying information about
 // the trace kind, C++ type and allocation size.
 #define FOR_EACH_OBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind    TypeName           SizedType */ \
-    D(FUNCTION,            Object,      JSObject,          JSFunction) \
-    D(FUNCTION_EXTENDED,   Object,      JSObject,          FunctionExtended) \
-    D(OBJECT0,             Object,      JSObject,          JSObject_Slots0) \
-    D(OBJECT0_BACKGROUND,  Object,      JSObject,          JSObject_Slots0) \
-    D(OBJECT2,             Object,      JSObject,          JSObject_Slots2) \
-    D(OBJECT2_BACKGROUND,  Object,      JSObject,          JSObject_Slots2) \
-    D(OBJECT4,             Object,      JSObject,          JSObject_Slots4) \
-    D(OBJECT4_BACKGROUND,  Object,      JSObject,          JSObject_Slots4) \
-    D(OBJECT8,             Object,      JSObject,          JSObject_Slots8) \
-    D(OBJECT8_BACKGROUND,  Object,      JSObject,          JSObject_Slots8) \
-    D(OBJECT12,            Object,      JSObject,          JSObject_Slots12) \
-    D(OBJECT12_BACKGROUND, Object,      JSObject,          JSObject_Slots12) \
-    D(OBJECT16,            Object,      JSObject,          JSObject_Slots16) \
-    D(OBJECT16_BACKGROUND, Object,      JSObject,          JSObject_Slots16)
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(FUNCTION,            Object,        JSObject,          JSFunction) \
+    D(FUNCTION_EXTENDED,   Object,        JSObject,          FunctionExtended) \
+    D(OBJECT0,             Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT0_BACKGROUND,  Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT2,             Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT2_BACKGROUND,  Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT4,             Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT4_BACKGROUND,  Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT8,             Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT8_BACKGROUND,  Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT12,            Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT12_BACKGROUND, Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT16,            Object,        JSObject,          JSObject_Slots16) \
+    D(OBJECT16_BACKGROUND, Object,        JSObject,          JSObject_Slots16)
 
 #define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind    TypeName           SizedType */ \
-    D(SCRIPT,              Script,      JSScript,          JSScript) \
-    D(LAZY_SCRIPT,         LazyScript,  js::LazyScript,    js::LazyScript) \
-    D(SHAPE,               Shape,       js::Shape,         js::Shape) \
-    D(ACCESSOR_SHAPE,      Shape,       js::AccessorShape, js::AccessorShape) \
-    D(BASE_SHAPE,          BaseShape,   js::BaseShape,     js::BaseShape) \
-    D(OBJECT_GROUP,        ObjectGroup, js::ObjectGroup,   js::ObjectGroup) \
-    D(FAT_INLINE_STRING,   String,      JSFatInlineString, JSFatInlineString) \
-    D(STRING,              String,      JSString,          JSString) \
-    D(EXTERNAL_STRING,     String,      JSExternalString,  JSExternalString) \
-    D(FAT_INLINE_ATOM,     String,      js::FatInlineAtom, js::FatInlineAtom) \
-    D(ATOM,                String,      js::NormalAtom,    js::NormalAtom) \
-    D(SYMBOL,              Symbol,      JS::Symbol,        JS::Symbol) \
-    D(JITCODE,             JitCode,     js::jit::JitCode,  js::jit::JitCode) \
-    D(SCOPE,               Scope,       js::Scope,         js::Scope)
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(SCRIPT,              Script,        JSScript,          JSScript) \
+    D(LAZY_SCRIPT,         LazyScript,    js::LazyScript,    js::LazyScript) \
+    D(SHAPE,               Shape,         js::Shape,         js::Shape) \
+    D(ACCESSOR_SHAPE,      Shape,         js::AccessorShape, js::AccessorShape) \
+    D(BASE_SHAPE,          BaseShape,     js::BaseShape,     js::BaseShape) \
+    D(OBJECT_GROUP,        ObjectGroup,   js::ObjectGroup,   js::ObjectGroup) \
+    D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString) \
+    D(STRING,              String,        JSString,          JSString) \
+    D(EXTERNAL_STRING,     String,        JSExternalString,  JSExternalString) \
+    D(FAT_INLINE_ATOM,     String,        js::FatInlineAtom, js::FatInlineAtom) \
+    D(ATOM,                String,        js::NormalAtom,    js::NormalAtom) \
+    D(SYMBOL,              Symbol,        JS::Symbol,        JS::Symbol) \
+    D(JITCODE,             JitCode,       js::jit::JitCode,  js::jit::JitCode) \
+    D(SCOPE,               Scope,         js::Scope,         js::Scope) \
+    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
 
 #define FOR_EACH_ALLOCKIND(D) \
     FOR_EACH_OBJECT_ALLOCKIND(D) \
     FOR_EACH_NONOBJECT_ALLOCKIND(D)
 
 static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
                                           "please audit them carefully!");
 static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -438,16 +438,24 @@ template <typename T>
 void
 js::TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name)
 {
     if (InternalBarrierMethods<T>::isMarkable(thingp->get()))
         DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
 }
 
 template <typename T>
+void
+js::TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name)
+{
+    if (InternalBarrierMethods<T>::isMarkable(thingp->unbarrieredGet()))
+        DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
+}
+
+template <typename T>
 JS_PUBLIC_API(void)
 JS::TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (InternalBarrierMethods<T>::isMarkable(*thingp->unsafeGet()))
         DispatchToTracer(trc, ConvertToBase(thingp->unsafeGet()), name);
 }
 
@@ -554,16 +562,17 @@ js::TraceRootRange(JSTracer* trc, size_t
     }
 }
 
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
     template void js::TraceEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
     template void js::TraceEdge<type>(JSTracer*, ReadBarriered<type>*, const char*); \
     template void js::TraceNullableEdge<type>(JSTracer*, WriteBarrieredBase<type>*, const char*); \
+    template void js::TraceNullableEdge<type>(JSTracer*, ReadBarriered<type>*, const char*); \
     template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
     template void js::TraceWeakEdge<type>(JSTracer*, WeakRef<type>*, const char*); \
     template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
     template void js::TraceRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
     template void js::TraceNullableRoot<type>(JSTracer*, type*, const char*); \
     template void js::TraceNullableRoot<type>(JSTracer*, ReadBarriered<type>*, const char*); \
     template void js::TraceRange<type>(JSTracer*, size_t, WriteBarrieredBase<type>*, const char*); \
     template void js::TraceRootRange<type>(JSTracer*, size_t, type*, const char*);
@@ -870,16 +879,17 @@ js::GCMarker::markAndTraceChildren(T* th
     if (ThingIsPermanentAtomOrWellKnownSymbol(thing))
         return;
     if (mark(thing))
         thing->traceChildren(this);
 }
 namespace js {
 template <> void GCMarker::traverse(BaseShape* thing) { markAndTraceChildren(thing); }
 template <> void GCMarker::traverse(JS::Symbol* thing) { markAndTraceChildren(thing); }
+template <> void GCMarker::traverse(RegExpShared* thing) { markAndTraceChildren(thing); }
 } // namespace js
 
 // Strings, LazyScripts, Shapes, and Scopes are extremely common, but have
 // simple patterns of recursion. We traverse trees of these edges immediately,
 // with aggressive, manual inlining, implemented by eagerlyTraceChildren.
 template <typename T>
 void
 js::GCMarker::markAndScan(T* thing)
@@ -2570,44 +2580,33 @@ GCMarker::sizeOfExcludingThis(mozilla::M
 {
     size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
     for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
         size += zone->gcGrayRoots().sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
 
 #ifdef DEBUG
-
-static bool
-IsCrossCompartmentEdge(JSObject* source, const Cell* target)
-{
-    if (!source->is<ProxyObject>())
-        return false;
-
-    const Value& priv = source->as<ProxyObject>().private_();
-    return priv.isGCThing() && priv.toGCThing() == target;
-}
-
 Zone*
 GCMarker::stackContainsCrossZonePointerTo(const Cell* target) const
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
 
     Zone* targetZone = target->asTenured().zone();
 
     for (MarkStackIter iter(stack); !iter.done(); iter.next()) {
         if (iter.peekTag() != MarkStack::ObjectTag)
             continue;
 
         auto source = iter.peekPtr().as<JSObject>();
         Zone* sourceZone = source->zone();
         if (sourceZone == targetZone)
             continue;
 
-        if (IsCrossCompartmentEdge(source, target) ||
+        if ((source->is<ProxyObject>() && source->as<ProxyObject>().target() == target) ||
             Debugger::isDebuggerCrossCompartmentEdge(source, target))
         {
             return sourceZone;
         }
     }
 
     return nullptr;
 }
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -81,16 +81,17 @@ class JitCode;
     D(js::ModuleEnvironmentObject*) \
     D(js::ModuleNamespaceObject*) \
     D(js::ModuleObject*) \
     D(js::NativeObject*) \
     D(js::ObjectGroup*) \
     D(js::PlainObject*) \
     D(js::PropertyName*) \
     D(js::RegExpObject*) \
+    D(js::RegExpShared*) \
     D(js::SavedFrame*) \
     D(js::Scope*) \
     D(js::ScriptSourceObject*) \
     D(js::Shape*) \
     D(js::SharedArrayBufferObject*) \
     D(js::StructTypeDescr*) \
     D(js::UnownedBaseShape*) \
     D(js::WasmFunctionScope*) \
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -179,16 +179,17 @@ static const PhaseInfo phases[] = {
             { PHASE_SWEEP_MISC, "Sweep Miscellaneous", PHASE_SWEEP_COMPARTMENTS, 29 },
             { PHASE_SWEEP_TYPES, "Sweep type information", PHASE_SWEEP_COMPARTMENTS, 30 },
                 { PHASE_SWEEP_TYPES_BEGIN, "Sweep type tables and compilations", PHASE_SWEEP_TYPES, 31 },
                 { PHASE_SWEEP_TYPES_END, "Free type arena", PHASE_SWEEP_TYPES, 32 },
         { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP, 33 },
         { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP, 34 },
         { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP, 35 },
         { PHASE_SWEEP_SCOPE, "Sweep Scope", PHASE_SWEEP, 59 },
+        { PHASE_SWEEP_REGEXP_SHARED, "Sweep RegExpShared", PHASE_SWEEP, 61 },
         { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP, 36 },
         { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP, 37 },
         { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP, 38 },
         { PHASE_DESTROY, "Deallocate", PHASE_SWEEP, 39 },
     { PHASE_COMPACT, "Compact", PHASE_NO_PARENT, 40 },
         { PHASE_COMPACT_MOVE, "Compact Move", PHASE_COMPACT, 41 },
         { PHASE_COMPACT_UPDATE, "Compact Update", PHASE_COMPACT, 42 },
             /* PHASE_MARK_ROOTS */
@@ -206,19 +207,19 @@ static const PhaseInfo phases[] = {
         { PHASE_BUFFER_GRAY_ROOTS, "Buffer Gray Roots", PHASE_MARK_ROOTS, 49 },
         { PHASE_MARK_CCWS, "Mark Cross Compartment Wrappers", PHASE_MARK_ROOTS, 50 },
         { PHASE_MARK_STACK, "Mark C and JS stacks", PHASE_MARK_ROOTS, 51 },
         { PHASE_MARK_RUNTIME_DATA, "Mark Runtime-wide Data", PHASE_MARK_ROOTS, 52 },
         { PHASE_MARK_EMBEDDING, "Mark Embedding", PHASE_MARK_ROOTS, 53 },
         { PHASE_MARK_COMPARTMENTS, "Mark Compartments", PHASE_MARK_ROOTS, 54 },
     { PHASE_PURGE_SHAPE_TABLES, "Purge ShapeTables", PHASE_NO_PARENT, 60 },
 
-    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 60 }
+    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT, 61 }
 
-    // Current number of telemetryBuckets is 60. If you insert new phases
+    // Current number of telemetryBuckets is 61. If you insert new phases
     // somewhere, start at that number and count up. Do not change any existing
     // numbers.
 };
 
 static mozilla::EnumeratedArray<Phase, PHASE_LIMIT, ExtraPhaseInfo> phaseExtra;
 
 // Mapping from all nodes with a multi-parented child to a Vector of all
 // multi-parented children and their descendants. (Single-parented children will
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -64,16 +64,17 @@ enum Phase : uint8_t {
     PHASE_SWEEP_MISC,
     PHASE_SWEEP_TYPES,
     PHASE_SWEEP_TYPES_BEGIN,
     PHASE_SWEEP_TYPES_END,
     PHASE_SWEEP_OBJECT,
     PHASE_SWEEP_STRING,
     PHASE_SWEEP_SCRIPT,
     PHASE_SWEEP_SCOPE,
+    PHASE_SWEEP_REGEXP_SHARED,
     PHASE_SWEEP_SHAPE,
     PHASE_SWEEP_JITCODE,
     PHASE_FINALIZE_END,
     PHASE_DESTROY,
     PHASE_COMPACT,
     PHASE_COMPACT_MOVE,
     PHASE_COMPACT_UPDATE,
     PHASE_COMPACT_UPDATE_CELLS,
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -60,16 +60,20 @@ template <typename T>
 void
 TraceEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
 
 // Trace through an edge in the live object graph on behalf of tracing.
 template <typename T>
 void
 TraceNullableEdge(JSTracer* trc, WriteBarrieredBase<T>* thingp, const char* name);
 
+template <typename T>
+void
+TraceNullableEdge(JSTracer* trc, ReadBarriered<T>* thingp, const char* name);
+
 // Trace through a "root" edge. These edges are the initial edges in the object
 // graph traversal. Root edges are asserted to only be traversed in the initial
 // phase of a GC.
 template <typename T>
 void
 TraceRoot(JSTracer* trc, T* thingp, const char* name);
 
 template <typename T>
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1709,17 +1709,17 @@ IsNativeRegExpEnabled(JSContext* cx)
 #ifdef JS_CODEGEN_NONE
     return false;
 #else
     return cx->options().nativeRegExp();
 #endif
 }
 
 RegExpCode
-irregexp::CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
+irregexp::CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
                          HandleLinearString sample, bool is_global, bool ignore_case,
                          bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                          bool unicode)
 {
     if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
         JS_ReportErrorASCII(cx, "regexp too big");
         return RegExpCode();
     }
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -81,17 +81,17 @@ struct RegExpCode
     }
 
     void destroy() {
         js_free(byteCode);
     }
 };
 
 RegExpCode
-CompilePattern(JSContext* cx, RegExpShared* shared, RegExpCompileData* data,
+CompilePattern(JSContext* cx, HandleRegExpShared shared, RegExpCompileData* data,
                HandleLinearString sample,  bool is_global, bool ignore_case,
                bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                bool unicode);
 
 // Note: this may return RegExpRunStatus_Error if an interrupt was requested
 // while the code was executing.
 template <typename CharT>
 RegExpRunStatus
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/wasm-11.js
@@ -0,0 +1,34 @@
+// Test single-stepping where the TLS register can be evicted by a non-trivial
+// function body.
+
+if (!wasmIsSupported())
+  quit();
+
+var g = newGlobal();
+g.parent = this;
+g.eval(`
+    var dbg = new Debugger(parent);
+`);
+
+var i = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(`
+    (module
+        (func (export "f2")
+            i64.const 0
+            i64.const 0
+            i32.const 0
+            select
+            drop
+        )
+    )
+`)));
+
+g.eval(`
+    var calledOnStep = 0;
+    dbg.onEnterFrame = frame => {
+        if (frame.type === "wasmcall")
+            frame.onStep = () => { calledOnStep++ }
+    };
+`);
+
+i.exports.f2();
+assertEq(g.calledOnStep >= 5, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1342261.js
@@ -0,0 +1,7 @@
+// With --baseline-eager, create a cross-compartment wrapper IC referring to a
+// target in an otherwise-doomed compartment.
+
+fullcompartmentchecks(true);
+newGlobal({
+    sameZoneAs: []
+}).z;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/latin1/rope-stringchar.js
@@ -0,0 +1,16 @@
+function f(a, b) {
+    assertEq(isLatin1(a), true);
+    assertEq(isLatin1(b), false);
+    var c = a + b;
+    assertEq(isLatin1(c), false);
+    assertEq(c[4], 'b');
+    assertEq(c.charCodeAt(4), 98);
+}
+
+function test() {
+    for (var i = 0; i < 15; i++) {
+        f("aaaab\x00\x00\x00\x00\x00\x00", "\u{2603}");
+    }
+}
+
+test();
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -261,17 +261,17 @@ BaselineCacheIRCompiler::emitGuardProto(
     masm.branchPtr(Assembler::NotEqual, addr, scratch, failure->label());
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitGuardCompartment()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
-    reader.stubOffset(); // Read global.
+    reader.stubOffset(); // Read global wrapper.
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
     Address addr(stubAddress(reader.stubOffset()));
     masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -842,22 +842,16 @@ DoGetElemFallback(JSContext* cx, Baselin
     if (!stub->addMonitorStubForValue(cx, &info, res))
     {
         return false;
     }
 
     if (attached)
         return true;
 
-    // GetElem operations on non-native objects cannot be cached by either
-    // Baseline or Ion. Indicate this in the cache so that Ion does not
-    // generate a cache for this op.
-    if (lhs.isObject() && !lhs.toObject().isNative())
-        stub->noteNonNativeAccess();
-
     // GetElem operations which could access negative indexes generally can't
     // be optimized without the potential for bailouts, as we can't statically
     // determine that an object has no properties on such indexes.
     if (rhs.isNumber() && rhs.toNumber() < 0)
         stub->noteNegativeIndex();
 
     if (!attached && !isTemporarilyUnoptimizable)
         stub->noteUnoptimizableAccess();
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -366,28 +366,20 @@ class ICToNumber_Fallback : public ICFal
 class ICGetElem_Fallback : public ICMonitoredFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICGetElem_Fallback(JitCode* stubCode)
       : ICMonitoredFallbackStub(ICStub::GetElem_Fallback, stubCode)
     { }
 
-    static const uint16_t EXTRA_NON_NATIVE = 0x1;
-    static const uint16_t EXTRA_NEGATIVE_INDEX = 0x2;
-    static const uint16_t EXTRA_UNOPTIMIZABLE_ACCESS = 0x4;
+    static const uint16_t EXTRA_NEGATIVE_INDEX = 0x1;
+    static const uint16_t EXTRA_UNOPTIMIZABLE_ACCESS = 0x2;
 
   public:
-    void noteNonNativeAccess() {
-        extra_ |= EXTRA_NON_NATIVE;
-    }
-    bool hasNonNativeAccess() const {
-        return extra_ & EXTRA_NON_NATIVE;
-    }
-
     void noteNegativeIndex() {
         extra_ |= EXTRA_NEGATIVE_INDEX;
     }
     bool hasNegativeIndex() const {
         return extra_ & EXTRA_NEGATIVE_INDEX;
     }
     void noteUnoptimizableAccess() {
         extra_ |= EXTRA_UNOPTIMIZABLE_ACCESS;
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -473,30 +473,16 @@ BaselineInspector::expectedBinaryArithSp
         if (TryToSpecializeBinaryArithOp(stubs, 2, &result))
             return result;
     }
 
     return MIRType::None;
 }
 
 bool
-BaselineInspector::hasSeenNonNativeGetElement(jsbytecode* pc)
-{
-    if (!hasBaselineScript())
-        return false;
-
-    const ICEntry& entry = icEntryFromPC(pc);
-    ICStub* stub = entry.fallbackStub();
-
-    if (stub->isGetElem_Fallback())
-        return stub->toGetElem_Fallback()->hasNonNativeAccess();
-    return false;
-}
-
-bool
 BaselineInspector::hasSeenNegativeIndexGetElement(jsbytecode* pc)
 {
     if (!hasBaselineScript())
         return false;
 
     const ICEntry& entry = icEntryFromPC(pc);
     ICStub* stub = entry.fallbackStub();
 
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -100,17 +100,16 @@ class BaselineInspector
         return makeICInspector<SetElemICInspector>(pc, ICStub::SetElem_Fallback);
     }
 
     MIRType expectedResultType(jsbytecode* pc);
     MCompare::CompareType expectedCompareType(jsbytecode* pc);
     MIRType expectedBinaryArithSpecialization(jsbytecode* pc);
     MIRType expectedPropertyAccessInputType(jsbytecode* pc);
 
-    bool hasSeenNonNativeGetElement(jsbytecode* pc);
     bool hasSeenNegativeIndexGetElement(jsbytecode* pc);
     bool hasSeenAccessedGetter(jsbytecode* pc);
     bool hasSeenDoubleResult(jsbytecode* pc);
     bool hasSeenNonStringIterMore(jsbytecode* pc);
 
     MOZ_MUST_USE bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut,
                                                    JSString** sepOut, JSObject** objOut);
     JSObject* getTemplateObject(jsbytecode* pc);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -682,16 +682,20 @@ GetPropIRGenerator::tryAttachCrossCompar
 
     RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
     MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
 
     // If we allowed different zones we would have to wrap strings.
     if (unwrapped->compartment()->zone() != cx_->compartment()->zone())
         return false;
 
+    RootedObject wrappedGlobal(cx_, &obj->global());
+    if (!cx_->compartment()->wrap(cx_, &wrappedGlobal))
+        return false;
+
     AutoCompartment ac(cx_, unwrapped);
 
     // The first CCW for iframes is almost always wrapping another WindowProxy
     // so we optimize for that case as well.
     bool isWindowProxy = IsWindowProxy(unwrapped);
     if (isWindowProxy) {
         MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->compartment()->maybeGlobal());
         unwrapped = cx_->global();
@@ -720,17 +724,17 @@ GetPropIRGenerator::tryAttachCrossCompar
     maybeEmitIdGuard(id);
     writer.guardIsProxy(objId);
     writer.guardIsCrossCompartmentWrapper(objId);
 
     // Load the object wrapped by the CCW
     ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
 
     // If the compartment of the wrapped object is different we should fail.
-    writer.guardCompartment(wrapperTargetId, unwrapped->compartment());
+    writer.guardCompartment(wrapperTargetId, wrappedGlobal, unwrapped->compartment());
 
     ObjOperandId unwrappedId = wrapperTargetId;
     if (isWindowProxy) {
         // For the WindowProxy case also unwrap the inner window.
         // We avoid loadObject, because storing cross compartment objects in
         // stubs / JIT code is tricky.
         writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
         unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
@@ -1280,20 +1284,36 @@ GetPropIRGenerator::tryAttachStringLengt
 bool
 GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
 {
     MOZ_ASSERT(idVal_.isInt32());
 
     if (!val_.isString())
         return false;
 
+    int32_t index = idVal_.toInt32();
+    if (index < 0)
+        return false;
+
     JSString* str = val_.toString();
-    int32_t index = idVal_.toInt32();
-    if (size_t(index) >= str->length() ||
-        !str->isLinear() ||
+    if (size_t(index) >= str->length())
+        return false;
+
+    // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases.
+    if (str->isRope()) {
+        JSRope* rope = &str->asRope();
+
+        // Make sure the left side contains the index.
+        if (size_t(index) >= rope->leftChild()->length())
+            return false;
+
+        str = rope->leftChild();
+    }
+
+    if (!str->isLinear() ||
         str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
     {
         return false;
     }
 
     StringOperandId strId = writer.guardIsString(valId);
     Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
     writer.loadStringCharResult(strId, int32IndexId);
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -514,23 +514,22 @@ class MOZ_RAII CacheIRWriter : public JS
     void guardSpecificSymbol(SymbolOperandId sym, JS::Symbol* expected) {
         writeOpWithOperandId(CacheOp::GuardSpecificSymbol, sym);
         addStubField(uintptr_t(expected), StubField::Type::Symbol);
     }
     void guardMagicValue(ValOperandId val, JSWhyMagic magic) {
         writeOpWithOperandId(CacheOp::GuardMagicValue, val);
         buffer_.writeByte(uint32_t(magic));
     }
-    void guardCompartment(ObjOperandId obj, JSCompartment* compartment) {
+    void guardCompartment(ObjOperandId obj, JSObject* global, JSCompartment* compartment) {
         writeOpWithOperandId(CacheOp::GuardCompartment, obj);
         // Add a reference to the compartment's global to keep it alive.
-        addStubField(uintptr_t(compartment->maybeGlobal()), StubField::Type::JSObject);
+        addStubField(uintptr_t(global), StubField::Type::JSObject);
         // Use RawWord, because compartments never move and it can't be GCed.
         addStubField(uintptr_t(compartment), StubField::Type::RawWord);
-
     }
     void guardNoDetachedTypedObjects() {
         writeOp(CacheOp::GuardNoDetachedTypedObjects);
     }
     void guardFrameHasNoArgumentsObject() {
         writeOp(CacheOp::GuardFrameHasNoArgumentsObject);
     }
 
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1756,22 +1756,20 @@ CacheIRCompiler::emitLoadStringCharResul
     Register index = allocator.useRegister(masm, reader.int32OperandId());
     AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output);
     AutoScratchRegister scratch2(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
-    masm.branchIfRope(str, failure->label());
-
     // Bounds check, load string char.
     masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()),
                   index, failure->label());
-    masm.loadStringChar(str, index, scratch1);
+    masm.loadStringChar(str, index, scratch1, failure->label());
 
     // Load StaticString for this char.
     masm.branch32(Assembler::AboveOrEqual, scratch1, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
                   failure->label());
     masm.movePtr(ImmPtr(&cx_->staticStrings().unitStaticTable), scratch2);
     masm.loadPtr(BaseIndex(scratch2, scratch1, ScalePointer), scratch2);
 
     EmitStoreResult(masm, scratch2, JSVAL_TYPE_STRING, output);
--- a/js/src/jit/CacheIRSpewer.cpp
+++ b/js/src/jit/CacheIRSpewer.cpp
@@ -16,18 +16,21 @@
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
 
 #include <stdarg.h>
 
 #include "jsfun.h"
+#include "jsobj.h"
 #include "jsscript.h"
+
 #include "jscompartmentinlines.h"
+#include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 CacheIRSpewer cacheIRspewer;
 
 CacheIRSpewer&
 jit::GetCacheIRSpewerSingleton()
@@ -144,16 +147,19 @@ CacheIRSpewer::valueProperty(LockGuard<M
         j.doubleProperty("value", v.toDouble());
     } else if (v.isString() || v.isSymbol()) {
         JSString* str = v.isString() ? v.toString() : v.toSymbol()->description();
         if (str && str->isLinear()) {
             j.beginStringProperty("value");
             QuoteString(output, &str->asLinear());
             j.endStringProperty();
         }
+    } else if (v.isObject()) {
+        j.stringProperty("value", "%p (shape: %p)", &v.toObject(),
+                         v.toObject().maybeShape());
     }
 
     j.endObject();
 }
 
 void
 CacheIRSpewer::attached(LockGuard<Mutex>&, const char* name)
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7957,20 +7957,17 @@ static const VMFunction CharCodeAtInfo =
 void
 CodeGenerator::visitCharCodeAt(LCharCodeAt* lir)
 {
     Register str = ToRegister(lir->str());
     Register index = ToRegister(lir->index());
     Register output = ToRegister(lir->output());
 
     OutOfLineCode* ool = oolCallVM(CharCodeAtInfo, lir, ArgList(str, index), StoreRegisterTo(output));
-
-    masm.branchIfRope(str, ool->entry());
-    masm.loadStringChar(str, index, output);
-
+    masm.loadStringChar(str, index, output, ool->entry());
     masm.bind(ool->rejoin());
 }
 
 typedef JSFlatString* (*StringFromCharCodeFn)(JSContext*, int32_t);
 static const VMFunction StringFromCharCodeInfo =
     FunctionInfo<StringFromCharCodeFn>(jit::StringFromCharCode, "StringFromCharCode");
 
 void
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7591,38 +7591,17 @@ IonBuilder::jsop_getelem()
 
         trackOptimizationAttempt(TrackedStrategy::GetElem_String);
         MOZ_TRY(getElemTryString(&emitted, obj, index));
         if (emitted)
             return Ok();
     }
 
     trackOptimizationAttempt(TrackedStrategy::GetElem_InlineCache);
-    MOZ_TRY(getElemTryCache(&emitted, obj, index));
-    if (emitted)
-        return Ok();
-
-    // Emit call.
-    MInstruction* ins = MCallGetElement::New(alloc(), obj, index);
-
-    current->add(ins);
-    current->push(ins);
-
-    MOZ_TRY(resumeAfter(ins));
-
-    if (*pc == JSOP_CALLELEM && IsNullOrUndefined(obj->type())) {
-        // Due to inlining, it's possible the observed TypeSet is non-empty,
-        // even though we know |obj| is null/undefined and the MCallGetElement
-        // will throw. Don't push a TypeBarrier in this case, to avoid
-        // inlining the following (unreachable) JSOP_CALL.
-        return Ok();
-    }
-
-    TemporaryTypeSet* types = bytecodeTypes(pc);
-    return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
+    return getElemAddCache(obj, index);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index)
 {
     MOZ_ASSERT(*emitted == false);
 
     // The next several failures are all due to types not predicting that we
@@ -8261,59 +8240,38 @@ IonBuilder::getElemTryArgumentsInlined(b
         return Ok();
     }
 
     // inlined not constant not supported, yet.
     return abort(AbortReason::Disable, "NYI inlined not constant get argument element");
 }
 
 AbortReasonOr<Ok>
-IonBuilder::getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index)
-{
-    MOZ_ASSERT(*emitted == false);
-
-    // Make sure we have at least an object.
-    if (!obj->mightBeType(MIRType::Object)) {
-        trackOptimizationOutcome(TrackedOutcome::NotObject);
-        return Ok();
-    }
-
-    // Don't cache for strings.
-    if (obj->mightBeType(MIRType::String)) {
-        trackOptimizationOutcome(TrackedOutcome::GetElemStringNotCached);
-        return Ok();
-    }
-
-    // Index should be integer, string, or symbol
-    if (!index->mightBeType(MIRType::Int32) &&
-        !index->mightBeType(MIRType::String) &&
-        !index->mightBeType(MIRType::Symbol))
-    {
-        trackOptimizationOutcome(TrackedOutcome::IndexType);
-        return Ok();
-    }
-
-    // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
-    // of this getelem.
-    bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
-    if (index->mightBeType(MIRType::Int32) && nonNativeGetElement) {
-        trackOptimizationOutcome(TrackedOutcome::NonNativeReceiver);
-        return Ok();
-    }
-
-    // Emit GetElementCache.
+IonBuilder::getElemAddCache(MDefinition* obj, MDefinition* index)
+{
+    // Emit GetPropertyCache.
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
-                                                       nullptr, types);
-
-    // Always add a barrier if the index might be a string or symbol, so that
-    // the cache can attach stubs for particular properties.
-    if (index->mightBeType(MIRType::String) || index->mightBeType(MIRType::Symbol))
+
+    BarrierKind barrier;
+    if (obj->type() == MIRType::Object) {
+        // Always add a barrier if the index is not an int32 value, so we can
+        // attach stubs for particular properties.
+        if (index->type() == MIRType::Int32) {
+            barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
+                                                   nullptr, types);
+        } else {
+            barrier = BarrierKind::TypeSet;
+        }
+    } else {
+        // PropertyReadNeedsTypeBarrier only accounts for object types, so for
+        // now always insert a barrier if the input is not known to be an
+        // object.
         barrier = BarrierKind::TypeSet;
+    }
 
     // Ensure we insert a type barrier for reads from typed objects, as type
     // information does not account for the initial undefined/null types.
     if (barrier != BarrierKind::TypeSet && !types->unknown()) {
         MOZ_ASSERT(obj->resultTypeSet());
         switch (obj->resultTypeSet()->forAllClasses(constraints(), IsTypedObjectClass)) {
           case TemporaryTypeSet::ForAllResult::ALL_FALSE:
           case TemporaryTypeSet::ForAllResult::EMPTY:
@@ -8339,17 +8297,16 @@ IonBuilder::getElemTryCache(bool* emitte
 
         if (knownType != MIRType::Value && knownType != MIRType::Double)
             ins->setResultType(knownType);
     }
 
     MOZ_TRY(pushTypeBarrier(ins, types, barrier));
 
     trackOptimizationSuccess();
-    *emitted = true;
     return Ok();
 }
 
 TemporaryTypeSet*
 IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id)
 {
     if (objTypes->unknownObject() || objTypes->getObjectCount() == 0)
         return nullptr;
@@ -9039,34 +8996,24 @@ IonBuilder::initOrSetElemTryCache(bool* 
 {
     MOZ_ASSERT(*emitted == false);
 
     if (!object->mightBeType(MIRType::Object)) {
         trackOptimizationOutcome(TrackedOutcome::NotObject);
         return Ok();
     }
 
-    if (!index->mightBeType(MIRType::Int32) &&
-        !index->mightBeType(MIRType::String) &&
-        !index->mightBeType(MIRType::Symbol))
-    {
-        trackOptimizationOutcome(TrackedOutcome::IndexType);
-        return Ok();
-    }
-
     if (value->type() == MIRType::MagicHole)
     {
         trackOptimizationOutcome(TrackedOutcome::InitHole);
         return Ok();
     }
 
     bool barrier = true;
-    bool indexIsInt32 = index->type() == MIRType::Int32;
-
-    if (indexIsInt32 &&
+    if (index->type() == MIRType::Int32 &&
         !PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                        &object, nullptr, &value, /* canModify = */ true))
     {
         barrier = false;
     }
 
     // We can avoid worrying about holes in the IC if we know a priori we are safe
     // from them. If TI can guard that there are no indexed properties on the prototype
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -424,17 +424,17 @@ class IonBuilder
     AbortReasonOr<Ok> getElemTryGetProp(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryTypedArray(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryString(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryArguments(bool* emitted, MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryArgumentsInlined(bool* emitted, MDefinition* obj,
                                                  MDefinition* index);
-    AbortReasonOr<Ok> getElemTryCache(bool* emitted, MDefinition* obj, MDefinition* index);
+    AbortReasonOr<Ok> getElemAddCache(MDefinition* obj, MDefinition* index);
     AbortReasonOr<Ok> getElemTryScalarElemOfTypedObject(bool* emitted,
                                                         MDefinition* obj,
                                                         MDefinition* index,
                                                         TypedObjectPrediction objTypeReprs,
                                                         TypedObjectPrediction elemTypeReprs,
                                                         uint32_t elemSize);
     AbortReasonOr<Ok> getElemTryReferenceElemOfTypedObject(bool* emitted,
                                                            MDefinition* obj,
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -538,17 +538,17 @@ IonCacheIRCompiler::emitGuardProto()
     masm.branchPtr(Assembler::NotEqual, scratch, ImmGCPtr(proto), failure->label());
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitGuardCompartment()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
-    objectStubField(reader.stubOffset()); // Read global.
+    objectStubField(reader.stubOffset()); // Read global wrapper.
     JSCompartment* compartment = compartmentStubField(reader.stubOffset());
 
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -404,16 +404,24 @@ MacroAssembler::branchIfRopeOrExternal(R
 
     static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0");
     branchTest32(Assembler::Zero, temp, temp, label);
 
     branch32(Assembler::Equal, temp, Imm32(JSString::EXTERNAL_FLAGS), label);
 }
 
 void
+MacroAssembler::branchIfNotRope(Register str, Label* label)
+{
+    Address flags(str, JSString::offsetOfFlags());
+    static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0");
+    branchTest32(Assembler::NonZero, flags, Imm32(JSString::TYPE_FLAGS_MASK), label);
+}
+
+void
 MacroAssembler::branchLatin1String(Register string, Label* label)
 {
     branchTest32(Assembler::NonZero, Address(string, JSString::offsetOfFlags()),
                  Imm32(JSString::LATIN1_CHARS_BIT), label);
 }
 
 void
 MacroAssembler::branchTwoByteString(Register string, Label* label)
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1378,29 +1378,50 @@ MacroAssembler::loadStringChars(Register
 
     bind(&isInline);
     computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest);
 
     bind(&done);
 }
 
 void
-MacroAssembler::loadStringChar(Register str, Register index, Register output)
+MacroAssembler::loadStringChar(Register str, Register index, Register output, Label* fail)
 {
     MOZ_ASSERT(str != output);
     MOZ_ASSERT(index != output);
 
-    loadStringChars(str, output);
+    movePtr(str, output);
+
+    // This follows JSString::getChar.
+    Label notRope;
+    branchIfNotRope(str, &notRope);
+
+    // Load leftChild.
+    loadPtr(Address(str, JSRope::offsetOfLeft()), output);
+
+    // Check if the index is contained in the leftChild.
+    // Todo: Handle index in the rightChild.
+    branch32(Assembler::BelowOrEqual, Address(output, JSString::offsetOfLength()), index, fail);
+
+    // If the left side is another rope, give up.
+    branchIfRope(output, fail);
+
+    bind(&notRope);
 
     Label isLatin1, done;
-    branchLatin1String(str, &isLatin1);
+    // We have to check the left/right side for ropes,
+    // because a TwoByte rope might have a Latin1 child.
+    branchLatin1String(output, &isLatin1);
+
+    loadStringChars(output, output);
     load16ZeroExtend(BaseIndex(output, index, TimesTwo), output);
     jump(&done);
 
     bind(&isLatin1);
+    loadStringChars(output, output);
     load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
 
     bind(&done);
 }
 
 void
 MacroAssembler::loadJSContext(Register dest)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1112,16 +1112,18 @@ class MacroAssembler : public MacroAssem
     inline void branchIfFalseBool(Register reg, L label);
 
     // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
     inline void branchIfTrueBool(Register reg, Label* label);
 
     inline void branchIfRope(Register str, Label* label);
     inline void branchIfRopeOrExternal(Register str, Register temp, Label* label);
 
+    inline void branchIfNotRope(Register str, Label* label);
+
     inline void branchLatin1String(Register string, Label* label);
     inline void branchTwoByteString(Register string, Label* label);
 
     inline void branchIfFunctionHasNoScript(Register fun, Label* label);
     inline void branchIfInterpreted(Register fun, Label* label);
 
     inline void branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun,
                                    Register scratch, Label* label);
@@ -1510,17 +1512,17 @@ class MacroAssembler : public MacroAssem
         loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest);
     }
 
     void loadStringLength(Register str, Register dest) {
         load32(Address(str, JSString::offsetOfLength()), dest);
     }
 
     void loadStringChars(Register str, Register dest);
-    void loadStringChar(Register str, Register index, Register output);
+    void loadStringChar(Register str, Register index, Register output, Label* fail);
 
     void loadJSContext(Register dest);
     void loadJitActivation(Register dest) {
         loadJSContext(dest);
         loadPtr(Address(dest, offsetof(JSContext, activation_)), dest);
     }
 
     void loadWasmActivationFromTls(Register dest);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6093,32 +6093,32 @@ JS_ObjectIsRegExp(JSContext* cx, HandleO
 }
 
 JS_PUBLIC_API(unsigned)
 JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
-    RegExpGuard shared(cx);
+    RootedRegExpShared shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return false;
-    return shared.re()->getFlags();
+    return shared->getFlags();
 }
 
 JS_PUBLIC_API(JSString*)
 JS_GetRegExpSource(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
-    RegExpGuard shared(cx);
+    RootedRegExpShared shared(cx);
     if (!RegExpToShared(cx, obj, &shared))
         return nullptr;
-    return shared.re()->getSource();
+    return shared->getSource();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(bool)
 JS_SetDefaultLocale(JSContext* cx, const char* locale)
 {
     AssertHeapIsIdle();
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -62,17 +62,17 @@ JSCompartment::JSCompartment(Zone* zone,
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     allocationMetadataBuilder(nullptr),
     lastAnimationTime(0),
-    regExps(runtime_),
+    regExps(zone),
     globalWriteBarriered(0),
     detachedTypedObjects(0),
     objectMetadataState(ImmediateMetadata()),
     selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     innerViews(zone, InnerViewTable()),
     lazyArrayBuffers(nullptr),
     wasm(zone),
@@ -223,16 +223,23 @@ JSCompartment::ensureJitCompartmentExist
         jitCompartment_ = nullptr;
         return false;
     }
 
     return true;
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
+
+void
+js::DtoaCache::checkCacheAfterMovingGC()
+{
+    MOZ_ASSERT(!s || !IsForwarded(s));
+}
+
 namespace {
 struct CheckGCThingAfterMovingGCFunctor {
     template <class T> void operator()(T* t) { CheckGCThingAfterMovingGC(*t); }
 };
 } // namespace (anonymous)
 
 void
 JSCompartment::checkWrapperMapAfterMovingGC()
@@ -245,17 +252,18 @@ JSCompartment::checkWrapperMapAfterMovin
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         e.front().mutableKey().applyToWrapped(CheckGCThingAfterMovingGCFunctor());
         e.front().mutableKey().applyToDebugger(CheckGCThingAfterMovingGCFunctor());
 
         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
-#endif
+
+#endif // JSGC_HASH_TABLE_CHECKS
 
 bool
 JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
                           const js::Value& wrapper)
 {
     MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
     MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
 
@@ -413,18 +421,16 @@ JSCompartment::getNonWrapperObjectForCur
     MOZ_ASSERT(!IsWindow(obj));
 
     return true;
 }
 
 bool
 JSCompartment::getOrCreateWrapper(JSContext* cx, HandleObject existing, MutableHandleObject obj)
 {
-    MOZ_ASSERT(JS::ObjectIsNotGray(obj));
-
     // If we already have a wrapper for this value, use it.
     RootedValue key(cx, ObjectValue(*obj));
     if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) {
         obj.set(&p->value().get().toObject());
         MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>());
         return true;
     }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -61,17 +61,17 @@ class DtoaCache {
 
     void cache(int base, double d, JSFlatString* s) {
         this->base = base;
         this->d = d;
         this->s = s;
     }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
-    void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
+    void checkCacheAfterMovingGC();
 #endif
 };
 
 // Cache to speed up the group/shape lookup in ProxyObject::create. A proxy's
 // group/shape is only determined by the Class + proto, so a small cache for
 // this is very effective in practice.
 class NewProxyCache
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1158,19 +1158,19 @@ CastToJSFreeOp(FreeOp* fop)
 extern JS_FRIEND_API(JSFlatString*)
 GetErrorTypeName(JSContext* cx, int16_t exnType);
 
 #ifdef JS_DEBUG
 extern JS_FRIEND_API(unsigned)
 GetEnterCompartmentDepth(JSContext* cx);
 #endif
 
-class RegExpGuard;
 extern JS_FRIEND_API(bool)
-RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
+RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp,
+                        JS::MutableHandle<RegExpShared*> shared);
 
 /* Implemented in CrossCompartmentWrapper.cpp. */
 typedef enum NukeReferencesToWindow {
     NukeWindowReferences,
     DontNukeWindowReferences
 } NukeReferencesToWindow;
 
 typedef enum NukeReferencesFromTarget {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -362,17 +362,22 @@ static const FinalizePhase BackgroundFin
             AllocKind::OBJECT4_BACKGROUND,
             AllocKind::OBJECT8_BACKGROUND,
             AllocKind::OBJECT12_BACKGROUND,
             AllocKind::OBJECT16_BACKGROUND
         }
     },
     {
         gcstats::PHASE_SWEEP_SCOPE, {
-            AllocKind::SCOPE
+            AllocKind::SCOPE,
+        }
+    },
+    {
+        gcstats::PHASE_SWEEP_REGEXP_SHARED, {
+            AllocKind::REGEXP_SHARED,
         }
     },
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::FAT_INLINE_STRING,
             AllocKind::STRING,
             AllocKind::FAT_INLINE_ATOM,
             AllocKind::ATOM,
@@ -1717,25 +1722,26 @@ static const AllocKind AllocKindsToReloc
     AllocKind::OBJECT8,
     AllocKind::OBJECT8_BACKGROUND,
     AllocKind::OBJECT12,
     AllocKind::OBJECT12_BACKGROUND,
     AllocKind::OBJECT16,
     AllocKind::OBJECT16_BACKGROUND,
     AllocKind::SCRIPT,
     AllocKind::LAZY_SCRIPT,
-    AllocKind::SCOPE,
     AllocKind::SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::BASE_SHAPE,
     AllocKind::FAT_INLINE_STRING,
     AllocKind::STRING,
     AllocKind::EXTERNAL_STRING,
     AllocKind::FAT_INLINE_ATOM,
-    AllocKind::ATOM
+    AllocKind::ATOM,
+    AllocKind::SCOPE,
+    AllocKind::REGEXP_SHARED
 };
 
 Arena*
 ArenaList::removeRemainingArenas(Arena** arenap)
 {
     // This is only ever called to remove arenas that are after the cursor, so
     // we don't need to update it.
 #ifdef DEBUG
@@ -2049,71 +2055,33 @@ GCRuntime::relocateArenas(Zone* zone, JS
             freeCells += arena->countFreeCells();
         MOZ_ASSERT(freeCells < Arena::thingsPerArena(i));
     }
 #endif
 
     return true;
 }
 
-void
-MovingTracer::onObjectEdge(JSObject** objp)
-{
-    JSObject* obj = *objp;
-    if (obj->runtimeFromAnyThread() == runtime() && IsForwarded(obj))
-        *objp = Forwarded(obj);
-}
-
-void
-MovingTracer::onShapeEdge(Shape** shapep)
-{
-    Shape* shape = *shapep;
-    if (shape->runtimeFromAnyThread() == runtime() && IsForwarded(shape))
-        *shapep = Forwarded(shape);
-}
-
-void
-MovingTracer::onStringEdge(JSString** stringp)
-{
-    JSString* string = *stringp;
-    if (string->runtimeFromAnyThread() == runtime() && IsForwarded(string))
-        *stringp = Forwarded(string);
-}
-
-void
-MovingTracer::onScriptEdge(JSScript** scriptp)
-{
-    JSScript* script = *scriptp;
-    if (script->runtimeFromAnyThread() == runtime() && IsForwarded(script))
-        *scriptp = Forwarded(script);
-}
-
-void
-MovingTracer::onLazyScriptEdge(LazyScript** lazyp)
-{
-    LazyScript* lazy = *lazyp;
-    if (lazy->runtimeFromAnyThread() == runtime() && IsForwarded(lazy))
-        *lazyp = Forwarded(lazy);
-}
-
-void
-MovingTracer::onBaseShapeEdge(BaseShape** basep)
-{
-    BaseShape* base = *basep;
-    if (base->runtimeFromAnyThread() == runtime() && IsForwarded(base))
-        *basep = Forwarded(base);
-}
-
-void
-MovingTracer::onScopeEdge(Scope** scopep)
-{
-    Scope* scope = *scopep;
-    if (scope->runtimeFromAnyThread() == runtime() && IsForwarded(scope))
-        *scopep = Forwarded(scope);
-}
+template <typename T>
+inline void
+MovingTracer::updateEdge(T** thingp)
+{
+    auto thing = *thingp;
+    if (thing->runtimeFromAnyThread() == runtime() && IsForwarded(thing))
+        *thingp = Forwarded(thing);
+}
+
+void MovingTracer::onObjectEdge(JSObject** objp) { updateEdge(objp); }
+void MovingTracer::onShapeEdge(Shape** shapep) { updateEdge(shapep); }
+void MovingTracer::onStringEdge(JSString** stringp) { updateEdge(stringp); }
+void MovingTracer::onScriptEdge(JSScript** scriptp) { updateEdge(scriptp); }
+void MovingTracer::onLazyScriptEdge(LazyScript** lazyp) { updateEdge(lazyp); }
+void MovingTracer::onBaseShapeEdge(BaseShape** basep) { updateEdge(basep); }
+void MovingTracer::onScopeEdge(Scope** scopep) { updateEdge(scopep); }
+void MovingTracer::onRegExpSharedEdge(RegExpShared** sharedp) { updateEdge(sharedp); }
 
 void
 Zone::prepareForCompacting()
 {
     FreeOp* fop = runtimeFromActiveCooperatingThread()->defaultFreeOp();
     discardJitCode(fop);
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -116,16 +116,17 @@ IsNurseryAllocable(AllocKind kind)
         false,     /* AllocKind::FAT_INLINE_STRING */
         false,     /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
         false,     /* AllocKind::FAT_INLINE_ATOM */
         false,     /* AllocKind::ATOM */
         false,     /* AllocKind::SYMBOL */
         false,     /* AllocKind::JITCODE */
         false,     /* AllocKind::SCOPE */
+        false,     /* AllocKind::REGEXP_SHARED */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
     return map[size_t(kind)];
 }
 
 static inline bool
 IsBackgroundFinalized(AllocKind kind)
 {
@@ -154,16 +155,17 @@ IsBackgroundFinalized(AllocKind kind)
         true,      /* AllocKind::FAT_INLINE_STRING */
         true,      /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
         true,      /* AllocKind::FAT_INLINE_ATOM */
         true,      /* AllocKind::ATOM */
         true,      /* AllocKind::SYMBOL */
         false,     /* AllocKind::JITCODE */
         true,      /* AllocKind::SCOPE */
+        true,      /* AllocKind::REGEXP_SHARED */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
     return map[size_t(kind)];
 }
 
 static inline bool
 CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
 {
@@ -1131,119 +1133,39 @@ class RelocationOverlay
 //                  pointer to old location.
 //
 // MaybeForwarded - used before dereferencing a pointer that may refer to a
 //                  moved GC thing without updating it. For JSObjects this will
 //                  also update the object's shape pointer if it has been moved
 //                  to allow slots to be accessed.
 
 template <typename T>
-struct MightBeForwarded
-{
-    static_assert(mozilla::IsBaseOf<Cell, T>::value,
-                  "T must derive from Cell");
-    static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
-                  "T must not be Cell or TenuredCell");
-
-    static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
-                              mozilla::IsBaseOf<Shape, T>::value ||
-                              mozilla::IsBaseOf<BaseShape, T>::value ||
-                              mozilla::IsBaseOf<JSString, T>::value ||
-                              mozilla::IsBaseOf<JSScript, T>::value ||
-                              mozilla::IsBaseOf<js::LazyScript, T>::value ||
-                              mozilla::IsBaseOf<js::Scope, T>::value;
-};
+inline bool IsForwarded(T* t);
+inline bool IsForwarded(const JS::Value& value);
 
 template <typename T>
-inline bool
-IsForwarded(T* t)
-{
-    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
-    if (!MightBeForwarded<T>::value) {
-        MOZ_ASSERT(!overlay->isForwarded());
-        return false;
-    }
+inline T* Forwarded(T* t);
 
-    return overlay->isForwarded();
-}
-
-struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
-    template <typename T> bool operator()(T* t) { return IsForwarded(t); }
-};
-
-inline bool
-IsForwarded(const JS::Value& value)
-{
-    return DispatchTyped(IsForwardedFunctor(), value);
-}
+inline Value Forwarded(const JS::Value& value);
 
 template <typename T>
-inline T*
-Forwarded(T* t)
-{
-    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
-    MOZ_ASSERT(overlay->isForwarded());
-    return reinterpret_cast<T*>(overlay->forwardingAddress());
-}
-
-struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
-    template <typename T> inline Value operator()(T* t) {
-        return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
-    }
-};
-
-inline Value
-Forwarded(const JS::Value& value)
-{
-    return DispatchTyped(ForwardedFunctor(), value);
-}
-
-template <typename T>
-inline T
-MaybeForwarded(T t)
-{
-    if (IsForwarded(t))
-        t = Forwarded(t);
-    MakeAccessibleAfterMovingGC(t);
-    return t;
-}
+inline T MaybeForwarded(T t);
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 
 template <typename T>
-inline bool
-IsGCThingValidAfterMovingGC(T* t)
-{
-    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
-}
-
-template <typename T>
-inline void
-CheckGCThingAfterMovingGC(T* t)
-{
-    if (t)
-        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
-}
+inline bool IsGCThingValidAfterMovingGC(T* t);
 
 template <typename T>
-inline void
-CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
-{
-    CheckGCThingAfterMovingGC(t.unbarrieredGet());
-}
+inline void CheckGCThingAfterMovingGC(T* t);
 
-struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
-    template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
-};
+template <typename T>
+inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t);
 
-inline void
-CheckValueAfterMovingGC(const JS::Value& value)
-{
-    DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
-}
+inline void CheckValueAfterMovingGC(const JS::Value& value);
 
 #endif // JSGC_HASH_TABLE_CHECKS
 
 #define JS_FOR_EACH_ZEAL_MODE(D)               \
             D(Poke, 1)                         \
             D(Alloc, 2)                        \
             D(FrameGC, 3)                      \
             D(VerifierPre, 4)                  \
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -483,12 +483,120 @@ RelocationOverlay::forwardTo(Cell* cell)
     static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) &&
                   offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) &&
                   offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.flags),
                   "RelocationOverlay::magic_ is in the wrong location");
     magic_ = Relocated;
     newLocation_ = cell;
 }
 
+template <typename T>
+struct MightBeForwarded
+{
+    static_assert(mozilla::IsBaseOf<Cell, T>::value,
+                  "T must derive from Cell");
+    static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
+                  "T must not be Cell or TenuredCell");
+
+    static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
+                              mozilla::IsBaseOf<Shape, T>::value ||
+                              mozilla::IsBaseOf<BaseShape, T>::value ||
+                              mozilla::IsBaseOf<JSString, T>::value ||
+                              mozilla::IsBaseOf<JSScript, T>::value ||
+                              mozilla::IsBaseOf<js::LazyScript, T>::value ||
+                              mozilla::IsBaseOf<js::Scope, T>::value ||
+                              mozilla::IsBaseOf<js::RegExpShared, T>::value;
+};
+
+template <typename T>
+inline bool
+IsForwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    if (!MightBeForwarded<T>::value) {
+        MOZ_ASSERT(!overlay->isForwarded());
+        return false;
+    }
+
+    return overlay->isForwarded();
+}
+
+struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
+    template <typename T> bool operator()(T* t) { return IsForwarded(t); }
+};
+
+inline bool
+IsForwarded(const JS::Value& value)
+{
+    return DispatchTyped(IsForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T*
+Forwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    MOZ_ASSERT(overlay->isForwarded());
+    return reinterpret_cast<T*>(overlay->forwardingAddress());
+}
+
+struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
+    template <typename T> inline Value operator()(T* t) {
+        return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
+    }
+};
+
+inline Value
+Forwarded(const JS::Value& value)
+{
+    return DispatchTyped(ForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T
+MaybeForwarded(T t)
+{
+    if (IsForwarded(t))
+        t = Forwarded(t);
+    MakeAccessibleAfterMovingGC(t);
+    return t;
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+template <typename T>
+inline bool
+IsGCThingValidAfterMovingGC(T* t)
+{
+    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(T* t)
+{
+    if (t)
+        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
+{
+    CheckGCThingAfterMovingGC(t.unbarrieredGet());
+}
+
+struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
+    template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
+};
+
+inline void
+CheckValueAfterMovingGC(const JS::Value& value)
+{
+    DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
+}
+
+#endif // JSGC_HASH_TABLE_CHECKS
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* jsgcinlines_h */
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -113,17 +113,17 @@ class JS_FRIEND_API(Wrapper) : public Ba
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 RegExpGuard* g) const override;
+                                 MutableHandle<RegExpShared*> shared) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                   MutableHandleValue vp) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
     virtual JSObject* weakmapKeyDelegate(JSObject* proxy) const override;
 
   public:
     using BaseProxyHandler::Action;
@@ -136,17 +136,16 @@ class JS_FRIEND_API(Wrapper) : public Ba
     static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
                          const WrapperOptions& options = WrapperOptions());
 
     static JSObject* Renew(JSObject* existing, JSObject* obj, const Wrapper* handler);
 
     static const Wrapper* wrapperHandler(JSObject* wrapper);
 
     static JSObject* wrappedObject(JSObject* wrapper);
-    static JSObject* wrappedObjectMaybeGray(JSObject* wrapper);
 
     unsigned flags() const {
         return mFlags;
     }
 
     static const char family;
     static const Wrapper singleton;
     static const Wrapper singletonWithPrototype;
@@ -208,17 +207,18 @@ class JS_FRIEND_API(CrossCompartmentWrap
                                               AutoIdVector& props) const override;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
                              bool* bp) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
                                    unsigned indent) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<RegExpShared*> shared) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
     // Allocate CrossCompartmentWrappers in the nursery.
     virtual bool canNurseryAllocate() const override { return true; }
 
     static const CrossCompartmentWrapper singleton;
     static const CrossCompartmentWrapper singletonWithPrototype;
 };
@@ -306,17 +306,18 @@ class JS_FRIEND_API(SecurityWrapper) : p
     virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                               ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
 
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<RegExpShared*> shared) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
     // against.
 
     virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                        JS::HandleObject callable) const override;
     virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -322,17 +322,17 @@ BaseProxyHandler::fun_toString(JSContext
         return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
     RootedValue v(cx, ObjectValue(*proxy));
     ReportIsNotFunction(cx, v);
     return nullptr;
 }
 
 bool
 BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
-                                  RegExpGuard* g) const
+                                  MutableHandleRegExpShared shared) const
 {
     MOZ_CRASH("This should have been a wrapped regexp");
 }
 
 bool
 BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
 {
     vp.setUndefined();
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -453,29 +453,30 @@ CrossCompartmentWrapper::fun_toString(JS
             return nullptr;
     }
     if (!cx->compartment()->wrap(cx, &str))
         return nullptr;
     return str;
 }
 
 bool
-CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const
+CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper,
+                                         MutableHandleRegExpShared shared) const
 {
-    RegExpGuard wrapperGuard(cx);
+    RootedRegExpShared re(cx);
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
-        if (!Wrapper::regexp_toShared(cx, wrapper, &wrapperGuard))
+        if (!Wrapper::regexp_toShared(cx, wrapper, &re))
             return false;
     }
 
     // Get an equivalent RegExpShared associated with the current compartment.
-    RegExpShared* re = wrapperGuard.re();
-    cx->markAtom(re->getSource());
-    return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
+    RootedAtom source(cx, re->getSource());
+    cx->markAtom(source);
+    return cx->compartment()->regExps.get(cx, source, re->getFlags(), shared);
 }
 
 bool
 CrossCompartmentWrapper::boxedValue_unbox(JSContext* cx, HandleObject wrapper, MutableHandleValue vp) const
 {
     PIERCE(cx, wrapper,
            NOTHING,
            Wrapper::boxedValue_unbox(cx, wrapper, vp),
@@ -640,18 +641,16 @@ js::RemapWrapper(JSContext* cx, JSObject
 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
 // |newTarget|. All wrappers are recomputed.
 JS_FRIEND_API(bool)
 js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
                               JSObject* newTargetArg)
 {
     MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
     MOZ_ASSERT(!IsInsideNursery(newTargetArg));
-    MOZ_ASSERT(JS::ObjectIsNotGray(oldTargetArg));
-    MOZ_ASSERT(JS::ObjectIsNotGray(newTargetArg));
 
     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     RootedObject newTarget(cx, newTargetArg);
 
     AutoWrapperVector toTransplant(cx);
     if (!toTransplant.reserve(cx->runtime()->numCompartments))
         return false;
 
@@ -696,15 +695,14 @@ js::RecomputeWrappers(JSContext* cx, con
             if (!toRecompute.append(WrapperValue(e)))
                 return false;
         }
     }
 
     // Recompute all the wrappers in the list.
     for (const WrapperValue& v : toRecompute) {
         JSObject* wrapper = &v.toObject();
-        JSObject* wrapped = Wrapper::wrappedObjectMaybeGray(wrapper);
-        JS::ExposeObjectToActiveJS(wrapped);
+        JSObject* wrapped = Wrapper::wrappedObject(wrapper);
         RemapWrapper(cx, wrapper, wrapped);
     }
 
     return true;
 }
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -137,17 +137,18 @@ DeadObjectProxy::className(JSContext* cx
 JSString*
 DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     ReportDead(cx);
     return nullptr;
 }
 
 bool
-DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<RegExpShared*> shared) const
 {
     ReportDead(cx);
     return false;
 }
 
 const char DeadObjectProxy::family = 0;
 const DeadObjectProxy DeadObjectProxy::singleton;
 
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -44,17 +44,18 @@ class DeadObjectProxy : public BaseProxy
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
-    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
+    virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                 MutableHandle<RegExpShared*> shared) const override;
 
     static const char family;
     static const DeadObjectProxy singleton;
 };
 
 bool
 IsDeadProxyObject(JSObject* obj);
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -554,21 +554,21 @@ Proxy::fun_toString(JSContext* cx, Handl
                            BaseProxyHandler::GET, /* mayThrow = */ false);
     // Do the safe thing if the policy rejects.
     if (!policy.allowed())
         return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
     return handler->fun_toString(cx, proxy, indent);
 }
 
 bool
-Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
+Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared)
 {
     if (!CheckRecursionLimit(cx))
         return false;
-    return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
+    return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, shared);
 }
 
 bool
 Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
 {
     if (!CheckRecursionLimit(cx))
         return false;
     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -8,18 +8,16 @@
 #define proxy_Proxy_h
 
 #include "NamespaceImports.h"
 
 #include "js/Class.h"
 
 namespace js {
 
-class RegExpGuard;
-
 /*
  * Dispatch point for handlers that executes the appropriate C++ or scripted traps.
  *
  * Important: All proxy methods need either (a) an AutoEnterPolicy in their
  * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
  * 945826 comment 0.
  */
 class Proxy
@@ -57,17 +55,18 @@ class Proxy
                                              AutoIdVector& props);
     static bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                            const CallArgs& args);
     static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
     static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
     static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
     static const char* className(JSContext* cx, HandleObject proxy);
     static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
-    static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
+    static bool regexp_toShared(JSContext* cx, HandleObject proxy,
+                                MutableHandle<RegExpShared*> shared);
     static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
 
     static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
     static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
 
     static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
                             ElementAdder* adder);
 
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1263,17 +1263,18 @@ JSString*
 ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                               js_Function_str, js_toString_str, "object");
     return nullptr;
 }
 
 bool
-ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
+                                      MutableHandleRegExpShared shared) const
 {
     MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
     return false;
 }
 
 bool
 ScriptedProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                        MutableHandleValue vp) const
--- a/js/src/proxy/ScriptedProxyHandler.h
+++ b/js/src/proxy/ScriptedProxyHandler.h
@@ -65,17 +65,17 @@ class ScriptedProxyHandler : public Base
                              bool* bp) const override;
     virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
-                                 RegExpGuard* g) const override;
+                                 MutableHandle<RegExpShared*> shared) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
                                   MutableHandleValue vp) const override;
 
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
 
     virtual bool isScripted() const override { return true; }
 
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -83,19 +83,20 @@ SecurityWrapper<Base>::isArray(JSContext
 {
     // This should ReportAccessDenied(cx), but bug 849730 disagrees.  :-(
     *answer = JS::IsArrayAnswer::NotArray;
     return true;
 }
 
 template <class Base>
 bool
-SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
+SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj,
+                                       MutableHandle<RegExpShared*> shared) const
 {
-    return Base::regexp_toShared(cx, obj, g);
+    return Base::regexp_toShared(cx, obj, shared);
 }
 
 template <class Base>
 bool
 SecurityWrapper<Base>::boxedValue_unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) const
 {
     vp.setUndefined();
     return true;
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -263,20 +263,20 @@ JSString*
 Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return fun_toStringHelper(cx, target, indent);
 }
 
 bool
-Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
+Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return RegExpToShared(cx, target, g);
+    return RegExpToShared(cx, target, shared);
 }
 
 bool
 Wrapper::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return Unbox(cx, target, vp);
 }
@@ -323,40 +323,35 @@ Wrapper::wrapperHandler(JSObject* wrappe
 {
     MOZ_ASSERT(wrapper->is<WrapperObject>());
     return static_cast<const Wrapper*>(wrapper->as<ProxyObject>().handler());
 }
 
 JSObject*
 Wrapper::wrappedObject(JSObject* wrapper)
 {
-    JSObject* target = wrappedObjectMaybeGray(wrapper);
-    MOZ_ASSERT(JS::ObjectIsNotGray(target));
+    MOZ_ASSERT(wrapper->is<WrapperObject>());
+    JSObject* target = wrapper->as<ProxyObject>().target();
+    if (target)
+        JS::ExposeObjectToActiveJS(target);
     return target;
 }
 
-JSObject*
-Wrapper::wrappedObjectMaybeGray(JSObject* wrapper)
-{
-    MOZ_ASSERT(wrapper->is<WrapperObject>());
-    return wrapper->as<ProxyObject>().target();
-}
-
 JS_FRIEND_API(JSObject*)
 js::UncheckedUnwrap(JSObject* wrapped, bool stopAtWindowProxy, unsigned* flagsp)
 {
     unsigned flags = 0;
     while (true) {
         if (!wrapped->is<WrapperObject>() ||
             MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(wrapped)))
         {
             break;
         }
         flags |= Wrapper::wrapperHandler(wrapped)->flags();
-        wrapped = wrapped->as<ProxyObject>().target();
+        wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
 
         // This can be called from Wrapper::weakmapKeyDelegate() on a wrapper
         // whose referent has been moved while it is still unmarked.
         if (wrapped)
             wrapped = MaybeForwarded(wrapped);
     }
     if (flagsp)
         *flagsp = flags;
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -14,16 +14,18 @@
 
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitFrameIterator.h"
 #include "jit/JitFrames.h"
 #include "vm/StringBuffer.h"
 
+#include "jsgcinlines.h"
+
 using namespace js;
 
 using mozilla::DebugOnly;
 
 GeckoProfiler::GeckoProfiler(JSRuntime* rt)
   : rt(rt),
     strings(mutexid::GeckoProfilerStrings),
     stack_(nullptr),
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -593,16 +593,23 @@ StatsCellCallback(JSRuntime* rt, void* d
 
       case JS::TraceKind::Scope: {
         Scope* scope = static_cast<Scope*>(thing);
         zStats->scopesGCHeap += thingSize;
         zStats->scopesMallocHeap += scope->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         break;
       }
 
+      case JS::TraceKind::RegExpShared: {
+        auto regexp = static_cast<RegExpShared*>(thing);
+        zStats->regExpSharedsGCHeap += thingSize;
+        zStats->regExpSharedsMallocHeap += regexp->sizeOfExcludingThis(rtStats->mallocSizeOf_);
+        break;
+      }
+
       default:
         MOZ_CRASH("invalid traceKind in StatsCellCallback");
     }
 
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     zStats->unusedGCThings.addToKind(traceKind, -thingSize);
 }
 
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1222,17 +1222,16 @@ class NativeObject : public ShapedObject
     }
     void setPrivate(void* data) {
         void** pprivate = &privateRef(numFixedSlots());
         privateWriteBarrierPre(pprivate);
         *pprivate = data;
     }
 
     void setPrivateGCThing(gc::Cell* cell) {
-        CheckEdgeIsNotBlackToGray(this, cell);
         void** pprivate = &privateRef(numFixedSlots());
         privateWriteBarrierPre(pprivate);
         *pprivate = reinterpret_cast<void*>(cell);
         privateWriteBarrierPost(pprivate);
     }
 
     void setPrivateUnbarriered(void* data) {
         void** pprivate = &privateRef(numFixedSlots());
--- a/js/src/vm/Printer.cpp
+++ b/js/src/vm/Printer.cpp
@@ -436,17 +436,17 @@ Fprinter::finish()
         fclose(file_);
     file_ = nullptr;
 }
 
 bool
 Fprinter::put(const char* s, size_t len)
 {
     MOZ_ASSERT(file_);
-    int i = fwrite(s, len, 1, file_);
+    int i = fwrite(s, /*size=*/ 1, /*nitems=*/ len, file_);
     if (size_t(i) != len) {
         reportOutOfMemory();
         return false;
     }
     return true;
 }
 
 LSprinter::LSprinter(LifoAlloc* lifoAlloc)
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -96,25 +96,23 @@ ProxyObject::objectMovedDuringMinorGC(Te
     else
         dst->zone()->group()->nursery().removeMallocedBuffer(psrc.data.values);
     return sizeof(detail::ProxyValueArray);
 }
 
 void
 ProxyObject::setCrossCompartmentPrivate(const Value& priv)
 {
-    CheckEdgeIsNotBlackToGray(this, priv);
     *slotOfPrivate() = priv;
 }
 
 void
 ProxyObject::setSameCompartmentPrivate(const Value& priv)
 {
     MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
-    CheckEdgeIsNotBlackToGray(this, priv);
     *slotOfPrivate() = priv;
 }
 
 void
 ProxyObject::nuke()
 {
     // Clear the target reference.
     setSameCompartmentPrivate(NullValue());
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -4,16 +4,17 @@
  * 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 "vm/RegExpObject.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 
+#include "jshashutil.h"
 #include "jsstr.h"
 #ifdef DEBUG
 #include "jsutil.h"
 #endif
 
 #include "builtin/RegExp.h"
 #include "frontend/TokenStream.h"
 #ifdef DEBUG
@@ -114,39 +115,26 @@ VectorMatchPairs::allocOrExpandArray(siz
 
     pairs_ = &vec_[0];
     pairCount_ = pairCount;
     return true;
 }
 
 /* RegExpObject */
 
-static inline void
-RegExpSharedReadBarrier(JSContext* cx, RegExpShared* shared)
+/* static */ bool
+RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp,
+                        MutableHandleRegExpShared shared)
 {
-    Zone* zone = cx->zone();
-    if (zone->needsIncrementalBarrier())
-        shared->trace(zone->barrierTracer());
-    if (shared->isMarkedGray())
-        shared->unmarkGray();
-}
-
-/* static */ bool
-RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
-{
-    if (RegExpShared* shared = regexp->maybeShared()) {
-        // Fetching a RegExpShared from an object requires a read
-        // barrier, as the shared pointer might be weak.
-        RegExpSharedReadBarrier(cx, shared);
-
-        g->init(*shared);
+    if (regexp->hasShared()) {
+        shared.set(regexp->sharedRef());
         return true;
     }
 
-    return createShared(cx, regexp, g);
+    return createShared(cx, regexp, shared);
 }
 
 /* static */ bool
 RegExpObject::isOriginalFlagGetter(JSNative native, RegExpFlag* mask)
 {
   if (native == regexp_global) {
       *mask = GlobalFlag;
       return true;
@@ -169,36 +157,42 @@ RegExpObject::isOriginalFlagGetter(JSNat
   }
 
   return false;
 }
 
 /* static */ void
 RegExpObject::trace(JSTracer* trc, JSObject* obj)
 {
-    RegExpShared* shared = obj->as<RegExpObject>().maybeShared();
-    if (!shared)
-        return;
+    obj->as<RegExpObject>().trace(trc);
+}
 
-    // When tracing through the object normally, we have the option of
-    // unlinking the object from its RegExpShared so that the RegExpShared may
-    // be collected. To detect this we need to test all the following
-    // conditions, since:
+static inline bool
+IsMarkingTrace(JSTracer* trc)
+{
+    // Determine whether tracing is happening during normal marking.  We need to
+    // test all the following conditions, since:
+    //
     //   1. During TraceRuntime, CurrentThreadIsHeapBusy() is true, but the
     //      tracer might not be a marking tracer.
     //   2. When a write barrier executes, IsMarkingTracer is true, but
     //      CurrentThreadIsHeapBusy() will be false.
-    if (JS::CurrentThreadIsHeapCollecting() &&
-        trc->isMarkingTracer() &&
-        !obj->asTenured().zone()->isPreservingCode())
-    {
-        obj->as<RegExpObject>().NativeObject::setPrivate(nullptr);
-    } else {
-        shared->trace(trc);
-    }
+
+    return JS::CurrentThreadIsHeapCollecting() && trc->isMarkingTracer();
+}
+
+void
+RegExpObject::trace(JSTracer* trc)
+{
+    // When marking the object normally we have the option of unlinking the
+    // object from its RegExpShared so that the RegExpShared may be collected.
+    if (IsMarkingTrace(trc) && !zone()->isPreservingCode())
+        sharedRef() = nullptr;
+
+    TraceNullableEdge(trc, &sharedRef(), "RegExpObject shared");
 }
 
 static JSObject*
 CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
 {
     return GlobalObject::createBlankPrototype(cx, cx->global(), &RegExpObject::protoClass_);
 }
 
@@ -275,23 +269,25 @@ RegExpObject::create(JSContext* cx, Hand
         return nullptr;
 
     regexp->initAndZeroLastIndex(source, flags, cx);
 
     return regexp;
 }
 
 /* static */ bool
-RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
+RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
+                           MutableHandleRegExpShared shared)
 {
-    MOZ_ASSERT(!regexp->maybeShared());
-    if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
+    MOZ_ASSERT(!regexp->hasShared());
+    RootedAtom source(cx, regexp->getSource());
+    if (!cx->compartment()->regExps.get(cx, source, regexp->getFlags(), shared))
         return false;
 
-    regexp->setShared(**g);
+    regexp->setShared(*shared);
     return true;
 }
 
 Shape*
 RegExpObject::assignInitialShape(JSContext* cx, Handle<RegExpObject*> self)
 {
     MOZ_ASSERT(self->empty());
 
@@ -503,24 +499,25 @@ RegExpObject::toString(JSContext* cx) co
         return nullptr;
     if (sticky() && !sb.append('y'))
         return nullptr;
 
     return sb.finishString();
 }
 
 #ifdef DEBUG
-bool
-RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input)
+/* static */ bool
+RegExpShared::dumpBytecode(JSContext* cx, MutableHandleRegExpShared re, bool match_only,
+                           HandleLinearString input)
 {
     CompilationMode mode = match_only ? MatchOnly : Normal;
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!RegExpShared::compileIfNecessary(cx, re, input, mode, ForceByteCode))
         return false;
 
-    const uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    const uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
     const uint8_t* pc = byteCode;
 
     auto Load32Aligned = [](const uint8_t* pc) -> int32_t {
         MOZ_ASSERT((reinterpret_cast<uintptr_t>(pc) & 3) == 0);
         return *reinterpret_cast<const int32_t*>(pc);
     };
 
     auto Load16Aligned = [](const uint8_t* pc) -> int32_t {
@@ -889,21 +886,21 @@ RegExpShared::dumpBytecode(JSContext* cx
 
     return true;
 }
 
 /* static */ bool
 RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
                            bool match_only, HandleLinearString input)
 {
-    RegExpGuard g(cx);
-    if (!getShared(cx, regexp, &g))
+    RootedRegExpShared shared(cx);
+    if (!getShared(cx, regexp, &shared))
         return false;
 
-    return g.re()->dumpBytecode(cx, match_only, input);
+    return RegExpShared::dumpBytecode(cx, &shared, match_only, input);
 }
 #endif
 
 template <typename CharT>
 static MOZ_ALWAYS_INLINE bool
 IsRegExpMetaChar(CharT ch)
 {
     switch (ch) {
@@ -942,190 +939,181 @@ js::StringHasRegExpMetaChars(JSLinearStr
         return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
 
     return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
 }
 
 /* RegExpShared */
 
 RegExpShared::RegExpShared(JSAtom* source, RegExpFlag flags)
-  : source(source), flags(flags), parenCount(0), canStringMatch(false), marked_(false)
+  : source(source), flags(flags), canStringMatch(false), parenCount(0)
 {}
 
-RegExpShared::~RegExpShared()
+void
+RegExpShared::traceChildren(JSTracer* trc)
 {
-    for (size_t i = 0; i < tables.length(); i++)
-        js_delete(tables[i]);
-}
-
-void
-RegExpShared::trace(JSTracer* trc)
-{
-    if (trc->isMarkingTracer())
-        marked_ = true;
+    // Discard code to avoid holding onto ExecutablePools.
+    if (IsMarkingTrace(trc) && trc->runtime()->gc.isShrinkingGC())
+        discardJitCode();
 
     TraceNullableEdge(trc, &source, "RegExpShared source");
     for (auto& comp : compilationArray)
         TraceNullableEdge(trc, &comp.jitCode, "RegExpShared code");
 }
 
-bool
-RegExpShared::isMarkedGray() const
+void
+RegExpShared::discardJitCode()
 {
-    if (source && source->isMarked(gc::GRAY))
-        return true;
-    for (const auto& comp : compilationArray) {
-        if (comp.jitCode && comp.jitCode->isMarked(gc::GRAY))
-            return true;
-    }
-    return false;
+    for (auto& comp : compilationArray)
+        comp.jitCode = nullptr;
 }
 
 void
-RegExpShared::unmarkGray()
+RegExpShared::finalize(FreeOp* fop)
 {
-    if (source)
-        JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(source));
-    for (const auto& comp : compilationArray) {
-        if (comp.jitCode)
-            JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(comp.jitCode.get()));
-    }
+    for (auto& comp : compilationArray)
+        js_free(comp.byteCode);
+    for (size_t i = 0; i < tables.length(); i++)
+        js_free(tables[i]);
+    tables.~JitCodeTables();
 }
 
-bool
-RegExpShared::compile(JSContext* cx, HandleLinearString input,
+/* static */ bool
+RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
                       CompilationMode mode, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
 
-    RootedAtom pattern(cx, source);
-    return compile(cx, pattern, input, mode, force);
+    RootedAtom pattern(cx, re->source);
+    return compile(cx, re, pattern, input, mode, force);
 }
 
-bool
-RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+/* static */ bool
+RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
+                      HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
 {
-    if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
-        canStringMatch = true;
+    if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
+        re->canStringMatch = true;
 
     CompileOptions options(cx);
     TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
 
     /* Parse the pattern. */
     irregexp::RegExpCompileData data;
     if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
-                                multiline(), mode == MatchOnly, unicode(), ignoreCase(),
-                                global(), sticky(), &data))
+                                re->multiline(), mode == MatchOnly, re->unicode(),
+                                re->ignoreCase(), re->global(), re->sticky(), &data))
     {
         return false;
     }
 
-    this->parenCount = data.capture_count;
+    re->parenCount = data.capture_count;
 
-    irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
+    irregexp::RegExpCode code = irregexp::CompilePattern(cx, re, &data, input,
                                                          false /* global() */,
-                                                         ignoreCase(),
+                                                         re->ignoreCase(),
                                                          input->hasLatin1Chars(),
                                                          mode == MatchOnly,
                                                          force == ForceByteCode,
-                                                         sticky(), unicode());
+                                                         re->sticky(),
+                                                         re->unicode());
     if (code.empty())
         return false;
 
     MOZ_ASSERT(!code.jitCode || !code.byteCode);
     MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
 
-    RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
+    RegExpCompilation& compilation = re->compilation(mode, input->hasLatin1Chars());
     if (code.jitCode)
         compilation.jitCode = code.jitCode;
     else if (code.byteCode)
         compilation.byteCode = code.byteCode;
 
     return true;
 }
 
-bool
-RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
-                                 CompilationMode mode, ForceByteCodeEnum force)
+/* static */ bool
+RegExpShared::compileIfNecessary(JSContext* cx, MutableHandleRegExpShared re,
+                                 HandleLinearString input, CompilationMode mode,
+                                 ForceByteCodeEnum force)
 {
-    if (isCompiled(mode, input->hasLatin1Chars(), force))
+    if (re->isCompiled(mode, input->hasLatin1Chars(), force))
         return true;
-    return compile(cx, input, mode, force);
+    return compile(cx, re, input, mode, force);
 }
 
-RegExpRunStatus
-RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
-                      MatchPairs* matches, size_t* endIndex)
+/* static */ RegExpRunStatus
+RegExpShared::execute(JSContext* cx, MutableHandleRegExpShared re, HandleLinearString input,
+                      size_t start, MatchPairs* matches, size_t* endIndex)
 {
     MOZ_ASSERT_IF(matches, !endIndex);
     MOZ_ASSERT_IF(!matches, endIndex);
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
 
     CompilationMode mode = matches ? Normal : MatchOnly;
 
     /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
+    if (!compileIfNecessary(cx, re, input, mode, DontForceByteCode))
         return RegExpRunStatus_Error;
 
     /*
      * Ensure sufficient memory for output vector.
      * No need to initialize it. The RegExp engine fills them in on a match.
      */
-    if (matches && !matches->allocOrExpandArray(pairCount())) {
+    if (matches && !matches->allocOrExpandArray(re->pairCount())) {
         ReportOutOfMemory(cx);
         return RegExpRunStatus_Error;
     }
 
     size_t length = input->length();
 
     // Reset the Irregexp backtrack stack if it grows during execution.
     irregexp::RegExpStackScope stackScope(cx);
 
-    if (canStringMatch) {
-        MOZ_ASSERT(pairCount() == 1);
-        size_t sourceLength = source->length();
-        if (sticky()) {
+    if (re->canStringMatch) {
+        MOZ_ASSERT(re->pairCount() == 1);
+        size_t sourceLength = re->source->length();
+        if (re->sticky()) {
             // First part checks size_t overflow.
             if (sourceLength + start < sourceLength || sourceLength + start > length)
                 return RegExpRunStatus_Success_NotFound;
-            if (!HasSubstringAt(input, source, start))
+            if (!HasSubstringAt(input, re->source, start))
                 return RegExpRunStatus_Success_NotFound;
 
             if (matches) {
                 (*matches)[0].start = start;
                 (*matches)[0].limit = start + sourceLength;
 
                 matches->checkAgainst(length);
             } else if (endIndex) {
                 *endIndex = start + sourceLength;
             }
             return RegExpRunStatus_Success;
         }
 
-        int res = StringFindPattern(input, source, start);
+        int res = StringFindPattern(input, re->source, start);
         if (res == -1)
             return RegExpRunStatus_Success_NotFound;
 
         if (matches) {
             (*matches)[0].start = res;
             (*matches)[0].limit = res + sourceLength;
 
             matches->checkAgainst(length);
         } else if (endIndex) {
             *endIndex = res + sourceLength;
         }
         return RegExpRunStatus_Success;
     }
 
     do {
-        jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
+        jit::JitCode* code = re->compilation(mode, input->hasLatin1Chars()).jitCode;
         if (!code)
             break;
 
         RegExpRunStatus result;
         {
             AutoTraceLog logJIT(logger, TraceLogger_IrregexpExecute);
             AutoCheckCannotGC nogc;
             if (input->hasLatin1Chars()) {
@@ -1154,20 +1142,20 @@ RegExpShared::execute(JSContext* cx, Han
         MOZ_ASSERT(result == RegExpRunStatus_Success);
 
         if (matches)
             matches->checkAgainst(length);
         return RegExpRunStatus_Success;
     } while (false);
 
     // Compile bytecode for the RegExp if necessary.
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!compileIfNecessary(cx, re, input, mode, ForceByteCode))
         return RegExpRunStatus_Error;
 
-    uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    uint8_t* byteCode = re->compilation(mode, input->hasLatin1Chars()).byteCode;
     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
 
     AutoStableStringChars inputChars(cx);
     if (!inputChars.init(cx, input))
         return RegExpRunStatus_Error;
 
     RegExpRunStatus result;
     if (inputChars.isLatin1()) {
@@ -1179,62 +1167,55 @@ RegExpShared::execute(JSContext* cx, Han
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
 size_t
-RegExpShared::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+RegExpShared::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
-    size_t n = mallocSizeOf(this);
+    size_t n = 0;
 
     for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
         const RegExpCompilation& compilation = compilationArray[i];
         if (compilation.byteCode)
             n += mallocSizeOf(compilation.byteCode);
     }
 
     n += tables.sizeOfExcludingThis(mallocSizeOf);
     for (size_t i = 0; i < tables.length(); i++)
         n += mallocSizeOf(tables[i]);
 
     return n;
 }
 
 /* RegExpCompartment */
 
-RegExpCompartment::RegExpCompartment(JSRuntime* rt)
-  : set_(rt),
+RegExpCompartment::RegExpCompartment(Zone* zone)
+  : set_(zone, Set(zone->runtimeFromActiveCooperatingThread())),
     matchResultTemplateObject_(nullptr),
     optimizableRegExpPrototypeShape_(nullptr),
     optimizableRegExpInstanceShape_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
-    // Because of stray mark bits being set (see RegExpCompartment::sweep)
-    // there might still be RegExpShared instances which haven't been deleted.
-    if (set_.initialized()) {
-        for (Set::Enum e(set_); !e.empty(); e.popFront()) {
-            RegExpShared* shared = e.front();
-            js_delete(shared);
-        }
-    }
+    MOZ_ASSERT_IF(set_.initialized(), set_.empty());
 }
 
 ArrayObject*
 RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
 {
     MOZ_ASSERT(!matchResultTemplateObject_);
 
     /* Create template array object */
     RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
-                                     nullptr, TenuredObject));
+                                                                  nullptr, TenuredObject));
     if (!templateObject)
         return matchResultTemplateObject_; // = nullptr
 
     // Create a new group for the template.
     Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
     if (!group)
         return matchResultTemplateObject_; // = nullptr
@@ -1280,69 +1261,19 @@ RegExpCompartment::init(JSContext* cx)
         if (cx)
             ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
-bool
-RegExpShared::needsSweep(JSRuntime* rt)
-{
-    // Sometimes RegExpShared instances are marked without the compartment
-    // being subsequently cleared. This can happen if a GC is restarted while
-    // in progress (i.e. performing a full GC in the middle of an incremental
-    // GC) or if a RegExpShared referenced via the stack is traced but is not
-    // in a zone being collected.
-    //
-    // Because of this we only treat the marked_ bit as a hint, and destroy the
-    // RegExpShared if it was accidentally marked earlier but wasn't marked by
-    // the current trace.
-    bool keep = marked() && IsMarked(rt, &source);
-    for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
-        RegExpShared::RegExpCompilation& compilation = compilationArray[i];
-        if (compilation.jitCode && gc::IsAboutToBeFinalized(&compilation.jitCode))
-            keep = false;
-    }
-
-    MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
-    if (keep || rt->gc.isHeapCompacting()) {
-        clearMarked();
-        return false;
-    }
-
-    return true;
-}
-
-void
-RegExpShared::discardJitCode()
-{
-    for (size_t i = 0; i < ArrayLength(compilationArray); i++)
-        compilationArray[i].jitCode = nullptr;
-}
-
 void
 RegExpCompartment::sweep(JSRuntime* rt)
 {
-    if (!set_.initialized())
-        return;
-
-    for (Set::Enum e(set_); !e.empty(); e.popFront()) {
-        RegExpShared* shared = e.front();
-        if (shared->needsSweep(rt)) {
-            js_delete(shared);
-            e.removeFront();
-        } else {
-            // Discard code to avoid holding onto ExecutablePools.
-            if (rt->gc.isHeapCompacting())
-                shared->discardJitCode();
-        }
-    }
-
     if (matchResultTemplateObject_ &&
         IsAboutToBeFinalized(&matchResultTemplateObject_))
     {
         matchResultTemplateObject_.set(nullptr);
     }
 
     if (optimizableRegExpPrototypeShape_ &&
         IsAboutToBeFinalized(&optimizableRegExpPrototypeShape_))
@@ -1353,65 +1284,55 @@ RegExpCompartment::sweep(JSRuntime* rt)
     if (optimizableRegExpInstanceShape_ &&
         IsAboutToBeFinalized(&optimizableRegExpInstanceShape_))
     {
         optimizableRegExpInstanceShape_.set(nullptr);
     }
 }
 
 bool
-RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
+RegExpCompartment::get(JSContext* cx, HandleAtom source, RegExpFlag flags,
+                       MutableHandleRegExpShared result)
 {
-    Key key(source, flags);
-    Set::AddPtr p = set_.lookupForAdd(key);
+    DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
     if (p) {
-        // Trigger a read barrier on existing RegExpShared instances fetched
-        // from the table (which only holds weak references).
-        RegExpSharedReadBarrier(cx, *p);
-
-        g->init(**p);
+        result.set(*p);
         return true;
     }
 
-    ScopedJSDeletePtr<RegExpShared> shared(cx->new_<RegExpShared>(source, flags));
+    auto shared = Allocate<RegExpShared>(cx);
     if (!shared)
         return false;
 
-    if (!set_.add(p, shared)) {
+    new (shared) RegExpShared(source, flags);
+
+    if (!p.add(cx, set_.get(), Key(source, flags), shared)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    // Trace RegExpShared instances created during an incremental GC.
-    RegExpSharedReadBarrier(cx, shared);
-
-    g->init(*shared.forget());
+    result.set(shared);
     return true;
 }
 
 bool
-RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
+RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt,
+                       MutableHandleRegExpShared shared)
 {
     RegExpFlag flags = RegExpFlag(0);
     if (opt && !ParseRegExpFlags(cx, opt, &flags))
         return false;
 
-    return get(cx, atom, flags, g);
+    return get(cx, atom, flags, shared);
 }
 
 size_t
 RegExpCompartment::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
-    size_t n = 0;
-    n += set_.sizeOfExcludingThis(mallocSizeOf);
-    for (Set::Enum e(set_); !e.empty(); e.popFront()) {
-        RegExpShared* shared = e.front();
-        n += shared->sizeOfIncludingThis(mallocSizeOf);
-    }
-    return n;
+    return set_.sizeOfExcludingThis(mallocSizeOf);
 }
 
 /* Functions */
 
 JSObject*
 js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
 {
     Rooted<RegExpObject*> regex(cx, &obj_->as<RegExpObject>());
@@ -1424,22 +1345,22 @@ js::CloneRegExpObject(JSContext* cx, JSO
         return nullptr;
     clone->initPrivate(nullptr);
 
     if (!EmptyShape::ensureInitialCustomShape<RegExpObject>(cx, clone))
         return nullptr;
 
     Rooted<JSAtom*> source(cx, regex->getSource());
 
-    RegExpGuard g(cx);
-    if (!RegExpObject::getShared(cx, regex, &g))
+    RootedRegExpShared shared(cx);
+    if (!RegExpObject::getShared(cx, regex, &shared))
         return nullptr;
 
-    clone->initAndZeroLastIndex(source, g->getFlags(), cx);
-    clone->setShared(*g.re());
+    clone->initAndZeroLastIndex(source, shared->getFlags(), cx);
+    clone->setShared(*shared);
 
     return clone;
 }
 
 static bool
 HandleRegExpFlag(RegExpFlag flag, RegExpFlag* flags)
 {
     if (*flags & flag)
@@ -1559,12 +1480,19 @@ js::CloneScriptRegExpObject(JSContext* c
 
     RootedAtom source(cx, reobj.getSource());
     cx->markAtom(source);
 
     return RegExpObject::create(cx, source, reobj.getFlags(), nullptr, cx->tempLifoAlloc());
 }
 
 JS_FRIEND_API(bool)
-js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
+js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
 {
-    return RegExpToShared(cx, obj, g);
+    return RegExpToShared(cx, obj, shared);
 }
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<RegExpShared>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return js::gc::Arena::thingSize(gc::AllocKind::REGEXP_SHARED) +
+        get().sizeOfExcludingThis(mallocSizeOf);
+}
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -27,31 +27,33 @@
  *   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 active on the callstack) is discarded.
- * Because of the last point, any code using a RegExpShared (viz., by executing
- * a regexp) must indicate the RegExpShared is active via RegExpGuard.
+ * up in a per-compartment table to allow reuse between objects. Lastly, on GC,
+ * every RegExpShared that is not in active use is discarded.
  */
 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
+enum RegExpFlag : uint8_t
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
     StickyFlag      = 0x08,
     UnicodeFlag     = 0x10,
 
     NoFlags         = 0x00,
@@ -87,17 +89,17 @@ CloneRegExpObject(JSContext* cx, JSObjec
  *
  * 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
+class RegExpShared : public gc::TenuredCell
 {
   public:
     enum CompilationMode {
         Normal,
         MatchOnly
     };
 
     enum ForceByteCodeEnum {
@@ -108,73 +110,75 @@ class RegExpShared
   private:
     friend class RegExpCompartment;
     friend class RegExpStatics;
 
     typedef frontend::TokenStream TokenStream;
 
     struct RegExpCompilation
     {
-        HeapPtr<jit::JitCode*> jitCode;
+        ReadBarriered<jit::JitCode*> jitCode;
         uint8_t* byteCode;
 
         RegExpCompilation() : byteCode(nullptr) {}
-        ~RegExpCompilation() { js_free(byteCode); }
 
         bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
             return byteCode || (force == DontForceByteCode && jitCode);
         }
     };
 
     /* Source to the RegExp, for lazy compilation. */
-    HeapPtr<JSAtom*>     source;
+    HeapPtr<JSAtom*>   source;
 
     RegExpFlag         flags;
+    bool               canStringMatch;
     size_t             parenCount;
-    bool               canStringMatch;
-    bool               marked_;
 
     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.
-    Vector<uint8_t*, 0, SystemAllocPolicy> tables;
+    using JitCodeTables = Vector<uint8_t*, 0, SystemAllocPolicy>;
+    JitCodeTables tables;
 
     /* Internal functions. */
-    bool compile(JSContext* cx, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
-    bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+    RegExpShared(JSAtom* source, RegExpFlag flags);
 
-    bool compileIfNecessary(JSContext* cx, HandleLinearString input,
-                            CompilationMode mode, ForceByteCodeEnum force);
+    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(JSAtom* source, RegExpFlag flags);
-    ~RegExpShared();
+    ~RegExpShared() = delete;
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
-    RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
-                            MatchPairs* matches, size_t* endIndex);
+    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 */
 
@@ -198,25 +202,19 @@ class RegExpShared
                     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 trace(JSTracer* trc);
-    bool needsSweep(JSRuntime* rt);
+    void traceChildren(JSTracer* trc);
     void discardJitCode();
-
-    bool marked() const { return marked_; }
-    void clearMarked() { marked_ = false; }
-
-    bool isMarkedGray() const;
-    void unmarkGray();
+    void finalize(FreeOp* fop);
 
     static size_t offsetOfSource() {
         return offsetof(RegExpShared, source);
     }
 
     static size_t offsetOfFlags() {
         return offsetof(RegExpShared, flags);
     }
@@ -231,99 +229,54 @@ class RegExpShared
              + offsetof(RegExpCompilation, jitCode);
     }
     static size_t offsetOfTwoByteJitCode(CompilationMode mode) {
         return offsetof(RegExpShared, compilationArray)
              + (CompilationIndex(mode, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
 #ifdef DEBUG
-    bool dumpBytecode(JSContext* cx, bool match_only, HandleLinearString input);
+    static bool dumpBytecode(JSContext* cx, MutableHandleRegExpShared res, bool match_only,
+                             HandleLinearString input);
 #endif
 };
 
-/*
- * Extend the lifetime of a given RegExpShared to at least the lifetime of
- * the guard object. See Regular Expression comment at the top.
- */
-class RegExpGuard : public JS::CustomAutoRooter
-{
-    RegExpShared* re_;
-
-    RegExpGuard(const RegExpGuard&) = delete;
-    void operator=(const RegExpGuard&) = delete;
-
-  public:
-    explicit RegExpGuard(JSContext* cx)
-      : CustomAutoRooter(cx), re_(nullptr)
-    {}
-
-    RegExpGuard(JSContext* cx, RegExpShared& re)
-      : CustomAutoRooter(cx), re_(nullptr)
-    {
-        init(re);
-    }
-
-    ~RegExpGuard() {
-        release();
-    }
-
-  public:
-    void init(RegExpShared& re) {
-        MOZ_ASSERT(!initialized());
-        re_ = &re;
-    }
-
-    void release() {
-        re_ = nullptr;
-    }
-
-    virtual void trace(JSTracer* trc) {
-        if (re_)
-            re_->trace(trc);
-    }
-
-    bool initialized() const { return !!re_; }
-    RegExpShared* re() const { MOZ_ASSERT(initialized()); return re_; }
-    RegExpShared* operator->() { return re(); }
-    RegExpShared& operator*() { return *re(); }
-};
-
 class RegExpCompartment
 {
     struct Key {
         JSAtom* atom;
         uint16_t flag;
 
         Key() {}
         Key(JSAtom* atom, RegExpFlag flag)
           : atom(atom), flag(flag)
         { }
-        MOZ_IMPLICIT Key(RegExpShared* shared)
-          : atom(shared->getSource()), flag(shared->getFlags())
+        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.
      */
-    typedef HashSet<RegExpShared*, Key, RuntimeAllocPolicy> Set;
-    Set 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_;
 
@@ -346,28 +299,29 @@ class RegExpCompartment
      *   * lastProperty is lastIndex
      *   * prototype is RegExp.prototype
      */
     ReadBarriered<Shape*> optimizableRegExpInstanceShape_;
 
     ArrayObject* createMatchResultTemplateObject(JSContext* cx);
 
   public:
-    explicit RegExpCompartment(JSRuntime* rt);
+    explicit RegExpCompartment(Zone* zone);
     ~RegExpCompartment();
 
     bool init(JSContext* cx);
     void sweep(JSRuntime* rt);
 
     bool empty() { return set_.empty(); }
 
-    bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
+    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, RegExpGuard* g);
+    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);
     }
 
@@ -479,24 +433,29 @@ class RegExpObject : public NativeObject
     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,
-                                       RegExpGuard* g);
+                                       MutableHandleRegExpShared shared);
+
+    bool hasShared() {
+        return !!sharedRef();
+    }
 
     void setShared(RegExpShared& shared) {
-        MOZ_ASSERT(!maybeShared());
-        NativeObject::setPrivate(&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);
 
@@ -506,19 +465,21 @@ class RegExpObject : public NativeObject
 #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,
-                                          RegExpGuard* g);
-    RegExpShared* maybeShared() const {
-        return static_cast<RegExpShared*>(NativeObject::getPrivate(PRIVATE_SLOT));
+                                          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
@@ -526,22 +487,22 @@ class RegExpObject : public NativeObject
  *
  * 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, RegExpGuard* g)
+RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
 {
     if (obj->is<RegExpObject>())
-        return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g);
+        return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared);
 
-    return Proxy::regexp_toShared(cx, obj, g);
+    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);
@@ -554,9 +515,34 @@ 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 */
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -76,28 +76,30 @@ RegExpStatics::executeLazy(JSContext* cx
     if (!pendingLazyEvaluation)
         return true;
 
     MOZ_ASSERT(lazySource);
     MOZ_ASSERT(matchesInput);
     MOZ_ASSERT(lazyIndex != size_t(-1));
 
     /* Retrieve or create the RegExpShared in this compartment. */
-    RegExpGuard g(cx);
-    if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &g))
+    RootedRegExpShared shared(cx);
+    RootedAtom source(cx, lazySource);
+    if (!cx->compartment()->regExps.get(cx, source, lazyFlags, &shared))
         return false;
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     /* Execute the full regular expression. */
     RootedLinearString input(cx, matchesInput);
-    RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches, nullptr);
+    RegExpRunStatus status = RegExpShared::execute(cx, &shared, input, lazyIndex, &this->matches,
+                                                   nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /*
      * RegExpStatics are only updated on successful (matching) execution.
      * Re-running the same expression must therefore produce a matching result.
      */
     MOZ_ASSERT(status == RegExpRunStatus_Success);
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -14,16 +14,17 @@
 #include "jsobj.h"
 
 #include "gc/Allocator.h"
 #include "vm/Interpreter.h"
 #include "vm/TypedArrayObject.h"
 
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
+#include "jsgcinlines.h"
 
 namespace js {
 
 inline
 AutoKeepShapeTables::AutoKeepShapeTables(JSContext* cx)
   : cx_(cx),
     prev_(cx->zone()->keepShapeTables())
 {
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -1408,17 +1408,17 @@ JSStructuredCloneWriter::startWrite(Hand
         if (backref)
             return true;
 
         ESClass cls;
         if (!GetBuiltinClass(context(), obj, &cls))
             return false;
 
         if (cls == ESClass::RegExp) {
-            RegExpGuard re(context());
+            RootedRegExpShared re(context());
             if (!RegExpToShared(context(), obj, &re))
                 return false;
             return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
                    writeString(SCTAG_STRING, re->getSource());
         } else if (cls == ESClass::Date) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -307,16 +307,17 @@ TracerConcrete<Referent>::zone() const
     return get().zoneFromAnyThread();
 }
 
 template JS::Zone* TracerConcrete<JSScript>::zone() const;
 template JS::Zone* TracerConcrete<js::LazyScript>::zone() const;
 template JS::Zone* TracerConcrete<js::Shape>::zone() const;
 template JS::Zone* TracerConcrete<js::BaseShape>::zone() const;
 template JS::Zone* TracerConcrete<js::ObjectGroup>::zone() const;
+template JS::Zone* TracerConcrete<js::RegExpShared>::zone() const;
 template JS::Zone* TracerConcrete<js::Scope>::zone() const;
 template JS::Zone* TracerConcrete<JS::Symbol>::zone() const;
 template JS::Zone* TracerConcrete<JSString>::zone() const;
 
 template<typename Referent>
 UniquePtr<EdgeRange>
 TracerConcrete<Referent>::edges(JSContext* cx, bool wantNames) const {
     UniquePtr<SimpleEdgeRange, JS::DeletePolicy<SimpleEdgeRange>> range(js_new<SimpleEdgeRange>());
@@ -329,16 +330,17 @@ TracerConcrete<Referent>::edges(JSContex
     return UniquePtr<EdgeRange>(range.release());
 }
 
 template UniquePtr<EdgeRange> TracerConcrete<JSScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::LazyScript>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Shape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::BaseShape>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::ObjectGroup>::edges(JSContext* cx, bool wantNames) const;
+template UniquePtr<EdgeRange> TracerConcrete<js::RegExpShared>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<js::Scope>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<JS::Symbol>::edges(JSContext* cx, bool wantNames) const;
 template UniquePtr<EdgeRange> TracerConcrete<JSString>::edges(JSContext* cx, bool wantNames) const;
 
 template<typename Referent>
 JSCompartment*
 TracerConcreteWithCompartment<Referent>::compartment() const
 {
@@ -393,16 +395,17 @@ Concrete<JSObject>::jsObjectConstructorN
 const char16_t Concrete<JS::Symbol>::concreteTypeName[] = u"JS::Symbol";
 const char16_t Concrete<JSScript>::concreteTypeName[] = u"JSScript";
 const char16_t Concrete<js::LazyScript>::concreteTypeName[] = u"js::LazyScript";
 const char16_t Concrete<js::jit::JitCode>::concreteTypeName[] = u"js::jit::JitCode";
 const char16_t Concrete<js::Shape>::concreteTypeName[] = u"js::Shape";
 const char16_t Concrete<js::BaseShape>::concreteTypeName[] = u"js::BaseShape";
 const char16_t Concrete<js::ObjectGroup>::concreteTypeName[] = u"js::ObjectGroup";
 const char16_t Concrete<js::Scope>::concreteTypeName[] = u"js::Scope";
+const char16_t Concrete<js::RegExpShared>::concreteTypeName[] = u"js::RegExpShared";
 
 namespace JS {
 namespace ubi {
 
 RootList::RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC, bool wantNames /* = false */)
   : noGC(noGC),
     cx(cx),
     edges(),
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -2190,16 +2190,19 @@ class BaseCompiler
         return iter_.controlOutermost();
     }
 
     ////////////////////////////////////////////////////////////
     //
     // Labels
 
     void insertBreakablePoint(CallSiteDesc::Kind kind) {
+        // The debug trap exit requires WasmTlsReg be loaded. However, since we
+        // are emitting millions of these breakable points inline, we push this
+        // loading of TLS into the FarJumpIsland created by patchCallSites.
         masm.nopPatchableToCall(CallSiteDesc(iter_.errorOffset(), kind));
     }
 
     //////////////////////////////////////////////////////////////////////
     //
     // Function prologue and epilogue.
 
     void beginFunction() {
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -387,18 +387,21 @@ ModuleGenerator::patchCallSites()
           }
           case CallSiteDesc::Breakpoint:
           case CallSiteDesc::EnterFrame:
           case CallSiteDesc::LeaveFrame: {
             Uint32Vector& jumps = metadata_->debugTrapFarJumpOffsets;
             if (jumps.empty() ||
                 uint32_t(abs(int32_t(jumps.back()) - int32_t(callerOffset))) >= JumpRange())
             {
+                // See BaseCompiler::insertBreakablePoint for why we must
+                // reload the TLS register on this path.
                 Offsets offsets;
                 offsets.begin = masm_.currentOffset();
+                masm_.loadPtr(Address(FramePointer, offsetof(Frame, tls)), WasmTlsReg);
                 uint32_t jumpOffset = masm_.farJumpWithPatch().offset();
                 offsets.end = masm_.currentOffset();
                 if (masm_.oom())
                     return false;
 
                 if (!metadata_->codeRanges.emplaceBack(CodeRange::FarJumpIsland, offsets))
                     return false;
                 if (!debugTrapFarJumps_.emplaceBack(jumpOffset))
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1939,16 +1939,24 @@ ReportZoneStats(const JS::ZoneStats& zSt
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/gc-heap"),
         zStats.scopesGCHeap,
         "Scope information for scripts.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/malloc-heap"),
         zStats.scopesMallocHeap,
         "Arrays of binding names and other binding-related data.");
 
+    ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/gc-heap"),
+        zStats.regExpSharedsGCHeap,
+        "Shared compiled regexp data.");
+
+    ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("regexp-shareds/malloc-heap"),
+        zStats.regExpSharedsMallocHeap,
+        "Shared compiled regexp data.");
+
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
         zStats.typePool,
         "Type sets and related data.");
 
     ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
         zStats.baselineStubsOptimized,
         "The Baseline JIT's optimized IC stubs (excluding code).");
 
@@ -2973,16 +2981,20 @@ JSReporter::CollectReports(WindowPaths* 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
         KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
         "Unused lazy script cells within non-empty arenas.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
         KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
         "Unused jitcode cells within non-empty arenas.");
 
+    REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/regexp-shareds"),
+        KIND_OTHER, rtStats.zTotals.unusedGCThings.regExpShared,
+        "Unused regexpshared cells within non-empty arenas.");
+
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
         KIND_OTHER, rtStats.gcHeapChunkAdmin,
         "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
         KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
         "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
 
@@ -3024,16 +3036,20 @@ JSReporter::CollectReports(WindowPaths* 
     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
         KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
         "Used lazy script cells.");
 
     MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
         KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
         "Used jitcode cells.");
 
+    MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/regexp-shareds"),
+        KIND_OTHER, rtStats.zTotals.regExpSharedsGCHeap,
+        "Used regexpshared cells.");
+
     MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
 
     // Report xpconnect.
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
         KIND_HEAP, xpcJSRuntimeSize,
         "The XPConnect runtime.");
 
--- a/layout/base/nsGenConList.cpp
+++ b/layout/base/nsGenConList.cpp
@@ -14,16 +14,17 @@ void
 nsGenConList::Clear()
 {
   // Delete entire list.
   mNodes.Clear();
   while (nsGenConNode* node = mList.popFirst()) {
     delete node;
   }
   mSize = 0;
+  mLastInserted = nullptr;
 }
 
 bool
 nsGenConList::DestroyNodesFor(nsIFrame* aFrame)
 {
   // This algorithm relies on the invariant that nodes of a frame are
   // put contiguously in the linked list. This is guaranteed because
   // each frame is mapped to only one (nsIContent, pseudoType) pair,
@@ -36,16 +37,19 @@ nsGenConList::DestroyNodesFor(nsIFrame* 
   MOZ_ASSERT(node->mPseudoFrame == aFrame);
 
   while (node && node->mPseudoFrame == aFrame) {
     nsGenConNode* nextNode = Next(node);
     Destroy(node);
     node = nextNode;
   }
 
+  // Modification of the list invalidates the cached pointer.
+  mLastInserted = nullptr;
+
   return true;
 }
 
 /**
  * Compute the type of the pseudo and the content for the pseudo that
  * we'll use for comparison purposes.
  * @param aContent the content to use is stored here; it's the element
  * that generated the ::before or ::after content, or (if not for generated
@@ -103,16 +107,21 @@ nsGenConList::NodeAfter(const nsGenConNo
 }
 
 void
 nsGenConList::Insert(nsGenConNode* aNode)
 {
   // Check for append.
   if (mList.isEmpty() || NodeAfter(aNode, mList.getLast())) {
     mList.insertBack(aNode);
+  } else if (mLastInserted && mLastInserted != mList.getLast() &&
+             NodeAfter(aNode, mLastInserted) &&
+             NodeAfter(Next(mLastInserted), aNode)) {
+    // Fast path for inserting many consecutive nodes in one place
+    mLastInserted->setNext(aNode);
   } else {
     // Binary search.
 
     // the range of indices at which |aNode| could end up.
     // (We already know it can't be at index mSize.)
     uint32_t first = 0, last = mSize - 1;
 
     // A cursor to avoid walking more than the length of the list.
@@ -137,16 +146,18 @@ nsGenConList::Insert(nsGenConNode* aNode
       } else {
         last = test;
       }
     }
     curNode->setPrevious(aNode);
   }
   ++mSize;
 
+  mLastInserted = aNode;
+
   // Set the mapping only if it is the first node of the frame.
   // The DEBUG blocks below are for ensuring the invariant required by
   // nsGenConList::DestroyNodesFor. See comment there.
   if (IsFirst(aNode) ||
       Prev(aNode)->mPseudoFrame != aNode->mPseudoFrame) {
 #ifdef DEBUG
     if (nsGenConNode* oldFrameFirstNode = mNodes.Get(aNode->mPseudoFrame)) {
       MOZ_ASSERT(Next(aNode) == oldFrameFirstNode,
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -81,17 +81,17 @@ protected:
 };
 
 class nsGenConList {
 protected:
   mozilla::LinkedList<nsGenConNode> mList;
   uint32_t mSize;
 
 public:
-  nsGenConList() : mSize(0) {}
+  nsGenConList() : mSize(0), mLastInserted(nullptr) {}
   ~nsGenConList() { Clear(); }
   void Clear();
   static nsGenConNode* Next(nsGenConNode* aNode) {
     MOZ_ASSERT(aNode, "aNode cannot be nullptr!");
     return aNode->getNext();
   }
   static nsGenConNode* Prev(nsGenConNode* aNode) {
     MOZ_ASSERT(aNode, "aNode cannot be nullptr!");
@@ -122,11 +122,15 @@ private:
   {
     MOZ_ASSERT(aNode, "aNode cannot be nullptr!");
     delete aNode;
     mSize--;
   }
 
   // Map from frame to the first nsGenConNode of it in the list.
   nsDataHashtable<nsPtrHashKey<nsIFrame>, nsGenConNode*> mNodes;
+
+  // A weak pointer to the node most recently inserted, used to avoid repeated
+  // list traversals in Insert().
+  nsGenConNode* mLastInserted;
 };
 
 #endif /* nsGenConList_h___ */
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1693,21 +1693,24 @@ nsImageFrame::PaintImage(nsRenderingCont
                                                      StylePosition(),
                                                      &anchorPoint);
 
   uint32_t flags = aFlags;
   if (mForceSyncDecoding) {
     flags |= imgIContainer::FLAG_SYNC_DECODE;
   }
 
+  Maybe<SVGImageContext> svgContext;
+  SVGImageContext::MaybeInitAndStoreContextPaint(svgContext, this, aImage);
+
   DrawResult result =
     nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(),
       PresContext(), aImage,
       nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
-      /* no SVGImageContext */ Nothing(), flags, &anchorPoint);
+      svgContext, flags, &anchorPoint);
 
   nsImageMap* map = GetImageMap();
   if (map) {
     gfxPoint devPixelOffset =
       nsLayoutUtils::PointToGfxPoint(dest.TopLeft(),
                                      PresContext()->AppUnitsPerDevPixel());
     AutoRestoreTransform autoRestoreTransform(drawTarget);
     drawTarget->SetTransform(
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/black100x100-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <div style="width: 100px; height: 100px; background: black"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/blue100x100-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <div style="width: 100px; height: 100px; background: blue"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Basic context-fill test</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  fill: lime;
+}
+
+    </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-fill-02.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Basic context-fill test (without a fallback color)</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  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>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-03.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill where fill is semi-transparent</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  fill: rgb(0% 100% 0% / 50%);
+}
+
+    </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 red'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-04.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill with fill-opacity</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  fill: lime;
+}
+
+    </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 red' fill-opacity='0.5'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-05.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill with different fill values (test image caching correctness)</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+}
+
+#img1 {
+  fill: blue;
+}
+
+#img2 {
+  fill: yellow;
+}
+
+    </style>
+  </head>
+  <body>
+    <img id="img1" src="context-fill-05.svg">
+    <img id="img2" src="context-fill-05.svg">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-05.svg
@@ -0,0 +1,3 @@
+<svg xmlns='http://www.w3.org/2000/svg'>
+  <rect width='100%' height='100%' fill='context-fill red'/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-06.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill works as a stroke value</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  fill: lime;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-fill red'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-07-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  width: 100px;
+  height: 100px;
+  background-color: black;
+  display: inline-block;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-07.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-fill when no context fill is provided</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+}
+
+    </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>">
+    <!-- The initial value for 'fill' is black, so we expect that to be the
+         context-fill that the following rect uses (the fallback color should
+         not be used). -->
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='context-fill red'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-fill-or-stroke-05-ref.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+  <style>
+
+div {
+  width: 100px;
+  height: 100px;
+  display: inline-block;
+}
+
+  </style>
+</head>
+<body>
+  <div style="background: blue"></div>
+  <div style="background: yellow"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-01.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Basic context-stroke test</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  stroke: lime;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke blue'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-02.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Basic context-stroke test (without a fallback color)</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  stroke: lime;
+  border: 1px solid black;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-03.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke where stroke is semi-transparent</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  stroke: rgb(0% 100% 0% / 50%);
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke red'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-04.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke with stroke-opacity</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  stroke: lime;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke red' stroke-opacity='0.5'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-05.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke with different stroke values (test image caching correctness)</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+}
+
+#img1 {
+  stroke: blue;
+}
+
+#img2 {
+  stroke: yellow;
+}
+
+    </style>
+  </head>
+  <body>
+    <img id="img1" src="context-stroke-05.svg">
+    <img id="img2" src="context-stroke-05.svg">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-05.svg
@@ -0,0 +1,3 @@
+<svg xmlns='http://www.w3.org/2000/svg'>
+  <line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke red'/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-06.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke works as a fill value</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  stroke: lime;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect width='100%' height='100%' fill='context-stroke red'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-07-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  width: 100px;
+  height: 100px;
+  display: inline-block;
+  border: 1px solid black;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+    <div style="background-color: lime;"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/context-stroke-07.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Test context-stroke when no context stroke is provided</title>
+    <style>
+
+img {
+  width: 100px;
+  height: 100px;
+  border: 1px solid black;
+}
+
+    </style>
+  </head>
+  <body>
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke'/></svg>">
+    <!-- The following image currently results in the 'lime' fallback color
+         being used, but it probably shouldn't. See bug 1351243. -->
+    <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='50%' x2='100%' y2='50%' stroke-width='120%' stroke='context-stroke lime'/></svg>">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/lime100x100-50pct-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <div style="width: 100px; height: 100px; background: rgb(0% 100% 0% / 50%)"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/lime100x100-w-border-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <div style="width: 100px; height: 100px; background: lime; border: 1px solid black"></div>
+</body>
+</html>
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -49,16 +49,38 @@ fuzzy-if(winWidget&&!d2d,1,10000) fuzzy-
 fuzzy(1,2) fuzzy-if(azureSkia,1,40000) == canvas-drawImage-alpha-2.html canvas-drawImage-alpha-2-ref.html
 
 == canvas-drawImage-slice-1a.html lime100x100-ref.html
 == canvas-drawImage-slice-1b.html lime100x100-ref.html
 
 == canvas-drawImage-origin-clean-1.html lime100x100-ref.html
 == canvas-drawImage-transform-restored.html canvas-drawImage-transform-restored-ref.html
 
+# Context paint tests (this feature is currently not part of any spec.)
+== context-fill-01.html blue100x100-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-fill-01.html lime100x100-ref.html
+== context-fill-02.html transparent100x100-w-border-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-fill-02.html lime100x100-w-border-ref.html
+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
+fuzzy-if(winWidget,1,10000) test-pref(svg.context-properties.content.enabled,true) == context-fill-04.html lime100x100-50pct-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-fill-05.html context-fill-or-stroke-05-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-fill-06.html lime100x100-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-fill-07.html context-fill-07-ref.html
+== context-stroke-01.html blue100x100-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-stroke-01.html lime100x100-ref.html
+== context-stroke-02.html transparent100x100-w-border-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-stroke-02.html lime100x100-w-border-ref.html
+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
+fuzzy-if(winWidget,1,10000) test-pref(svg.context-properties.content.enabled,true) == context-stroke-04.html lime100x100-50pct-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-stroke-05.html context-fill-or-stroke-05-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-stroke-06.html lime100x100-ref.html
+test-pref(svg.context-properties.content.enabled,true) == context-stroke-07.html context-stroke-07-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
 == img-simple-6.html  lime100x100-ref.html
 fuzzy-if(skiaContent,255,27) == img-simple-7.html  img-simple-7-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/as-image/transparent100x100-w-border-ref.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+  <div style="width: 100px; height: 100px; border: 1px solid black"></div>
+</body>
+</html>
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -142,16 +142,21 @@ StyleSheet*
 nsLayoutStylesheetCache::MinimalXULSheet()
 {
   return mMinimalXULSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::XULSheet()
 {
+  if (!mXULSheet) {
+    LoadSheetURL("chrome://global/content/xul.css",
+                 &mXULSheet, eAgentSheetFeatures, eCrash);
+  }
+
   return mXULSheet;
 }
 
 StyleSheet*
 nsLayoutStylesheetCache::QuirkSheet()
 {
   return mQuirkSheet;
 }
@@ -333,18 +338,20 @@ nsLayoutStylesheetCache::nsLayoutStylesh
   LoadSheetURL("resource://gre-resources/html.css",
                &mHTMLSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("chrome://global/content/minimal-xul.css",
                &mMinimalXULSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("resource://gre-resources/quirk.css",
                &mQuirkSheet, eAgentSheetFeatures, eCrash);
   LoadSheetURL("resource://gre/res/svg.css",
                &mSVGSheet, eAgentSheetFeatures, eCrash);
-  LoadSheetURL("chrome://global/content/xul.css",
-               &mXULSheet, eAgentSheetFeatures, eCrash);
+  if (XRE_IsParentProcess()) {
+    // We know we need xul.css for the UI, so load that now too:
+    XULSheet();
+  }
 
   if (gUserContentSheetURL) {
     MOZ_ASSERT(XRE_IsContentProcess(), "Only used in content processes.");
     LoadSheet(gUserContentSheetURL, &mUserContentSheet, eUserSheetFeatures, eLogToConsole);
     gUserContentSheetURL = nullptr;
   }
 
   // The remaining sheets are created on-demand do to their use being rarer
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -87,25 +87,25 @@ public:
 
   static SVGContextPaint* GetContextPaint(nsIContent* aContent);
 
   // XXX This gets the geometry params from the gfxContext.  We should get that
   // information from the actual paint context!
   void InitStrokeGeometry(gfxContext *aContext,
                           float devUnitsPerSVGUnit);
 
-  FallibleTArray<gfxFloat>& GetStrokeDashArray() {
+  const FallibleTArray<gfxFloat>& GetStrokeDashArray() const {
     return mDashes;
   }
 
-  gfxFloat GetStrokeDashOffset() {
+  gfxFloat GetStrokeDashOffset() const {
     return mDashOffset;
   }
 
-  gfxFloat GetStrokeWidth() {
+  gfxFloat GetStrokeWidth() const {
     return mStrokeWidth;
   }
 
   virtual uint32_t Hash() const {
     MOZ_ASSERT_UNREACHABLE("Only VectorImage needs to hash, and that should "
                            "only be operating on our SVGEmbeddingContextPaint "
                            "subclass");
     return 0;
@@ -240,16 +240,25 @@ public:
 class SVGEmbeddingContextPaint : public SVGContextPaint
 {
   typedef gfx::Color Color;
   typedef mozilla::image::DrawResult DrawResult;
 
 public:
   SVGEmbeddingContextPaint() {}
 
+  bool operator==(const SVGEmbeddingContextPaint& aOther) const {
+    MOZ_ASSERT(GetStrokeWidth() == aOther.GetStrokeWidth() &&
+               GetStrokeDashOffset() == aOther.GetStrokeDashOffset() &&
+               GetStrokeDashArray() == aOther.GetStrokeDashArray(),
+               "We don't currently include these in the context information "
+               "from an embedding element");
+    return mFill == aOther.mFill && mStroke == aOther.mStroke;
+  }
+
   void SetFill(nscolor aFill) {
     mFill.emplace(gfx::ToDeviceColor(aFill));
   }
   void SetStroke(nscolor aStroke) {
     mStroke.emplace(gfx::ToDeviceColor(aStroke));
   }
 
   /**
--- a/layout/svg/SVGImageContext.cpp
+++ b/layout/svg/SVGImageContext.cpp
@@ -10,32 +10,41 @@
 // Keep others in (case-insensitive) order:
 #include "gfxUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsIFrame.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 
-bool
-SVGImageContext::MaybeStoreContextPaint(nsIFrame* aFromFrame)
+/* static */ void
+SVGImageContext::MaybeInitAndStoreContextPaint(Maybe<SVGImageContext>& aContext,
+                                               nsIFrame* aFromFrame,
+                                               imgIContainer* aImgContainer)
 {
   static bool sEnabledForContent = false;
   static bool sEnabledForContentCached = false;
 
+  MOZ_ASSERT(!aContext, "The emplace() call below with overwrite this object");
+
   if (!sEnabledForContentCached) {
     Preferences::AddBoolVarCache(&sEnabledForContent,
                                  "svg.context-properties.content.enabled", false);
     sEnabledForContentCached = true;
   }
 
   if (!sEnabledForContent &&
       !aFromFrame->PresContext()->IsChrome()) {
     // Context paint is pref'ed off for content and this is a content doc.
-    return false;
+    return;
+  }
+
+  if (aImgContainer->GetType() != imgIContainer::TYPE_VECTOR) {
+    // Avoid this overhead for raster images.
+    return;
   }
 
   // XXX return early if the 'context-properties' property is not set.
 
   bool haveContextPaint = false;
 
   RefPtr<SVGEmbeddingContextPaint> contextPaint = new SVGEmbeddingContextPaint();
 
@@ -48,15 +57,14 @@ SVGImageContext::MaybeStoreContextPaint(
     contextPaint->SetFill(style->mFill.GetColor());
   }
   if (style->mStroke.Type() == eStyleSVGPaintType_Color) {
     haveContextPaint = true;
     contextPaint->SetStroke(style->mStroke.GetColor());
   }
 
   if (haveContextPaint) {
-    mContextPaint = contextPaint.forget();
+    aContext.emplace();
+    aContext->mContextPaint = contextPaint.forget();
   }
-
-  return mContextPaint != nullptr;
 }
 
 } // namespace mozilla
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -52,17 +52,19 @@ public:
                            gfxFloat aOpacity = 1.0,
                            bool aIsPaintingSVGImageElement = false)
     : mViewportSize(aViewportSize)
     , mPreserveAspectRatio(aPreserveAspectRatio)
     , mGlobalOpacity(aOpacity)
     , mIsPaintingSVGImageElement(aIsPaintingSVGImageElement)
   { }
 
-  bool MaybeStoreContextPaint(nsIFrame* aFromFrame);
+  static void MaybeInitAndStoreContextPaint(Maybe<SVGImageContext>& aContext,
+                                            nsIFrame* aFromFrame,
+                                            imgIContainer* aImgContainer);
 
   const Maybe<CSSIntSize>& GetViewportSize() const {
     return mViewportSize;
   }
 
   void SetViewportSize(const Maybe<CSSIntSize>& aSize) {
     mViewportSize = aSize;
   }
@@ -74,26 +76,34 @@ public:
   void SetPreserveAspectRatio(const Maybe<SVGPreserveAspectRatio>& aPAR) {
     mPreserveAspectRatio = aPAR;
   }
 
   gfxFloat GetGlobalOpacity() const {
     return mGlobalOpacity;
   }
 
-  const SVGContextPaint* GetContextPaint() const {
+  const SVGEmbeddingContextPaint* GetContextPaint() const {
     return mContextPaint.get();
   }
 
   bool IsPaintingForSVGImageElement() const {
     return mIsPaintingSVGImageElement;
   }
 
   bool operator==(const SVGImageContext& aOther) const {
-    return mViewportSize == aOther.mViewportSize &&
+    bool contextPaintIsEqual =
+      // neither have context paint, or they have the same object:
+      (mContextPaint == aOther.mContextPaint) ||
+      // or both have context paint that are different but equivalent objects:
+      (mContextPaint && aOther.mContextPaint &&
+       *mContextPaint == *aOther.mContextPaint);
+
+    return contextPaintIsEqual &&
+           mViewportSize == aOther.mViewportSize &&
            mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
            mGlobalOpacity == aOther.mGlobalOpacity &&
            mIsPaintingSVGImageElement == aOther.mIsPaintingSVGImageElement;
   }
 
   bool operator!=(const SVGImageContext& aOther) const {
     return !(*this == aOther);
   }
@@ -114,17 +124,17 @@ private:
   static uint32_t HashSize(const CSSIntSize& aSize) {
     return HashGeneric(aSize.width, aSize.height);
   }
   static uint32_t HashPAR(const SVGPreserveAspectRatio& aPAR) {
     return aPAR.Hash();
   }
 
   // NOTE: When adding new member-vars, remember to update Hash() & operator==.
-  RefPtr<SVGContextPaint>       mContextPaint;
+  RefPtr<SVGEmbeddingContextPaint> mContextPaint;
   Maybe<CSSIntSize>             mViewportSize;
   Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
   gfxFloat                      mGlobalOpacity;
   bool                          mIsPaintingSVGImageElement;
 };
 
 } // namespace mozilla
 
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -405,21 +405,17 @@ nsImageBoxFrame::PaintImage(nsRenderingC
     dest = nsLayoutUtils::ComputeObjectDestRect(constraintRect,
                                                 intrinsicSize,
                                                 intrinsicRatio,
                                                 StylePosition(),
                                                 anchorPoint.ptr());
   }
 
   Maybe<SVGImageContext> svgContext;
-  if (imgCon->GetType() == imgIContainer::TYPE_VECTOR) {
-    // We avoid this overhead for raster images.
-    svgContext.emplace();
-    svgContext->MaybeStoreContextPaint(this);
-  }
+  SVGImageContext::MaybeInitAndStoreContextPaint(svgContext, this, imgCon);
 
   return nsLayoutUtils::DrawSingleImage(
            *aRenderingContext.ThebesContext(),
            PresContext(), imgCon,
            nsLayoutUtils::GetSamplingFilterForFrame(this),
            dest, dirty,
            svgContext, aFlags,
            anchorPoint.ptrOr(nullptr),
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -193,17 +193,17 @@ WebrtcVideoConduit::WebrtcVideoConduit(R
   , mStartBitrate(0)
   , mPrefMaxBitrate(0)
   , mNegotiatedMaxBitrate(0)
   , mMinBitrateEstimate(0)
   , mCodecMode(webrtc::kRealtimeVideo)
   , mCall(aCall) // refcounted store of the call object
   , mSendStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
   , mRecvStreamConfig(this) // 'this' is stored but not  dereferenced in the constructor.
-  , mRecvSSRCSet(false)
+  , mRecvSSRC(0)
   , mRecvSSRCSetInProgress(false)
   , mSendCodecPlugin(nullptr)
   , mRecvCodecPlugin(nullptr)
   , mVideoStatsTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
 {
   mRecvStreamConfig.renderer = this;
 
   // Video Stats Callback
@@ -694,17 +694,16 @@ bool
 WebrtcVideoConduit::SetRemoteSSRC(unsigned int ssrc)
 {
   mRecvStreamConfig.rtp.remote_ssrc = ssrc;
 
   unsigned int current_ssrc;
   if (!GetRemoteSSRC(&current_ssrc)) {
     return false;
   }
-  mRecvSSRCSet = true;
 
   if (current_ssrc == ssrc) {
     return true;
   }
 
   bool wasReceiving = mEngineReceiving;
   if (StopReceiving() != kMediaConduitNoError) {
     return false;
@@ -1172,30 +1171,34 @@ WebrtcVideoConduit::ConfigureRecvMediaCo
     mRecvStreamConfig.rtp.keyframe_method = kf_request_method;
 
     if (use_fec) {
       mRecvStreamConfig.rtp.fec.ulpfec_payload_type = ulpfec_payload_type;
       mRecvStreamConfig.rtp.fec.red_payload_type = red_payload_type;
       mRecvStreamConfig.rtp.fec.red_rtx_payload_type = -1;
     }
 
-    if (!mRecvSSRCSet) {
+    // XXX ugh! same SSRC==0 problem that webrtc.org has
+    if (mRecvSSRC == 0) {
       // Handle un-signalled SSRCs by creating a random one and then when it actually gets set,
       // we'll destroy and recreate.  Simpler than trying to unwind all the logic that assumes
       // the receive stream is created and started when we ConfigureRecvMediaCodecs()
       unsigned int ssrc;
       do {
         SECStatus rv = PK11_GenerateRandom(reinterpret_cast<unsigned char*>(&ssrc), sizeof(ssrc));
         if (rv != SECSuccess) {
           return kMediaConduitUnknownError;
         }
       } while (ssrc == 0); // webrtc.org code has fits if you select an SSRC of 0
 
       mRecvStreamConfig.rtp.remote_ssrc = ssrc;
     }
+    // Either set via SetRemoteSSRC, or temp one we created.
+    mRecvSSRC = mRecvStreamConfig.rtp.remote_ssrc;
+
     // 0 isn't allowed.  Would be best to ask for a random SSRC from the
     // RTP code.  Would need to call rtp_sender.cc -- GenerateNewSSRC(),
     // which isn't exposed.  It's called on collision, or when we decide to
     // send.  it should be called on receiver creation.  Here, we're
     // generating the SSRC value - but this causes ssrc_forced in set in
     // rtp_sender, which locks us into the SSRC - even a collision won't
     // change it!!!
     MOZ_ASSERT(!mSendStreamConfig.rtp.ssrcs.empty());
@@ -1404,33 +1407,35 @@ WebrtcVideoConduit::SelectBitrates(
   } else {
     // At low framerates, don't reduce bandwidth as much - cut slope to 1/2.
     // Mostly this would be ultra-low-light situations/mobile or screensharing.
     out_min = out_min * ((10 - (framerate / 2)) / 30);
     out_start = out_start * ((10 - (framerate / 2)) / 30);
     out_max = std::max(static_cast<int>(out_max * ((10 - (framerate / 2)) / 30)), cap);
   }
 
+  // Note: mNegotiatedMaxBitrate is the max transport bitrate - it applies to
+  // a single codec encoding, but should also apply to the sum of all
+  // simulcast layers in this encoding!  So sum(layers.maxBitrate) <=
+  // mNegotiatedMaxBitrate
+  // Note that out_max already has had mPrefMaxBitrate applied to it
+  out_max = MinIgnoreZero((int)mNegotiatedMaxBitrate, out_max);
+  out_min = std::min(out_min, out_max);
+  out_start = std::min(out_start, out_max);
+
   if (mMinBitrate && mMinBitrate > out_min) {
     out_min = mMinBitrate;
   }
   // If we try to set a minimum bitrate that is too low, ViE will reject it.
   out_min = std::max(kViEMinCodecBitrate_bps, out_min);
   if (mStartBitrate && mStartBitrate > out_start) {
     out_start = mStartBitrate;
   }
   out_start = std::max(out_start, out_min);
 
-  // Note: mNegotiatedMaxBitrate is the max transport bitrate - it applies to
-  // a single codec encoding, but should also apply to the sum of all
-  // simulcast layers in this encoding!  So sum(layers.maxBitrate) <=
-  // mNegotiatedMaxBitrate
-  // Note that out_max already has had mPrefMaxBitrate applied to it
-  out_max = MinIgnoreZero((int)mNegotiatedMaxBitrate, out_max);
-
   MOZ_ASSERT(mPrefMaxBitrate == 0 || out_max <= mPrefMaxBitrate);
 }
 
 template <class t>
 static void
 ConstrainPreservingAspectRatioExact(uint32_t max_fs, t* width, t* height)
 {
   // We could try to pick a better starting divisor, but it won't make any real
@@ -1798,20 +1803,25 @@ WebrtcVideoConduit::DeliverPacket(const 
 
   return kMediaConduitNoError;
 }
 
 MediaConduitErrorCode
 WebrtcVideoConduit::ReceivedRTPPacket(const void* data, int len, uint32_t ssrc)
 {
   bool queue = mRecvSSRCSetInProgress;
-  if (!mRecvSSRCSet && !mRecvSSRCSetInProgress) {
+  if (mRecvSSRC != ssrc && !queue) {
+    // we "switch" here immediately, but buffer until the queue is released
+    mRecvSSRC = ssrc;
     mRecvSSRCSetInProgress = true;
     queue = true;
-    // Handle the ssrc-not-signaled case; lock onto first ssrc
+    // any queued packets are from a previous switch that hasn't completed
+    // yet; drop them and only process the latest SSRC
+    mQueuedPackets.Clear();
+    // Handle the unknown ssrc (and ssrc-not-signaled case).
     // We can't just do this here; it has to happen on MainThread :-(
     // We also don't want to drop the packet, nor stall this thread, so we hold
     // the packet (and any following) for inserting once the SSRC is set.
 
     // Ensure lamba captures refs
     RefPtr<WebrtcVideoConduit> self = this;
     nsCOMPtr<nsIThread> thread;
     if (NS_WARN_IF(NS_FAILED(NS_GetCurrentThread(getter_AddRefs(thread))))) {
@@ -1821,29 +1831,33 @@ WebrtcVideoConduit::ReceivedRTPPacket(co
           // Normally this is done in CreateOrUpdateMediaPipeline() for
           // initial creation and renegotiation, but here we're rebuilding the
           // Receive channel at a lower level.  This is needed whenever we're
           // creating a GMPVideoCodec (in particular, H264) so it can communicate
           // errors to the PC.
           WebrtcGmpPCHandleSetter setter(self->mPCHandle);
           self->SetRemoteSSRC(ssrc); // this will likely re-create the VideoReceiveStream
           // We want to unblock the queued packets on the original thread
-          thread->Dispatch(media::NewRunnableFrom([self]() mutable {
-                self->mRecvSSRCSetInProgress = false;
-                // SSRC is set; insert queued packets
-                for (auto& packet : self->mQueuedPackets) {
-                  CSFLogDebug(logTag, "%s: seq# %u, Len %d ", __FUNCTION__,
-                              (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
+          thread->Dispatch(media::NewRunnableFrom([self, ssrc]() mutable {
+                if (ssrc == self->mRecvSSRC) {
+                  // SSRC is set; insert queued packets
+                  for (auto& packet : self->mQueuedPackets) {
+                    CSFLogDebug(logTag, "%s: seq# %u, Len %d ", __FUNCTION__,
+                                (uint16_t)ntohs(((uint16_t*) packet->mData)[1]), packet->mLen);
 
-                  if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
-                    CSFLogError(logTag, "%s RTP Processing Failed", __FUNCTION__);
-                    // Keep delivering and then clear the queue
+                    if (self->DeliverPacket(packet->mData, packet->mLen) != kMediaConduitNoError) {
+                      CSFLogError(logTag, "%s RTP Processing Failed", __FUNCTION__);
+                      // Keep delivering and then clear the queue
+                    }
                   }
+                  self->mQueuedPackets.Clear();
+                  // we don't leave inprogress until there are no changes in-flight
+                  self->mRecvSSRCSetInProgress = false;
                 }
-                self->mQueuedPackets.Clear();
+                // else this is an intermediate switch; another is in-flight
 
                 return NS_OK;
               }), NS_DISPATCH_NORMAL);
           return NS_OK;
         }));
     // we'll return after queuing
   }
   if (queue) {
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -498,20 +498,20 @@ private:
   // WEBRTC.ORG Call API
   RefPtr<WebRtcCallWrapper> mCall;
 
   webrtc::VideoSendStream::Config mSendStreamConfig;
   VideoEncoderConfigBuilder mEncoderConfig;
   webrtc::VideoCodecH264 mEncoderSpecificH264;
 
   webrtc::VideoReceiveStream::Config mRecvStreamConfig;
-  // We can't create mRecvStream without knowing the remote SSRC
-  // Atomic since we key off this on packet insertion, which happens
-  // on a different thread.
-  Atomic<bool> mRecvSSRCSet;
+
+  // accessed on creation, and when receiving packets
+  uint32_t mRecvSSRC; // this can change during a stream!
+
   // The runnable to set the SSRC is in-flight; queue packets until it's done.
   bool mRecvSSRCSetInProgress;
   struct QueuedPacket {
     int mLen;
     uint8_t mData[1];
   };
   nsTArray<UniquePtr<QueuedPacket>> mQueuedPackets;
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2971,16 +2971,24 @@ pref("svg.paint-order.enabled", true);
 pref("svg.marker-improvements.enabled", true);
 
 // Is support for the new getBBox method from SVG 2 enabled?
 // See https://svgwg.org/svg2-draft/single-page.html#types-SVGBoundingBoxOptions
 pref("svg.new-getBBox.enabled", false);
 
 pref("svg.transform-box.enabled", true);
 
+# This pref controls whether the 'context-fill' and 'context-stroke' keywords
+# can be used in SVG-as-an-image in the content processes to use the fill/
+# stroke specified on the element that embeds the image.  (These keywords are
+# always enabled in the chrome process, regardless of this pref.)
+# Also, these keywords are currently not part of any spec, which is partly why
+# we disable them for web content.
+pref("svg.context-properties.content.enabled", false);
+
 // Default font types and sizes by locale
 pref("font.default.ar", "sans-serif");
 pref("font.minimum-size.ar", 0);
 pref("font.size.variable.ar", 16);
 pref("font.size.fixed.ar", 13);
 
 pref("font.default.el", "serif");
 pref("font.minimum-size.el", 0);
--- a/moz.configure
+++ b/moz.configure
@@ -300,16 +300,17 @@ set_config('MOZ_SYSTEM_HUNSPELL', depend
 
 @depends(target)
 @imports('os')
 def makensis_progs(target):
     if target.kernel != 'WINNT':
         return
 
     candidates = [
+        'makensis-3.01.exe',
         'makensis-3.0b3.exe',
         'makensis-3.0b1.exe',
         'makensis.exe',
         'makensis',
     ]
 
     # Look for nsis installed by msys environment. But only the 32-bit version.
     # We use an absolute path and insert as the first entry so it is preferred
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -220,16 +220,20 @@ static const DllBlockInfo sWindowsDllBlo
   { "vorbis.acm", MAKE_VERSION(0, 0, 3, 6) },
 
   // AhnLab Internet Security, bug 1311969
   { "nzbrcom.dll", ALL_VERSIONS },
 
   // K7TotalSecurity, bug 1339083.
   { "k7pswsen.dll", MAKE_VERSION(15, 2, 2, 95) },
 
+  // smci*.dll - goobzo crashware (bug 1339908)
+  { "smci32.dll", ALL_VERSIONS },
+  { "smci64.dll", ALL_VERSIONS },
+
   { nullptr, 0 }
 };
 
 #ifndef STATUS_DLL_NOT_FOUND
 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
 #endif
 
 // define this for very verbose dll load debug spew
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2994,25 +2994,19 @@ Http2Session::WriteSegments(nsAHttpSegme
 nsresult
 Http2Session::Finish0RTT(bool aRestart, bool aAlpnChanged)
 {
   MOZ_ASSERT(mAttemptingEarlyData);
   LOG3(("Http2Session::Finish0RTT %p aRestart=%d aAlpnChanged=%d", this,
         aRestart, aAlpnChanged));
 
   for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
-    // Instead of passing (aRestart, aAlpnChanged) here, we use aAlpnChanged for
-    // both arguments because as long as the alpn token stayed the same, we can
-    // just reuse what we have in our buffer to send instead of having to have
-    // the transaction rewind and read it all over again. We only need to rewind
-    // the transaction if we're switching to a new protocol, because our buffer
-    // won't get used in that case.
     Http2Stream *stream = mStreamIDHash.Get(m0RTTStreams[i]);
     if (stream) {
-      stream->Finish0RTT(aAlpnChanged, aAlpnChanged);
+      stream->Finish0RTT(aRestart, aAlpnChanged);
     }
   }
 
   if (aRestart) {
     // 0RTT failed
     if (aAlpnChanged) {
       // This is a slightly more involved case - we need to get all our streams/
       // transactions back in the queue so they can restart as http/1
--- a/netwerk/protocol/http/Http2Stream.cpp
+++ b/netwerk/protocol/http/Http2Stream.cpp
@@ -1508,14 +1508,30 @@ Http2Stream::Do0RTT()
   return mAttempting0RTT;
 }
 
 nsresult
 Http2Stream::Finish0RTT(bool aRestart, bool aAlpnChanged)
 {
   MOZ_ASSERT(mTransaction);
   mAttempting0RTT = false;
-  return mTransaction->Finish0RTT(aRestart, aAlpnChanged);
+  // Instead of passing (aRestart, aAlpnChanged) here, we use aAlpnChanged for
+  // both arguments because as long as the alpn token stayed the same, we can
+  // just reuse what we have in our buffer to send instead of having to have
+  // the transaction rewind and read it all over again. We only need to rewind
+  // the transaction if we're switching to a new protocol, because our buffer
+  // won't get used in that case.
+  // ..
+  // however, we send in the aRestart value to indicate that early data failed
+  // for devtools purposes
+  nsresult rv = mTransaction->Finish0RTT(aAlpnChanged, aAlpnChanged);
+  if (aRestart) {
+    nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
+    if (trans) {
+      trans->Refused0RTT();
+    }
+  }
+  return rv;
 }
 
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpAtomList.h
+++ b/netwerk/protocol/http/nsHttpAtomList.h
@@ -87,10 +87,11 @@ HTTP_ATOM(Upgrade,                   "Up
 HTTP_ATOM(User_Agent,                "User-Agent")
 HTTP_ATOM(Vary,                      "Vary")
 HTTP_ATOM(Version,                   "Version")
 HTTP_ATOM(WWW_Authenticate,          "WWW-Authenticate")
 HTTP_ATOM(Warning,                   "Warning")
 HTTP_ATOM(X_Content_Type_Options,    "X-Content-Type-Options")
 HTTP_ATOM(X_Firefox_Spdy,            "X-Firefox-Spdy")
 HTTP_ATOM(X_Firefox_Spdy_Proxy,      "X-Firefox-Spdy-Proxy")
+HTTP_ATOM(X_Firefox_Early_Data,      "X-Firefox-Early-Data")
 
 // methods are case sensitive and do not use atom table
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -136,16 +136,17 @@ nsHttpTransaction::nsHttpTransaction()
     , mForTakeResponseHead(nullptr)
     , mResponseHeadTaken(false)
     , mTopLevelOuterContentWindowId(0)
     , mSubmittedRatePacing(false)
     , mPassedRatePacing(false)
     , mSynchronousRatePaceRequest(false)
     , mClassOfService(0)
     , m0RTTInProgress(false)
+    , mEarlyDataDisposition(EARLY_NONE)
     , mTransportStatus(NS_OK)
 {
     LOG(("Creating nsHttpTransaction @%p\n", this));
 
 #ifdef MOZ_VALGRIND
     memset(&mSelfAddr, 0, sizeof(NetAddr));
     memset(&mPeerAddr, 0, sizeof(NetAddr));
 #endif
@@ -730,16 +731,21 @@ nsHttpTransaction::ReadSegments(nsAHttpS
         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     }
 
     mDeferredSendProgress = false;
     mReader = reader;
     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
     mReader = nullptr;
 
+    if (m0RTTInProgress && (mEarlyDataDisposition == EARLY_NONE) &&
+        NS_SUCCEEDED(rv) && (*countRead > 0)) {
+        mEarlyDataDisposition = EARLY_SENT;
+    }
+
     if (mDeferredSendProgress && mConnection && mConnection->Transport()) {
         // to avoid using mRequestStream concurrently, OnTransportStatus()
         // did not report upload status off the ReadSegments() stack from nsSocketTransport
         // do it now.
         OnTransportStatus(mConnection->Transport(), NS_NET_STATUS_SENDING_TO, 0);
     }
     mDeferredSendProgress = false;
 
@@ -1455,16 +1461,22 @@ nsHttpTransaction::ParseHead(char *buf,
 
 nsresult
 nsHttpTransaction::HandleContentStart()
 {
     LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     if (mResponseHead) {
+        if (mEarlyDataDisposition == EARLY_ACCEPTED) {
+            Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("accepted"));
+        } else if (mEarlyDataDisposition == EARLY_SENT) {
+            Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("sent"));
+        } // no header on NONE case
+
         if (LOG3_ENABLED()) {
             LOG3(("http response [\n"));
             nsAutoCString headers;
             mResponseHead->Flatten(headers, false);
             headers.AppendLiteral("  OriginalHeaders");
             headers.AppendLiteral("\r\n");
             mResponseHead->FlattenNetworkOriginalHeaders(headers);
             LogHeaders(headers.get());
@@ -2165,16 +2177,21 @@ nsHttpTransaction::Do0RTT()
 }
 
 nsresult
 nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
 {
     LOG(("nsHttpTransaction::Finish0RTT %p %d %d\n", this, aRestart, aAlpnChanged));
     MOZ_ASSERT(m0RTTInProgress);
     m0RTTInProgress = false;
+    if (!aRestart && (mEarlyDataDisposition == EARLY_SENT)) {
+        // note that if this is invoked by a 3 param version of finish0rtt this
+        // disposition might be reverted
+        mEarlyDataDisposition = EARLY_ACCEPTED;
+    }
     if (aRestart) {
         // Reset request headers to be sent again.
         nsCOMPtr<nsISeekableStream> seekable =
             do_QueryInterface(mRequestStream);
         if (seekable) {
             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
         } else {
             return NS_ERROR_FAILURE;
@@ -2182,10 +2199,19 @@ nsHttpTransaction::Finish0RTT(bool aRest
     } else if (!mConnected) {
         // this is code that was skipped in ::ReadSegments while in 0RTT
         mConnected = true;
         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     }
     return NS_OK;
 }
 
+void
+nsHttpTransaction::Refused0RTT()
+{
+    LOG(("nsHttpTransaction::Refused0RTT %p\n", this));
+    if (mEarlyDataDisposition == EARLY_ACCEPTED) {
+        mEarlyDataDisposition = EARLY_SENT; // undo accepted state
+    }
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -166,16 +166,20 @@ public:
     mozilla::TimeStamp GetResponseStart();
     mozilla::TimeStamp GetResponseEnd();
 
     int64_t GetTransferSize() { return mTransferSize; }
 
     MOZ_MUST_USE bool Do0RTT() override;
     MOZ_MUST_USE nsresult Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */) override;
 
+    // After Finish0RTT early data may have failed but the caller did not request
+    // restart - this indicates that state for dev tools
+    void Refused0RTT();
+
     uint64_t TopLevelOuterContentWindowId() override
     {
         return mTopLevelOuterContentWindowId;
     }
 private:
     friend class DeleteHttpTransaction;
     virtual ~nsHttpTransaction();
 
@@ -397,16 +401,22 @@ private:
 public:
     void GetNetworkAddresses(NetAddr &self, NetAddr &peer);
 
 private:
     NetAddr                         mSelfAddr;
     NetAddr                         mPeerAddr;
 
     bool                            m0RTTInProgress;
+    enum
+    {
+        EARLY_NONE,
+        EARLY_SENT,
+        EARLY_ACCEPTED
+    } mEarlyDataDisposition;
 
     nsresult                        mTransportStatus;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpTransaction_h__
--- a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
@@ -11,32 +11,37 @@
 #include "sandbox/win/src/policy_params.h"
 #include "sandbox/win/src/policy_target.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
 #include "sandbox/win/src/sharedmem_ipc_client.h"
 #include "sandbox/win/src/target_services.h"
 #include "mozilla/sandboxing/sandboxLogging.h"
 
+// This status occurs when trying to access a network share on the machine from
+// which it is shared.
+#define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS)0xC0000201L)
+
 namespace sandbox {
 
 NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
                                    PHANDLE file, ACCESS_MASK desired_access,
                                    POBJECT_ATTRIBUTES object_attributes,
                                    PIO_STATUS_BLOCK io_status,
                                    PLARGE_INTEGER allocation_size,
                                    ULONG file_attributes, ULONG sharing,
                                    ULONG disposition, ULONG options,
                                    PVOID ea_buffer, ULONG ea_length) {
   // Check if the process can open it first.
   NTSTATUS status = orig_CreateFile(file, desired_access, object_attributes,
                                     io_status, allocation_size,
                                     file_attributes, sharing, disposition,
                                     options, ea_buffer, ea_length);
-  if (STATUS_ACCESS_DENIED != status)
+  if (STATUS_ACCESS_DENIED != status &&
+      STATUS_NETWORK_OPEN_RESTRICTION != status)
     return status;
 
   mozilla::sandboxing::LogBlocked("NtCreateFile",
                                   object_attributes->ObjectName->Buffer,
                                   object_attributes->ObjectName->Length);
 
   // We don't trust that the IPC can work this early.
   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
@@ -106,17 +111,18 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCre
 NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file,
                                  ACCESS_MASK desired_access,
                                  POBJECT_ATTRIBUTES object_attributes,
                                  PIO_STATUS_BLOCK io_status, ULONG sharing,
                                  ULONG options) {
   // Check if the process can open it first.
   NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
                                   io_status, sharing, options);
-  if (STATUS_ACCESS_DENIED != status)
+  if (STATUS_ACCESS_DENIED != status &&
+      STATUS_NETWORK_OPEN_RESTRICTION != status)
     return status;
 
   mozilla::sandboxing::LogBlocked("NtOpenFile",
                                   object_attributes->ObjectName->Buffer,
                                   object_attributes->ObjectName->Length);
 
   // We don't trust that the IPC can work this early.
   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
@@ -182,17 +188,18 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenF
 }
 
 NTSTATUS WINAPI TargetNtQueryAttributesFile(
     NtQueryAttributesFileFunction orig_QueryAttributes,
     POBJECT_ATTRIBUTES object_attributes,
     PFILE_BASIC_INFORMATION file_attributes) {
   // Check if the process can query it first.
   NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
-  if (STATUS_ACCESS_DENIED != status)
+  if (STATUS_ACCESS_DENIED != status &&
+      STATUS_NETWORK_OPEN_RESTRICTION != status)
     return status;
 
   mozilla::sandboxing::LogBlocked("NtQueryAttributesFile",
                                   object_attributes->ObjectName->Buffer,
                                   object_attributes->ObjectName->Length);
 
   // We don't trust that the IPC can work this early.
   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
@@ -244,17 +251,18 @@ NTSTATUS WINAPI TargetNtQueryAttributesF
 
 NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
     NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
     POBJECT_ATTRIBUTES object_attributes,
     PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
   // Check if the process can query it first.
   NTSTATUS status = orig_QueryFullAttributes(object_attributes,
                                              file_attributes);
-  if (STATUS_ACCESS_DENIED != status)
+  if (STATUS_ACCESS_DENIED != status &&
+      STATUS_NETWORK_OPEN_RESTRICTION != status)
     return status;
 
   mozilla::sandboxing::LogBlocked("NtQueryFullAttributesFile",
                                   object_attributes->ObjectName->Buffer,
                                   object_attributes->ObjectName->Length);
 
   // We don't trust that the IPC can work this early.
   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
--- a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
@@ -77,17 +77,21 @@ namespace sandbox {
 bool FileSystemPolicy::GenerateRules(const wchar_t* name,
                                      TargetPolicy::Semantics semantics,
                                      LowLevelPolicy* policy) {
   base::string16 mod_name(name);
   if (mod_name.empty()) {
     return false;
   }
 
-  if (!PreProcessName(&mod_name)) {
+  // Don't pre-process the path name and check for reparse points if it is the
+  // special case of allowing read access to all paths.
+  if (!(semantics == TargetPolicy::FILES_ALLOW_READONLY
+        && mod_name.compare(L"*") == 0)
+      && !PreProcessName(&mod_name)) {
     // The path to be added might contain a reparse point.
     NOTREACHED();
     return false;
   }
 
   // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
   // infrastructure to normalize names. In any case we need to escape the
   // question marks.
--- a/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt
+++ b/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt
@@ -1,8 +1,9 @@
 Please add a link to the bugzilla bug and patch name that should be re-applied.
 Also, please update any existing links to their actual mozilla-central changeset.
 
 https://hg.mozilla.org/mozilla-central/rev/a05726163a79
 https://hg.mozilla.org/mozilla-central/rev/e834e810a3fa
 https://hg.mozilla.org/mozilla-central/rev/c70d06fa5302
 https://hg.mozilla.org/mozilla-central/rev/d24db55deb85
-https://bugzilla.mozilla.org/show_bug.cgi?id=1321724 bug1321724.patch
+https://hg.mozilla.org/mozilla-central/rev/0e6bf137521e
+https://bugzilla.mozilla.org/show_bug.cgi?id=1344453 bug1344453part1.patch
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -214,16 +214,27 @@ SandboxBroker::SetSecurityLevelForConten
   mitigations =
     sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
     sandbox::MITIGATION_DLL_SEARCH_ORDER;
 
   result = mPolicy->SetDelayedProcessMitigations(mitigations);
   MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
                      "Invalid flags for SetDelayedProcessMitigations.");
 
+  // We still have edge cases where the child at low integrity can't read some
+  // files, so add a rule to allow read access to everything when required.
+  if (aSandboxLevel == 1 ||
+      aPrivs == base::ChildPrivileges::PRIVILEGES_FILEREAD) {
+    result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+                              sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+                              L"*");
+    MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+                       "With these static arguments AddRule should never fail, what happened?");
+  }
+
   // Add the policy for the client side of a pipe. It is just a file
   // in the \pipe\ namespace. We restrict it to pipes that start with
   // "chrome." so the sandboxed process cannot connect to system services.
   result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
                             sandbox::TargetPolicy::FILES_ALLOW_ANY,
                             L"\\??\\pipe\\chrome.*");
   MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
                      "With these static arguments AddRule should never fail, what happened?");
--- a/storage/mozStorageAsyncStatementExecution.cpp
+++ b/storage/mozStorageAsyncStatementExecution.cpp
@@ -128,36 +128,35 @@ private:
 class CompletionNotifier : public Runnable
 {
 public:
   /**
    * This takes ownership of the callback and the StatementData.  They are
    * released on the thread this is dispatched to (which should always be the
    * calling thread).
    */
-  CompletionNotifier(mozIStorageStatementCallback *aCallback,
+  CompletionNotifier(already_AddRefed<mozIStorageStatementCallback> aCallback,
                      ExecutionState aReason)
     : Runnable("storage::CompletionNotifier")
     , mCallback(aCallback)
     , mReason(aReason)
   {
   }
 
   NS_IMETHOD Run() override
   {
     if (mCallback) {
       (void)mCallback->HandleCompletion(mReason);
-      NS_RELEASE(mCallback);
     }
 
     return NS_OK;
   }
 
 private:
-  mozIStorageStatementCallback *mCallback;
+  RefPtr<mozIStorageStatementCallback> mCallback;
   ExecutionState mReason;
 };
 
 } // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncExecuteStatements
 
@@ -206,25 +205,25 @@ AsyncExecuteStatements::AsyncExecuteStat
 , mCallback(aCallback)
 , mCallingThread(::do_GetCurrentThread())
 , mMaxWait(TimeDuration::FromMilliseconds(MAX_MILLISECONDS_BETWEEN_RESULTS))
 , mIntervalStart(TimeStamp::Now())
 , mState(PENDING)
 , mCancelRequested(false)
 , mMutex(aConnection->sharedAsyncExecutionMutex)
 , mDBMutex(aConnection->sharedDBMutex)
-  , mRequestStartDate(TimeStamp::Now())
+, mRequestStartDate(TimeStamp::Now())
 {
   (void)mStatements.SwapElements(aStatements);
   NS_ASSERTION(mStatements.Length(), "We weren't given any statements!");
-  NS_IF_ADDREF(mCallback);
 }
 
 AsyncExecuteStatements::~AsyncExecuteStatements()
 {
+  MOZ_ASSERT(!mCallback, "Never called the Completion callback!");
   MOZ_ASSERT(!mHasTransaction, "There should be no transaction at this point");
 }
 
 bool
 AsyncExecuteStatements::shouldNotify()
 {
 #ifdef DEBUG
   mMutex.AssertNotCurrentThreadOwns();
@@ -463,22 +462,21 @@ AsyncExecuteStatements::notifyComplete()
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Transaction failed to rollback");
     }
     mHasTransaction = false;
   }
 
   // Always generate a completion notification; it is what guarantees that our
   // destruction does not happen here on the async thread.
   RefPtr<CompletionNotifier> completionEvent =
-    new CompletionNotifier(mCallback, mState);
-
+    new CompletionNotifier(mCallback.forget(), mState);
   // We no longer own mCallback (the CompletionNotifier takes ownership).
-  mCallback = nullptr;
-
-  (void)mCallingThread->Dispatch(completionEvent, NS_DISPATCH_NORMAL);
+  // Make sure we don't delete the event on this thread, since it has
+  // other-thread-only members.
+  (void)mCallingThread->Dispatch(completionEvent.forget(), NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
 
 nsresult
 AsyncExecuteStatements::notifyError(int32_t aErrorCode,
                                     const char *aMessage)
 {
@@ -502,32 +500,38 @@ AsyncExecuteStatements::notifyError(mozI
 
   if (!mCallback)
     return NS_OK;
 
   RefPtr<ErrorNotifier> notifier =
     new ErrorNotifier(mCallback, aError, this);
   NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY);
 
-  return mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
+  // Make sure we don't delete the event on this thread, since it has
+  // other-thread-only members.
+  return mCallingThread->Dispatch(notifier.forget(), NS_DISPATCH_NORMAL);
 }
 
 nsresult
 AsyncExecuteStatements::notifyResults()
 {
   mMutex.AssertNotCurrentThreadOwns();
   NS_ASSERTION(mCallback, "notifyResults called without a callback!");
 
   RefPtr<CallbackResultNotifier> notifier =
     new CallbackResultNotifier(mCallback, mResultSet, this);
   NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY);
 
-  nsresult rv = mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
-  if (NS_SUCCEEDED(rv))
+  // Make sure we don't delete the event on this thread, since it has
+  // other-thread-only members.
+  nsresult rv = mCallingThread->Dispatch(notifier.forget(), NS_DISPATCH_NORMAL);
+  if (NS_SUCCEEDED(rv)) {
+    // it may be freed here or on the CallingThread
     mResultSet = nullptr; // we no longer own it on success
+  }
   return rv;
 }
 
 NS_IMPL_ISUPPORTS(
   AsyncExecuteStatements,
   nsIRunnable,
   mozIStoragePendingStatement
 )
--- a/storage/mozStorageAsyncStatementExecution.h
+++ b/storage/mozStorageAsyncStatementExecution.h
@@ -181,17 +181,20 @@ private:
    * @return true if an explicit transaction is needed, false otherwise.
    */
   bool statementsNeedTransaction();
 
   StatementDataArray mStatements;
   RefPtr<Connection> mConnection;
   sqlite3 *mNativeConnection;
   bool mHasTransaction;
-  mozIStorageStatementCallback *mCallback;
+  // Note, this may not be a threadsafe object - never addref/release off
+  // the calling thread.  We take a reference when this is created, and
+  // release it in the CompletionNotifier::Run() call back to this thread.
+  RefPtr<mozIStorageStatementCallback> mCallback;
   nsCOMPtr<nsIThread> mCallingThread;
   RefPtr<ResultSet> mResultSet;
 
   /**
    * The maximum amount of time we want to wait between results.  Defined by
    * MAX_MILLISECONDS_BETWEEN_RESULTS and set at construction.
    */
   const TimeDuration mMaxWait;
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
@@ -234,16 +234,17 @@ class TestExecuteContent(MarionetteTestC
 
         self.assertTrue(send("return typeof Components.utils == 'undefined'"))
         self.assertTrue(send("return typeof window.wrappedJSObject == 'undefined'"))
 
     def test_no_callback(self):
         self.assertTrue(self.marionette.execute_script(
             "return typeof arguments[0] == 'undefined'"))
 
+    @skip_if_mobile("Intermittent on Android - bug 1334035")
     def test_window_set_timeout_is_not_cancelled(self):
         def content_timeout_triggered(mn):
             return mn.execute_script("return window.n", sandbox=None) > 0
 
         # subsequent call to execute_script after this
         # should not cancel the setTimeout event
         self.marionette.navigate(inline("""
             <script>
--- a/testing/mozharness/configs/releases/updates_firefox_release.py
+++ b/testing/mozharness/configs/releases/updates_firefox_release.py
@@ -34,14 +34,16 @@ config = {
         },
         "release": {
             "version_regex": r"^\d+\.\d+(\.\d+)?$",
             "requires_mirrors": True,
             "patcher_config": "mozRelease-branch-patcher2.cfg",
             "update_verify_channel": "release-localtest",
             "mar_channel_ids": [],
             "channel_names": ["release", "release-localtest", "release-cdntest"],
-            "rules_to_update": ["firefox-release-cdntest", "firefox-release-localtest"],
-            "publish_rules": ["firefox-release", "firefox-release-nowebsense-bypass"],
+            "rules_to_update": ["firefox-release-cdntest", "firefox-release-localtest", "firefox-release-localtest-nowebsense-bypass",
+                                "firefox-release-localtest-knownwebsense-bypass", "firefox-release-cdntest-nowebsense-bypass",
+                                "firefox-release-cdntest-knownwebsense-bypass"],
+            "publish_rules": ["firefox-release", "firefox-release-nowebsense-bypass", "firefox-release-knownwebsense-bypass"],
         },
     },
     "balrog_use_dummy_suffix": False,
 }
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -571,29 +571,18 @@ function updateAboutMemoryFromJSONObject
                 "missing 'hasMozMallocUsableSize' property");
     assertInput(aObj.reports && aObj.reports instanceof Array,
                 "missing or non-array 'reports' property");
 
     let processMemoryReportsFromFile =
         function(aHandleReport, aDisplayReports) {
       for (let i = 0; i < aObj.reports.length; i++) {
         let r = aObj.reports[i];
-
-        // A hack: for a brief time (late in the FF26 and early in the FF27
-        // cycle) we were dumping memory report files that contained reports
-        // whose path began with "redundant/".  Such reports were ignored by
-        // about:memory.  These reports are no longer produced, but some older
-        // builds are still floating around and producing files that contain
-        // them, so we need to still handle them (i.e. ignore them).  This hack
-        // can be removed once FF26 and associated products (e.g. B2G 1.2) are
-        // no longer in common use.
-        if (!r.path.startsWith("redundant/")) {
-          aHandleReport(r.process, r.path, r.kind, r.units, r.amount,
-                        r.description, r._presence);
-        }
+        aHandleReport(r.process, r.path, r.kind, r.units, r.amount,
+                      r.description, r._presence);
       }
       aDisplayReports();
     }
     appendAboutMemoryMain(processMemoryReportsFromFile,
                           aObj.hasMozMallocUsableSize);
   } catch (ex) {
     handleException(ex);
   }
--- a/toolkit/components/aboutmemory/tests/memory-reports-good.json
+++ b/toolkit/components/aboutmemory/tests/memory-reports-good.json
@@ -9,18 +9,16 @@
 
     {"process": "Main Process (pid NNN)", "path": "size/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"},
     {"process": "Main Process (pid NNN)", "path": "rss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"},
     {"process": "Main Process (pid NNN)", "path": "pss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"},
     {"process": "Main Process (pid NNN)", "path": "swap/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"},
     {"process": "Main Process (pid NNN)", "path": "compartments/system/a", "kind": 1, "units": 0, "amount": 1024, "description": ""},
     {"process": "Main Process (pid NNN)", "path": "ghost-windows/a", "kind": 1, "units": 0, "amount": 1024, "description": ""},
 
-    {"process": "Main Process (pid NNN)", "path": "redundant/should-be-ignored", "kind": 1, "units": 0, "amount": 1024, "description": ""},
-
     {"process": "Heap-unclassified process", "path": "heap-allocated", "kind": 2, "units": 0, "amount": 262144000, "description": "Heap allocated."},
     {"process": "Heap-unclassified process", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 52428800, "description": "A b."},
     {"process": "Heap-unclassified process", "path": "explicit/heap-unclassified", "kind": 1, "units": 0, "amount": 209715200, "description": "Heap unclassified"},
 
     {"process": "Explicit-only process", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."},
 
     {"process": "Other-only process", "path": "a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."},
     {"process": "Other-only process", "path": "a/c", "kind": 1, "units": 0, "amount": 100000, "description": "A c."},
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -29,17 +29,16 @@ toolkit.jar:
   skin/classic/global/numberbox.css
   skin/classic/global/popup.css
   skin/classic/global/preferences.css
   skin/classic/global/progressmeter.css
   skin/classic/global/radio.css
   skin/classic/global/resizer.css
   skin/classic/global/richlistbox.css
   skin/classic/global/scrollbars.css                                 (nativescrollbars.css)
-  skin/classic/global/scale.css
   skin/classic/global/scrollbox.css
   skin/classic/global/spinbuttons.css
   skin/classic/global/splitter.css
   skin/classic/global/tabprompts.css
   skin/classic/global/tabbox.css
   skin/classic/global/textbox.css
   skin/classic/global/datetimepicker.css
   skin/classic/global/toolbar.css
@@ -117,18 +116,16 @@ toolkit.jar:
   skin/classic/global/icons/question-large.png                       (icons/question-large.png)
   skin/classic/global/icons/sslWarning.png                           (icons/sslWarning.png)
   skin/classic/global/notification/close.png                         (notification/close.png)
   skin/classic/global/notification/error-icon.png                    (notification/error-icon.png)
   skin/classic/global/notification/info-icon.png                     (notification/info-icon.png)
   skin/classic/global/notification/warning-icon.png                  (notification/warning-icon.png)
 * skin/classic/global/in-content/common.css                          (in-content/common.css)
 * skin/classic/global/in-content/info-pages.css                      (in-content/info-pages.css)
-  skin/classic/global/scale/scale-tray-horiz.gif                     (scale/scale-tray-horiz.gif)
-  skin/classic/global/scale/scale-tray-vert.gif                      (scale/scale-tray-vert.gif)
   skin/classic/global/splitter/dimple.png                            (splitter/dimple.png)
   skin/classic/global/splitter/grip-bottom.gif                       (splitter/grip-bottom.gif)
   skin/classic/global/splitter/grip-top.gif                          (splitter/grip-top.gif)
   skin/classic/global/splitter/grip-left.gif                         (splitter/grip-left.gif)
   skin/classic/global/splitter/grip-right.gif                        (splitter/grip-right.gif)
   skin/classic/global/toolbar/spring.png                             (toolbar/spring.png)
   skin/classic/global/toolbar/toolbar-separator.png                  (toolbar/toolbar-separator.png)
   skin/classic/global/tree/arrow-disclosure.svg                      (tree/arrow-disclosure.svg)
deleted file mode 100644
index b87fe68c15a880deadf73304263a50657d7e6c7d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 97687b2e229de10d081cb99cadc6fb92fd7af5ca..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -19,16 +19,18 @@ toolkit.jar:
   skin/classic/global/aboutReaderContent.css               (../../shared/aboutReaderContent.css)
 * skin/classic/global/aboutReaderControls.css              (../../shared/aboutReaderControls.css)
   skin/classic/global/aboutSupport.css                     (../../shared/aboutSupport.css)
   skin/classic/global/appPicker.css                        (../../shared/appPicker.css)
   skin/classic/global/config.css                           (../../shared/config.css)
   skin/classic/global/datetimeinputpickers.css             (../../shared/datetimeinputpickers.css)
   skin/classic/global/datetimepopup.css                    (../../shared/datetimepopup.css)
   skin/classic/global/filters.svg                          (../../shared/filters.svg)
+  skin/classic/global/passwordmgr.css                      (../../shared/passwordmgr.css)
+  skin/classic/global/scale.css                            (../../shared/scale.css)
   skin/classic/global/icons/calendar-arrows.svg            (../../shared/icons/calendar-arrows.svg)
   skin/classic/global/icons/find-arrows.svg                (../../shared/icons/find-arrows.svg)
   skin/classic/global/icons/info.svg                       (../../shared/incontent-icons/info.svg)
   skin/classic/global/icons/input-clear.svg                (../../shared/icons/input-clear.svg)
   skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
   skin/classic/global/icons/loading@2x.png                 (../../shared/icons/loading@2x.png)
   skin/classic/global/icons/spinner-arrows.svg             (../../shared/icons/spinner-arrows.svg)
   skin/classic/global/icons/menubutton-dropmarker.svg      (../../shared/icons/menubutton-dropmarker.svg)
@@ -49,17 +51,16 @@ toolkit.jar:
   skin/classic/global/menu/shared-menu-check-active.svg    (../../shared/menu-check-active.svg)
   skin/classic/global/menu/shared-menu-check-black.svg     (../../shared/menu-check-black.svg)
   skin/classic/global/menu/shared-menu-check-hover.svg     (../../shared/menu-check-hover.svg)
   skin/classic/global/in-content/check.svg                 (../../shared/in-content/check.svg)
   skin/classic/global/in-content/check-partial.svg         (../../shared/in-content/check-partial.svg)
   skin/classic/global/in-content/dropdown.svg              (../../shared/in-content/dropdown.svg)
   skin/classic/global/in-content/help-glyph.svg            (../../shared/in-content/help-glyph.svg)
   skin/classic/global/in-content/radio.svg                 (../../shared/in-content/radio.svg)
-  skin/classic/global/passwordmgr.css                      (../../shared/passwordmgr.css)
   skin/classic/global/reader/RM-Close-24x24.svg            (../../shared/reader/RM-Close-24x24.svg)
   skin/classic/global/reader/RM-Minus-24x24.svg            (../../shared/reader/RM-Minus-24x24.svg)
   skin/classic/global/reader/RM-Plus-24x24.svg             (../../shared/reader/RM-Plus-24x24.svg)
   skin/classic/global/reader/RM-Type-Controls-24x24.svg    (../../shared/reader/RM-Type-Controls-24x24.svg)
   skin/classic/global/reader/RM-Type-Controls-Arrow.svg    (../../shared/reader/RM-Type-Controls-Arrow.svg)
   skin/classic/global/reader/RM-Content-Width-Minus-42x16.svg            (../../shared/reader/RM-Content-Width-Minus-42x16.svg)
   skin/classic/global/reader/RM-Content-Width-Plus-44x16.svg             (../../shared/reader/RM-Content-Width-Plus-44x16.svg)
   skin/classic/global/reader/RM-Line-Height-Minus-38x14.svg            (../../shared/reader/RM-Line-Height-Minus-38x14.svg)
--- a/toolkit/themes/shared/non-mac.jar.inc.mn
+++ b/toolkit/themes/shared/non-mac.jar.inc.mn
@@ -16,17 +16,16 @@
   skin/classic/global/dialog.css                           (../../windows/global/dialog.css)
   skin/classic/global/expander.css                         (../../windows/global/expander.css)
   skin/classic/global/filefield.css                        (../../windows/global/filefield.css)
   skin/classic/global/globalBindings.xml                   (../../windows/global/globalBindings.xml)
   skin/classic/global/linkTree.css                         (../../windows/global/linkTree.css)
   skin/classic/global/progressmeter.css                    (../../windows/global/progressmeter.css)
   skin/classic/global/resizer.css                          (../../windows/global/resizer.css)
   skin/classic/global/richlistbox.css                      (../../windows/global/richlistbox.css)
-  skin/classic/global/scale.css                            (../../windows/global/scale.css)
 #ifndef MOZ_THEME_FASTSTRIPE
   skin/classic/global/scrollbars.css                       (../../windows/global/xulscrollbars.css)
 #endif
   skin/classic/global/spinbuttons.css                      (../../windows/global/spinbuttons.css)
   skin/classic/global/tabprompts.css                       (../../windows/global/tabprompts.css)
   skin/classic/global/wizard.css                           (../../windows/global/wizard.css)
 
   skin/classic/global/arrow/arrow-dn.gif                   (../../windows/global/arrow/arrow-dn.gif)
@@ -80,18 +79,16 @@
   skin/classic/global/icons/tabprompts-bgtexture.png       (../../windows/global/icons/tabprompts-bgtexture.png)
   skin/classic/global/icons/Warning.png                    (../../windows/global/icons/Warning.png)
   skin/classic/global/icons/warning-large.png              (../../windows/global/icons/warning-large.png)
   skin/classic/global/icons/warning-16.png                 (../../windows/global/icons/warning-16.png)
   skin/classic/global/icons/warning-64.png                 (../../windows/global/icons/warning-64.png)
   skin/classic/global/icons/windowControls.png             (../../windows/global/icons/windowControls.png)
   skin/classic/global/radio/radio-check.gif                (../../windows/global/radio/radio-check.gif)
   skin/classic/global/radio/radio-check-dis.gif            (../../windows/global/radio/radio-check-dis.gif)
-  skin/classic/global/scale/scale-tray-horiz.gif           (../../windows/global/scale/scale-tray-horiz.gif)
-  skin/classic/global/scale/scale-tray-vert.gif            (../../windows/global/scale/scale-tray-vert.gif)
   skin/classic/global/scrollbar/slider.gif                 (../../windows/global/scrollbar/slider.gif)
   skin/classic/global/splitter/grip-bottom.gif             (../../windows/global/splitter/grip-bottom.gif)
   skin/classic/global/splitter/grip-top.gif                (../../windows/global/splitter/grip-top.gif)
   skin/classic/global/splitter/grip-left.gif               (../../windows/global/splitter/grip-left.gif)
   skin/classic/global/splitter/grip-right.gif              (../../windows/global/splitter/grip-right.gif)
   skin/classic/global/toolbar/chevron.gif                  (../../windows/global/toolbar/chevron.gif)
   skin/classic/global/toolbar/chevron-inverted.png         (../../windows/global/toolbar/chevron-inverted.png)
   skin/classic/global/tree/columnpicker.gif                (../../windows/global/tree/columnpicker.gif)
rename from toolkit/themes/osx/global/scale.css
rename to toolkit/themes/shared/scale.css
--- a/toolkit/themes/osx/global/scale.css
+++ b/toolkit/themes/shared/scale.css
@@ -1,46 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 .scale-slider {
   -moz-appearance: scale-horizontal;
-  background: url("chrome://global/skin/scale/scale-tray-horiz.gif") 0% 50% repeat-x;
   margin: 2px 4px;
   width: 100px;
 }
 
-.scale-slider[orient="vertical"]
-{
+.scale-slider[orient="vertical"] {
   -moz-appearance: scale-vertical;
-  background: url("chrome://global/skin/scale/scale-tray-vert.gif") 50% 0% repeat-y;
   margin: 4px 2px;
   width: auto;
   height: 100px;
 }
 
 .scale-thumb {
   -moz-appearance: scalethumb-horizontal;
-  border: 2px solid;
-  -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight;
-  -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
-  -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
-  -moz-border-left-colors: ThreeDLightShadow ThreeDHighlight;
-  background-color: -moz-Dialog;
   min-width: 30px;
   min-height: 15px;
 }
 
 .scale-thumb[orient="vertical"] {
   -moz-appearance: scalethumb-vertical;
   min-width: 15px;
   min-height: 30px;
 }
 
-.scale-thumb[disabled="true"] {
-  -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow !important;
-  -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow !important;
-  -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow !important;
-  -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow !important;
-}
deleted file mode 100644
--- a/toolkit/themes/windows/global/scale.css
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-
-/* ::::: scale ::::: */
-
-.scale-slider {
-  -moz-appearance: scale-horizontal;
-  background: url("chrome://global/skin/scale/scale-tray-horiz.gif") 0% 50% repeat-x;
-  margin: 2px 4px;
-  width: 100px;
-}
-
-.scale-slider[orient="vertical"]
-{
-  -moz-appearance: scale-vertical;
-  background: url("chrome://global/skin/scale/scale-tray-vert.gif") 50% 0% repeat-y;
-  margin: 4px 2px;
-  width: auto;
-  height: 100px;
-}
-
-/* ::::: scale thumb ::::: */
-
-.scale-thumb {
-  -moz-appearance: scalethumb-horizontal;
-  border: 2px solid;
-  -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight;
-  -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
-  -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
-  -moz-border-left-colors: ThreeDLightShadow ThreeDHighlight;
-  background-color: -moz-Dialog;
-  min-width: 30px;
-  min-height: 15px;
-}
-
-.scale-thumb[orient="vertical"] {
-  -moz-appearance: scalethumb-vertical;
-  min-width: 15px;
-  min-height: 30px;
-}
-
-.scale-thumb[disabled="true"] {
-  -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow !important;
-  -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow !important;
-  -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow !important;
-  -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow !important;
-}
deleted file mode 100644
index b87fe68c15a880deadf73304263a50657d7e6c7d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 97687b2e229de10d081cb99cadc6fb92fd7af5ca..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -333,16 +333,40 @@ AddContentSandboxLevelAnnotation()
     levelString.AppendInt(level);
     CrashReporter::AnnotateCrashReport(
       NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
   }
 }
 #endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
 #endif /* MOZ_CRASHREPORTER */
 
+namespace {
+
+int GetDebugChildPauseTime() {
+  auto pauseStr = PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE");
+  if (pauseStr && *pauseStr) {
+    int pause = atoi(pauseStr);
+    if (pause != 1) { // must be !=1 since =1 enables the default pause time
+#if defined(OS_WIN)
+      pause *= 1000; // convert to ms
+#endif
+      return pause;
+    }
+  }
+#ifdef OS_POSIX
+  return 30; // seconds
+#elif defined(OS_WIN)
+  return 10000; // milliseconds
+#else
+  return 0;
+#endif
+}
+
+} // namespace
+
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      const XREChildData* aChildData)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
@@ -538,27 +562,27 @@ XRE_InitChildProcess(int aArgc,
       PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
 #if defined(XP_LINUX) && defined(DEBUG)
     if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) != 0) {
       printf_stderr("Could not allow ptrace from any process.\n");
     }
 #endif
     printf_stderr("\n\nCHILDCHILDCHILDCHILD\n  debug me @ %d\n\n",
                   base::GetCurrentProcId());
-    sleep(30);
+    sleep(GetDebugChildPauseTime());
   }
 #elif defined(OS_WIN)
   if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) {
     NS_DebugBreak(NS_DEBUG_BREAK,
                   "Invoking NS_DebugBreak() to debug child process",
                   nullptr, __FILE__, __LINE__);
   } else if (PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
     printf_stderr("\n\nCHILDCHILDCHILDCHILD\n  debug me @ %d\n\n",
                   base::GetCurrentProcId());
-    ::Sleep(10000);
+    ::Sleep(GetDebugChildPauseTime());
   }
 #endif
 
   // child processes launched by GeckoChildProcessHost get this magic
   // argument appended to their command lines
   const char* const parentPIDString = aArgv[aArgc-1];
   MOZ_ASSERT(parentPIDString, "NULL parent PID");
   --aArgc;
--- a/widget/windows/WinMessages.h
+++ b/widget/windows/WinMessages.h
@@ -10,16 +10,18 @@
  * MOZ_WM_* messages
  ****************************************************************************/
 
 // A magic APP message that can be sent to quit, sort of like a
 // QUERYENDSESSION/ENDSESSION, but without the query.
 #define MOZ_WM_APP_QUIT                   (WM_APP+0x0300)
 // Used as a "tracer" event to probe event loop latency.
 #define MOZ_WM_TRACE                      (WM_APP+0x0301)
+// accessibility priming
+#define MOZ_WM_STARTA11Y                  (WM_APP+0x0302)
 // Our internal message for WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL and
 // WM_HSCROLL
 #define MOZ_WM_MOUSEVWHEEL                (WM_APP+0x0310)
 #define MOZ_WM_MOUSEHWHEEL                (WM_APP+0x0311)
 #define MOZ_WM_VSCROLL                    (WM_APP+0x0312)
 #define MOZ_WM_HSCROLL                    (WM_APP+0x0313)
 #define MOZ_WM_MOUSEWHEEL_FIRST           MOZ_WM_MOUSEVWHEEL
 #define MOZ_WM_MOUSEWHEEL_LAST            MOZ_WM_HSCROLL
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -932,16 +932,24 @@ nsWindow::Create(nsIWidget* aParent,
   }
 
   // Query for command button metric data for rendering the titlebar. We
   // only do this once on the first window that has an actual titlebar
   if (ShouldCacheTitleBarInfo(mWindowType, mBorderStyle)) {
     nsUXThemeData::UpdateTitlebarInfo(mWnd);
   }
 
+  static bool a11yPrimed = false;
+  if (!a11yPrimed &&
+      mWindowType == eWindowType_toplevel) {
+    a11yPrimed = true;
+    if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
+      ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0);
+    }
+  }
   return NS_OK;
 }
 
 // Close this nsWindow
 void nsWindow::Destroy()
 {
   // WM_DESTROY has already fired, avoid calling it twice
   if (mOnDestroyCalled)
@@ -5057,16 +5065,21 @@ nsWindow::ProcessMessage(UINT msg, WPARA
         bool abortQuit;
         cancelQuit->GetData(&abortQuit);
         sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
       }
       *aRetValue = sCanQuit ? TRUE : FALSE;
       result = true;
       break;
 
+    case MOZ_WM_STARTA11Y:
+      (void*)GetAccessible();
+      result = true;
+      break;
+
     case WM_ENDSESSION:
     case MOZ_WM_APP_QUIT:
       if (msg == MOZ_WM_APP_QUIT || (wParam == TRUE && sCanQuit == TRI_TRUE))
       {
         // Let's fake a shutdown sequence without actually closing windows etc.
         // to avoid Windows killing us in the middle. A proper shutdown would
         // require having a chance to pump some messages. Unfortunately
         // Windows won't let us do that. Bug 212316.
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -478,17 +478,20 @@ private:
   EnvironmentPreparer mEnvironmentPreparer;
 };
 
 void TraceScriptHolder(nsISupports* aHolder, JSTracer* aTracer);
 
 // Returns true if the JS::TraceKind is one the cycle collector cares about.
 inline bool AddToCCKind(JS::TraceKind aKind)
 {
-  return aKind == JS::TraceKind::Object || aKind == JS::TraceKind::Script || aKind == JS::TraceKind::Scope;
+  return aKind == JS::TraceKind::Object ||
+         aKind == JS::TraceKind::Script ||
+         aKind == JS::TraceKind::Scope ||
+         aKind == JS::TraceKind::RegExpShared;
 }
 
 bool
 GetBuildId(JS::BuildIdCharVector* aBuildID);
 
 } // namespace mozilla
 
 #endif // mozilla_CycleCollectedJSContext_h__