Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Fri, 01 Jun 2018 00:51:37 +0300
changeset 420724 42880a726964a0bd66e2f636931e8322eae86ef7
parent 420659 0dc7bfc7b0c1de8d390cea05f0248fbadeb4d5fc (current diff)
parent 420723 833f1e224c8816a588d2416775bc43ed005c3e5b (diff)
child 420779 f364ab92b59ebeafe26ef6d579328c35a4bb99a0
child 420817 731cfcb5e07ca52408c0b156adea25500bf9dc26
push id34077
push usernerli@mozilla.com
push dateThu, 31 May 2018 21:51:59 +0000
treeherdermozilla-central@42880a726964 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
42880a726964 / 62.0a1 / 20180531220139 / files
nightly linux64
42880a726964 / 62.0a1 / 20180531220139 / files
nightly mac
42880a726964 / 62.0a1 / 20180531220139 / files
nightly win32
42880a726964 / 62.0a1 / 20180531220139 / files
nightly win64
42880a726964 / 62.0a1 / 20180531220139 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/base/nsContentUtils.cpp
testing/web-platform/meta/content-security-policy/media-src/media-src-redir-bug.sub.html.ini
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2356,25 +2356,29 @@ dependencies = [
  "time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender_bindings"
 version = "0.1.0"
 dependencies = [
  "app_units 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nsstring 0.1.0",
  "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender 0.57.2",
 ]
 
 [[package]]
 name = "which"
 version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1248,25 +1248,16 @@ nsScriptSecurityManager::GetDocShellCode
                                                       nsIPrincipal** aPrincipal)
 {
   nsCOMPtr<nsIPrincipal> prin =
     BasePrincipal::CreateCodebasePrincipal(aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes());
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
-// static
-nsIPrincipal*
-nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
-{
-    JSCompartment *compartment = js::GetObjectCompartment(aObj);
-    JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
-    return nsJSPrincipals::get(principals);
-}
-
 NS_IMETHODIMP
 nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
                                           const nsIID &aIID,
                                           nsISupports *aObj,
                                           nsIClassInfo *aClassInfo)
 {
 // XXX Special case for Exception ?
 
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -88,20 +88,16 @@ private:
 
     // Decides, based on CSP, whether or not eval() and stuff can be executed.
     static bool
     ContentSecurityPolicyPermitsJSAction(JSContext *cx);
 
     static bool
     JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second);
 
-    // Returns null if a principal cannot be found; generally callers
-    // should error out at that point.
-    static nsIPrincipal* doGetObjectPrincipal(JSObject* obj);
-
     nsresult
     Init();
 
     nsresult
     InitPrefs();
 
     inline void
     ScriptSecurityPrefChanged();
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 60
+Version 61
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-59...release-60
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-60...release-61
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -2112,16 +2112,20 @@ menuseparator {
   color: var(--string-color);
   border-width: 1px;
   border-style: solid;
   border-radius: 2px;
   font-size: 0.8em;
   padding: 0 2px;
 }
 
+.objectBox-node.clickable {
+  cursor: pointer;
+}
+
 /******************************************************************************/
 
 .objectBox-event,
 .objectBox-eventLog,
 .objectBox-regexp,
 .objectBox-object {
   color: var(--object-color);
   white-space: pre-wrap;
@@ -2218,16 +2222,17 @@ menuseparator {
 
 button.open-inspector {
   mask: url("chrome://devtools/skin/images/devtools-reps/open-inspector.svg") no-repeat;
   display: inline-block;
   background-color: var(--comment-node-color);
   height: 16px;
   margin-left: 0.25em;
   vertical-align: middle;
+  cursor: pointer;
 }
 
 .objectBox-node:hover .open-inspector,
 .objectBox-textNode:hover .open-inspector,
 .open-inspector:hover {
   background-color: var(--theme-highlight-blue);
 }
 
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1217,17 +1217,17 @@ function isAwaitExpression(path) {
 }
 
 function isYieldExpression(path) {
   const { node, parent } = path;
   return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent);
 }
 
 function isObjectShorthand(parent) {
-  return t.isObjectProperty(parent) && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName;
+  return t.isObjectProperty(parent) && parent.value && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName;
 }
 
 function getObjectExpressionValue(node) {
   const { value } = node;
 
   if (t.isIdentifier(value)) {
     return value.name;
   }
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -8169,17 +8169,17 @@ var _Svg2 = _interopRequireDefault(_Svg)
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 // We cannot directly export literals containing special characters
 // (eg. "my-module/Test") which is why they are nested in "vendored".
 // The keys of the vendored object should match the module names
 // !!! Should remain synchronized with .babel/transform-mc.js !!!
-const vendored = {
+const vendored = exports.vendored = {
   classnames: _classnames2.default,
   "devtools-components": devtoolsComponents,
   "devtools-config": devtoolsConfig,
   "devtools-contextmenu": devtoolsContextmenu,
   "devtools-environment": devtoolsEnvironment,
   "devtools-modules": devtoolsModules,
   "devtools-splitter": _devtoolsSplitter2.default,
   "devtools-utils": devtoolsUtils,
@@ -8205,17 +8205,16 @@ const vendored = {
  * same way:
  * - always with destructuring (import { a } from "modA";)
  * - always without destructuring (import modB from "modB")
  *
  * Both are fine, but cannot be mixed for the same module.
  */
 
 // Modules imported with destructuring
-exports.vendored = vendored;
 
 /***/ }),
 
 /***/ 4:
 /***/ (function(module, exports) {
 
 module.exports = __WEBPACK_EXTERNAL_MODULE_4__;
 
--- a/devtools/client/debugger/new/src/actions/pause/extra.js
+++ b/devtools/client/debugger/new/src/actions/pause/extra.js
@@ -86,15 +86,15 @@ function getExtra(expression, result) {
     dispatch,
     getState,
     client,
     sourceMaps
   }) => {
     const selectedFrame = (0, _selectors.getSelectedFrame)(getState());
 
     if (!selectedFrame) {
-      return;
+      return {};
     }
 
     const extra = await getExtraProps(getState, expression, result, expr => client.evaluateInFrame(expr, selectedFrame.id));
     return extra;
   };
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/client/index.js
+++ b/devtools/client/debugger/new/src/client/index.js
@@ -1,14 +1,14 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.onConnect = undefined;
+exports.onConnect = onConnect;
 
 var _firefox = require("./firefox");
 
 var firefox = _interopRequireWildcard(_firefox);
 
 var _prefs = require("../utils/prefs");
 
 var _dbg = require("../utils/dbg");
@@ -63,11 +63,9 @@ async function onConnect(connection, {
   });
   (0, _bootstrap.bootstrapApp)(store);
   return {
     store,
     actions,
     selectors,
     client: commands
   };
-}
-
-exports.onConnect = onConnect;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
@@ -232,17 +232,17 @@ class Popup extends _react.Component {
 
     let header = null;
 
     if ((0, _preview.isImmutable)(this.getObjectProperties())) {
       header = this.renderImmutable(extra.immutable);
       roots = roots.filter(r => r.type != NODE_TYPES.PROTOTYPE);
     }
 
-    if ((0, _preview.isReactComponent)(this.getObjectProperties())) {
+    if (extra.react && (0, _preview.isReactComponent)(this.getObjectProperties())) {
       header = this.renderReact(extra.react);
       roots = roots.filter(r => ["state", "props"].includes(r.name));
     }
 
     return _react2.default.createElement("div", {
       className: "preview-popup"
     }, header, this.renderObjectInspector(roots));
   }
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -119,157 +119,166 @@ class SourcesTree extends _react.Compone
         debuggeeUrl,
         projectRoot,
         uncollapsedTree,
         sourceTree
       }));
     }
   }
 
+  // NOTE: we get the source from sources because item.contents is cached
+  getSource(item) {
+    return this.props.sources.get(item.contents.id);
+  }
+
+  isEmpty() {
+    const {
+      sourceTree
+    } = this.state;
+    return sourceTree.contents.length === 0;
+  }
+
   renderItemName(name) {
     const hosts = {
       "ng://": "Angular",
       "webpack://": "Webpack",
       "moz-extension://": L10N.getStr("extensionsText")
     };
     return hosts[name] || name;
   }
 
   renderEmptyElement(message) {
     return _react2.default.createElement("div", {
+      key: "empty",
       className: "no-sources-message"
     }, message);
   }
 
-  render() {
+  renderProjectRootHeader() {
     const {
-      expanded,
       projectRoot
     } = this.props;
     const {
-      focusedItem,
-      highlightItems,
-      listItems,
-      parentMap,
       sourceTree
     } = this.state;
 
-    const onExpand = (item, expandedState) => {
-      this.props.setExpandedState(expandedState);
-    };
-
-    const onCollapse = (item, expandedState) => {
-      this.props.setExpandedState(expandedState);
-    };
-
-    const isEmpty = sourceTree.contents.length === 0;
-    const isCustomRoot = projectRoot !== "";
-
-    let roots = () => sourceTree.contents;
-
-    let clearProjectRootButton = null; // The "sourceTree.contents[0]" check ensures that there are contents
-    // A custom root with no existing sources will be ignored
-
-    if (isCustomRoot) {
-      const sourceContents = sourceTree.contents[0];
-      let rootLabel = projectRoot.split("/").pop();
+    if (!projectRoot) {
+      return null;
+    }
 
-      roots = () => sourceContents.contents;
-
-      if (sourceContents && sourceContents.name !== rootLabel) {
-        rootLabel = sourceContents.contents[0].name;
-
-        roots = () => sourceContents.contents[0].contents;
-      }
+    const sourceContents = sourceTree.contents[0];
+    let rootLabel = projectRoot.split("/").pop();
 
-      clearProjectRootButton = _react2.default.createElement("button", {
-        className: "sources-clear-root",
-        onClick: () => this.props.clearProjectDirectoryRoot(),
-        title: L10N.getStr("removeDirectoryRoot.label")
-      }, _react2.default.createElement(_Svg2.default, {
-        name: "home"
-      }), _react2.default.createElement(_Svg2.default, {
-        name: "breadcrumb",
-        "class": true
-      }), _react2.default.createElement("span", {
-        className: "sources-clear-root-label"
-      }, rootLabel));
+    if (sourceContents && sourceContents.name !== rootLabel) {
+      rootLabel = sourceContents.contents[0].name;
     }
 
-    if (isEmpty && !isCustomRoot) {
-      return this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"));
-    }
+    return _react2.default.createElement("div", {
+      key: "root",
+      className: "sources-clear-root-container"
+    }, _react2.default.createElement("button", {
+      className: "sources-clear-root",
+      onClick: () => this.props.clearProjectDirectoryRoot(),
+      title: L10N.getStr("removeDirectoryRoot.label")
+    }, _react2.default.createElement(_Svg2.default, {
+      name: "home"
+    }), _react2.default.createElement(_Svg2.default, {
+      name: "breadcrumb",
+      "class": true
+    }), _react2.default.createElement("span", {
+      className: "sources-clear-root-label"
+    }, rootLabel)));
+  }
 
+  renderTree() {
+    const {
+      expanded
+    } = this.props;
+    const {
+      highlightItems,
+      listItems,
+      parentMap
+    } = this.state;
     const treeProps = {
       autoExpandAll: false,
       autoExpandDepth: expanded ? 0 : 1,
       expanded,
       getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
       getParent: item => parentMap.get(item),
       getPath: this.getPath,
-      getRoots: roots,
+      getRoots: this.getRoots,
       highlightItems,
       itemHeight: 21,
-      key: isEmpty ? "empty" : "full",
+      key: this.isEmpty() ? "empty" : "full",
       listItems,
-      onCollapse,
-      onExpand,
+      onCollapse: this.onCollapse,
+      onExpand: this.onExpand,
       onFocus: this.focusItem,
       renderItem: this.renderItem
     };
-
-    const tree = _react2.default.createElement(_ManagedTree2.default, treeProps);
+    return _react2.default.createElement(_ManagedTree2.default, treeProps);
+  }
 
-    const onKeyDown = e => {
-      if (e.keyCode === 13 && focusedItem) {
-        this.selectItem(focusedItem);
-      }
-    };
-
+  renderPane(...children) {
+    const {
+      projectRoot
+    } = this.props;
     return _react2.default.createElement("div", {
+      key: "pane",
       className: (0, _classnames2.default)("sources-pane", {
-        "sources-list-custom-root": isCustomRoot
+        "sources-list-custom-root": projectRoot
       })
-    }, isCustomRoot ? _react2.default.createElement("div", {
-      className: "sources-clear-root-container"
-    }, clearProjectRootButton) : null, isEmpty ? this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot")) : _react2.default.createElement("div", {
+    }, children);
+  }
+
+  render() {
+    const {
+      projectRoot
+    } = this.props;
+
+    if (this.isEmpty()) {
+      if (projectRoot) {
+        return this.renderPane(this.renderProjectRootHeader(), this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailableRoot")));
+      }
+
+      return this.renderPane(this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable")));
+    }
+
+    return this.renderPane(this.renderProjectRootHeader(), _react2.default.createElement("div", {
+      key: "tree",
       className: "sources-list",
-      onKeyDown: onKeyDown
-    }, tree));
+      onKeyDown: this.onKeyDown
+    }, this.renderTree()));
   }
 
 }
 
 var _initialiseProps = function () {
   this.focusItem = item => {
     this.setState({
       focusedItem: item
     });
   };
 
   this.selectItem = item => {
-    if (!(0, _sourcesTree.nodeHasChildren)(item)) {
-      this.props.selectLocation({
-        sourceId: item.contents.get("id")
-      });
+    if (!(0, _sourcesTree.isDirectory)(item)) {
+      this.props.selectSource(item.contents.id);
     }
   };
 
   this.getPath = item => {
-    const {
-      sources
-    } = this.props;
-    const obj = item.contents.get && item.contents.get("id");
-    let blackBoxedPart = "";
+    const path = `${item.path}/${item.name}`;
 
-    if (typeof obj !== "undefined" && sources.has(obj) && sources.get(obj).get("isBlackBoxed")) {
-      blackBoxedPart = "update";
+    if ((0, _sourcesTree.isDirectory)(item)) {
+      return path;
     }
 
-    return `${item.path}/${item.name}/${blackBoxedPart}`;
+    const source = this.getSource(item);
+    const blackBoxedPart = source.isBlackBoxed ? ":blackboxed" : "";
+    return `${path}${blackBoxedPart}`;
   };
 
   this.getIcon = (sources, item, depth) => {
     const {
       debuggeeUrl,
       projectRoot
     } = this.props;
 
@@ -290,48 +299,45 @@ var _initialiseProps = function () {
     if (depth === 0 && projectRoot === "") {
       return _react2.default.createElement("img", {
         className: (0, _classnames2.default)("domain", {
           debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
         })
       });
     }
 
-    if (!(0, _sourcesTree.nodeHasChildren)(item)) {
-      const obj = item.contents.get("id");
-      const source = sources.get(obj);
-      const className = (0, _classnames2.default)((0, _source.getSourceClassnames)(source), "source-icon");
+    if ((0, _sourcesTree.isDirectory)(item)) {
       return _react2.default.createElement("img", {
-        className: className
+        className: "folder"
       });
     }
 
+    const source = this.getSource(item);
     return _react2.default.createElement("img", {
-      className: "folder"
+      className: (0, _classnames2.default)((0, _source.getSourceClassnames)(source), "source-icon")
     });
   };
 
   this.onContextMenu = (event, item) => {
     const copySourceUri2Label = L10N.getStr("copySourceUri2");
     const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
     const setDirectoryRootLabel = L10N.getStr("setDirectoryRoot.label");
     const setDirectoryRootKey = L10N.getStr("setDirectoryRoot.accesskey");
     const removeDirectoryRootLabel = L10N.getStr("removeDirectoryRoot.label");
     event.stopPropagation();
     event.preventDefault();
     const menuOptions = [];
 
     if (!(0, _sourcesTree.isDirectory)(item)) {
-      const source = item.contents.get("url");
       const copySourceUri2 = {
         id: "node-menu-copy-source",
         label: copySourceUri2Label,
         accesskey: copySourceUri2Key,
         disabled: false,
-        click: () => (0, _clipboard.copyToTheClipboard)(source)
+        click: () => (0, _clipboard.copyToTheClipboard)(item.contents.url)
       };
       menuOptions.push(copySourceUri2);
     }
 
     if ((0, _sourcesTree.isDirectory)(item) && _prefs.features.root) {
       const {
         path
       } = item;
@@ -355,16 +361,34 @@ var _initialiseProps = function () {
           click: () => this.props.setProjectDirectoryRoot(path)
         });
       }
     }
 
     (0, _devtoolsContextmenu.showMenu)(event, menuOptions);
   };
 
+  this.onExpand = (item, expandedState) => {
+    this.props.setExpandedState(expandedState);
+  };
+
+  this.onCollapse = (item, expandedState) => {
+    this.props.setExpandedState(expandedState);
+  };
+
+  this.onKeyDown = e => {
+    const {
+      focusedItem
+    } = this.state;
+
+    if (e.keyCode === 13 && focusedItem) {
+      this.selectItem(focusedItem);
+    }
+  };
+
   this.renderItem = (item, depth, focused, _, expanded, {
     setExpanded
   }) => {
     const arrow = (0, _sourcesTree.nodeHasChildren)(item) ? _react2.default.createElement("img", {
       className: (0, _classnames2.default)("arrow", {
         expanded: expanded
       })
     }) : _react2.default.createElement("i", {
@@ -388,28 +412,50 @@ var _initialiseProps = function () {
           this.selectItem(item);
         }
       },
       onContextMenu: e => this.onContextMenu(e, item)
     }, arrow, icon, _react2.default.createElement("span", {
       className: "label"
     }, " ", this.renderItemName(item.name), " "));
   };
+
+  this.getRoots = () => {
+    const {
+      projectRoot
+    } = this.props;
+    const {
+      sourceTree
+    } = this.state;
+    const sourceContents = sourceTree.contents[0];
+    const rootLabel = projectRoot.split("/").pop(); // The "sourceTree.contents[0]" check ensures that there are contents
+    // A custom root with no existing sources will be ignored
+
+    if (projectRoot) {
+      if (sourceContents && sourceContents.name !== rootLabel) {
+        return sourceContents.contents[0].contents;
+      }
+
+      return sourceContents.contents;
+    }
+
+    return sourceTree.contents;
+  };
 };
 
 const mapStateToProps = state => {
   return {
     shownSource: (0, _selectors.getShownSource)(state),
     selectedSource: (0, _selectors.getSelectedSource)(state),
     debuggeeUrl: (0, _selectors.getDebuggeeUrl)(state),
     expanded: (0, _selectors.getExpandedState)(state),
     projectRoot: (0, _selectors.getProjectDirectoryRoot)(state),
     sources: (0, _selectors.getSources)(state)
   };
 };
 
 const actionCreators = {
   setExpandedState: _sourceTree.setExpandedState,
-  selectLocation: _sources.selectLocation,
+  selectSource: _sources.selectSource,
   setProjectDirectoryRoot: _ui.setProjectDirectoryRoot,
   clearProjectDirectoryRoot: _ui.clearProjectDirectoryRoot
 };
 exports.default = (0, _reactRedux.connect)(mapStateToProps, actionCreators)(SourcesTree);
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/editor/source-documents.js
+++ b/devtools/client/debugger/new/src/utils/editor/source-documents.js
@@ -1,14 +1,24 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.showLoading = exports.showErrorMessage = exports.showSourceText = exports.clearEditor = exports.updateDocument = exports.updateLineNumberFormat = exports.clearDocuments = exports.removeDocument = exports.hasDocument = exports.setDocument = exports.getDocument = undefined;
+exports.getDocument = getDocument;
+exports.hasDocument = hasDocument;
+exports.setDocument = setDocument;
+exports.removeDocument = removeDocument;
+exports.clearDocuments = clearDocuments;
+exports.updateLineNumberFormat = updateLineNumberFormat;
+exports.updateDocument = updateDocument;
+exports.clearEditor = clearEditor;
+exports.showLoading = showLoading;
+exports.showErrorMessage = showErrorMessage;
+exports.showSourceText = showSourceText;
 
 var _source = require("../source");
 
 var _wasm = require("../wasm");
 
 var _ui = require("../ui");
 
 var _sourceEditor = require("devtools/client/sourceeditor/editor");
@@ -163,21 +173,9 @@ function showSourceText(editor, source, 
   }
 
   const doc = editor.createDocument();
   setDocument(source.id, doc);
   editor.replaceDocument(doc);
   setEditorText(editor, source);
   editor.setMode((0, _source.getMode)(source, symbols));
   updateLineNumberFormat(editor, source.id);
-}
-
-exports.getDocument = getDocument;
-exports.setDocument = setDocument;
-exports.hasDocument = hasDocument;
-exports.removeDocument = removeDocument;
-exports.clearDocuments = clearDocuments;
-exports.updateLineNumberFormat = updateLineNumberFormat;
-exports.updateDocument = updateDocument;
-exports.clearEditor = clearEditor;
-exports.showSourceText = showSourceText;
-exports.showErrorMessage = showErrorMessage;
-exports.showLoading = showLoading;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/path.js
+++ b/devtools/client/debugger/new/src/utils/path.js
@@ -1,13 +1,18 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.basename = basename;
+exports.dirname = dirname;
+exports.isURL = isURL;
+exports.isAbsolute = isAbsolute;
+exports.join = join;
 
 /* 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/>. */
 function basename(path) {
   return path.split("/").pop();
 }
 
@@ -21,15 +26,9 @@ function isURL(str) {
 }
 
 function isAbsolute(str) {
   return str[0] === "/";
 }
 
 function join(base, dir) {
   return `${base}/${dir}`;
-}
-
-exports.basename = basename;
-exports.dirname = dirname;
-exports.isURL = isURL;
-exports.isAbsolute = isAbsolute;
-exports.join = join;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/result-list.js
+++ b/devtools/client/debugger/new/src/utils/result-list.js
@@ -1,14 +1,14 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.scrollList = undefined;
+exports.scrollList = scrollList;
 
 var _devtoolsEnvironment = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-environment"];
 
 var _Modal = require("../components/shared/Modal");
 
 /* 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/>. */
@@ -48,11 +48,9 @@ function chromeScrollList(elem, index) {
 
   const resultsHeight = resultsEl.clientHeight;
   const itemHeight = resultsEl.children[0].clientHeight;
   const numVisible = resultsHeight / itemHeight;
   const positionsToScroll = index - numVisible + 1;
   const itemOffset = resultsHeight % itemHeight;
   const scroll = positionsToScroll * (itemHeight + 2) + itemOffset;
   resultsEl.scrollTop = Math.max(0, scroll);
-}
-
-exports.scrollList = scrollList;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -421,27 +421,22 @@ function isLoaded(source) {
   return source.get("loadedState") === "loaded";
 }
 
 function isLoading(source) {
   return source.get("loadedState") === "loading";
 }
 
 function getTextAtPosition(source, location) {
-  if (!source || !source.text) {
+  if (!source || !source.text || source.isWasm) {
     return "";
   }
 
   const line = location.line;
   const column = location.column || 0;
-
-  if (source.isWasm) {
-    return "";
-  }
-
   const lineText = source.text.split("\n")[line - 1];
 
   if (!lineText) {
     return "";
   }
 
   return lineText.slice(column, column + 100).trim();
 }
@@ -461,10 +456,10 @@ function getSourceClassnames(source, sou
   if (isPretty(source)) {
     return "prettyPrint";
   }
 
   if (source.isBlackBoxed) {
     return "blackBox";
   }
 
-  return sourceTypes[(0, _sourcesTree.getExtension)(source.url)] || defaultClassName;
+  return sourceTypes[(0, _sourcesTree.getFileExtension)(source.url)] || defaultClassName;
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/addToTree.js
@@ -124,17 +124,17 @@ function addSourceToNode(node, url, sour
 }
 /**
  * @memberof utils/sources-tree
  * @static
  */
 
 
 function addToTree(tree, source, debuggeeUrl, projectRoot) {
-  const url = (0, _getURL.getURL)(source.get("url"), debuggeeUrl);
+  const url = (0, _getURL.getURL)(source.get ? source.get("url") : source.url, debuggeeUrl);
   const debuggeeHost = (0, _treeOrder.getDomain)(debuggeeUrl);
 
   if ((0, _utils.isInvalidUrl)(url, source) || !isUnderRoot(url, projectRoot)) {
     return;
   }
 
   const finalNode = traverseTree(url, tree, debuggeeHost, projectRoot);
   finalNode.contents = addSourceToNode(finalNode, url, source);
--- a/devtools/client/debugger/new/src/utils/sources-tree/index.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/index.js
@@ -97,16 +97,22 @@ Object.defineProperty(exports, "createNo
   }
 });
 Object.defineProperty(exports, "createParentMap", {
   enumerable: true,
   get: function () {
     return _utils.createParentMap;
   }
 });
+Object.defineProperty(exports, "getFileExtension", {
+  enumerable: true,
+  get: function () {
+    return _utils.getFileExtension;
+  }
+});
 Object.defineProperty(exports, "getRelativePath", {
   enumerable: true,
   get: function () {
     return _utils.getRelativePath;
   }
 });
 Object.defineProperty(exports, "isDirectory", {
   enumerable: true,
@@ -126,15 +132,9 @@ Object.defineProperty(exports, "isNotJav
     return _utils.isNotJavaScript;
   }
 });
 Object.defineProperty(exports, "nodeHasChildren", {
   enumerable: true,
   get: function () {
     return _utils.nodeHasChildren;
   }
-});
-Object.defineProperty(exports, "getExtension", {
-  enumerable: true,
-  get: function () {
-    return _utils.getExtension;
-  }
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/sources-tree/utils.js
+++ b/devtools/client/debugger/new/src/utils/sources-tree/utils.js
@@ -1,17 +1,17 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.nodeHasChildren = nodeHasChildren;
 exports.isExactUrlMatch = isExactUrlMatch;
 exports.isDirectory = isDirectory;
-exports.getExtension = getExtension;
+exports.getFileExtension = getFileExtension;
 exports.isNotJavaScript = isNotJavaScript;
 exports.isInvalidUrl = isInvalidUrl;
 exports.partIsFile = partIsFile;
 exports.createNode = createNode;
 exports.createParentMap = createParentMap;
 exports.getRelativePath = getRelativePath;
 
 var _url = require("devtools/client/debugger/new/dist/vendors").vendored["url"];
@@ -42,32 +42,32 @@ function isExactUrlMatch(pathPart, debug
 
 function isDirectory(url) {
   const parts = url.path.split("/").filter(p => p !== ""); // Assume that all urls point to files except when they end with '/'
   // Or directory node has children
 
   return (parts.length === 0 || url.path.slice(-1) === "/" || nodeHasChildren(url)) && url.name != "(index)";
 }
 
-function getExtension(url = "") {
+function getFileExtension(url = "") {
   const parsedUrl = (0, _url.parse)(url).pathname;
 
   if (!parsedUrl) {
     return "";
   }
 
   return parsedUrl.split(".").pop();
 }
 
 function isNotJavaScript(source) {
-  return ["css", "svg", "png"].includes(getExtension(source.url));
+  return ["css", "svg", "png"].includes(getFileExtension(source.url));
 }
 
 function isInvalidUrl(url, source) {
-  return IGNORED_URLS.indexOf(url) != -1 || !source.get("url") || !url.group || (0, _source.isPretty)(source) || isNotJavaScript(source);
+  return IGNORED_URLS.indexOf(url) != -1 || !(source.get ? source.get("url") : source.url) || !url.group || (0, _source.isPretty)(source) || isNotJavaScript(source);
 }
 
 function partIsFile(index, parts, url) {
   const isLastPart = index === parts.length - 1;
   return !isDirectory(url) && isLastPart;
 }
 
 function createNode(name, path, contents) {
--- a/devtools/client/debugger/new/src/utils/task.js
+++ b/devtools/client/debugger/new/src/utils/task.js
@@ -10,17 +10,17 @@ Object.defineProperty(exports, "__esModu
 
 /* 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/>. */
 
 /**
  * This object provides the public module functions.
  */
-const Task = {
+const Task = exports.Task = {
   // XXX: Not sure if this works in all cases...
   async: function (task) {
     return function () {
       return Task.spawn(task, this, arguments);
     };
   },
 
   /**
@@ -44,10 +44,9 @@ const Task = {
           reject(error);
           iterator.throw(error);
         });
       };
 
       callNext(undefined);
     });
   }
-};
-exports.Task = Task;
\ No newline at end of file
+};
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/text.js
+++ b/devtools/client/debugger/new/src/utils/text.js
@@ -1,14 +1,15 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.truncateMiddleText = exports.formatKeyShortcut = undefined;
+exports.formatKeyShortcut = formatKeyShortcut;
+exports.truncateMiddleText = truncateMiddleText;
 
 var _devtoolsModules = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-modules"];
 
 /* 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/>. */
 
 /**
@@ -54,12 +55,9 @@ function formatKeyShortcut(shortcut) {
 function truncateMiddleText(sourceText, maxLength) {
   let truncatedText = sourceText;
 
   if (sourceText.length > maxLength) {
     truncatedText = `${sourceText.substring(0, Math.round(maxLength / 2) - 2)}...${sourceText.substring(sourceText.length - Math.round(maxLength / 2 - 1))}`;
   }
 
   return truncatedText;
-}
-
-exports.formatKeyShortcut = formatKeyShortcut;
-exports.truncateMiddleText = truncateMiddleText;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/utils.js
+++ b/devtools/client/debugger/new/src/utils/utils.js
@@ -1,13 +1,17 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.handleError = handleError;
+exports.promisify = promisify;
+exports.endTruncateStr = endTruncateStr;
+exports.waitForMs = waitForMs;
 
 /* 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/>. */
 
 /**
  * Utils for utils, by utils
  * @module utils/utils
@@ -49,14 +53,9 @@ function endTruncateStr(str, size) {
     return `...${str.slice(str.length - size)}`;
   }
 
   return str;
 }
 
 function waitForMs(ms) {
   return new Promise(resolve => setTimeout(resolve, ms));
-}
-
-exports.handleError = handleError;
-exports.promisify = promisify;
-exports.endTruncateStr = endTruncateStr;
-exports.waitForMs = waitForMs;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/utils/wasm.js
+++ b/devtools/client/debugger/new/src/utils/wasm.js
@@ -1,14 +1,20 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.renderWasmText = exports.clearWasmStates = exports.wasmOffsetToLine = exports.lineToWasmOffset = exports.isWasm = exports.getWasmLineNumberFormatter = exports.getWasmText = undefined;
+exports.getWasmText = getWasmText;
+exports.getWasmLineNumberFormatter = getWasmLineNumberFormatter;
+exports.isWasm = isWasm;
+exports.lineToWasmOffset = lineToWasmOffset;
+exports.wasmOffsetToLine = wasmOffsetToLine;
+exports.clearWasmStates = clearWasmStates;
+exports.renderWasmText = renderWasmText;
 
 var _WasmParser = require("devtools/client/shared/vendor/WasmParser");
 
 var _WasmDis = require("devtools/client/shared/vendor/WasmDis");
 
 /* 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/>. */
@@ -174,17 +180,9 @@ function renderWasmText(sourceId, {
   const MAX_LINES = 1000000;
 
   if (lines.length > MAX_LINES) {
     lines.splice(MAX_LINES, lines.length - MAX_LINES);
     lines.push(";; .... text is truncated due to the size");
   }
 
   return lines;
-}
-
-exports.getWasmText = getWasmText;
-exports.getWasmLineNumberFormatter = getWasmLineNumberFormatter;
-exports.isWasm = isWasm;
-exports.lineToWasmOffset = lineToWasmOffset;
-exports.wasmOffsetToLine = wasmOffsetToLine;
-exports.clearWasmStates = clearWasmStates;
-exports.renderWasmText = renderWasmText;
\ No newline at end of file
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/utils/helpers.js
+++ b/devtools/client/debugger/new/src/workers/parser/utils/helpers.js
@@ -51,17 +51,17 @@ function isYieldExpression(path) {
   const {
     node,
     parent
   } = path;
   return t.isYieldExpression(node) || t.isYieldExpression(parent.init) || t.isYieldExpression(parent);
 }
 
 function isObjectShorthand(parent) {
-  return t.isObjectProperty(parent) && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName;
+  return t.isObjectProperty(parent) && parent.value && parent.key.start == parent.value.start && parent.key.loc.identifierName === parent.value.loc.identifierName;
 }
 
 function getObjectExpressionValue(node) {
   const {
     value
   } = node;
 
   if (t.isIdentifier(value)) {
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -221,16 +221,20 @@ html[dir="rtl"] .tree-node img.arrow {
   color: var(--string-color);
   border-width: 1px;
   border-style: solid;
   border-radius: 2px;
   font-size: 0.8em;
   padding: 0 2px;
 }
 
+.objectBox-node.clickable {
+  cursor: pointer;
+}
+
 /******************************************************************************/
 
 .objectBox-event,
 .objectBox-eventLog,
 .objectBox-regexp,
 .objectBox-object {
   color: var(--object-color);
   white-space: pre-wrap;
@@ -327,16 +331,17 @@ html[dir="rtl"] .tree-node img.arrow {
 
 button.open-inspector {
   mask: url("chrome://devtools/skin/images/devtools-reps/open-inspector.svg") no-repeat;
   display: inline-block;
   background-color: var(--comment-node-color);
   height: 16px;
   margin-left: 0.25em;
   vertical-align: middle;
+  cursor: pointer;
 }
 
 .objectBox-node:hover .open-inspector,
 .objectBox-textNode:hover .open-inspector,
 .open-inspector:hover {
   background-color: var(--theme-highlight-blue);
 }
 
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -5675,17 +5675,18 @@ function ElementNode(props) {
   const baseConfig = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-node"
   };
   let inspectIcon;
   if (isInTree) {
     if (onDOMNodeClick) {
       Object.assign(baseConfig, {
-        onClick: _ => onDOMNodeClick(object)
+        onClick: _ => onDOMNodeClick(object),
+        className: `${baseConfig.className} clickable`
       });
     }
 
     if (onDOMNodeMouseOver) {
       Object.assign(baseConfig, {
         onMouseOver: _ => onDOMNodeMouseOver(object)
       });
     }
--- a/devtools/server/actors/accessibility.js
+++ b/devtools/server/actors/accessibility.js
@@ -498,18 +498,25 @@ const AccessibleWalkerActor = ActorClass
       this.refMap.clear();
     }
   },
 
   /**
    * A helper method. Accessibility walker is assumed to have only 1 child which
    * is the top level document.
    */
-  children() {
-    return Promise.all([this.getDocument()]);
+  async children() {
+    if (this._childrenPromise) {
+      return this._childrenPromise;
+    }
+
+    this._childrenPromise = Promise.all([this.getDocument()]);
+    let children = await this._childrenPromise;
+    this._childrenPromise = null;
+    return children;
   },
 
   /**
    * A promise for a root document accessible actor that only resolves when its
    * corresponding document accessible object is fully loaded.
    *
    * @return {Promise}
    */
--- a/docshell/base/nsPingListener.cpp
+++ b/docshell/base/nsPingListener.cpp
@@ -157,17 +157,17 @@ SendPing(void* aClosure, nsIContent* aCo
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
 
   nsCOMPtr<nsIScriptSecurityManager> sm =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
 
   if (sm && info->referrer) {
     bool referrerIsSecure;
-    uint32_t flags = nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+    uint32_t flags = nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY;
     rv = NS_URIChainHasFlags(info->referrer, flags, &referrerIsSecure);
 
     // Default to sending less data if NS_URIChainHasFlags() fails.
     referrerIsSecure = NS_FAILED(rv) || referrerIsSecure;
 
     bool sameOrigin =
       NS_SUCCEEDED(sm->CheckSameOriginURI(info->referrer, aURI, false));
 
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -179,10 +179,22 @@ DocGroup::MoveSignalSlotListTo(nsTArray<
   aDest.SetCapacity(aDest.Length() + mSignalSlotList.Length());
   for (RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
     slot->RemovedFromSignalSlotList();
     aDest.AppendElement(Move(slot));
   }
   mSignalSlotList.Clear();
 }
 
+bool
+DocGroup::IsActive() const
+{
+  for (nsIDocument* doc : mDocuments) {
+    if (doc->IsCurrentActiveDocument()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -117,16 +117,19 @@ public:
   // microtask.
   void SignalSlotChange(HTMLSlotElement& aSlot);
 
   void MoveSignalSlotListTo(nsTArray<RefPtr<HTMLSlotElement>>& aDest);
 
   // List of DocGroups that has non-empty signal slot list.
   static AutoTArray<RefPtr<DocGroup>, 2>* sPendingDocGroups;
 
+  // Returns true if any of its documents are active but not in the bfcache.
+  bool IsActive() const;
+
 private:
   DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
   ~DocGroup();
 
   nsCString mKey;
   RefPtr<TabGroup> mTabGroup;
   nsTArray<nsIDocument*> mDocuments;
   RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -301,10 +301,27 @@ TabGroup::IsBackground() const
     }
   }
   MOZ_ASSERT(foregrounded == mForegroundCount);
 #endif
 
   return mForegroundCount == 0;
 }
 
+uint32_t
+TabGroup::Count(bool aActiveOnly) const
+{
+  if (!aActiveOnly) {
+    return mDocGroups.Count();
+  }
+
+  uint32_t count = 0;
+  for (auto iter = mDocGroups.ConstIter(); !iter.Done(); iter.Next()) {
+    if (iter.Get()->mDocGroup->IsActive()) {
+      ++count;
+    }
+  }
+
+  return count;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -95,16 +95,20 @@ public:
 
   void Leave(nsPIDOMWindowOuter* aWindow);
 
   Iterator Iter()
   {
     return mDocGroups.Iter();
   }
 
+  // Returns the size of the set of "similar-origin" DocGroups. To
+  // only consider DocGroups with at least one active document, call
+  // Count with 'aActiveOnly' = true
+  uint32_t Count(bool aActiveOnly = false) const;
 
   // Returns the nsIDocShellTreeItem with the given name, searching each of the
   // docShell trees which are within this TabGroup. It will pass itself as
   // aRequestor to each docShellTreeItem which it asks to search for the name,
   // and will not search the docShellTreeItem which is passed as aRequestor.
   //
   // This method is used in order to correctly namespace named windows based on
   // their unit of related browsing contexts.
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -167,17 +167,16 @@
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollable.h"
 #include "nsIStreamConverterService.h"
 #include "nsIStringBundle.h"
 #include "nsIURI.h"
-#include "nsIURIWithPrincipal.h"
 #include "nsIURL.h"
 #include "nsIWebNavigation.h"
 #include "nsIWindowMediator.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsMappedAttributes.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
@@ -3080,39 +3079,39 @@ nsContentUtils::GenerateStateKey(nsICont
 
 // static
 nsIPrincipal*
 nsContentUtils::SubjectPrincipal(JSContext* aCx)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // As opposed to SubjectPrincipal(), we do in fact assume that
-  // we're in a compartment here; anyone who calls this function
-  // in situations where that's not the case is doing it wrong.
-  JSCompartment* compartment = js::GetContextCompartment(aCx);
-  MOZ_ASSERT(compartment);
-
-  JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+  // we're in a realm here; anyone who calls this function in
+  // situations where that's not the case is doing it wrong.
+  JS::Realm* realm = js::GetContextRealm(aCx);
+  MOZ_ASSERT(realm);
+
+  JSPrincipals* principals = JS::GetRealmPrincipals(realm);
   return nsJSPrincipals::get(principals);
 }
 
 // static
 nsIPrincipal*
 nsContentUtils::SubjectPrincipal()
 {
   MOZ_ASSERT(IsInitialized());
   MOZ_ASSERT(NS_IsMainThread());
   JSContext* cx = GetCurrentJSContext();
   if (!cx) {
     MOZ_CRASH("Accessing the Subject Principal without an AutoJSAPI on the stack is forbidden");
   }
 
-  JSCompartment *compartment = js::GetContextCompartment(cx);
-
-  // When an AutoJSAPI is instantiated, we are in a null compartment until the
+  JS::Realm* realm = js::GetContextRealm(cx);
+
+  // When an AutoJSAPI is instantiated, we are in a null realm until the
   // first JSAutoRealm, which is kind of a purgatory as far as permissions
   // go. It would be nice to just hard-abort if somebody does a security check
   // in this purgatory zone, but that would be too fragile, since it could be
   // triggered by random IsCallerChrome() checks 20-levels deep.
   //
   // So we want to return _something_ here - and definitely not the System
   // Principal, since that would make an AutoJSAPI a very dangerous thing to
   // instantiate.
@@ -3120,19 +3119,19 @@ nsContentUtils::SubjectPrincipal()
   // The natural thing to return is a null principal. Ideally, we'd return a
   // different null principal each time, to avoid any unexpected interactions
   // when the principal accidentally gets inherited somewhere. But
   // SubjectPrincipal doesn't return strong references, so there's no way to
   // sanely manage the lifetime of multiple null principals.
   //
   // So we use a singleton null principal. To avoid it being accidentally
   // inherited and becoming a "real" subject or object principal, we do a
-  // release-mode assert during compartment creation against using this
-  // principal on an actual global.
-  if (!compartment) {
+  // release-mode assert during realm creation against using this principal on
+  // an actual global.
+  if (!realm) {
     return sNullSubjectPrincipal;
   }
 
   return SubjectPrincipal(cx);
 }
 
 // static
 nsIPrincipal*
@@ -6280,40 +6279,43 @@ nsContentUtils::GetASCIIOrigin(nsIPrinci
 }
 
 /* static */
 nsresult
 nsContentUtils::GetASCIIOrigin(nsIURI* aURI, nsACString& aOrigin)
 {
   MOZ_ASSERT(aURI, "missing uri");
 
-  // For Blob URI we have to return the origin of page using its principal.
-  nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(aURI);
-  if (uriWithPrincipal) {
-    nsCOMPtr<nsIPrincipal> principal;
-    uriWithPrincipal->GetPrincipal(getter_AddRefs(principal));
-
-    if (principal) {
-      nsCOMPtr<nsIURI> uri;
-      nsresult rv = principal->GetURI(getter_AddRefs(uri));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (uri && uri != aURI) {
-        return GetASCIIOrigin(uri, aOrigin);
-      }
-    }
+  bool isBlobURL = false;
+  nsresult rv = aURI->SchemeIs(BLOBURI_SCHEME, &isBlobURL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // For Blob URI, the path is the URL of the owning page.
+  if (isBlobURL) {
+    nsAutoCString path;
+    rv = aURI->GetPathQueryRef(path);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = NS_NewURI(getter_AddRefs(uri), path);
+    if (NS_FAILED(rv)) {
+      aOrigin.AssignLiteral("null");
+      return NS_OK;
+    }
+
+    return GetASCIIOrigin(uri, aOrigin);
   }
 
   aOrigin.Truncate();
 
   nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
   NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
 
   nsCString host;
-  nsresult rv = uri->GetAsciiHost(host);
+  rv = uri->GetAsciiHost(host);
 
   if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
     nsCString scheme;
     rv = uri->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
 
     int32_t port = -1;
     uri->GetPort(&port);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2359,19 +2359,19 @@ nsIDocument::ResetToURI(nsIURI* aURI,
       }
     }
   }
 
   if (mFontFaceSet) {
     mFontFaceSet->RefreshStandardFontLoadPrincipal();
   }
 
-  // Refresh the principal on the compartment.
+  // Refresh the principal on the realm.
   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
-    nsGlobalWindowInner::Cast(win)->RefreshCompartmentPrincipal();
+    nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
   }
 }
 
 already_AddRefed<nsIPrincipal>
 nsIDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
 {
   if (!aPrincipal) {
     return nullptr;
@@ -8490,16 +8490,28 @@ DispatchFullScreenChange(nsIDocument* aT
     /* Bubbles */ true, /* OnlyChrome */ false);
 }
 
 static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
 
 void
 nsIDocument::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget)
 {
+  if (mDocGroup && Telemetry::CanRecordExtended() &&
+      IsTopLevelContentDocument()) {
+    TabGroup* tabGroup = mDocGroup->GetTabGroup();
+
+    if (tabGroup) {
+      Telemetry::Accumulate(Telemetry::ACTIVE_DOCGROUPS_PER_TABGROUP,
+                            tabGroup->Count(true /* aActiveOnly */));
+      Telemetry::Accumulate(Telemetry::TOTAL_DOCGROUPS_PER_TABGROUP,
+                            tabGroup->Count());
+    }
+  }
+
   // Send out notifications that our <link> elements are detached,
   // but only if this is not a full unload.
   Element* root = GetRootElement();
   if (aPersisted && root) {
     RefPtr<nsContentList> links = NS_GetContentList(root,
                                                     kNameSpaceID_XHTML,
                                                     NS_LITERAL_STRING("link"));
 
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -3627,20 +3627,20 @@ nsGlobalWindowInner::GetChildWindow(cons
 {
   if (GetOuterWindowInternal()) {
     return GetOuterWindowInternal()->GetChildWindow(aName);
   }
   return nullptr;
 }
 
 void
-nsGlobalWindowInner::RefreshCompartmentPrincipal()
-{
-  JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
-                              nsJSPrincipals::get(mDoc->NodePrincipal()));
+nsGlobalWindowInner::RefreshRealmPrincipal()
+{
+  JS::SetRealmPrincipals(js::GetNonCCWObjectRealm(GetWrapperPreserveColor()),
+                         nsJSPrincipals::get(mDoc->NodePrincipal()));
 }
 
 already_AddRefed<nsIWidget>
 nsGlobalWindowInner::GetMainWidget()
 {
   FORWARD_TO_OUTER(GetMainWidget, (), nullptr);
 }
 
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -372,17 +372,17 @@ public:
                                   bool aForceReuseInnerWindow) override;
 
   virtual void SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
                                bool aOriginalOpener) override;
 
   virtual void MaybeUpdateTouchState() override;
 
   // Inner windows only.
-  void RefreshCompartmentPrincipal();
+  void RefreshRealmPrincipal();
 
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Inner windows only.
   virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override;
   void NotifyVREventListenerAdded();
   bool HasUsedVR() const;
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1742,34 +1742,36 @@ nsGlobalWindowOuter::SetNewDocument(nsID
     // so all expandos and such defined on the outer window should go away. Force
     // all Xray wrappers to be recomputed.
     JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
     if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
       return NS_ERROR_FAILURE;
     }
 
     // Inner windows are only reused for same-origin principals, but the principals
-    // don't necessarily match exactly. Update the principal on the compartment to
-    // match the new document.
-    // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here
+    // don't necessarily match exactly. Update the principal on the realm to match
+    // the new document.
+    // NB: We don't just call currentInner->RefreshRealmPrincipals() here
     // because we haven't yet set its mDoc to aDocument.
-    JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal);
+    JS::Realm* realm = js::GetNonCCWObjectRealm(newInnerGlobal);
 #ifdef DEBUG
     bool sameOrigin = false;
     nsIPrincipal *existing =
-      nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
+      nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
     aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
     MOZ_ASSERT(sameOrigin);
-#endif
+
+    JSCompartment* compartment = JS::GetCompartmentForRealm(realm);
     MOZ_ASSERT_IF(aDocument == oldDoc,
                   xpc::GetCompartmentPrincipal(compartment) ==
                   aDocument->NodePrincipal());
+#endif
     if (aDocument != oldDoc) {
-      JS_SetCompartmentPrincipals(compartment,
-                                  nsJSPrincipals::get(aDocument->NodePrincipal()));
+      JS::SetRealmPrincipals(realm,
+                             nsJSPrincipals::get(aDocument->NodePrincipal()));
       // Make sure we clear out the old content XBL scope, so the new one will
       // get created with a principal that subsumes our new principal.
       xpc::ClearContentXBLScope(newInnerGlobal);
     }
   } else {
     if (aState) {
       newInnerWindow = wsh->GetInnerWindow();
       newInnerGlobal = newInnerWindow->GetWrapperPreserveColor();
@@ -1975,23 +1977,23 @@ nsGlobalWindowOuter::SetNewDocument(nsID
   // We no longer need the old inner window.  Start its destruction if
   // its not being reused and clear our reference.
   if (doomCurrentInner) {
     currentInner->FreeInnerObjects();
   }
   currentInner = nullptr;
 
   // Ask the JS engine to assert that it's valid to access our DocGroup whenever
-  // it runs JS code for this compartment. We skip the check if this window is
-  // for chrome JS or an add-on.
+  // it runs JS code for this realm. We skip the check if this window is for
+  // chrome JS or an add-on.
   nsCOMPtr<nsIPrincipal> principal = mDoc->NodePrincipal();
   if (GetDocGroup() && !nsContentUtils::IsSystemPrincipal(principal) &&
       !BasePrincipal::Cast(principal)->AddonPolicy()) {
-    js::SetCompartmentValidAccessPtr(cx, newInnerGlobal,
-                                     newInnerWindow->GetDocGroup()->GetValidAccessPtr());
+    js::SetRealmValidAccessPtr(cx, newInnerGlobal,
+                               newInnerWindow->GetDocGroup()->GetValidAccessPtr());
   }
 
   kungFuDeathGrip->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
     nsContentUtils::AddScriptRunner(
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2558,31 +2558,31 @@ static bool
 AsmJSCacheOpenEntryForRead(JS::Handle<JSObject*> aGlobal,
                            const char16_t* aBegin,
                            const char16_t* aLimit,
                            size_t* aSize,
                            const uint8_t** aMemory,
                            intptr_t *aHandle)
 {
   nsIPrincipal* principal =
-    nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
+    nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)));
   return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
                                       aHandle);
 }
 
 static JS::AsmJSCacheResult
 AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
                             const char16_t* aBegin,
                             const char16_t* aEnd,
                             size_t aSize,
                             uint8_t** aMemory,
                             intptr_t* aHandle)
 {
   nsIPrincipal* principal =
-    nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
+    nsJSPrincipals::get(JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)));
   return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory,
                                        aHandle);
 }
 
 class JSDispatchableRunnable final : public Runnable
 {
   ~JSDispatchableRunnable()
   {
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2457,19 +2457,19 @@ GlobalObject::GetAsSupports() const
 
 nsIPrincipal*
 GlobalObject::GetSubjectPrincipal() const
 {
   if (!NS_IsMainThread()) {
     return nullptr;
   }
 
-  JSCompartment* compartment = js::GetContextCompartment(mCx);
-  MOZ_ASSERT(compartment);
-  JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+  JS::Realm* realm = js::GetContextRealm(mCx);
+  MOZ_ASSERT(realm);
+  JSPrincipals* principals = JS::GetRealmPrincipals(realm);
   return nsJSPrincipals::get(principals);
 }
 
 CallerType
 GlobalObject::CallerType() const
 {
   return nsContentUtils::ThreadsafeIsSystemCaller(mCx) ?
     dom::CallerType::System : dom::CallerType::NonSystem;
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -128,20 +128,20 @@ CallbackObject::Callback(JSContext* aCx)
   MOZ_DIAGNOSTIC_ASSERT(callback);
   return callback;
 }
 
 CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
                                      ErrorResult& aRv,
                                      const char* aExecutionReason,
                                      ExceptionHandling aExceptionHandling,
-                                     JSCompartment* aCompartment,
+                                     JS::Realm* aRealm,
                                      bool aIsJSImplementedWebIDL)
   : mCx(nullptr)
-  , mCompartment(aCompartment)
+  , mRealm(aRealm)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
 {
   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
   if (ccjs) {
     ccjs->EnterMicroTask();
   }
@@ -256,69 +256,69 @@ CallbackObject::CallSetup::CallSetup(Cal
   // And now we're ready to go.
   mCx = cx;
 }
 
 bool
 CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
 {
   if (mExceptionHandling == eRethrowExceptions) {
-    if (!mCompartment) {
+    if (!mRealm) {
       // Caller didn't ask us to filter for only exceptions we subsume.
       return true;
     }
 
     // On workers, we don't have nsIPrincipals to work with.  But we also only
-    // have one compartment, so check whether mCompartment is the same as the
-    // current compartment of mCx.
-    if (mCompartment == js::GetContextCompartment(mCx)) {
+    // have one realm, so check whether mRealm is the same as the current realm
+    // of mCx.
+    if (mRealm == js::GetContextRealm(mCx)) {
       return true;
     }
 
     MOZ_ASSERT(NS_IsMainThread());
 
-    // At this point mCx is in the compartment of our unwrapped callback, so
-    // just check whether the principal of mCompartment subsumes that of the
-    // current compartment/global of mCx.
+    // At this point mCx is in the realm of our unwrapped callback, so just
+    // check whether the principal of mRealm subsumes that of the current
+    // realm/global of mCx.
     nsIPrincipal* callerPrincipal =
-      nsJSPrincipals::get(JS_GetCompartmentPrincipals(mCompartment));
+      nsJSPrincipals::get(JS::GetRealmPrincipals(mRealm));
     nsIPrincipal* calleePrincipal = nsContentUtils::SubjectPrincipal();
     if (callerPrincipal->SubsumesConsideringDomain(calleePrincipal)) {
       return true;
     }
   }
 
-  MOZ_ASSERT(mCompartment);
+  MOZ_ASSERT(mRealm);
 
   // Now we only want to throw an exception to the caller if the object that was
-  // thrown is in the caller compartment (which we stored in mCompartment).
+  // thrown is in the caller realm (which we stored in mRealm).
 
   if (!aException.isObject()) {
     return false;
   }
 
   JS::Rooted<JSObject*> obj(mCx, &aException.toObject());
   obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
-  return js::GetObjectCompartment(obj) == mCompartment;
+  return js::GetNonCCWObjectRealm(obj) == mRealm;
 }
 
 CallbackObject::CallSetup::~CallSetup()
 {
   // To get our nesting right we have to destroy our JSAutoRealm first.
   // In particular, we want to do this before we try reporting any exceptions,
   // so we end up reporting them while in the realm of our entry point,
   // not whatever cross-compartment wrappper mCallback might be.
   // Be careful: the JSAutoRealm might not have been constructed at all!
   mAr.reset();
 
   // Now, if we have a JSContext, report any pending errors on it, unless we
   // were told to re-throw them.
   if (mCx) {
     bool needToDealWithException = mAutoEntryScript->HasException();
-    if ((mCompartment && mExceptionHandling == eRethrowContentExceptions) ||
+    if ((mRealm && mExceptionHandling == eRethrowContentExceptions) ||
         mExceptionHandling == eRethrowExceptions) {
       mErrorResult.MightThrowJSException();
       if (needToDealWithException) {
         JS::Rooted<JS::Value> exn(mCx);
         if (mAutoEntryScript->PeekException(&exn) &&
             ShouldRethrowException(exn)) {
           mAutoEntryScript->ClearException();
           MOZ_ASSERT(!mAutoEntryScript->HasException());
@@ -344,17 +344,17 @@ CallbackObject::CallSetup::~CallSetup()
       }
     }
   }
 
   mAutoIncumbentScript.reset();
   mAutoEntryScript.reset();
 
   // It is important that this is the last thing we do, after leaving the
-  // compartment and undoing all our entry/incumbent script changes
+  // realm and undoing all our entry/incumbent script changes
   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
   if (ccjs) {
     ccjs->LeaveMicroTask();
   }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -89,17 +89,17 @@ public:
   // doesn't trigger any scripts before it accesses it.
   JS::Handle<JSObject*> CallbackOrNull() const
   {
     mCallback.exposeToActiveJS();
     return CallbackPreserveColor();
   }
 
   // Like CallbackOrNull(), but will return a new dead proxy object in the
-  // caller's compartment if the callback is null.
+  // caller's realm if the callback is null.
   JSObject* Callback(JSContext* aCx);
 
   JSObject* GetCreationStack() const
   {
     return mCreationStack;
   }
 
   void MarkForCC()
@@ -141,20 +141,19 @@ public:
 
   enum ExceptionHandling {
     // Report any exception and don't throw it to the caller code.
     eReportExceptions,
     // Throw an exception to the caller code if the thrown exception is a
     // binding object for a DOMException from the caller's scope, otherwise
     // report it.
     eRethrowContentExceptions,
-    // Throw exceptions to the caller code, unless the caller compartment is
+    // Throw exceptions to the caller code, unless the caller realm is
     // provided, the exception is not a DOMException from the caller
-    // compartment, and the caller compartment does not subsume our unwrapped
-    // callback.
+    // realm, and the caller realm does not subsume our unwrapped callback.
     eRethrowExceptions
   };
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   {
     return aMallocSizeOf(this);
   }
 
@@ -290,27 +289,27 @@ protected:
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
      */
   public:
-    // If aExceptionHandling == eRethrowContentExceptions then aCompartment
-    // needs to be set to the compartment in which exceptions will be rethrown.
+    // If aExceptionHandling == eRethrowContentExceptions then aRealm
+    // needs to be set to the realm in which exceptions will be rethrown.
     //
-    // If aExceptionHandling == eRethrowExceptions then aCompartment may be set
-    // to the compartment in which exceptions will be rethrown.  In that case
-    // they will only be rethrown if that compartment's principal subsumes the
+    // If aExceptionHandling == eRethrowExceptions then aRealm may be set
+    // to the realm in which exceptions will be rethrown.  In that case
+    // they will only be rethrown if that realm's principal subsumes the
     // principal of our (unwrapped) callback.
     CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
               const char* aExecutionReason,
               ExceptionHandling aExceptionHandling,
-              JSCompartment* aCompartment = nullptr,
+              JS::Realm* aRealm = nullptr,
               bool aIsJSImplementedWebIDL = false);
     ~CallSetup();
 
     JSContext* GetContext() const
     {
       return mCx;
     }
 
@@ -318,19 +317,19 @@ protected:
     // We better not get copy-constructed
     CallSetup(const CallSetup&) = delete;
 
     bool ShouldRethrowException(JS::Handle<JS::Value> aException);
 
     // Members which can go away whenever
     JSContext* mCx;
 
-    // Caller's compartment. This will only have a sensible value if
+    // Caller's realm. This will only have a sensible value if
     // mExceptionHandling == eRethrowContentExceptions or eRethrowExceptions.
-    JSCompartment* mCompartment;
+    JS::Realm* mRealm;
 
     // And now members whose construction/destruction order we need to control.
     Maybe<AutoEntryScript> mAutoEntryScript;
     Maybe<AutoIncumbentScript> mAutoIncumbentScript;
 
     Maybe<JS::Rooted<JSObject*> > mRootedCallable;
 
     // Members which are used to set the async stack.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7231,19 +7231,19 @@ class CGCallGenerator(CGThing):
                       principal = nullptr;
                     }
                     """)
             else:
                 checkPrincipal = ""
 
             getPrincipal = fill(
                 """
-                JSCompartment* compartment = js::GetContextCompartment(cx);
-                MOZ_ASSERT(compartment);
-                JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+                JS::Realm* realm = js::GetContextRealm(cx);
+                MOZ_ASSERT(realm);
+                JSPrincipals* principals = JS::GetRealmPrincipals(realm);
                 nsIPrincipal* principal = nsJSPrincipals::get(principals);
                 ${checkPrincipal}
                 """,
                 checkPrincipal=checkPrincipal)
 
             if descriptor.interface.isExposedInAnyWorker():
                 self.cgRoot.prepend(CGGeneric(fill(
                     """
@@ -7558,17 +7558,17 @@ class CGPerSignatureCall(CGThing):
             if descriptor.interface.isJSImplemented():
                 # We need the desired proto in our constructor, because the
                 # constructor will actually construct our reflector.
                 argsPost.append("desiredProto")
         elif descriptor.interface.isJSImplemented():
             if not idlNode.isStatic():
                 needsUnwrap = True
                 needsUnwrappedVar = True
-                argsPost.append("js::GetObjectCompartment(unwrappedObj ? *unwrappedObj : obj)")
+                argsPost.append("(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))")
         elif needScopeObject(returnType, arguments, self.extendedAttributes,
                              descriptor.wrapperCache, True,
                              idlNode.getExtendedAttribute("StoreInSlot")):
             needsUnwrap = True
             needsUnwrappedVar = True
             argsPre.append("unwrappedObj ? *unwrappedObj : obj")
 
         if needsUnwrap and needsUnwrappedVar:
@@ -15004,17 +15004,17 @@ class CGJSImplMember(CGNativeMember):
                                 visibility=visibility,
                                 variadicIsSequence=variadicIsSequence,
                                 virtual=virtual,
                                 override=override)
         self.body = self.getImpl()
 
     def getArgs(self, returnType, argList):
         args = CGNativeMember.getArgs(self, returnType, argList)
-        args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+        args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
         return args
 
 
 class CGJSImplMethod(CGJSImplMember):
     """
     Class for generating code for the methods for a JS-implemented WebIDL
     interface.
     """
@@ -15053,17 +15053,17 @@ class CGJSImplMethod(CGJSImplMember):
             # arguments to the WebIDL constructor, so don't pass them to
             # __Init().  The last argument is the prototype we're supposed to
             # use, and shouldn't get passed to __Init() either.
             assert args[0].argType == 'const GlobalObject&'
             assert args[1].argType == 'JSContext*'
             assert args[-1].argType == 'JS::Handle<JSObject*>'
             assert args[-1].name == 'aGivenProto'
             constructorArgs = [arg.name for arg in args[2:-1]]
-            constructorArgs.append("js::GetObjectCompartment(scopeObj)")
+            constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)")
             initCall = fill(
                 """
                 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
                 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
                 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
                 JS::Rooted<JS::Value> wrappedVal(cx);
                 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
                   MOZ_ASSERT(JS_IsExceptionPending(cx));
@@ -15501,61 +15501,61 @@ class CGCallback(CGClass):
         argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames
         argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames
         # Now that we've recorded the argnames for our call to our private
         # method, insert our optional argument for the execution reason.
         args.append(Argument("const char*", "aExecutionReason",
                              "nullptr"))
 
         # Make copies of the arg list for the two "without rv" overloads.  Note
-        # that those don't need aExceptionHandling or aCompartment arguments
-        # because those would make not sense anyway: the only sane thing to do
-        # with exceptions in the "without rv" cases is to report them.
+        # that those don't need aExceptionHandling or aRealm arguments because
+        # those would make not sense anyway: the only sane thing to do with
+        # exceptions in the "without rv" cases is to report them.
         argsWithoutRv = list(args)
         argsWithoutRv.pop(rvIndex)
         argsWithoutThisAndRv = list(argsWithoutRv)
 
         # Add the potional argument for deciding whether the CallSetup should
         # re-throw exceptions on aRv.
         args.append(Argument("ExceptionHandling", "aExceptionHandling",
                              "eReportExceptions"))
         # And the argument for communicating when exceptions should really be
         # rethrown.  In particular, even when aExceptionHandling is
-        # eRethrowExceptions they won't get rethrown if aCompartment is provided
+        # eRethrowExceptions they won't get rethrown if aRealm is provided
         # and its principal doesn't subsume either the callback or the
         # exception.
-        args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+        args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
         # And now insert our template argument.
         argsWithoutThis = list(args)
         args.insert(0, Argument("const T&",  "thisVal"))
         argsWithoutRv.insert(0, Argument("const T&",  "thisVal"))
 
         argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
         argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()");
         # If we just leave things like that, and have no actual arguments in the
         # IDL, we will end up trying to call the templated "without rv" overload
         # with "rv" as the thisVal.  That's no good.  So explicitly append the
-        # aExceptionHandling and aCompartment values we need to end up matching
-        # the signature of our non-templated "with rv" overload.
+        # aExceptionHandling and aRealm values we need to end up matching the
+        # signature of our non-templated "with rv" overload.
         argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
 
         argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
         # Note that we need to insert at rvIndex + 1, since we inserted a
         # thisVal arg at the start.
         argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()")
 
         errorReturn = method.getDefaultRetval()
 
         setupCall = fill(
             """
             MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
             if (!aExecutionReason) {
               aExecutionReason = "${executionReason}";
             }
-            CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aCompartment);
+            CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm);
             if (!s.GetContext()) {
               MOZ_ASSERT(aRv.Failed());
               return${errorReturn};
             }
             """,
             errorReturn=errorReturn,
             executionReason=method.getPrettyName())
 
@@ -15956,35 +15956,35 @@ class CallbackMember(CGNativeMember):
         if not self.needThisHandling:
             # Since we don't need this handling, we're the actual method that
             # will be called, so we need an aRethrowExceptions argument.
             if not self.rethrowContentException:
                 args.append(Argument("const char*", "aExecutionReason",
                                      "nullptr"))
                 args.append(Argument("ExceptionHandling", "aExceptionHandling",
                                      "eReportExceptions"))
-            args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+            args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
             return args
         # We want to allow the caller to pass in a "this" value, as
         # well as a JSContext.
         return [Argument("JSContext*", "cx"),
                 Argument("JS::Handle<JS::Value>", "aThisVal")] + args
 
     def getCallSetup(self):
         if self.needThisHandling:
             # It's been done for us already
             return ""
         callSetup = "CallSetup s(this, aRv"
         if self.rethrowContentException:
             # getArgs doesn't add the aExceptionHandling argument but does add
-            # aCompartment for us.
-            callSetup += ', "%s", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName()
+            # aRealm for us.
+            callSetup += ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ ' % self.getPrettyName()
             callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
         else:
-            callSetup += ', "%s", aExceptionHandling, aCompartment' % self.getPrettyName()
+            callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName()
         callSetup += ");\n"
         return fill(
             """
             $*{callSetup}
             JSContext* cx = s.GetContext();
             if (!cx) {
               MOZ_ASSERT(aRv.Failed());
               return${errorReturn};
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -184,17 +184,17 @@ CreateException(nsresult aRv, const nsAC
 }
 
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack(int32_t aMaxDepth)
 {
   // is there a current context available?
   JSContext* cx = nsContentUtils::GetCurrentJSContext();
 
-  if (!cx || !js::GetContextCompartment(cx)) {
+  if (!cx || !js::GetContextRealm(cx)) {
     return nullptr;
   }
 
   static const unsigned MAX_FRAMES = 100;
   if (aMaxDepth < 0) {
     aMaxDepth = MAX_FRAMES;
   }
 
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -68,16 +68,18 @@ skip-if = debug == false
 [test_unforgeablesonexpando.html]
 [test_crossOriginWindowSymbolAccess.html]
 [test_primitive_this.html]
 [test_callback_exceptions.html]
 [test_bug1123516_maplikesetlike.html]
 skip-if = debug == false
 [test_jsimplemented_eventhandler.html]
 skip-if = debug == false
+[test_jsimplemented_cross_realm_this.html]
+skip-if = debug == false
 [test_iterable.html]
 skip-if = debug == false
 [test_oom_reporting.html]
 [test_domProxyArrayLengthGetter.html]
 [test_exceptionSanitization.html]
 skip-if = debug == false
 [test_stringBindings.html]
 skip-if = debug == false
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_jsimplemented_cross_realm_this.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1464374-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1464374</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1464374">Mozilla Bug 1464374</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<iframe></iframe>
+<script type="application/javascript">
+  /** Test for Bug 1464374 **/
+  SimpleTest.waitForExplicitFinish();
+
+  function doTest() {
+    var frame = frames[0];
+    var obj = new frame.TestInterfaceJS();
+    var ex;
+    try {
+      TestInterfaceJS.prototype.testThrowTypeError.call(obj);
+    } catch(e) {
+      ex = e;
+    }
+    ok(ex, "Should have an exception");
+    SimpleTest.finish();
+  }
+
+  SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+                            doTest);
+</script>
+
+</body>
+</html>
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1532,20 +1532,20 @@ nsHTMLDocument::Open(JSContext* cx,
 
   CreateAndAddWyciwygChannel();
 
   --mWriteLevel;
 
   SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
 
   // After changing everything around, make sure that the principal on the
-  // document's compartment exactly matches NodePrincipal().
+  // document's realm exactly matches NodePrincipal().
   DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor();
   MOZ_ASSERT_IF(wrapper,
-                JS_GetCompartmentPrincipals(js::GetObjectCompartment(wrapper)) ==
+                JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(wrapper)) ==
                 nsJSPrincipals::get(NodePrincipal()));
 
   return kungFuDeathGrip.forget();
 }
 
 void
 nsHTMLDocument::Close(ErrorResult& rv)
 {
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -613,17 +613,17 @@ AutoJSAPI::ReportException()
   }
 }
 
 bool
 AutoJSAPI::PeekException(JS::MutableHandle<JS::Value> aVal)
 {
   MOZ_ASSERT_IF(mIsMainThread, IsStackTop());
   MOZ_ASSERT(HasException());
-  MOZ_ASSERT(js::GetContextCompartment(cx()));
+  MOZ_ASSERT(js::GetContextRealm(cx()));
   if (!JS_GetPendingException(cx(), aVal)) {
     return false;
   }
   return true;
 }
 
 bool
 AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal)
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -885,22 +885,26 @@ nsContentSecurityManager::IsOriginPotent
   NS_WARNING_ASSERTION(!scheme.EqualsLiteral("blob"),
                        "IsOriginPotentiallyTrustworthy ignoring blob scheme");
 
   // According to the specification, the user agent may choose to extend the
   // trust to other, vendor-specific URL schemes. We use this for "resource:",
   // which is technically a substituting protocol handler that is not limited to
   // local resource mapping, but in practice is never mapped remotely as this
   // would violate assumptions a lot of code makes.
-  if (scheme.EqualsLiteral("https") ||
-      scheme.EqualsLiteral("file") ||
-      scheme.EqualsLiteral("resource") ||
-      scheme.EqualsLiteral("app") ||
-      scheme.EqualsLiteral("moz-extension") ||
-      scheme.EqualsLiteral("wss")) {
+  // We use nsIProtocolHandler flags to determine which protocols we consider a priori
+  // authenticated.
+  bool aPrioriAuthenticated = false;
+  if (NS_FAILED(NS_URIChainHasFlags(uri,
+                                    nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
+                                    &aPrioriAuthenticated))) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (aPrioriAuthenticated) {
     *aIsTrustWorthy = true;
     return NS_OK;
   }
 
   nsAutoCString host;
   rv = uri->GetHost(host);
   if (NS_FAILED(rv)) {
     return NS_OK;
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -592,29 +592,29 @@ nsMixedContentBlocker::ShouldLoad(bool a
   * URI_DOES_NOT_RETURN_DATA - e.g.
   *   "mailto"
   * URI_IS_LOCAL_RESOURCE - e.g.
   *   "data",
   *   "resource",
   *   "moz-icon"
   * URI_INHERITS_SECURITY_CONTEXT - e.g.
   *   "javascript"
-  * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g.
+  * URI_IS_POTENTIALLY_TRUSTWORTHY - e.g.
   *   "https",
   *   "moz-safe-about"
   *
   */
   bool schemeLocal = false;
   bool schemeNoReturnData = false;
   bool schemeInherits = false;
   bool schemeSecure = false;
   if (NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal))  ||
       NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) ||
       NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) ||
-      NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) {
+      NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY, &schemeSecure))) {
     *aDecision = REJECT_REQUEST;
     return NS_ERROR_FAILURE;
   }
   // TYPE_IMAGE redirects are cached based on the original URI, not the final
   // destination and hence cache hits for images may not have the correct
   // innerContentLocation.  Check if the cached hit went through an http redirect,
   // and if it did, we can't treat this as a secure subresource.
   if (!aHadInsecureImageRedirect &&
new file mode 100644
--- /dev/null
+++ b/dom/security/test/gtest/TestSecureContext.cpp
@@ -0,0 +1,94 @@
+/* -*- 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 "gtest/gtest.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "nsContentSecurityManager.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
+#include "nsScriptSecurityManager.h"
+#include "NullPrincipal.h"
+
+static const uint32_t kURIMaxLength = 64;
+
+struct TestExpectations {
+  char uri[kURIMaxLength ];
+  bool expectedResult;
+};
+
+// ============================= TestDirectives ========================
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithCodeBasePrincipal)
+{
+  //boolean isOriginPotentiallyTrustworthy(in nsIPrincipal aPrincipal);
+
+  static const TestExpectations uris[] = {
+    { "http://example.com/", false },
+    { "https://example.com/", true },
+    { "ws://example.com/", false },
+    { "wss://example.com/", true },
+    { "file:///xyzzy", true },
+    { "ftp://example.com", false },
+    { "about:config", false },
+    { "http://localhost", true },
+    { "http://xyzzy.localhost", false },
+    { "http://127.0.0.1", true },
+    { "resource://xyzzy", true },
+    { "moz-extension://xyzzy", true },
+    { "data:data:text/plain;charset=utf-8;base64,eHl6enk=", false },
+    { "blob://unique-id", false },
+    { "mailto:foo@bar.com", false },
+    { "moz-icon://example.com", false },
+    { "javascript:42", false },
+  };
+
+  uint32_t numExpectations = sizeof(uris) / sizeof(TestExpectations);
+  nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+  ASSERT_TRUE(!!csManager);
+
+  nsresult rv;
+  for (uint32_t i = 0; i < numExpectations; i++) {
+    nsCOMPtr<nsIPrincipal> prin;
+    nsAutoCString uri(uris[i].uri);
+    rv = nsScriptSecurityManager::GetScriptSecurityManager()->
+           CreateCodebasePrincipalFromOrigin(uri, getter_AddRefs(prin));
+    bool isPotentiallyTrustworthy = false;
+    rv = csManager->IsOriginPotentiallyTrustworthy(prin, &isPotentiallyTrustworthy);
+    ASSERT_EQ(NS_OK, rv);
+    ASSERT_EQ(isPotentiallyTrustworthy, uris[i].expectedResult);
+  }
+}
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithSystemPrincipal)
+{
+  RefPtr<nsScriptSecurityManager> ssManager = nsScriptSecurityManager::GetScriptSecurityManager();
+  ASSERT_TRUE(!!ssManager);
+  nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+  ASSERT_TRUE(!!csManager);
+
+  nsCOMPtr<nsIPrincipal> sysPrin = nsContentUtils::GetSystemPrincipal();
+  bool isPotentiallyTrustworthy;
+  nsresult rv = csManager->IsOriginPotentiallyTrustworthy(sysPrin, &isPotentiallyTrustworthy);
+  ASSERT_EQ(rv, NS_OK);
+  ASSERT_TRUE(isPotentiallyTrustworthy);
+}
+
+TEST(SecureContext, IsOriginPotentiallyTrustworthyWithNullPrincipal)
+{
+  RefPtr<nsScriptSecurityManager> ssManager = nsScriptSecurityManager::GetScriptSecurityManager();
+  ASSERT_TRUE(!!ssManager);
+  nsCOMPtr<nsIContentSecurityManager> csManager = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+  ASSERT_TRUE(!!csManager);
+
+  RefPtr<NullPrincipal> nullPrin = NullPrincipal::CreateWithoutOriginAttributes();
+  bool isPotentiallyTrustworthy;
+  nsresult rv = csManager->IsOriginPotentiallyTrustworthy(nullPrin, &isPotentiallyTrustworthy);
+  ASSERT_EQ(rv, NS_OK);
+  ASSERT_TRUE(!isPotentiallyTrustworthy);
+}
--- a/dom/security/test/gtest/moz.build
+++ b/dom/security/test/gtest/moz.build
@@ -1,11 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
      'TestCSPParser.cpp',
+     'TestSecureContext.cpp',
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
+
+LOCAL_INCLUDES += [
+    '/caps',
+]
--- a/dom/tests/mochitest/chrome/chrome.ini
+++ b/dom/tests/mochitest/chrome/chrome.ini
@@ -41,22 +41,20 @@ support-files =
 [test_DOM_element_instanceof.xul]
 [test_activation.xul]
 tags = fullscreen
 [test_bug799299.xul]
 [test_bug800817.xul]
 [test_bug830858.xul]
 [test_bug1224790-1.xul]
 tags = openwindow
-# synthesizeNativeOSXClick does not work on 10.6
-skip-if = os != 'mac' || os_version == '10.6'
+skip-if = os != 'mac'
 [test_bug1224790-2.xul]
 tags = openwindow
-# synthesizeNativeOSXClick does not work on 10.6
-skip-if = os != 'mac' || os_version == '10.6'
+skip-if = os != 'mac'
 [test_callback_wrapping.xul]
 [test_clonewrapper.xul]
 [test_cyclecollector.xul]
 [test_docshell_swap.xul]
 [test_focus.xul]
 skip-if = os == 'linux' && !debug # bug 1296622
 [test_focus_docnav.xul]
 [test_focus_switchbinding.xul]
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1201,25 +1201,22 @@ public:
                       override
   {
     MOZ_ASSERT(!aRealmStats->extra);
 
     // ReportJSRuntimeExplicitTreeStats expects that
     // aRealmStats->extra is a xpc::RealmStatsExtras pointer.
     xpc::RealmStatsExtras* extras = new xpc::RealmStatsExtras;
 
-    // This is the |jsPathPrefix|.  Each worker has exactly two realms:
-    // one for atoms, and one for everything else.
+    // This is the |jsPathPrefix|.  Each worker has exactly one realm.
     JSCompartment* compartment = JS::GetCompartmentForRealm(aRealm);
     extras->jsPathPrefix.Assign(mRtPath);
     extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/",
                                             (void *)js::GetCompartmentZone(compartment));
-    extras->jsPathPrefix += js::IsAtomsRealm(aRealm)
-                            ? NS_LITERAL_CSTRING("realm(web-worker-atoms)/")
-                            : NS_LITERAL_CSTRING("realm(web-worker)/");
+    extras->jsPathPrefix += NS_LITERAL_CSTRING("realm(web-worker)/");
 
     // This should never be used when reporting with workers (hence the "?!").
     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
 
     MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
     MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
 
     extras->location = nullptr;
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -34,19 +34,21 @@ class gfxVarReceiver;
   _(PDMWMFDisableD3D11Dlls,     nsCString,        nsCString())          \
   _(PDMWMFDisableD3D9Dlls,      nsCString,        nsCString())          \
   _(DXInterop2Blocked,          bool,             false)                \
   _(DXNV12Blocked,              bool,             false)                \
   _(UseWebRender,               bool,             false)                \
   _(UseWebRenderANGLE,          bool,             false)                \
   _(UseWebRenderDCompWin,       bool,             false)                \
   _(UseWebRenderProgramBinary,  bool,             false)                \
+  _(UseWebRenderProgramBinaryDisk, bool,          false)                \
   _(WebRenderDebugFlags,        int32_t,          0)                    \
   _(ScreenDepth,                int32_t,          0)                    \
   _(GREDirectory,               nsString,         nsString())           \
+  _(ProfDirectory,              nsString,         nsString())           \
   _(UseOMTP,                    bool,             false)                \
   _(AllowD3D11KeyedMutex,       bool,             false)                \
 
   /* Add new entries above this line. */
 
 // Some graphics settings are computed on the UI process and must be
 // communicated to content and GPU processes. gfxVars helps facilitate
 // this. Its function is similar to gfxPrefs, except rather than hold
--- a/gfx/layers/client/MultiTiledContentClient.cpp
+++ b/gfx/layers/client/MultiTiledContentClient.cpp
@@ -321,17 +321,16 @@ void ClientMultiTiledLayerBuffer::Update
 
       // Reset
       mPaintTiles.clear();
       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                                std::numeric_limits<int32_t>::max());
     }
 
     bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
-
     for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
       TileClient& tile = mRetainedTiles[i];
 
       // Only worry about padding when not doing low-res because it simplifies
       // the math and the artifacts won't be noticable
       // Edge padding prevents sampling artifacts when compositing.
       if (edgePaddingEnabled && mResolution == 1 &&
           tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
@@ -437,16 +436,18 @@ ClientMultiTiledLayerBuffer::ValidateTil
   // Add the region we copied from the front buffer into the painted region
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
 
   if (!backBuffer) {
     return false;
   }
 
+  // Get the targets to draw into, and create a dual target
+  // if we are using component alpha
   RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
   RefPtr<DrawTarget> dtOnWhite;
   if (backBufferOnWhite) {
     dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
   }
 
   if (!dt || (backBufferOnWhite && !dtOnWhite)) {
     aTile.DiscardBuffers();
@@ -455,49 +456,59 @@ ClientMultiTiledLayerBuffer::ValidateTil
 
   RefPtr<DrawTarget> drawTarget;
   if (dtOnWhite) {
     drawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
   } else {
     drawTarget = dt;
   }
 
-  auto clear = CapturedTiledPaintState::Clear{
-    dt,
-    dtOnWhite,
-    tileDirtyRegion
-  };
+  // We need to clear the dirty region of the tile before painting
+  // if we are painting non-opaque content
+  Maybe<CapturedTiledPaintState::Clear> clear = Nothing();
+  if (mode != SurfaceMode::SURFACE_OPAQUE) {
+    clear = Some(CapturedTiledPaintState::Clear{
+      dt,
+      dtOnWhite,
+      tileDirtyRegion
+    });
+  }
 
+  // Queue or execute the paint operation
   gfx::Tile paintTile;
   paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
 
   if (aFlags & TilePaintFlags::Async) {
     RefPtr<CapturedTiledPaintState> asyncPaint = new CapturedTiledPaintState();
 
     RefPtr<DrawTargetCapture> captureDT =
       Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(),
                                        drawTarget->GetSize(),
                                        drawTarget->GetFormat());
     paintTile.mDrawTarget = captureDT;
     asyncPaint->mTarget = drawTarget;
     asyncPaint->mCapture = captureDT;
 
     asyncPaint->mCopies = std::move(asyncPaintCopies);
-    asyncPaint->mClears.push_back(clear);
+    if (clear) {
+      asyncPaint->mClears.push_back(*clear);
+    }
 
     asyncPaint->mClients = std::move(asyncPaintClients);
     asyncPaint->mClients.push_back(backBuffer);
     if (backBufferOnWhite) {
       asyncPaint->mClients.push_back(backBufferOnWhite);
     }
 
     mPaintStates.push_back(asyncPaint);
   } else {
     paintTile.mDrawTarget = drawTarget;
-    clear.ClearBuffer();
+    if (clear) {
+      clear->ClearBuffer();
+    }
   }
 
   mPaintTiles.push_back(paintTile);
 
   mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
   mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
 
   // The new buffer is now validated, remove the dirty region from it.
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -192,78 +192,31 @@ ClientSingleTiledLayerBuffer::PaintThebe
 
   // If the old frontbuffer was discarded then attempt to copy what we
   // can from it to the new backbuffer.
   if (discardedFrontBuffer) {
     nsIntRegion copyableRegion;
     copyableRegion.And(aNewValidRegion, discardedValidRegion);
     copyableRegion.SubOut(aDirtyRegion);
 
-    if (!copyableRegion.IsEmpty()) {
-      OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC
-                                       : OpenMode::OPEN_NONE;
-
-      TextureClientAutoLock frontLock(discardedFrontBuffer,
-                                      OpenMode::OPEN_READ | asyncFlags);
-      Maybe<TextureClientAutoLock> frontOnWhiteLock;
-      if (discardedFrontBufferOnWhite && backBufferOnWhite) {
-        frontOnWhiteLock.emplace(discardedFrontBufferOnWhite, OpenMode::OPEN_READ | asyncFlags);
-      }
-
-      // Copy to both backBuffer and backBufferOnWhite if required, or copy to neither.
-      if (frontLock.Succeeded() && (!frontOnWhiteLock || frontOnWhiteLock->Succeeded())) {
-        RefPtr<gfx::DrawTarget> frontBuffer = discardedFrontBuffer->BorrowDrawTarget();
-
-        if (frontBuffer) {
-          for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
-            const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
-            const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
-
-            auto copy = CapturedTiledPaintState::Copy{
-              frontBuffer, dt, rect, dest
-            };
-            if (asyncPaint) {
-              paintCopies.push_back(copy);
-            } else {
-              copy.CopyBuffer();
-            }
-          }
+    if (!mTile.CopyFromBuffer(discardedFrontBuffer,
+                              discardedFrontBufferOnWhite,
+                              discardedValidRegion.GetBounds().TopLeft(),
+                              mTilingOrigin,
+                              copyableRegion,
+                              aFlags,
+                              &paintCopies)) {
+      gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
+    } else {
+      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
 
-          if (frontOnWhiteLock) {
-            RefPtr<gfx::DrawTarget> frontBufferOnWhite = discardedFrontBufferOnWhite->BorrowDrawTarget();
-
-            if (frontBufferOnWhite) {
-              for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
-                const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
-                const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
-
-                auto copy = CapturedTiledPaintState::Copy{
-                  frontBufferOnWhite, dtOnWhite, rect, dest
-                };
-                if (asyncPaint) {
-                  paintCopies.push_back(copy);
-                } else {
-                  copy.CopyBuffer();
-                }
-              }
-            } else {
-              gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
-            }
-          }
-        } else {
-          gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
-        }
-
-        TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
-
-        // We don't need to repaint valid content that was just copied.
-        paintRegion.SubOut(copyableRegion);
-        copyableRegion.MoveBy(-mTilingOrigin);
-        tileDirtyRegion.SubOut(copyableRegion);
-      }
+      // We don't need to repaint valid content that was just copied.
+      paintRegion.SubOut(copyableRegion);
+      copyableRegion.MoveBy(-mTilingOrigin);
+      tileDirtyRegion.SubOut(copyableRegion);
     }
   }
 
   if (mode != SurfaceMode::SURFACE_OPAQUE) {
     auto clear = CapturedTiledPaintState::Clear{
       dt,
       dtOnWhite,
       tileDirtyRegion,
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -478,55 +478,59 @@ CopyFrontToBack(TextureClient* aFront,
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
                                         const nsIntRegion& aVisibleRegion,
                                         nsIntRegion& aAddPaintedRegion,
                                         TilePaintFlags aFlags,
                                         std::vector<CapturedTiledPaintState::Copy>* aCopies,
                                         std::vector<RefPtr<TextureClient>>* aClients)
 {
-  if (mBackBuffer && mFrontBuffer) {
-    gfx::IntSize tileSize = mFrontBuffer->GetSize();
-    const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
+  if (!mBackBuffer || !mFrontBuffer) {
+    return;
+  }
+
+  gfx::IntSize tileSize = mFrontBuffer->GetSize();
+  const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
 
-    if (aDirtyRegion.Contains(tileRect)) {
-      // The dirty region means that we no longer need the front buffer, so
-      // discard it.
-      DiscardFrontBuffer();
-    } else {
-      // Region that needs copying.
-      nsIntRegion regionToCopy = mInvalidBack;
+  if (aDirtyRegion.Contains(tileRect)) {
+    // The dirty region means that we no longer need the front buffer, so
+    // discard it.
+    DiscardFrontBuffer();
+    return;
+  }
 
-      regionToCopy.Sub(regionToCopy, aDirtyRegion);
-      regionToCopy.And(regionToCopy, aVisibleRegion);
+  // Region that needs copying.
+  nsIntRegion regionToCopy = mInvalidBack;
 
-      aAddPaintedRegion = regionToCopy;
+  regionToCopy.Sub(regionToCopy, aDirtyRegion);
+  regionToCopy.And(regionToCopy, aVisibleRegion);
 
-      if (regionToCopy.IsEmpty()) {
-        // Just redraw it all.
-        return;
-      }
+  aAddPaintedRegion = regionToCopy;
+
+  if (regionToCopy.IsEmpty()) {
+    // Just redraw it all.
+    return;
+  }
 
-      // Copy the bounding rect of regionToCopy. As tiles are quite small, it
-      // is unlikely that we'd save much by copying each individual rect of the
-      // region, but we can reevaluate this if it becomes an issue.
-      const IntRect rectToCopy = regionToCopy.GetBounds();
-      gfx::IntRect gfxRectToCopy(rectToCopy.X(), rectToCopy.Y(), rectToCopy.Width(), rectToCopy.Height());
-      if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy, aFlags, aCopies, aClients)) {
-        if (mBackBufferOnWhite) {
-          MOZ_ASSERT(mFrontBufferOnWhite);
-          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy, aFlags, aCopies, aClients)) {
-            mInvalidBack.Sub(mInvalidBack, aVisibleRegion);
-          }
-        } else {
-          mInvalidBack.Sub(mInvalidBack, aVisibleRegion);
-        }
-      }
+  // Copy the bounding rect of regionToCopy. As tiles are quite small, it
+  // is unlikely that we'd save much by copying each individual rect of the
+  // region, but we can reevaluate this if it becomes an issue.
+  const IntRect rectToCopy = regionToCopy.GetBounds();
+  if (!CopyFrontToBack(mFrontBuffer, mBackBuffer, rectToCopy, aFlags, aCopies, aClients)) {
+    return;
+  }
+
+  if (mBackBufferOnWhite) {
+    MOZ_ASSERT(mFrontBufferOnWhite);
+    if (!CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, rectToCopy, aFlags, aCopies, aClients)) {
+      return;
     }
   }
+
+  mInvalidBack.Sub(mInvalidBack, aVisibleRegion);
 }
 
 void
 TileClient::DiscardFrontBuffer()
 {
   if (mFrontBuffer) {
     MOZ_ASSERT(mFrontBuffer->GetReadLock());
 
@@ -575,16 +579,69 @@ TileClient::DiscardBackBuffer()
   if (mBackBuffer) {
     DiscardTexture(mBackBuffer, mAllocator);
     mBackBuffer.Set(this, nullptr);
     DiscardTexture(mBackBufferOnWhite, mAllocator);
     mBackBufferOnWhite = nullptr;
   }
 }
 
+bool
+TileClient::CopyFromBuffer(RefPtr<TextureClient> aBuffer,
+                           RefPtr<TextureClient> aBufferOnWhite,
+                           nsIntPoint aBufferOrigin,
+                           nsIntPoint aTileOrigin,
+                           const nsIntRegion& aRegion,
+                           TilePaintFlags aFlags,
+                           std::vector<CapturedTiledPaintState::Copy>* aCopies)
+{
+  if (aRegion.IsEmpty()) {
+    return true;
+  }
+
+  bool asyncPaint = !!(aFlags & TilePaintFlags::Async);
+  auto CopyBuffer = [&aRegion, asyncPaint, &aCopies] (auto aSrc, auto aSrcOrigin, auto aDest, auto aDestOrigin) {
+    MOZ_ASSERT(aDest->IsLocked());
+
+    OpenMode asyncFlags = asyncPaint ? OpenMode::OPEN_ASYNC
+                                     : OpenMode::OPEN_NONE;
+    TextureClientAutoLock lock(aSrc, OpenMode::OPEN_READ | asyncFlags);
+    if (!lock.Succeeded()) {
+      return false;
+    }
+
+    RefPtr<gfx::DrawTarget> srcTarget = aSrc->BorrowDrawTarget();
+    RefPtr<gfx::DrawTarget> destTarget = aDest->BorrowDrawTarget();
+    if (!srcTarget || !destTarget) {
+      return false;
+    }
+
+    for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+      const gfx::IntRect src = iter.Get() - aSrcOrigin;
+      const gfx::IntPoint dest = iter.Get().TopLeft() - aDestOrigin;
+
+      auto copy = CapturedTiledPaintState::Copy{
+        srcTarget, destTarget, src, dest
+      };
+
+      if (asyncPaint) {
+        aCopies->push_back(copy);
+      } else {
+        copy.CopyBuffer();
+      }
+    }
+    return true;
+  };
+
+  return CopyBuffer(aBuffer, aBufferOrigin, mBackBuffer, aTileOrigin) &&
+    (!aBufferOnWhite ||
+     !mBackBufferOnWhite ||
+     CopyBuffer(aBufferOnWhite, aBufferOrigin, mBackBufferOnWhite, aTileOrigin));
+}
+
 static already_AddRefed<TextureClient>
 CreateBackBufferTexture(TextureClient* aCurrentTexture,
                         CompositableClient& aCompositable,
                         TextureClientAllocator* aAllocator)
 {
   if (aCurrentTexture) {
     // Our current back-buffer is still locked by the compositor. This can occur
     // when the client is producing faster than the compositor can consume. In
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -135,16 +135,28 @@ struct TileClient
                                RefPtr<TextureClient>* aTextureClientOnWhite,
                                std::vector<CapturedTiledPaintState::Copy>* aCopies,
                                std::vector<RefPtr<TextureClient>>* aClients);
 
   void DiscardFrontBuffer();
 
   void DiscardBackBuffer();
 
+  /*
+   * Copy aRegion from aBuffer and aBufferOnWhite positioned at aBufferOrigin
+   * into ourselves assuming we are positioned at aTileOrigin.
+   */
+  bool CopyFromBuffer(RefPtr<TextureClient> aBuffer,
+                      RefPtr<TextureClient> aBufferOnWhite,
+                      nsIntPoint aBufferOrigin,
+                      nsIntPoint aTileOrigin,
+                      const nsIntRegion& aRegion,
+                      TilePaintFlags aFlags,
+                      std::vector<CapturedTiledPaintState::Copy>* aCopies);
+
   /* We wrap the back buffer in a class that disallows assignment
    * so that we can track when ever it changes so that we can update
    * the expiry tracker for expiring the back buffers */
   class PrivateProtector {
     public:
       void Set(TileClient * container, RefPtr<TextureClient>);
       void Set(TileClient * container, TextureClient*);
       // Implicitly convert to TextureClient* because we can't chain
@@ -163,18 +175,20 @@ struct TileClient
   bool mWasPlaceholder;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
 #endif
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
 private:
-  // Copies dirty pixels from the front buffer into the back buffer,
-  // and records the copied region in aAddPaintedRegion.
+  /*
+   * Copies dirty pixels from the front buffer into the back buffer,
+   * and records the copied region in aAddPaintedRegion.
+   */
   void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
                                    const nsIntRegion& aVisibleRegion,
                                    nsIntRegion& aAddPaintedRegion,
                                    TilePaintFlags aFlags,
                                    std::vector<CapturedTiledPaintState::Copy>* aCopies,
                                    std::vector<RefPtr<TextureClient>>* aClients);
 };
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/gfx/GraphicsMessages.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/Services.h"
+#include "nsAppDirectoryServiceDefs.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "gfxConfig.h"
@@ -861,16 +862,28 @@ gfxPlatform::Init()
       Preferences::Unlock(FONT_VARIATIONS_PREF);
       if (!gPlatform->HasVariationFontSupport()) {
         // Ensure variation fonts are disabled and the pref is locked.
         Preferences::SetBool(FONT_VARIATIONS_PREF, false,
                              PrefValueKind::Default);
         Preferences::SetBool(FONT_VARIATIONS_PREF, false);
         Preferences::Lock(FONT_VARIATIONS_PREF);
       }
+
+      nsCOMPtr<nsIFile> profDir;
+      rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP, getter_AddRefs(profDir));
+      if (NS_FAILED(rv)) {
+        gfxVars::SetProfDirectory(nsString());
+      } else {
+        nsAutoString path;
+        profDir->GetPath(path);
+        gfxVars::SetProfDirectory(nsString(path));
+      }
+
+      gfxUtils::RemoveShaderCacheFromDiskIfNecessary();
     }
 
     if (obs) {
       obs->NotifyObservers(nullptr, "gfx-features-ready", nullptr);
     }
 }
 
 /* static*/ bool
@@ -2621,17 +2634,20 @@ gfxPlatform::InitWebRenderConfig()
         NS_LITERAL_CSTRING("FEATURE_FAILURE_ANGLE_DISABLED"));
     } else {
       gfxVars::SetUseWebRenderANGLE(gfxConfig::IsEnabled(Feature::WEBRENDER));
     }
   }
 #endif
 
   if (Preferences::GetBool("gfx.webrender.program-binary", false)) {
-    gfx::gfxVars::SetUseWebRenderProgramBinary(gfxConfig::IsEnabled(Feature::WEBRENDER));
+    gfxVars::SetUseWebRenderProgramBinary(gfxConfig::IsEnabled(Feature::WEBRENDER));
+    if (Preferences::GetBool("gfx.webrender.program-binary-disk", false)) {
+      gfxVars::SetUseWebRenderProgramBinaryDisk(gfxConfig::IsEnabled(Feature::WEBRENDER));
+    }
   }
 
 #ifdef MOZ_WIDGET_ANDROID
   featureWebRender.ForceDisable(
     FeatureStatus::Unavailable,
     "WebRender not ready for use on Android",
     NS_LITERAL_CSTRING("FEATURE_FAILURE_ANDROID"));
 #endif
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -17,21 +17,24 @@
 #include "mozilla/dom/ImageEncoder.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/Swizzle.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Vector.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "nsAppRunner.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
 #include "nsIGfxInfo.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRegion.h"
 #include "nsServiceManagerUtils.h"
@@ -1463,16 +1466,64 @@ gfxUtils::ThreadSafeGetFeatureStatus(con
     }
 
     return runnable->GetNSResult();
   }
 
   return gfxInfo->GetFeatureStatus(feature, failureId, status);
 }
 
+#define GFX_SHADER_CHECK_BUILD_VERSION_PREF "gfx-shader-check.build-version"
+#define GFX_SHADER_CHECK_DEVICE_ID_PREF "gfx-shader-check.device-id"
+#define GFX_SHADER_CHECK_DRIVER_VERSION_PREF "gfx-shader-check.driver-version"
+
+/* static */ void
+gfxUtils::RemoveShaderCacheFromDiskIfNecessary()
+{
+  if (!gfxVars::UseWebRenderProgramBinaryDisk()) {
+    return;
+  }
+
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+
+  // Get current values
+  nsCString buildID(mozilla::PlatformBuildID());
+  nsString deviceID, driverVersion;
+  gfxInfo->GetAdapterDeviceID(deviceID);
+  gfxInfo->GetAdapterDriverVersion(driverVersion);
+
+  // Get pref stored values
+  nsAutoCString buildIDChecked;
+  Preferences::GetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildIDChecked);
+  nsAutoString deviceIDChecked, driverVersionChecked;
+  Preferences::GetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceIDChecked);
+  Preferences::GetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersionChecked);
+
+  if (buildID == buildIDChecked &&
+      deviceID == deviceIDChecked &&
+      driverVersion == driverVersionChecked) {
+      return;
+  }
+
+  nsAutoString path(gfx::gfxVars::ProfDirectory());
+
+  if (!wr::remove_program_binary_disk_cache(&path)) {
+    // Failed to remove program binary disk cache. The disk cache might have
+    // invalid data. Disable program binary disk cache usage.
+    gfxVars::SetUseWebRenderProgramBinaryDisk(false);
+    return;
+  }
+
+  Preferences::SetCString(GFX_SHADER_CHECK_BUILD_VERSION_PREF, buildID);
+  Preferences::SetString(GFX_SHADER_CHECK_DEVICE_ID_PREF, deviceID);
+  Preferences::SetString(GFX_SHADER_CHECK_DRIVER_VERSION_PREF, driverVersion);
+  return;
+}
+
+
 /* static */ bool
 gfxUtils::DumpDisplayList() {
   return gfxPrefs::LayoutDumpDisplayList() ||
          (gfxPrefs::LayoutDumpDisplayListParent() && XRE_IsParentProcess()) ||
          (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
 }
 
 FILE *gfxUtils::sDumpPaintFile = stderr;
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -290,16 +290,18 @@ public:
                                    const char16_t* aEncoderOptions,
                                    nsIInputStream** outStream);
 
     static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                                                int32_t feature,
                                                nsACString& failureId,
                                                int32_t* status);
 
+    static void RemoveShaderCacheFromDiskIfNecessary();
+
     /**
      * Copy to the clipboard as a PNG encoded Data URL.
      */
     static void CopyAsDataURI(SourceSurface* aSourceSurface);
     static void CopyAsDataURI(DrawTarget* aDT);
 
     static bool DumpDisplayList();
 
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -6,22 +6,26 @@ license = "MPL-2.0"
 
 [dependencies]
 rayon = "1"
 thread_profiler = "0.1.1"
 euclid = { version = "0.17", features = ["serde"] }
 app_units = "0.6"
 gleam = "0.5"
 log = "0.4"
+nsstring = { path = "../../servo/support/gecko/nsstring" }
+bincode = "1.0"
+uuid = {version = "0.1.18"}
+fxhash = "0.2.1"
 
 [dependencies.webrender]
 path = "../webrender"
 version = "0.57.2"
 default-features = false
-features = ["capture"]
+features = ["capture", "serialize_program"]
 
 [target.'cfg(target_os = "windows")'.dependencies]
 dwrote = "0.4.1"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.5"
 core-graphics = "0.13"
 foreign-types = "0.3.0"
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -65,16 +65,26 @@ RenderThread::Start()
     return;
   }
 
   sRenderThread = new RenderThread(thread);
 #ifdef XP_WIN
   widget::WinCompositorWindowThread::Start();
 #endif
   layers::SharedSurfacesParent::Initialize();
+
+  if (XRE_IsGPUProcess() &&
+      gfx::gfxVars::UseWebRenderProgramBinary()) {
+    MOZ_ASSERT(gfx::gfxVars::UseWebRender());
+    // Initialize program cache if necessary
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<RenderThread>(sRenderThread.get()),
+      &RenderThread::ProgramCacheTask);
+    sRenderThread->Loop()->PostTask(runnable.forget());
+  }
 }
 
 // static
 void
 RenderThread::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sRenderThread);
@@ -506,40 +516,53 @@ RenderThread::GetRenderTexture(wr::WrExt
 {
   MOZ_ASSERT(IsInRenderThread());
 
   MutexAutoLock lock(mRenderTextureMapLock);
   MOZ_ASSERT(mRenderTextures.GetWeak(aExternalImageId.mHandle));
   return mRenderTextures.GetWeak(aExternalImageId.mHandle);
 }
 
+void
+RenderThread::ProgramCacheTask()
+{
+  ProgramCache();
+}
+
 WebRenderProgramCache*
 RenderThread::ProgramCache()
 {
   MOZ_ASSERT(IsInRenderThread());
 
   if (!mProgramCache) {
-    mProgramCache = MakeUnique<WebRenderProgramCache>();
+    mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw());
   }
   return mProgramCache.get();
 }
 
 WebRenderThreadPool::WebRenderThreadPool()
 {
   mThreadPool = wr_thread_pool_new();
 }
 
 WebRenderThreadPool::~WebRenderThreadPool()
 {
   wr_thread_pool_delete(mThreadPool);
 }
 
-WebRenderProgramCache::WebRenderProgramCache()
+WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool)
 {
-  mProgramCache = wr_program_cache_new();
+  MOZ_ASSERT(aThreadPool);
+
+  nsAutoString path;
+  if (gfxVars::UseWebRenderProgramBinaryDisk()) {
+    path.Append(gfx::gfxVars::ProfDirectory());
+  }
+  mProgramCache = wr_program_cache_new(&path, aThreadPool);
+  wr_try_load_shader_from_disk(mProgramCache);
 }
 
 WebRenderProgramCache::~WebRenderProgramCache()
 {
   wr_program_cache_delete(mProgramCache);
 }
 
 } // namespace wr
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -37,17 +37,17 @@ public:
   wr::WrThreadPool* Raw() { return mThreadPool; }
 
 protected:
   wr::WrThreadPool* mThreadPool;
 };
 
 class WebRenderProgramCache {
 public:
-  WebRenderProgramCache();
+  explicit WebRenderProgramCache(wr::WrThreadPool* aThreadPool);
 
   ~WebRenderProgramCache();
 
   wr::WrProgramCache* Raw() { return mProgramCache; }
 
 protected:
   wr::WrProgramCache* mProgramCache;
 };
@@ -165,16 +165,17 @@ public:
   /// Can only be called from the render thread.
   WebRenderProgramCache* ProgramCache();
 
 private:
   explicit RenderThread(base::Thread* aThread);
 
   void DeferredRenderTextureHostDestroy(RefPtr<RenderTextureHost> aTexture);
   void ShutDownTask(layers::SynchronousTask* aTask);
+  void ProgramCacheTask();
 
   ~RenderThread();
 
   base::Thread* const mThread;
 
   WebRenderThreadPool mThreadPool;
   UniquePtr<WebRenderProgramCache> mProgramCache;
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -8,22 +8,24 @@ use std::os::raw::{c_void, c_char, c_flo
 use gleam::gl;
 
 use webrender::api::*;
 use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
 use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::DebugFlags;
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks};
-use webrender::{ProgramCache, UploadMethod, VertexUsageHint};
+use webrender::{UploadMethod, VertexUsageHint};
 use thread_profiler::register_thread_with_profiler;
 use moz2d_renderer::Moz2dImageRenderer;
+use program_cache::{WrProgramCache, remove_disk_cache};
 use app_units::Au;
 use rayon;
 use euclid::SideOffsets2D;
+use nsstring::nsAString;
 
 #[cfg(target_os = "windows")]
 use dwrote::{FontDescriptor, FontWeight, FontStretch, FontStyle};
 
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
@@ -822,33 +824,50 @@ pub unsafe extern "C" fn wr_thread_pool_
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub unsafe extern "C" fn wr_thread_pool_delete(thread_pool: *mut WrThreadPool) {
     Box::from_raw(thread_pool);
 }
 
-pub struct WrProgramCache(Rc<ProgramCache>);
-
 #[no_mangle]
-pub unsafe extern "C" fn wr_program_cache_new() -> *mut WrProgramCache {
-    let program_cache = ProgramCache::new(None);
-    Box::into_raw(Box::new(WrProgramCache(program_cache)))
+pub unsafe extern "C" fn wr_program_cache_new(prof_path: &nsAString, thread_pool: *mut WrThreadPool) -> *mut WrProgramCache {
+    let workers = &(*thread_pool).0;
+    let program_cache = WrProgramCache::new(prof_path, workers);
+    Box::into_raw(Box::new(program_cache))
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub unsafe extern "C" fn wr_program_cache_delete(program_cache: *mut WrProgramCache) {
-    Rc::from_raw(program_cache);
+    Box::from_raw(program_cache);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_try_load_shader_from_disk(program_cache: *mut WrProgramCache) {
+    if !program_cache.is_null() {
+        (*program_cache).try_load_from_disk();
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn remove_program_binary_disk_cache(prof_path: &nsAString) -> bool {
+    match remove_disk_cache(prof_path) {
+        Ok(_) => true,
+        Err(_) => {
+            error!("Failed to remove program binary disk cache");
+            false
+        }
+    }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_renderer_update_program_cache(renderer: &mut Renderer, program_cache: &mut WrProgramCache) {
-    let program_cache = Rc::clone(&program_cache.0);
+    let program_cache = Rc::clone(&program_cache.rc_get());
     renderer.update_program_cache(program_cache);
 }
 
 // Call MakeCurrent before this.
 #[no_mangle]
 pub extern "C" fn wr_window_new(window_id: WrWindowId,
                                 window_width: u32,
                                 window_height: u32,
--- a/gfx/webrender_bindings/src/lib.rs
+++ b/gfx/webrender_bindings/src/lib.rs
@@ -3,27 +3,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![deny(warnings)]
 
 extern crate webrender;
 extern crate euclid;
 extern crate app_units;
 extern crate gleam;
+extern crate nsstring;
 extern crate rayon;
 extern crate thread_profiler;
+extern crate bincode;
+extern crate uuid;
+extern crate fxhash;
 
 #[macro_use]
 extern crate log;
 
 #[cfg(target_os = "windows")]
 extern crate dwrote;
 
+
 #[cfg(target_os = "macos")]
 extern crate core_foundation;
 #[cfg(target_os = "macos")]
 extern crate core_graphics;
 #[cfg(target_os = "macos")]
 extern crate foreign_types;
 
+mod program_cache;
+
 #[allow(non_snake_case)]
 pub mod bindings;
 pub mod moz2d_renderer;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/src/program_cache.rs
@@ -0,0 +1,298 @@
+use std::cell::RefCell;
+use std::io::{Error, ErrorKind};
+use std::fs::{File, create_dir_all, read_dir};
+use std::io::{Read, Write};
+use std::path::{PathBuf};
+use std::rc::Rc;
+use std::sync::Arc;
+
+use webrender::{ProgramBinary, ProgramCache, ProgramCacheObserver};
+use bincode;
+use fxhash;
+use nsstring::nsAString;
+use rayon::ThreadPool;
+use uuid::Uuid;
+
+const MAX_LOAD_TIME_MS: u64 = 400;
+const MAX_CACHED_PROGRAM_COUNT: u32 = 15;
+
+fn deserialize_program_binary(path: &PathBuf) -> Result<Arc<ProgramBinary>, Error> {
+    let mut buf = vec![];
+    let mut file = File::open(path)?;
+    file.read_to_end(&mut buf)?;
+
+    if buf.len() <= 8 {
+        return Err(Error::new(ErrorKind::InvalidData, "File size is too small"));
+    }
+    let hash = &buf[0 .. 8];
+    let data = &buf[8 ..];
+
+    // Check if hash is correct
+    let hash:u64 = bincode::deserialize(&hash).unwrap();
+    let hash_data = fxhash::hash64(&data);
+    if hash != hash_data {
+        return Err(Error::new(ErrorKind::InvalidData, "File data is invalid"));
+    }
+
+    // Deserialize ProgramBinary
+    let binary = match bincode::deserialize(&data) {
+        Ok(binary) => binary,
+        Err(_) => return Err(Error::new(ErrorKind::InvalidData, "Failed to deserialize ProgramBinary")),
+    };
+
+    Ok(Arc::new(binary))
+}
+
+#[cfg(target_os = "windows")]
+fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option<PathBuf> {
+    if prof_path.is_empty() {
+        // Empty means that we do not use disk cache.
+        return None;
+    }
+
+    use std::ffi::OsString;
+    use std::os::windows::prelude::*;
+
+    let prof_path = OsString::from_wide(prof_path.as_ref());
+    let mut cache_path = PathBuf::from(&prof_path);
+    cache_path.push("shader-cache");
+
+    Some(cache_path)
+}
+
+#[cfg(not(target_os="windows"))]
+fn get_cache_path_from_prof_path(_prof_path: &nsAString) -> Option<PathBuf> {
+    // Not supported yet.
+    None
+}
+
+struct WrProgramBinaryDiskCache {
+    cache_path: Option<PathBuf>,
+    program_count: u32,
+    is_enabled: bool,
+    workers: Arc<ThreadPool>,
+}
+
+impl WrProgramBinaryDiskCache {
+    #[allow(dead_code)]
+    fn new(prof_path: &nsAString, workers: &Arc<ThreadPool>) -> Self {
+        let cache_path = get_cache_path_from_prof_path(prof_path);
+        let is_enabled = cache_path.is_some();
+        let workers = Arc::clone(workers);
+
+        WrProgramBinaryDiskCache{
+            cache_path,
+            program_count: 0,
+            is_enabled,
+            workers,
+        }
+    }
+
+    fn notify_binary_added(&mut self, program_binary: &Arc<ProgramBinary>) {
+        if !self.is_enabled {
+            return;
+        }
+
+        if let Some(ref cache_path) = self.cache_path {
+            if let Err(_) = create_dir_all(&cache_path) {
+                error!("failed to create dir for shader disk cache");
+                return;
+            }
+
+            self.program_count += 1;
+            if self.program_count > MAX_CACHED_PROGRAM_COUNT {
+                // Disable disk cache to avoid storing more shader programs to disk
+                self.is_enabled = false;
+                return;
+            }
+
+            // Use uuid for file name
+            let uuid1 = Uuid::new_v4();
+            let file_name = uuid1.to_hyphenated_string();
+            let program_binary = Arc::clone(program_binary);
+            let file_path = cache_path.join(&file_name);
+
+            let program_count = self.program_count;
+
+            // Save to disk on worker thread
+            self.workers.spawn(move || {
+
+                use std::time::{Instant};
+                let start = Instant::now();
+
+                let data: Vec<u8> = match bincode::serialize(&*program_binary) {
+                    Ok(data) => data,
+                    Err(err) => {
+                        error!("Failed to serialize program binary error: {}", err);
+                        return;
+                    }
+                };
+
+                let mut file = match File::create(&file_path) {
+                    Ok(file) => file,
+                    Err(err) => {
+                        error!("Unable to create file for program binary error: {}", err);
+                        return;
+                    }
+                };
+
+                // Write hash
+                let hash = fxhash::hash64(&data);
+                let hash = bincode::serialize(&hash).unwrap();
+                assert!(hash.len() == 8);
+                match file.write_all(&hash) {
+                    Err(err) => {
+                        error!("Failed to write hash to file error: {}", err);
+                    }
+                    _ => {},
+                };
+
+                // Write serialized data
+                match file.write_all(&data) {
+                    Err(err) => {
+                        error!("Failed to write program binary to file error: {}", err);
+                    }
+                    _ => {},
+                };
+
+                let elapsed = start.elapsed();
+                info!("notify_binary_added: {} ms program_count {}",
+                    (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64, program_count);
+
+            });
+        }
+    }
+
+    pub fn try_load_from_disk(&mut self, program_cache: &Rc<ProgramCache>) {
+        if !self.is_enabled {
+            return;
+        }
+
+        if let Some(ref cache_path) = self.cache_path {
+            use std::time::{Instant};
+            let start = Instant::now();
+
+            // Load program binaries if exist
+            if cache_path.exists() && cache_path.is_dir() {
+                for entry in read_dir(cache_path).unwrap() {
+                    let entry = entry.unwrap();
+                    let path = entry.path();
+
+                    info!("loading shader file");
+
+                    match deserialize_program_binary(&path) {
+                        Ok(program) => {
+                            program_cache.load_program_binary(program);
+                        }
+                        Err(err) => {
+                            error!("Failed to desriralize program binary error: {}", err);
+                        }
+                    };
+
+                    self.program_count += 1;
+
+                    let elapsed = start.elapsed();
+                    let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+                    info!("deserialize_program_binary: {} ms program_count {}", elapsed_ms, self.program_count);
+
+                    if self.program_count > MAX_CACHED_PROGRAM_COUNT || elapsed_ms > MAX_LOAD_TIME_MS {
+                        // Disable disk cache to avoid storing more shader programs to disk
+                        self.is_enabled = false;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
+
+pub struct WrProgramCacheObserver {
+    disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>,
+}
+
+impl WrProgramCacheObserver {
+    #[allow(dead_code)]
+    fn new(disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>) -> Self {
+        WrProgramCacheObserver{
+            disk_cache,
+        }
+    }
+}
+
+impl ProgramCacheObserver for WrProgramCacheObserver {
+    fn notify_binary_added(&self, program_binary: &Arc<ProgramBinary>) {
+        self.disk_cache.borrow_mut().notify_binary_added(program_binary);
+    }
+
+    fn notify_program_binary_failed(&self, _program_binary: &Arc<ProgramBinary>) {
+        error!("Failed program_binary");
+    }
+}
+
+
+pub struct WrProgramCache {
+    program_cache: Rc<ProgramCache>,
+    disk_cache: Option<Rc<RefCell<WrProgramBinaryDiskCache>>>,
+}
+
+impl WrProgramCache {
+    #[cfg(target_os = "windows")]
+    pub fn new(prof_path: &nsAString, workers: &Arc<ThreadPool>) -> Self {
+        let disk_cache = Rc::new(RefCell::new(WrProgramBinaryDiskCache::new(prof_path, workers)));
+        let program_cache_observer = Box::new(WrProgramCacheObserver::new(Rc::clone(&disk_cache)));
+        let program_cache = ProgramCache::new(Some(program_cache_observer));
+
+        WrProgramCache {
+            program_cache,
+            disk_cache: Some(disk_cache),
+        }
+    }
+
+    #[cfg(not(target_os="windows"))]
+    pub fn new(_prof_path: &nsAString, _: &Arc<ThreadPool>) -> Self {
+        let program_cache = ProgramCache::new(None);
+
+        WrProgramCache {
+            program_cache,
+            disk_cache: None,
+        }
+    }
+
+    pub fn rc_get(&self) -> &Rc<ProgramCache> {
+        &self.program_cache
+    }
+
+    pub fn try_load_from_disk(&self) {
+        if let Some(ref disk_cache) = self.disk_cache {
+            disk_cache.borrow_mut().try_load_from_disk(&self.program_cache);
+        } else {
+            error!("Shader disk cache is not supported");
+        }
+    }
+}
+
+#[cfg(target_os = "windows")]
+pub fn remove_disk_cache(prof_path: &nsAString) -> Result<(), Error> {
+    use std::fs::remove_dir_all;
+    use std::time::{Instant};
+
+    if let Some(cache_path) = get_cache_path_from_prof_path(prof_path) {
+        if cache_path.exists() {
+            let start = Instant::now();
+
+            remove_dir_all(&cache_path)?;
+
+            let elapsed = start.elapsed();
+            let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64;
+            info!("remove_disk_cache: {} ms", elapsed_ms);
+        }
+    }
+    Ok(())
+}
+
+#[cfg(not(target_os="windows"))]
+pub fn remove_disk_cache(_prof_path: &nsAString) -> Result<(), Error> {
+    error!("Shader disk cache is not supported");
+    return Err(Error::new(ErrorKind::Other, "Not supported"))
+}
+
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -12,16 +12,20 @@
  */
 
 #include <cstdint>
 #include <cstdlib>
 
 namespace mozilla {
 namespace wr {
 
+static const uint32_t MAX_CACHED_PROGRAM_COUNT = 15;
+
+static const uint64_t MAX_LOAD_TIME_MS = 400;
+
 enum class BorderStyle : uint32_t {
   None = 0,
   Solid = 1,
   Double = 2,
   Dotted = 3,
   Dashed = 4,
   Hidden = 5,
   Groove = 6,
@@ -996,16 +1000,20 @@ extern bool is_glcontext_egl(void *aGlco
 
 extern bool is_in_compositor_thread();
 
 extern bool is_in_main_thread();
 
 extern bool is_in_render_thread();
 
 WR_INLINE
+bool remove_program_binary_disk_cache(const nsAString *aProfPath)
+WR_FUNC;
+
+WR_INLINE
 const VecU8 *wr_add_ref_arc(const ArcVecU8 *aArc)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_capture(DocumentHandle *aDh,
                     const char *aPath,
                     uint32_t aBitsRaw)
 WR_FUNC;
@@ -1406,17 +1414,18 @@ WR_INLINE
 void wr_pipeline_info_delete(WrPipelineInfo aInfo)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_program_cache_delete(WrProgramCache *aProgramCache)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
-WrProgramCache *wr_program_cache_new()
+WrProgramCache *wr_program_cache_new(const nsAString *aProfPath,
+                                     WrThreadPool *aThreadPool)
 WR_FUNC;
 
 WR_INLINE
 bool wr_renderer_current_epoch(Renderer *aRenderer,
                                WrPipelineId aPipelineId,
                                WrEpoch *aOutEpoch)
 WR_FUNC;
 
@@ -1658,16 +1667,20 @@ WR_FUNC;
 
 WR_INLINE
 void wr_transaction_update_epoch(Transaction *aTxn,
                                  WrPipelineId aPipelineId,
                                  WrEpoch aEpoch)
 WR_FUNC;
 
 WR_INLINE
+void wr_try_load_shader_from_disk(WrProgramCache *aProgramCache)
+WR_FUNC;
+
+WR_INLINE
 void wr_vec_u8_free(WrVecU8 aV)
 WR_FUNC;
 
 WR_INLINE
 void wr_vec_u8_push_bytes(WrVecU8 *aV,
                           ByteSlice aBytes)
 WR_FUNC;
 
--- a/ipc/glue/IPCStream.ipdlh
+++ b/ipc/glue/IPCStream.ipdlh
@@ -29,16 +29,18 @@ struct IPCRemoteStream
 {
   // If this is true, the stream will send data only when the first operation
   // is done on the destination side. The benefit of this is that we do not
   // send data if not needed. On the other hand, it could have a performance
   // issue.
   bool delayedStart;
 
   IPCRemoteStreamType stream;
+
+  int64_t length;
 };
 
 // Use IPCStream or OptionalIPCStream in your ipdl to represent serialized
 // nsIInputStreams.  Then use AutoIPCStream from IPCStreamUtils.h to perform
 // the serialization.
 union IPCStream
 {
   InputStreamParamsWithFds;
--- a/ipc/glue/IPCStreamDestination.cpp
+++ b/ipc/glue/IPCStreamDestination.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "IPCStreamDestination.h"
+#include "mozilla/InputStreamLengthWrapper.h"
 #include "mozilla/Mutex.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsIBufferedStreams.h"
 #include "nsICloneableInputStream.h"
 #include "nsIPipe.h"
 
 namespace mozilla {
@@ -259,16 +260,19 @@ NS_INTERFACE_MAP_BEGIN(IPCStreamDestinat
 NS_INTERFACE_MAP_END
 
 // ----------------------------------------------------------------------------
 // IPCStreamDestination
 
 IPCStreamDestination::IPCStreamDestination()
   : mOwningThread(NS_GetCurrentThread())
   , mDelayedStart(false)
+#ifdef MOZ_DEBUG
+  , mLengthSet(false)
+#endif
 {
 }
 
 IPCStreamDestination::~IPCStreamDestination()
 {
 }
 
 nsresult
@@ -297,16 +301,34 @@ IPCStreamDestination::Initialize()
 }
 
 void
 IPCStreamDestination::SetDelayedStart(bool aDelayedStart)
 {
   mDelayedStart = aDelayedStart;
 }
 
+void
+IPCStreamDestination::SetLength(int64_t aLength)
+{
+  MOZ_ASSERT(mReader);
+  MOZ_ASSERT(!mLengthSet);
+
+#ifdef DEBUG
+  mLengthSet = true;
+#endif
+
+  if (aLength != -1) {
+    nsCOMPtr<nsIInputStream> finalStream;
+    finalStream = new InputStreamLengthWrapper(mReader.forget(), aLength);
+    mReader = do_QueryInterface(finalStream);
+    MOZ_ASSERT(mReader);
+  }
+}
+
 already_AddRefed<nsIInputStream>
 IPCStreamDestination::TakeReader()
 {
   MOZ_ASSERT(mReader);
   MOZ_ASSERT(!mDelayedStartInputStream);
 
   if (mDelayedStart) {
     mDelayedStartInputStream =
--- a/ipc/glue/IPCStreamDestination.h
+++ b/ipc/glue/IPCStreamDestination.h
@@ -34,16 +34,19 @@ public:
   Cast(PChildToParentStreamParent* aActor);
 
   static IPCStreamDestination*
   Cast(PParentToChildStreamChild* aActor);
 
   void
   SetDelayedStart(bool aDelayedStart);
 
+  void
+  SetLength(int64_t aLength);
+
   already_AddRefed<nsIInputStream>
   TakeReader();
 
   bool
   IsOnOwningThread() const;
 
   void
   DispatchRunnable(already_AddRefed<nsIRunnable>&& aRunnable);
@@ -91,14 +94,18 @@ private:
   // This is created by TakeReader() if we need to delay the reading of data.
   // We keep a reference to the stream in order to inform it when the actor goes
   // away. If that happens, the reading of data will not be possible anymore.
   class DelayedStartInputStream;
   RefPtr<DelayedStartInputStream> mDelayedStartInputStream;
 
   nsCOMPtr<nsIThread> mOwningThread;
   bool mDelayedStart;
+
+#ifdef MOZ_DEBUG
+  bool mLengthSet;
+#endif
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_IPCStreamDestination_h
--- a/ipc/glue/IPCStreamUtils.cpp
+++ b/ipc/glue/IPCStreamUtils.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 "IPCStreamUtils.h"
 
 #include "nsIIPCSerializableInputStream.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/InputStreamLengthHelper.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
@@ -132,16 +133,24 @@ SerializeInputStreamWithFdsParent(nsIIPC
 template<typename M>
 bool
 SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, M* aManager,
                      bool aDelayedStart)
 {
   MOZ_ASSERT(aStream);
   MOZ_ASSERT(aManager);
 
+  // Let's try to take the length using InputStreamLengthHelper. If the length
+  // cannot be taken synchronously, and its length is needed, the stream needs
+  // to be fully copied in memory on the deserialization side.
+  int64_t length;
+  if (!InputStreamLengthHelper::GetSyncLength(aStream, &length)) {
+    length = -1;
+  }
+
   // As a fallback, attempt to stream the data across using a IPCStream
   // actor. For blocking streams, create a nonblocking pipe instead,
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
   if (!asyncStream) {
     const uint32_t kBufferSize = 32768; // matches IPCStream buffer size.
     nsCOMPtr<nsIAsyncOutputStream> sink;
     nsresult rv = NS_NewPipe2(getter_AddRefs(asyncStream),
                               getter_AddRefs(sink),
@@ -163,16 +172,17 @@ SerializeInputStream(nsIInputStream* aSt
     }
   }
 
   MOZ_ASSERT(asyncStream);
 
   IPCRemoteStream remoteStream;
   remoteStream.delayedStart() = aDelayedStart;
   remoteStream.stream() = IPCStreamSource::Create(asyncStream, aManager);
+  remoteStream.length() = length;
   aValue = remoteStream;
 
   return true;
 }
 
 template<typename M>
 bool
 SerializeInputStreamChild(nsIInputStream* aStream, M* aManager,
@@ -374,16 +384,17 @@ DeserializeIPCStream(const IPCStream& aV
         IPCStreamDestination::Cast(remoteStreamType.get_PChildToParentStreamParent());
     } else {
       MOZ_ASSERT(remoteStreamType.type() == IPCRemoteStreamType::TPParentToChildStreamChild);
       destinationStream =
         IPCStreamDestination::Cast(remoteStreamType.get_PParentToChildStreamChild());
     }
 
     destinationStream->SetDelayedStart(remoteStream.delayedStart());
+    destinationStream->SetLength(remoteStream.length());
     return destinationStream->TakeReader();
   }
 
   // Note, we explicitly do not support deserializing the PChildToParentStream actor on
   // the child side nor the PParentToChildStream actor on the parent side.
   MOZ_ASSERT(aValue.type() == IPCStream::TInputStreamParamsWithFds);
 
   const InputStreamParamsWithFds& streamWithFds =
--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -56,16 +56,17 @@ union InputStreamParams
 {
   StringInputStreamParams;
   FileInputStreamParams;
   BufferedInputStreamParams;
   MIMEInputStreamParams;
   MultiplexInputStreamParams;
   SlicedInputStreamParams;
   IPCBlobInputStreamParams;
+  InputStreamLengthWrapperParams;
 };
 
 union OptionalInputStreamParams
 {
   void_t;
   InputStreamParams;
 };
 
@@ -77,10 +78,17 @@ struct BufferedInputStreamParams
 
 struct MIMEInputStreamParams
 {
   OptionalInputStreamParams optionalStream;
   HeaderEntry[] headers;
   bool startedReading;
 };
 
+struct InputStreamLengthWrapperParams
+{
+  InputStreamParams stream;
+  int64_t length;
+  bool consumed;
+};
+
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -8,16 +8,17 @@
 
 #include "nsIIPCSerializableInputStream.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ipc/IPCBlobInputStream.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/SlicedInputStream.h"
+#include "mozilla/InputStreamLengthWrapper.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsID.h"
 #include "nsIXULRuntime.h"
 #include "nsMIMEInputStream.h"
 #include "nsMultiplexInputStream.h"
 #include "nsNetCID.h"
 #include "nsStringStream.h"
@@ -95,16 +96,20 @@ InputStreamHelper::DeserializeInputStrea
     case InputStreamParams::TMultiplexInputStreamParams:
       serializable = do_CreateInstance(kMultiplexInputStreamCID);
       break;
 
     case InputStreamParams::TSlicedInputStreamParams:
       serializable = new mozilla::SlicedInputStream();
       break;
 
+    case InputStreamParams::TInputStreamLengthWrapperParams:
+      serializable = new mozilla::InputStreamLengthWrapper();
+      break;
+
     default:
       MOZ_ASSERT(false, "Unknown params!");
       return nullptr;
   }
 
   MOZ_ASSERT(serializable);
 
   if (!serializable->Deserialize(aParams, aFileDescriptors)) {
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -92,18 +92,18 @@ SetDestroyRealmCallback(JSContext* cx, D
 typedef void
 (* RealmNameCallback)(JSContext* cx, Handle<Realm*> realm, char* buf, size_t bufsize);
 
 // Set the callback SpiderMonkey calls to get the name of a realm, for
 // diagnostic output.
 extern JS_PUBLIC_API(void)
 SetRealmNameCallback(JSContext* cx, RealmNameCallback callback);
 
-// Get the global object for the given realm. Returns null only if `realm` is
-// the atoms realm.
+// Get the global object for the given realm. This only returns nullptr during
+// GC, between collecting the global object and destroying the Realm.
 extern JS_PUBLIC_API(JSObject*)
 GetRealmGlobalOrNull(Handle<Realm*> realm);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmObjectPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmFunctionPrototype(JSContext* cx);
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -879,17 +879,17 @@ class RootingContext
     static const RootingContext* get(const JSContext* cx) {
         return reinterpret_cast<const RootingContext*>(cx);
     }
 
     static RootingContext* get(JSContext* cx) {
         return reinterpret_cast<RootingContext*>(cx);
     }
 
-    friend JSCompartment* js::GetContextCompartment(const JSContext* cx);
+    friend JS::Realm* js::GetContextRealm(const JSContext* cx);
     friend JS::Zone* js::GetContextZone(const JSContext* cx);
 };
 
 class JS_PUBLIC_API(AutoGCRooter)
 {
   protected:
     enum class Tag : uint8_t {
         Array,          /* js::AutoArrayRooter */
@@ -1049,20 +1049,26 @@ namespace js {
  *
  * - These must not be available on the more restricted superclasses of
  *   JSContext, so we can't simply define them on RootingContext.
  *
  * - They're perfectly ordinary JSContext functionality, so ought to be
  *   usable without resorting to jsfriendapi.h, and when JSContext is an
  *   incomplete type.
  */
+inline JS::Realm*
+GetContextRealm(const JSContext* cx)
+{
+    return JS::RootingContext::get(cx)->realm_;
+}
+
 inline JSCompartment*
 GetContextCompartment(const JSContext* cx)
 {
-    return GetCompartmentForRealm(JS::RootingContext::get(cx)->realm_);
+    return GetCompartmentForRealm(GetContextRealm(cx));
 }
 
 inline JS::Zone*
 GetContextZone(const JSContext* cx)
 {
     return JS::RootingContext::get(cx)->zone_;
 }
 
--- a/js/public/Wrapper.h
+++ b/js/public/Wrapper.h
@@ -373,19 +373,16 @@ UnwrapOneChecked(JSObject* obj, bool sto
 // ExposeToActiveJS is not called on wrapper targets so this can be called from
 // the GC or off the main thread.
 JS_FRIEND_API(JSObject*)
 UncheckedUnwrapWithoutExpose(JSObject* obj);
 
 void
 ReportAccessDenied(JSContext* cx);
 
-JS_FRIEND_API(bool)
-IsCrossCompartmentWrapper(JSObject* obj);
-
 JS_FRIEND_API(void)
 NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
 
 void
 RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
 
 JS_FRIEND_API(bool)
 RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -169,16 +169,17 @@ const WHITELIST_TYPES: &'static [&'stati
     "JS::HandleString",
     "JS::HandleValue",
     "JS::HandleValueArray",
     "JS::IsAcceptableThis",
     "JSAutoRealm",
     "JSAutoStructuredCloneBuffer",
     "JSClass",
     "JSClassOps",
+    "JSCompartment",
     "JSContext",
     "JSErrNum",
     "JSErrorCallback",
     "JSErrorFormatString",
     "JSErrorReport",
     "JSExnType",
     "JSFlatString",
     "JSFunction",
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -1050,17 +1050,17 @@ unsafe fn get_object_group(obj: *mut JSO
 
 #[inline]
 pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
     (*get_object_group(obj)).clasp as *const _
 }
 
 #[inline]
 pub unsafe fn get_object_compartment(obj: *mut JSObject) -> *mut JSCompartment {
-    (*get_object_group(obj)).compartment
+    (*get_object_group(obj)).realm as *mut JSCompartment
 }
 
 #[inline]
 pub fn is_dom_class(class: &JSClass) -> bool {
     class.flags & JSCLASS_IS_DOMJSCLASS != 0
 }
 
 #[inline]
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1994,17 +1994,17 @@ CreateObjectConstructor(JSContext* cx, J
 
     fun->setJitInfo(&jit::JitInfo_Object);
     return fun;
 }
 
 static JSObject*
 CreateObjectPrototype(JSContext* cx, JSProtoKey key)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     MOZ_ASSERT(cx->global()->isNative());
 
     /*
      * Create |Object.prototype| first, mirroring CreateBlankProto but for the
      * prototype of the created object.
      */
     RootedPlainObject objectProto(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr,
                                                                            SingletonObject));
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1502,56 +1502,65 @@ BytecodeEmitter::TDZCheckCache::noteTDZC
     return true;
 }
 
 // Class for emitting bytecode for blocks like try-catch-finally.
 //
 // Usage: (check for the return value is omitted for simplicity)
 //
 //   `try { try_block } catch (ex) { catch_block }`
-//     TryEmitter tryCatch(this, TryEmitter::TryCatch);
+//     TryEmitter tryCatch(this, TryEmitter::Kind::TryCatch,
+//                         TryEmitter::ControlKind::Syntactic);
 //     tryCatch.emitTry();
 //     emit(try_block);
 //     tryCatch.emitCatch();
 //     emit(ex and catch_block); // use JSOP_EXCEPTION to get exception
 //     tryCatch.emitEnd();
 //
 //   `try { try_block } finally { finally_block }`
-//     TryEmitter tryCatch(this, TryEmitter::TryFinally);
+//     TryEmitter tryCatch(this, TryEmitter::Kind::TryFinally,
+//                         TryEmitter::ControlKind::Syntactic);
 //     tryCatch.emitTry();
 //     emit(try_block);
 //     // finally_pos: The "{" character's position in the source code text.
 //     tryCatch.emitFinally(Some(finally_pos));
 //     emit(finally_block);
 //     tryCatch.emitEnd();
 //
 //   `try { try_block } catch (ex) {catch_block} finally { finally_block }`
-//     TryEmitter tryCatch(this, TryEmitter::TryCatchFinally);
+//     TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally,
+//                         TryEmitter::ControlKind::Syntactic);
 //     tryCatch.emitTry();
 //     emit(try_block);
 //     tryCatch.emitCatch();
 //     emit(ex and catch_block);
 //     tryCatch.emitFinally(Some(finally_pos));
 //     emit(finally_block);
 //     tryCatch.emitEnd();
 //
 class MOZ_STACK_CLASS TryEmitter
 {
   public:
-    enum Kind {
+    enum class Kind {
         TryCatch,
         TryCatchFinally,
         TryFinally
     };
 
-    // Whether the catch and finally blocks handle the frame's return value.
-    // If UseRetVal is specified, the bytecode marked with "*" are emitted
-    // to clear return value with `undefined` before the catch block and the
-    // finally block, and also to save/restore the return value before/after
-    // the finally block.
+    // Syntactic try-catch-finally and internally used non-syntactic
+    // try-catch-finally behave differently for 2 points.
+    //
+    // The first one is whether TryFinallyControl is used or not.
+    // See the comment for `controlInfo_`.
+    //
+    // The second one is whether the catch and finally blocks handle the frame's
+    // return value.  For syntactic try-catch-finally, the bytecode marked with
+    // "*" are emitted to clear return value with `undefined` before the catch
+    // block and the finally block, and also to save/restore the return value
+    // before/after the finally block.
     //
     //     JSOP_TRY
     //
     //     try_body...
     //
     //     JSOP_GOSUB finally
     //     JSOP_JUMPTARGET
     //     JSOP_GOTO end:
@@ -1576,57 +1585,45 @@ class MOZ_STACK_CLASS TryEmitter
     //     finally_body...
     //
     //   * JSOP_SETRVAL
     //     JSOP_NOP
     //
     //   end:
     //     JSOP_JUMPTARGET
     //
-    // For syntactic try-catch-finally, UseRetVal should be used.
-    // For non-syntactic try-catch-finally, DontUseRetVal should be used.
-    enum ShouldUseRetVal {
-        UseRetVal,
-        DontUseRetVal
-    };
-
-    // Whether this class should use TryFinallyControl.
-    // See the comment for `controlInfo_`.
-    //
-    // For syntactic try-catch-finally, UseControl should be used.
-    // For non-syntactic try-catch-finally, DontUseControl should be used.
-    enum ShouldUseControl {
-        UseControl,
-        DontUseControl,
+    // For syntactic try-catch-finally, Syntactic should be used.
+    // For non-syntactic try-catch-finally, NonSyntactic should be used.
+    enum class ControlKind {
+        Syntactic,
+        NonSyntactic
     };
 
   private:
     BytecodeEmitter* bce_;
     Kind kind_;
-    ShouldUseRetVal retValKind_;
+    ControlKind controlKind_;
 
     // Track jumps-over-catches and gosubs-to-finally for later fixup.
     //
     // When a finally block is active, non-local jumps (including
     // jumps-over-catches) result in a GOSUB being written into the bytecode
     // stream and fixed-up later.
     //
-    // If ShouldUseControl is DontUseControl, all that handling is skipped.
-    // DontUseControl is used by yield* and the internal try-catch around
-    // IteratorClose. These internal uses must:
+    // For non-syntactic try-catch-finally, all that handling is skipped.
+    // The non-syntactic try-catch-finally must:
     //   * have only one catch block
     //   * have JSOP_GOTO at the end of catch-block
     //   * have no non-local-jump
     //   * don't use finally block for normal completion of try-block and
     //     catch-block
     //
-    // Additionally, a finally block may be emitted when ShouldUseControl is
-    // DontUseControl, even if the kind is not TryCatchFinally or TryFinally,
-    // because GOSUBs are not emitted. This internal use shares the
-    // requirements as above.
+    // Additionally, a finally block may be emitted for non-syntactic
+    // try-catch-finally, even if the kind is TryCatch, because GOSUBs are not
+    // emitted.
     Maybe<TryFinallyControl> controlInfo_;
 
     // The stack depth before emitting JSOP_TRY.
     int depth_;
 
     // The source note index for SRC_TRY.
     unsigned noteIndex_;
 
@@ -1637,78 +1634,81 @@ class MOZ_STACK_CLASS TryEmitter
     JumpList catchAndFinallyJump_;
 
     // The offset of JSOP_GOTO at the end of the try block.
     JumpTarget tryEnd_;
 
     // The offset of JSOP_JUMPTARGET at the beginning of the finally block.
     JumpTarget finallyStart_;
 
+#ifdef DEBUG
     // The state of this emitter.
     //
     // +-------+ emitTry +-----+   emitCatch +-------+      emitEnd  +-----+
     // | Start |-------->| Try |-+---------->| Catch |-+->+--------->| End |
     // +-------+         +-----+ |           +-------+ |  ^          +-----+
     //                           |                     |  |
     //                           |  +------------------+  +----+
     //                           |  |                          |
     //                           |  v emitFinally +---------+  |
     //                           +->+------------>| Finally |--+
     //                                            +---------+
-    enum State {
+    enum class State {
         // The initial state.
         Start,
 
         // After calling emitTry.
         Try,
 
         // After calling emitCatch.
         Catch,
 
         // After calling emitFinally.
         Finally,
 
         // After calling emitEnd.
         End
     };
     State state_;
+#endif
 
     bool hasCatch() const {
-        return kind_ == TryCatch || kind_ == TryCatchFinally;
+        return kind_ == Kind::TryCatch || kind_ == Kind::TryCatchFinally;
     }
     bool hasFinally() const {
-        return kind_ == TryCatchFinally || kind_ == TryFinally;
+        return kind_ == Kind::TryCatchFinally || kind_ == Kind::TryFinally;
     }
 
   public:
-    TryEmitter(BytecodeEmitter* bce, Kind kind, ShouldUseRetVal retValKind = UseRetVal,
-               ShouldUseControl controlKind = UseControl)
+    TryEmitter(BytecodeEmitter* bce, Kind kind, ControlKind controlKind)
       : bce_(bce),
         kind_(kind),
-        retValKind_(retValKind),
+        controlKind_(controlKind),
         depth_(0),
         noteIndex_(0),
-        tryStart_(0),
-        state_(Start)
+        tryStart_(0)
+#ifdef DEBUG
+      , state_(State::Start)
+#endif
     {
-        if (controlKind == UseControl)
+        if (controlKind_ == ControlKind::Syntactic)
             controlInfo_.emplace(bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try);
         finallyStart_.offset = 0;
     }
 
     // Emits JSOP_GOTO to the end of try-catch-finally.
     // Used in `yield*`.
-    bool emitJumpOverCatchAndFinally() {
+    MOZ_MUST_USE bool emitJumpOverCatchAndFinally() {
         if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
             return false;
         return true;
     }
 
-    bool emitTry() {
-        MOZ_ASSERT(state_ == Start);
+    MOZ_MUST_USE bool emitTry() {
+        MOZ_ASSERT(state_ == State::Start);
 
         // Since an exception can be thrown at any place inside the try block,
         // we need to restore the stack and the scope chain before we transfer
         // the control to the exception handler.
         //
         // For that we store in a try note associated with the catch or
         // finally block the stack depth upon the try entry. The interpreter
         // uses this depth to properly unwind the stack and the scope chain.
@@ -1716,23 +1716,25 @@ class MOZ_STACK_CLASS TryEmitter
 
         // Record the try location, then emit the try block.
         if (!bce_->newSrcNote(SRC_TRY, &noteIndex_))
             return false;
         if (!bce_->emit1(JSOP_TRY))
             return false;
         tryStart_ = bce_->offset();
 
-        state_ = Try;
+#ifdef DEBUG
+        state_ = State::Try;
+#endif
         return true;
     }
 
   private:
-    bool emitTryEnd() {
-        MOZ_ASSERT(state_ == Try);
+    MOZ_MUST_USE bool emitTryEnd() {
+        MOZ_ASSERT(state_ == State::Try);
         MOZ_ASSERT(depth_ == bce_->stackDepth);
 
         // GOSUB to finally, if present.
         if (hasFinally() && controlInfo_) {
             if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
                 return false;
         }
 
@@ -1746,41 +1748,43 @@ class MOZ_STACK_CLASS TryEmitter
 
         if (!bce_->emitJumpTarget(&tryEnd_))
             return false;
 
         return true;
     }
 
   public:
-    bool emitCatch() {
-        MOZ_ASSERT(state_ == Try);
+    MOZ_MUST_USE bool emitCatch() {
+        MOZ_ASSERT(state_ == State::Try);
         if (!emitTryEnd())
             return false;
 
         MOZ_ASSERT(bce_->stackDepth == depth_);
 
-        if (retValKind_ == UseRetVal) {
+        if (controlKind_ == ControlKind::Syntactic) {
             // Clear the frame's return value that might have been set by the
             // try block:
             //
             //   eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
             if (!bce_->emit1(JSOP_UNDEFINED))
                 return false;
             if (!bce_->emit1(JSOP_SETRVAL))
                 return false;
         }
 
-        state_ = Catch;
+#ifdef DEBUG
+        state_ = State::Catch;
+#endif
         return true;
     }
 
   private:
-    bool emitCatchEnd() {
-        MOZ_ASSERT(state_ == Catch);
+    MOZ_MUST_USE bool emitCatchEnd() {
+        MOZ_ASSERT(state_ == State::Catch);
 
         if (!controlInfo_)
             return true;
 
         // gosub <finally>, if required.
         if (hasFinally()) {
             if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
                 return false;
@@ -1794,34 +1798,36 @@ class MOZ_STACK_CLASS TryEmitter
         return true;
     }
 
   public:
     // If `finallyPos` is specified, it's an offset of the finally block's
     // "{" character in the source code text, to improve line:column number in
     // the error reporting.
     // For non-syntactic try-catch-finally, `finallyPos` can be omitted.
-    bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) {
+    MOZ_MUST_USE bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) {
         // If we are using controlInfo_ (i.e., emitting a syntactic try
         // blocks), we must have specified up front if there will be a finally
-        // close. For internal try blocks, like those emitted for yield* and
-        // IteratorClose inside for-of loops, we can emitFinally even without
-        // specifying up front, since the internal try blocks emit no GOSUBs.
+        // close. For internal non-syntactic try blocks, like those emitted for
+        // yield* and IteratorClose inside for-of loops, we can emitFinally even
+        // without specifying up front, since the internal non-syntactic try
+        // blocks emit no GOSUBs.
         if (!controlInfo_) {
-            if (kind_ == TryCatch)
-                kind_ = TryCatchFinally;
+            if (kind_ == Kind::TryCatch)
+                kind_ = Kind::TryCatchFinally;
         } else {
             MOZ_ASSERT(hasFinally());
         }
 
-        if (state_ == Try) {
+        if (!hasCatch()) {
+            MOZ_ASSERT(state_ == State::Try);
             if (!emitTryEnd())
                 return false;
         } else {
-            MOZ_ASSERT(state_ == Catch);
+            MOZ_ASSERT(state_ == State::Catch);
             if (!emitCatchEnd())
                 return false;
         }
 
         MOZ_ASSERT(bce_->stackDepth == depth_);
 
         if (!bce_->emitJumpTarget(&finallyStart_))
             return false;
@@ -1836,59 +1842,60 @@ class MOZ_STACK_CLASS TryEmitter
         }
         if (finallyPos) {
             if (!bce_->updateSourceCoordNotes(finallyPos.value()))
                 return false;
         }
         if (!bce_->emit1(JSOP_FINALLY))
             return false;
 
-        if (retValKind_ == UseRetVal) {
+        if (controlKind_ == ControlKind::Syntactic) {
             if (!bce_->emit1(JSOP_GETRVAL))
                 return false;
 
             // Clear the frame's return value to make break/continue return
             // correct value even if there's no other statement before them:
             //
             //   eval("x: try { 1 } finally { break x; }"); // undefined, not 1
             if (!bce_->emit1(JSOP_UNDEFINED))
                 return false;
             if (!bce_->emit1(JSOP_SETRVAL))
                 return false;
         }
 
-        state_ = Finally;
+#ifdef DEBUG
+        state_ = State::Finally;
+#endif
         return true;
     }
 
   private:
-    bool emitFinallyEnd() {
-        MOZ_ASSERT(state_ == Finally);
-
-        if (retValKind_ == UseRetVal) {
+    MOZ_MUST_USE bool emitFinallyEnd() {
+        MOZ_ASSERT(state_ == State::Finally);
+
+        if (controlKind_ == ControlKind::Syntactic) {
             if (!bce_->emit1(JSOP_SETRVAL))
                 return false;
         }
 
         if (!bce_->emit1(JSOP_RETSUB))
             return false;
 
         bce_->hasTryFinally = true;
         return true;
     }
 
   public:
-    bool emitEnd() {
-        if (state_ == Catch) {
-            MOZ_ASSERT(!hasFinally());
+    MOZ_MUST_USE bool emitEnd() {
+        if (!hasFinally()) {
+            MOZ_ASSERT(state_ == State::Catch);
             if (!emitCatchEnd())
                 return false;
         } else {
-            MOZ_ASSERT(state_ == Finally);
-            MOZ_ASSERT(hasFinally());
+            MOZ_ASSERT(state_ == State::Finally);
             if (!emitFinallyEnd())
                 return false;
         }
 
         MOZ_ASSERT(bce_->stackDepth == depth_);
 
         // ReconstructPCStack needs a NOP here to mark the end of the last
         // catch block.
@@ -1909,209 +1916,290 @@ class MOZ_STACK_CLASS TryEmitter
         // If we've got a finally, mark try+catch region with additional
         // trynote to catch exceptions (re)thrown from a catch block or
         // for the try{}finally{} case.
         if (hasFinally()) {
             if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset))
                 return false;
         }
 
-        state_ = End;
+#ifdef DEBUG
+        state_ = State::End;
+#endif
         return true;
     }
 };
 
 // Class for emitting bytecode for blocks like if-then-else.
 //
-// This class can be used to emit single if-then-else block.  Cascading
-// elseif's need multiple instances of this class.
+// This class can be used to emit single if-then-else block, or cascading
+// else-if blocks.
 //
 // Usage: (check for the return value is omitted for simplicity)
 //
 //   `if (cond) then_block`
 //     IfThenElseEmitter ifThen(this);
 //     emit(cond);
-//     ifThen.emitIf();
+//     ifThen.emitThen();
 //     emit(then_block);
 //     ifThen.emitEnd();
 //
 //   `if (cond) then_block else else_block`
 //     IfThenElseEmitter ifThenElse(this);
 //     emit(cond);
-//     ifThenElse.emitIfElse();
+//     ifThenElse.emitThenElse();
 //     emit(then_block);
 //     ifThenElse.emitElse();
 //     emit(else_block);
 //     ifThenElse.emitEnd();
 //
+//   `if (c1) b1 else if (c2) b2 else if (c3) b3 else b4`
+//     IfThenElseEmitter ifThenElse(this);
+//     emit(c1);
+//     ifThenElse.emitThenElse();
+//     emit(b1);
+//     ifThenElse.emitElseIf();
+//     emit(c2);
+//     ifThenElse.emitThenElse();
+//     emit(b2);
+//     ifThenElse.emitElseIf();
+//     emit(c3);
+//     ifThenElse.emitThenElse();
+//     emit(b3);
+//     ifThenElse.emitElse();
+//     emit(b4);
+//     ifThenElse.emitEnd();
+//
 //   `cond ? then_expr : else_expr`
 //     IfThenElseEmitter condElse(this);
 //     emit(cond);
 //     condElse.emitCond();
 //     emit(then_block);
 //     condElse.emitElse();
 //     emit(else_block);
 //     condElse.emitEnd();
 //
 class MOZ_STACK_CLASS IfThenElseEmitter
 {
     BytecodeEmitter* bce_;
 
+    // Jump around the then clause, to the beginning of the else clause.
     JumpList jumpAroundThen_;
+
+    // Jump around the else clause, to the end of the entire branch.
     JumpList jumpsAroundElse_;
 
     // The stack depth before emitting the then block.
     // Used for restoring stack depth before emitting the else block.
     // Also used for assertion to make sure then and else blocks pushed the
     // same number of values.
     int32_t thenDepth_;
 
 #ifdef DEBUG
     // The number of values pushed in the then and else blocks.
     int32_t pushed_;
     bool calculatedPushed_;
-#endif
 
     // The state of this emitter.
     //
-    // +-------+   emitIf     +----+                           emitEnd +-----+
-    // | Start |-+----------->| If |-------------------------+-------->| End |
-    // +-------+ |            +----+                         |         +-----+
+    // +-------+   emitCond +------+ emitElse +------+        emitEnd +-----+
+    // | Start |-+--------->| Cond |--------->| Else |------>+------->| End |
+    // +-------+ |          +------+          +------+       ^        +-----+
     //           |                                           |
-    //           | emitCond   +------+     emitElse +------+ |
-    //           +----------->| Cond |---+--------->| Else |-+
-    //           |            +------+   |          +------+
-    //           |                       |
-    //           | emitIfElse +--------+ |
-    //           +----------->| IfElse |-+
-    //                        +--------+
-    enum State {
+    //           v emitThen +------+                         |
+    //        +->+--------->| Then |------------------------>+
+    //        ^  |          +------+                         ^
+    //        |  |                                           |
+    //        |  |                                           +---+
+    //        |  |                                               |
+    //        |  | emitThenElse +----------+   emitElse +------+ |
+    //        |  +------------->| ThenElse |-+--------->| Else |-+
+    //        |                 +----------+ |          +------+
+    //        |                              |
+    //        |                              | emitElseIf +--------+
+    //        |                              +----------->| ElseIf |-+
+    //        |                                           +--------+ |
+    //        |                                                      |
+    //        +------------------------------------------------------+
+    enum class State {
         // The initial state.
         Start,
 
-        // After calling emitIf.
-        If,
+        // After calling emitThen.
+        Then,
 
         // After calling emitCond.
         Cond,
 
-        // After calling emitIfElse.
-        IfElse,
-
-        // After calling Else.
+        // After calling emitThenElse.
+        ThenElse,
+
+        // After calling emitElse.
         Else,
 
+        // After calling emitElseIf.
+        ElseIf,
+
         // After calling emitEnd.
         End
     };
     State state_;
+#endif
 
   public:
     explicit IfThenElseEmitter(BytecodeEmitter* bce)
       : bce_(bce),
-        thenDepth_(0),
+        thenDepth_(0)
 #ifdef DEBUG
-        pushed_(0),
-        calculatedPushed_(false),
+      , pushed_(0)
+      , calculatedPushed_(false)
+      , state_(State::Start)
 #endif
-        state_(Start)
     {}
 
     ~IfThenElseEmitter()
     {}
 
   private:
-    bool emitIf(State nextState) {
-        MOZ_ASSERT(state_ == Start || state_ == Else);
-        MOZ_ASSERT(nextState == If || nextState == IfElse || nextState == Cond);
-
-        // Clear jumpAroundThen_ offset that points previous JSOP_IFEQ.
-        if (state_ == Else)
-            jumpAroundThen_ = JumpList();
-
+    MOZ_MUST_USE bool emitIfInternal(SrcNoteType type) {
         // Emit an annotated branch-if-false around the then part.
-        SrcNoteType type = nextState == If ? SRC_IF : nextState == IfElse ? SRC_IF_ELSE : SRC_COND;
         if (!bce_->newSrcNote(type))
             return false;
         if (!bce_->emitJump(JSOP_IFEQ, &jumpAroundThen_))
             return false;
 
         // To restore stack depth in else part, save depth of the then part.
 #ifdef DEBUG
         // If DEBUG, this is also necessary to calculate |pushed_|.
         thenDepth_ = bce_->stackDepth;
 #else
-        if (nextState == IfElse || nextState == Cond)
+        if (type == SRC_COND || type == SRC_IF_ELSE)
             thenDepth_ = bce_->stackDepth;
 #endif
-        state_ = nextState;
         return true;
     }
 
     void calculateOrCheckPushed() {
 #ifdef DEBUG
         if (!calculatedPushed_) {
             pushed_ = bce_->stackDepth - thenDepth_;
             calculatedPushed_ = true;
         } else {
             MOZ_ASSERT(pushed_ == bce_->stackDepth - thenDepth_);
         }
 #endif
     }
 
   public:
-    bool emitIf() {
-        return emitIf(If);
-    }
-
-    bool emitCond() {
-        return emitIf(Cond);
-    }
-
-    bool emitIfElse() {
-        return emitIf(IfElse);
-    }
-
-    bool emitElse() {
-        MOZ_ASSERT(state_ == IfElse || state_ == Cond);
-
+    MOZ_MUST_USE bool emitThen() {
+        MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
+        if (!emitIfInternal(SRC_IF))
+            return false;
+
+#ifdef DEBUG
+        state_ = State::Then;
+#endif
+        return true;
+    }
+
+    MOZ_MUST_USE bool emitCond() {
+        MOZ_ASSERT(state_ == State::Start);
+        if (!emitIfInternal(SRC_COND))
+            return false;
+
+#ifdef DEBUG
+        state_ = State::Cond;
+#endif
+        return true;
+    }
+
+    MOZ_MUST_USE bool emitThenElse() {
+        MOZ_ASSERT(state_ == State::Start || state_ == State::ElseIf);
+        if (!emitIfInternal(SRC_IF_ELSE))
+            return false;
+
+#ifdef DEBUG
+        state_ = State::ThenElse;
+#endif
+        return true;
+    }
+
+  private:
+    MOZ_MUST_USE bool emitElseInternal() {
         calculateOrCheckPushed();
 
         // Emit a jump from the end of our then part around the else part. The
         // patchJumpsToTarget call at the bottom of this function will fix up
         // the offset with jumpsAroundElse value.
         if (!bce_->emitJump(JSOP_GOTO, &jumpsAroundElse_))
             return false;
 
         // Ensure the branch-if-false comes here, then emit the else.
         if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
             return false;
 
+        // Clear jumpAroundThen_ offset, to tell emitEnd there was an else part.
+        jumpAroundThen_ = JumpList();
+
         // Restore stack depth of the then part.
         bce_->stackDepth = thenDepth_;
-        state_ = Else;
+#ifdef DEBUG
+        state_ = State::Else;
+#endif
+        return true;
+    }
+
+  public:
+    MOZ_MUST_USE bool emitElse() {
+        MOZ_ASSERT(state_ == State::ThenElse || state_ == State::Cond);
+
+        if (!emitElseInternal())
+            return false;
+
+#ifdef DEBUG
+        state_ = State::Else;
+#endif
         return true;
     }
 
-    bool emitEnd() {
-        MOZ_ASSERT(state_ == If || state_ == Else);
+    MOZ_MUST_USE bool emitElseIf() {
+        MOZ_ASSERT(state_ == State::ThenElse);
+
+        if (!emitElseInternal())
+            return false;
+
+#ifdef DEBUG
+        state_ = State::ElseIf;
+#endif
+        return true;
+    }
+
+    MOZ_MUST_USE bool emitEnd() {
+        MOZ_ASSERT(state_ == State::Then || state_ == State::Else);
+        // If there was an else part for the last branch, jumpAroundThen_ is
+        // already fixed up when emitting the else part.
+        MOZ_ASSERT_IF(state_ == State::Then, jumpAroundThen_.offset != -1);
+        MOZ_ASSERT_IF(state_ == State::Else, jumpAroundThen_.offset == -1);
 
         calculateOrCheckPushed();
 
-        if (state_ == If) {
-            // No else part, fixup the branch-if-false to come here.
+        if (jumpAroundThen_.offset != -1) {
+            // No else part for the last branch, fixup the branch-if-false to
+            // come here.
             if (!bce_->emitJumpTargetAndPatch(jumpAroundThen_))
                 return false;
         }
 
         // Patch all the jumps around else parts.
         if (!bce_->emitJumpTargetAndPatch(jumpsAroundElse_))
             return false;
 
-        state_ = End;
+#ifdef DEBUG
+        state_ = State::End;
+#endif
         return true;
     }
 
 #ifdef DEBUG
     // Returns the number of values pushed onto the value stack inside
     // `then_block` and `else_block`.
     // Can be used in assertion after emitting if-then-else.
     int32_t pushed() const {
@@ -2181,18 +2269,17 @@ class ForOfLoopControl : public LoopCont
         iterDepth_(iterDepth),
         numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
         allowSelfHosted_(allowSelfHosted),
         iterKind_(iterKind)
     {
     }
 
     bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
-        tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
-                          TryEmitter::DontUseControl);
+        tryCatch_.emplace(bce, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic);
 
         if (!tryCatch_->emitTry())
             return false;
 
         MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
         numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields;
 
         return true;
@@ -2212,17 +2299,17 @@ class ForOfLoopControl : public LoopCont
         // IteratorClose for non-local jump, and we should't perform
         // IteratorClose again here.
         if (!bce->emit1(JSOP_UNDEFINED))          // ITER ... EXCEPTION ITER UNDEF
             return false;
         if (!bce->emit1(JSOP_STRICTNE))           // ITER ... EXCEPTION NE
             return false;
 
         IfThenElseEmitter ifIteratorIsNotClosed(bce);
-        if (!ifIteratorIsNotClosed.emitIf())      // ITER ... EXCEPTION
+        if (!ifIteratorIsNotClosed.emitThen())    // ITER ... EXCEPTION
             return false;
 
         MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
         if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
             return false;
         if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Throw))
             return false;                         // ITER ... EXCEPTION
 
@@ -2238,17 +2325,17 @@ class ForOfLoopControl : public LoopCont
         uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields;
         if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
             if (!tryCatch_->emitFinally())
                 return false;
 
             IfThenElseEmitter ifGeneratorClosing(bce);
             if (!bce->emit1(JSOP_ISGENCLOSING))   // ITER ... FTYPE FVALUE CLOSING
                 return false;
-            if (!ifGeneratorClosing.emitIf())     // ITER ... FTYPE FVALUE
+            if (!ifGeneratorClosing.emitThen())   // ITER ... FTYPE FVALUE
                 return false;
             if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
                 return false;
             if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Normal))
                 return false;                     // ITER ... FTYPE FVALUE
             if (!ifGeneratorClosing.emitEnd())    // ITER ... FTYPE FVALUE
                 return false;
         }
@@ -5418,17 +5505,17 @@ BytecodeEmitter::emitIteratorCloseInScop
 
     // Step 4.
     //
     // Do nothing if "return" is undefined or null.
     IfThenElseEmitter ifReturnMethodIsDefined(this);
     if (!emitPushNotUndefinedOrNull())                    // ... ITER RET NOT-UNDEF-OR-NULL
         return false;
 
-    if (!ifReturnMethodIsDefined.emitIfElse())            // ... ITER RET
+    if (!ifReturnMethodIsDefined.emitThenElse())          // ... ITER RET
         return false;
 
     if (completionKind == CompletionKind::Throw) {
         // 7.4.6 IteratorClose ( iterator, completion )
         //   ...
         //   3. Let return be ? GetMethod(iterator, "return").
         //   4. If return is undefined, return Completion(completion).
         //   5. Let innerResult be Call(return, iterator, « »).
@@ -5454,18 +5541,17 @@ BytecodeEmitter::emitIteratorCloseInScop
     // Call "return" if it is not undefined or null, and check that it returns
     // an Object.
     if (!emit1(JSOP_SWAP))                                // ... RET ITER
         return false;
 
     Maybe<TryEmitter> tryCatch;
 
     if (completionKind == CompletionKind::Throw) {
-        tryCatch.emplace(this, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
-                         TryEmitter::DontUseControl);
+        tryCatch.emplace(this, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic);
 
         // Mutate stack to balance stack for try-catch.
         if (!emit1(JSOP_UNDEFINED))                       // ... RET ITER UNDEF
             return false;
         if (!tryCatch->emitTry())                         // ... RET ITER UNDEF
             return false;
         if (!emitDupAt(2))                                // ... RET ITER UNDEF RET
             return false;
@@ -5799,17 +5885,17 @@ BytecodeEmitter::emitDestructuringOpsArr
         }
 
         if (member->isKind(ParseNodeKind::Spread)) {
             IfThenElseEmitter ifThenElse(this);
             if (!isFirst) {
                 // If spread is not the first element of the pattern,
                 // iterator can already be completed.
                                                                   // ... OBJ NEXT ITER *LREF DONE
-                if (!ifThenElse.emitIfElse())                     // ... OBJ NEXT ITER *LREF
+                if (!ifThenElse.emitThenElse())                   // ... OBJ NEXT ITER *LREF
                     return false;
 
                 if (!emitUint32Operand(JSOP_NEWARRAY, 0))         // ... OBJ NEXT ITER *LREF ARRAY
                     return false;
                 if (!ifThenElse.emitElse())                       // ... OBJ NEXT ITER *LREF
                     return false;
             }
 
@@ -5854,17 +5940,17 @@ BytecodeEmitter::emitDestructuringOpsArr
         if (member->isKind(ParseNodeKind::Assign))
             pndefault = member->pn_right;
 
         MOZ_ASSERT(!member->isKind(ParseNodeKind::Spread));
 
         IfThenElseEmitter ifAlreadyDone(this);
         if (!isFirst) {
                                                                   // ... OBJ NEXT ITER *LREF DONE
-            if (!ifAlreadyDone.emitIfElse())                      // ... OBJ NEXT ITER *LREF
+            if (!ifAlreadyDone.emitThenElse())                    // ... OBJ NEXT ITER *LREF
                 return false;
 
             if (!emit1(JSOP_UNDEFINED))                           // ... OBJ NEXT ITER *LREF UNDEF
                 return false;
             if (!emit1(JSOP_NOP_DESTRUCTURING))                   // ... OBJ NEXT ITER *LREF UNDEF
                 return false;
 
             // The iterator is done. Unpick a TRUE value for DONE above ITER.
@@ -5889,17 +5975,17 @@ BytecodeEmitter::emitDestructuringOpsArr
             return false;
 
         if (!emit1(JSOP_DUP))                                     // ... OBJ NEXT ITER *LREF RESULT DONE DONE
             return false;
         if (!emit2(JSOP_UNPICK, emitted + 2))                     // ... OBJ NEXT ITER DONE *LREF RESULT DONE
             return false;
 
         IfThenElseEmitter ifDone(this);
-        if (!ifDone.emitIfElse())                                 // ... OBJ NEXT ITER DONE *LREF RESULT
+        if (!ifDone.emitThenElse())                               // ... OBJ NEXT ITER DONE *LREF RESULT
             return false;
 
         if (!emit1(JSOP_POP))                                     // ... OBJ NEXT ITER DONE *LREF
             return false;
         if (!emit1(JSOP_UNDEFINED))                               // ... OBJ NEXT ITER DONE *LREF UNDEF
             return false;
         if (!emit1(JSOP_NOP_DESTRUCTURING))                       // ... OBJ NEXT ITER DONE *LREF UNDEF
             return false;
@@ -5941,17 +6027,17 @@ BytecodeEmitter::emitDestructuringOpsArr
                 return false;
         }
     }
 
     // The last DONE value is on top of the stack. If not DONE, call
     // IteratorClose.
                                                                   // ... OBJ NEXT ITER DONE
     IfThenElseEmitter ifDone(this);
-    if (!ifDone.emitIfElse())                                     // ... OBJ NEXT ITER
+    if (!ifDone.emitThenElse())                                   // ... OBJ NEXT ITER
         return false;
     if (!emitPopN(2))                                             // ... OBJ
         return false;
     if (!ifDone.emitElse())                                       // ... OBJ NEXT ITER
         return false;
     if (!emit1(JSOP_SWAP))                                        // ... OBJ ITER NEXT
         return false;
     if (!emit1(JSOP_POP))                                         // ... OBJ ITER
@@ -6783,24 +6869,24 @@ MOZ_NEVER_INLINE bool
 BytecodeEmitter::emitTry(ParseNode* pn)
 {
     ParseNode* catchScope = pn->pn_kid2;
     ParseNode* finallyNode = pn->pn_kid3;
 
     TryEmitter::Kind kind;
     if (catchScope) {
         if (finallyNode)
-            kind = TryEmitter::TryCatchFinally;
+            kind = TryEmitter::Kind::TryCatchFinally;
         else
-            kind = TryEmitter::TryCatch;
+            kind = TryEmitter::Kind::TryCatch;
     } else {
         MOZ_ASSERT(finallyNode);
-        kind = TryEmitter::TryFinally;
-    }
-    TryEmitter tryCatch(this, kind);
+        kind = TryEmitter::Kind::TryFinally;
+    }
+    TryEmitter tryCatch(this, kind, TryEmitter::ControlKind::Syntactic);
 
     if (!tryCatch.emitTry())
         return false;
 
     if (!emitTree(pn->pn_kid1))
         return false;
 
     // If this try has a catch block, emit it.
@@ -6847,36 +6933,40 @@ BytecodeEmitter::emitIf(ParseNode* pn)
 
   if_again:
     /* Emit code for the condition before pushing stmtInfo. */
     if (!emitTreeInBranch(pn->pn_kid1))
         return false;
 
     ParseNode* elseNode = pn->pn_kid3;
     if (elseNode) {
-        if (!ifThenElse.emitIfElse())
+        if (!ifThenElse.emitThenElse())
             return false;
     } else {
-        if (!ifThenElse.emitIf())
+        if (!ifThenElse.emitThen())
             return false;
     }
 
     /* Emit code for the then part. */
     if (!emitTreeInBranch(pn->pn_kid2))
         return false;
 
     if (elseNode) {
-        if (!ifThenElse.emitElse())
-            return false;
-
         if (elseNode->isKind(ParseNodeKind::If)) {
             pn = elseNode;
+
+            if (!ifThenElse.emitElseIf())
+                return false;
+
             goto if_again;
         }
 
+        if (!ifThenElse.emitElse())
+            return false;
+
         /* Emit code for the else part. */
         if (!emitTreeInBranch(elseNode))
             return false;
     }
 
     if (!ifThenElse.emitEnd())
         return false;
 
@@ -7069,17 +7159,17 @@ BytecodeEmitter::emitAsyncIterator()
     if (!emitElemOpBase(JSOP_CALLELEM))                           // OBJ ITERFN
         return false;
 
     IfThenElseEmitter ifAsyncIterIsUndefined(this);
     if (!emitPushNotUndefinedOrNull())                            // OBJ ITERFN !UNDEF-OR-NULL
         return false;
     if (!emit1(JSOP_NOT))                                         // OBJ ITERFN UNDEF-OR-NULL
         return false;
-    if (!ifAsyncIterIsUndefined.emitIfElse())                     // OBJ ITERFN
+    if (!ifAsyncIterIsUndefined.emitThenElse())                   // OBJ ITERFN
         return false;
 
     if (!emit1(JSOP_POP))                                         // OBJ
         return false;
     if (!emit1(JSOP_DUP))                                         // OBJ OBJ
         return false;
     if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator)))   // OBJ OBJ @@ITERATOR
         return false;
@@ -7384,17 +7474,17 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 
         if (!emit1(JSOP_DUP))                             // NEXT ITER RESULT RESULT
             return false;
         if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // NEXT ITER RESULT DONE
             return false;
 
         IfThenElseEmitter ifDone(this);
 
-        if (!ifDone.emitIf())                             // NEXT ITER RESULT
+        if (!ifDone.emitThen())                           // NEXT ITER RESULT
             return false;
 
         // Remove RESULT from the stack to release it.
         if (!emit1(JSOP_POP))                             // NEXT ITER
             return false;
         if (!emit1(JSOP_UNDEFINED))                       // NEXT ITER UNDEF
             return false;
 
@@ -8532,18 +8622,18 @@ BytecodeEmitter::emitYieldStar(ParseNode
     // Initial send value is undefined.
     if (!emit1(JSOP_UNDEFINED))                           // NEXT ITER RECEIVED
         return false;
 
     int32_t savedDepthTemp;
     int32_t startDepth = stackDepth;
     MOZ_ASSERT(startDepth >= 3);
 
-    TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal,
-                        TryEmitter::DontUseControl);
+    TryEmitter tryCatch(this, TryEmitter::Kind::TryCatchFinally,
+                        TryEmitter::ControlKind::NonSyntactic);
     if (!tryCatch.emitJumpOverCatchAndFinally())          // NEXT ITER RESULT
         return false;
 
     JumpTarget tryStart{ offset() };
     if (!tryCatch.emitTry())                              // NEXT ITER RESULT
         return false;
 
     MOZ_ASSERT(this->stackDepth == startDepth);
@@ -8577,17 +8667,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
     if (!emit1(JSOP_DUP))                                 // NEXT ITER RESULT EXCEPTION ITER THROW THROW
         return false;
     if (!emit1(JSOP_UNDEFINED))                           // NEXT ITER RESULT EXCEPTION ITER THROW THROW UNDEFINED
         return false;
     if (!emit1(JSOP_EQ))                                  // NEXT ITER RESULT EXCEPTION ITER THROW ?EQL
         return false;
 
     IfThenElseEmitter ifThrowMethodIsNotDefined(this);
-    if (!ifThrowMethodIsNotDefined.emitIf())              // NEXT ITER RESULT EXCEPTION ITER THROW
+    if (!ifThrowMethodIsNotDefined.emitThen())            // NEXT ITER RESULT EXCEPTION ITER THROW
         return false;
     savedDepthTemp = stackDepth;
     if (!emit1(JSOP_POP))                                 // NEXT ITER RESULT EXCEPTION ITER
         return false;
     // ES 14.4.13, YieldExpression : yield * AssignmentExpression, step 5.b.iii.2
     //
     // If the iterator does not have a "throw" method, it calls IteratorClose
     // and then throws a TypeError.
@@ -8635,17 +8725,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
     // ES 14.4.13, yield * AssignmentExpression, step 5.c
     //
     // Call iterator.return() for receiving a "forced return" completion from
     // the generator.
 
     IfThenElseEmitter ifGeneratorClosing(this);
     if (!emit1(JSOP_ISGENCLOSING))                        // NEXT ITER RESULT FTYPE FVALUE CLOSING
         return false;
-    if (!ifGeneratorClosing.emitIf())                     // NEXT ITER RESULT FTYPE FVALUE
+    if (!ifGeneratorClosing.emitThen())                   // NEXT ITER RESULT FTYPE FVALUE
         return false;
 
     // Step ii.
     //
     // Get the "return" method.
     if (!emitDupAt(3))                                    // NEXT ITER RESULT FTYPE FVALUE ITER
         return false;
     if (!emit1(JSOP_DUP))                                 // NEXT ITER RESULT FTYPE FVALUE ITER ITER
@@ -8659,17 +8749,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
     IfThenElseEmitter ifReturnMethodIsDefined(this);
     if (!emitPushNotUndefinedOrNull())                    // NEXT ITER RESULT FTYPE FVALUE ITER RET NOT-UNDEF-OR-NULL
         return false;
 
     // Step iv.
     //
     // Call "return" with the argument passed to Generator.prototype.return,
     // which is currently in rval.value.
-    if (!ifReturnMethodIsDefined.emitIfElse())            // NEXT ITER OLDRESULT FTYPE FVALUE ITER RET
+    if (!ifReturnMethodIsDefined.emitThenElse())          // NEXT ITER OLDRESULT FTYPE FVALUE ITER RET
         return false;
     if (!emit1(JSOP_SWAP))                                // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER
         return false;
     if (!emit1(JSOP_GETRVAL))                             // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER RVAL
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // NEXT ITER OLDRESULT FTYPE FVALUE RET ITER VALUE
         return false;
     if (!emitCall(JSOP_CALL, 1))                          // NEXT ITER OLDRESULT FTYPE FVALUE RESULT
@@ -8689,17 +8779,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
     //
     // Check if the returned object from iterator.return() is done. If not,
     // continuing yielding.
     IfThenElseEmitter ifReturnDone(this);
     if (!emit1(JSOP_DUP))                                 // NEXT ITER OLDRESULT FTYPE FVALUE RESULT RESULT
         return false;
     if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // NEXT ITER OLDRESULT FTYPE FVALUE RESULT DONE
         return false;
-    if (!ifReturnDone.emitIfElse())                       // NEXT ITER OLDRESULT FTYPE FVALUE RESULT
+    if (!ifReturnDone.emitThenElse())                     // NEXT ITER OLDRESULT FTYPE FVALUE RESULT
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // NEXT ITER OLDRESULT FTYPE FVALUE VALUE
         return false;
 
     if (!emitPrepareIteratorResult())                     // NEXT ITER OLDRESULT FTYPE FVALUE VALUE RESULT
         return false;
     if (!emit1(JSOP_SWAP))                                // NEXT ITER OLDRESULT FTYPE FVALUE RESULT VALUE
         return false;
@@ -9448,17 +9538,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
                 return false;
 
             if (!emit1(JSOP_OPTIMIZE_SPREADCALL))
                 return false;
 
             if (!emit1(JSOP_NOT))
                 return false;
 
-            if (!ifNotOptimizable.emitIf())
+            if (!ifNotOptimizable.emitThen())
                 return false;
 
             if (!emit1(JSOP_POP))
                 return false;
         }
 
         if (!emitArray(args, argc))
             return false;
@@ -10576,17 +10666,17 @@ BytecodeEmitter::emitClass(ParseNode* pn
         if (!emit1(JSOP_DUP))                                   // ... HERITAGE HERITAGE
             return false;
         if (!emit1(JSOP_NULL))                                  // ... HERITAGE HERITAGE NULL
             return false;
         if (!emit1(JSOP_STRICTNE))                              // ... HERITAGE NE
             return false;
 
         // [THEN] funProto = heritage, objProto = heritage.prototype
-        if (!ifThenElse.emitIfElse())
+        if (!ifThenElse.emitThenElse())
             return false;
         if (!emit1(JSOP_DUP))                                   // ... HERITAGE HERITAGE
             return false;
         if (!emitAtomOp(cx->names().prototype, JSOP_GETPROP))   // ... HERITAGE PROTO
             return false;
 
         // [ELSE] funProto = %FunctionPrototype%, objProto = null
         if (!ifThenElse.emitElse())
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -81,17 +81,17 @@ template JSObject* js::Allocate<JSObject
 template <AllowGC allowGC>
 JSObject*
 GCRuntime::tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots, const Class* clasp)
 {
     MOZ_RELEASE_ASSERT(!cx->helperThread());
 
     MOZ_ASSERT(cx->isNurseryAllocAllowed());
     MOZ_ASSERT(!cx->isNurseryAllocSuppressed());
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     JSObject* obj = cx->nursery().allocateObject(cx, thingSize, nDynamicSlots, clasp);
     if (obj)
         return obj;
 
     if (allowGC && !cx->suppressGC) {
         cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY);
 
@@ -135,17 +135,17 @@ GCRuntime::tryNewTenuredObject(JSContext
 template <AllowGC allowGC>
 JSString*
 GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize, AllocKind kind)
 {
     MOZ_ASSERT(IsNurseryAllocable(kind));
     MOZ_ASSERT(cx->isNurseryAllocAllowed());
     MOZ_ASSERT(!cx->helperThread());
     MOZ_ASSERT(!cx->isNurseryAllocSuppressed());
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     Cell* cell = cx->nursery().allocateString(cx->zone(), thingSize, kind);
     if (cell)
         return static_cast<JSString*>(cell);
 
     if (allowGC && !cx->suppressGC) {
         cx->runtime()->gc.minorGC(JS::gcreason::OUT_OF_NURSERY);
 
@@ -270,23 +270,23 @@ bool
 GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
 {
     if (allowGC) {
         if (!gcIfNeededAtAllocation(cx))
             return false;
     }
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
-    MOZ_ASSERT_IF(cx->realm()->isAtomsRealm(),
+    MOZ_ASSERT_IF(cx->zone()->isAtomsZone(),
                   kind == AllocKind::ATOM ||
                   kind == AllocKind::FAT_INLINE_ATOM ||
                   kind == AllocKind::SYMBOL ||
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
-    MOZ_ASSERT_IF(!cx->realm()->isAtomsRealm(),
+    MOZ_ASSERT_IF(!cx->zone()->isAtomsZone(),
                   kind != AllocKind::ATOM &&
                   kind != AllocKind::FAT_INLINE_ATOM);
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
     MOZ_ASSERT(cx->isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !cx->suppressGC)
--- a/js/src/gc/AtomMarking.cpp
+++ b/js/src/gc/AtomMarking.cpp
@@ -78,17 +78,17 @@ bool
 AtomMarkingRuntime::computeBitmapFromChunkMarkBits(JSRuntime* runtime, DenseBitmap& bitmap)
 {
     MOZ_ASSERT(CurrentThreadIsPerformingGC());
     MOZ_ASSERT(!runtime->hasHelperThreadZones());
 
     if (!bitmap.ensureSpace(allocatedWords))
         return false;
 
-    Zone* atomsZone = runtime->unsafeAtomsRealm()->zone();
+    Zone* atomsZone = runtime->unsafeAtomsZone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
             bitmap.copyBitsFrom(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 
@@ -112,17 +112,17 @@ template <typename Bitmap>
 static void
 AddBitmapToChunkMarkBits(JSRuntime* runtime, Bitmap& bitmap)
 {
     // Make sure that by copying the mark bits for one arena in word sizes we
     // do not affect the mark bits for other arenas.
     static_assert(ArenaBitmapBits == ArenaBitmapWords * JS_BITS_PER_WORD,
                   "ArenaBitmapWords must evenly divide ArenaBitmapBits");
 
-    Zone* atomsZone = runtime->unsafeAtomsRealm()->zone();
+    Zone* atomsZone = runtime->unsafeAtomsZone();
     for (auto thingKind : AllAllocKinds()) {
         for (ArenaIter aiter(atomsZone, thingKind); !aiter.done(); aiter.next()) {
             Arena* arena = aiter.get();
             uintptr_t* chunkWords = arena->chunk()->bitmap.arenaBits(arena);
             bitmap.bitwiseOrRangeInto(arena->atomBitmapStart(), ArenaBitmapWords, chunkWords);
         }
     }
 }
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -3847,17 +3847,16 @@ Zone::sweepCompartments(FreeOp* fop, boo
 
     JSCompartment** read = compartments().begin();
     JSCompartment** end = compartments().end();
     JSCompartment** write = read;
     bool foundOne = false;
     while (read < end) {
         JSCompartment* comp = *read++;
         Realm* realm = JS::GetRealmForCompartment(comp);
-        MOZ_ASSERT(!realm->isAtomsRealm());
 
         /*
          * Don't delete the last compartment and realm if all the ones before
          * it were deleted and keepAtleastOne is true.
          */
         bool dontDelete = read == end && !foundOne && keepAtleastOne;
         if ((!realm->marked() && !dontDelete) || destroyingRuntime) {
             realm->destroy(fop);
@@ -4121,17 +4120,17 @@ struct MaybeCompartmentFunctor {
     template <typename T> JSCompartment* operator()(T* t) { return t->maybeCompartment(); }
 };
 
 void
 CompartmentCheckTracer::onChild(const JS::GCCellPtr& thing)
 {
     JSCompartment* comp = DispatchTyped(MaybeCompartmentFunctor(), thing);
     if (comp && compartment) {
-        MOZ_ASSERT(comp == compartment || runtime()->isAtomsCompartment(comp) ||
+        MOZ_ASSERT(comp == compartment ||
                    (srcKind == JS::TraceKind::Object &&
                     InCrossCompartmentMap(static_cast<JSObject*>(src), thing)));
     } else {
         TenuredCell* tenured = TenuredCell::fromPointer(thing.asCell());
         Zone* thingZone = tenured->zoneFromAnyThread();
         MOZ_ASSERT(thingZone == zone || thingZone->isAtomsZone());
     }
 }
@@ -4218,17 +4217,17 @@ ShouldCollectZone(Zone* zone, JS::gcreas
 bool
 GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOut,
                                      AutoLockForExclusiveAccess& lock)
 {
 #ifdef DEBUG
     /* Assert that zone state is as we expect */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         MOZ_ASSERT(!zone->isCollecting());
-        MOZ_ASSERT(!zone->compartments().empty());
+        MOZ_ASSERT_IF(!zone->isAtomsZone(), !zone->compartments().empty());
         for (auto i : AllAllocKinds())
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
     }
 #endif
 
     *isFullOut = true;
     bool any = false;
 
@@ -4246,17 +4245,17 @@ GCRuntime::prepareZonesForCollection(JS:
 
         zone->setPreservingCode(false);
     }
 
     // Discard JIT code more aggressively if the process is approaching its
     // executable code limit.
     bool canAllocateMoreCode = jit::CanLikelyAllocateMoreExecutableMemory();
 
-    for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         c->scheduledForDestruction = false;
         c->maybeAlive = false;
         for (RealmsInCompartmentIter r(c); !r.done(); r.next()) {
             r->unmark();
             if (r->shouldTraceGlobal() || !r->zone()->isGCScheduled())
                 c->maybeAlive = true;
             if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
                 r->zone()->setPreservingCode(true);
@@ -4473,17 +4472,17 @@ GCRuntime::markCompartments()
      * for details on this problem. To avoid the problem, we try to avoid
      * allocation and read barriers during JS_TransplantObject and the like.
      */
 
     /* Propagate the maybeAlive flag via cross-compartment edges. */
 
     Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList;
 
-    for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
+    for (CompartmentsIter comp(rt); !comp.done(); comp.next()) {
         if (comp->maybeAlive) {
             if (!workList.append(comp))
                 return;
         }
     }
 
     while (!workList.empty()) {
         JSCompartment* comp = workList.popCopy();
@@ -4890,17 +4889,17 @@ GCRuntime::finishMarkingValidation()
 static void
 DropStringWrappers(JSRuntime* rt)
 {
     /*
      * String "wrappers" are dropped on GC because their presence would require
      * us to sweep the wrappers in all compartments every time we sweep a
      * compartment group.
      */
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         for (JSCompartment::StringWrapperEnum e(c); !e.empty(); e.popFront()) {
             MOZ_ASSERT(e.front().key().is<JSString*>());
             e.removeFront();
         }
     }
 }
 
 /*
@@ -5132,17 +5131,17 @@ AssertNotOnGrayList(JSObject* obj)
                   GetProxyReservedSlot(obj, ProxyObject::grayLinkReservedSlot(obj)).isUndefined());
 }
 #endif
 
 static void
 AssertNoWrappersInGrayList(JSRuntime* rt)
 {
 #ifdef DEBUG
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::NonStringWrapperEnum e(c); !e.empty(); e.popFront())
             AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
     }
 #endif
 }
 
 static JSObject*
@@ -5407,17 +5406,17 @@ UpdateAtomsBitmap(GCParallelTask* task)
         // conservative to just not call it.
     }
 
     runtime->gc.atomMarking.updateChunkMarkBits(runtime);
 
     // For convenience sweep these tables non-incrementally as part of bitmap
     // sweeping; they are likely to be much smaller than the main atoms table.
     runtime->unsafeSymbolRegistry().sweep();
-    for (RealmsIter realm(runtime, SkipAtoms); !realm.done(); realm.next())
+    for (RealmsIter realm(runtime); !realm.done(); realm.next())
         realm->sweepVarNames();
 }
 
 static void
 SweepCCWrappers(GCParallelTask* task)
 {
     JSRuntime* runtime = task->runtime();
     for (SweepGroupCompartmentsIter c(runtime); !c.done(); c.next())
@@ -6883,17 +6882,17 @@ GCRuntime::resetIncrementalGC(gc::AbortR
         MOZ_ASSERT(!marker.shouldCheckCompartments());
 
         break;
       }
 
       case State::Sweep: {
         marker.reset();
 
-        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+        for (CompartmentsIter c(rt); !c.done(); c.next())
             c->scheduledForDestruction = false;
 
         /* Finish sweeping the current sweep group, then abort. */
         abortSweepAfterCurrentGroup = true;
 
         /* Don't perform any compaction after sweeping. */
         bool wasCompacting = isCompacting;
         isCompacting = false;
@@ -7547,17 +7546,17 @@ GCRuntime::scanZonesBeforeGC()
 void
 GCRuntime::maybeDoCycleCollection()
 {
     const static double ExcessiveGrayRealms = 0.8;
     const static size_t LimitGrayRealms = 200;
 
     size_t realmsTotal = 0;
     size_t realmsGray = 0;
-    for (RealmsIter realm(rt, SkipAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(rt); !realm.done(); realm.next()) {
         ++realmsTotal;
         GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal();
         if (global && global->isMarkedGray())
             ++realmsGray;
     }
     double grayFraction = double(realmsGray) / double(realmsTotal);
     if (grayFraction > ExcessiveGrayRealms || realmsGray > LimitGrayRealms)
         callDoCycleCollectionCallback(rt->mainContextFromOwnThread());
@@ -7597,17 +7596,17 @@ bool
 GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental);
     MOZ_ASSERT(!isIncrementalGCInProgress());
 
     if (!isIncremental)
         return false;
 
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (c->scheduledForDestruction)
             return true;
     }
 
     return false;
 }
 
 void
@@ -7956,17 +7955,17 @@ js::NewRealm(JSContext* cx, JSPrincipals
         }
     }
 
     ScopedJSDeletePtr<Realm> realm(cx->new_<Realm>(zone, options));
     if (!realm || !realm->init(cx))
         return nullptr;
 
     // Set up the principals.
-    JS_SetCompartmentPrincipals(JS::GetCompartmentForRealm(realm), principals);
+    JS::SetRealmPrincipals(realm, principals);
 
     JSCompartment* comp = realm->compartment();
     if (!comp->realms().append(realm)) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     AutoLockGC lock(rt);
@@ -8483,17 +8482,17 @@ js::gc::CheckHashTablesAfterMovingGC(JSR
 
         JS::AutoCheckCannotGC nogc;
         for (auto baseShape = zone->cellIter<BaseShape>(); !baseShape.done(); baseShape.next()) {
             if (ShapeTable* table = baseShape->maybeTable(nogc))
                 table->checkAfterMovingGC();
         }
     }
 
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         c->checkWrapperMapAfterMovingGC();
 
         for (RealmsInCompartmentIter r(c); !r.done(); r.next()) {
             r->checkObjectGroupTablesAfterMovingGC();
             r->dtoaCache.checkCacheAfterMovingGC();
             r->checkScriptMapsAfterMovingGC();
             if (r->debugEnvs())
                 r->debugEnvs()->checkHashTablesAfterMovingGC();
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -1024,17 +1024,17 @@ js::Nursery::sweep(JSTracer* trc)
             obj->zone()->removeUniqueId(obj);
         } else {
             JSObject* dst = Forwarded(obj);
             dst->zone()->transferUniqueId(dst, obj);
         }
     }
     cellsWithUid_.clear();
 
-    for (CompartmentsIter c(runtime(), SkipAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(runtime()); !c.done(); c.next())
         c->sweepAfterMinorGC(trc);
 
     sweepDictionaryModeObjects();
     sweepMapAndSetObjects();
 }
 
 void
 js::Nursery::clear()
--- a/js/src/gc/PrivateIterators-inl.h
+++ b/js/src/gc/PrivateIterators-inl.h
@@ -62,17 +62,17 @@ class GCZonesIter
     ZonesIter zone;
 
   public:
     explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
         MOZ_ASSERT(JS::CurrentThreadIsHeapBusy());
         MOZ_ASSERT_IF(rt->gc.atomsZone->isCollectingFromAnyThread(),
                       !rt->hasHelperThreadZones());
 
-        if (!zone->isCollectingFromAnyThread())
+        if (!done() && !zone->isCollectingFromAnyThread())
             next();
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
         MOZ_ASSERT(!done());
         do {
@@ -90,28 +90,40 @@ class GCZonesIter
 };
 
 using GCCompartmentsIter = CompartmentsOrRealmsIterT<GCZonesIter, CompartmentsInZoneIter>;
 using GCRealmsIter = CompartmentsOrRealmsIterT<GCZonesIter, RealmsInZoneIter>;
 
 /* Iterates over all zones in the current sweep group. */
 class SweepGroupZonesIter {
     JS::Zone* current;
+    ZoneSelector selector;
 
   public:
-    explicit SweepGroupZonesIter(JSRuntime* rt) {
+    explicit SweepGroupZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
+      : selector(selector)
+    {
         MOZ_ASSERT(CurrentThreadIsPerformingGC());
         current = rt->gc.getCurrentSweepGroup();
+        maybeSkipAtomsZone();
+    }
+
+    void maybeSkipAtomsZone() {
+        if (selector == SkipAtoms && current && current->isAtomsZone()) {
+            current = current->nextNodeInGroup();
+            MOZ_ASSERT_IF(current, !current->isAtomsZone());
+        }
     }
 
     bool done() const { return !current; }
 
     void next() {
         MOZ_ASSERT(!done());
         current = current->nextNodeInGroup();
+        maybeSkipAtomsZone();
     }
 
     JS::Zone* get() const {
         MOZ_ASSERT(!done());
         return current;
     }
 
     operator JS::Zone*() const { return get(); }
--- a/js/src/gc/PublicIterators.cpp
+++ b/js/src/gc/PublicIterators.cpp
@@ -136,23 +136,23 @@ js::IterateGrayObjectsUnderCC(Zone* zone
 }
 
 JS_PUBLIC_API(void)
 JS_IterateCompartments(JSContext* cx, void* data,
                        JSIterateCompartmentCallback compartmentCallback)
 {
     AutoTraceSession session(cx->runtime());
 
-    for (CompartmentsIter c(cx->runtime(), WithAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next())
         (*compartmentCallback)(cx, data, c);
 }
 
 JS_PUBLIC_API(void)
 JS::IterateRealms(JSContext* cx, void* data, JS::IterateRealmCallback realmCallback)
 {
     AutoTraceSession session(cx->runtime());
 
     Rooted<Realm*> realm(cx);
-    for (RealmsIter r(cx->runtime(), WithAtoms); !r.done(); r.next()) {
+    for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
         realm = r;
         (*realmCallback)(cx, data, realm);
     }
 }
--- a/js/src/gc/PublicIterators.h
+++ b/js/src/gc/PublicIterators.h
@@ -188,24 +188,17 @@ class CompartmentsOrRealmsIterT
     using T = typename InnerIterT::ItemType;
 
     gc::AutoEnterIteration iterMarker;
     ZonesIterT zone;
     mozilla::Maybe<InnerIterT> inner;
 
   public:
     explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
-      : iterMarker(&rt->gc), zone(rt)
-    {
-        if (!zone.done())
-            inner.emplace(zone);
-    }
-
-    CompartmentsOrRealmsIterT(JSRuntime* rt, ZoneSelector selector)
-      : iterMarker(&rt->gc), zone(rt, selector)
+      : iterMarker(&rt->gc), zone(rt, SkipAtoms)
     {
         if (!zone.done())
             inner.emplace(zone);
     }
 
     bool done() const { return zone.done(); }
 
     void next() {
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -364,17 +364,17 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
     // Trace the shared Intl data.
     rt->traceSharedIntlData(trc);
 
     // Trace the JSContext.
     rt->mainContextFromOwnThread()->trace(trc);
 
     // Trace all realm roots, but not the realm itself; it is traced via the
     // parent pointer if traceRoots actually traces anything.
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->traceRoots(trc, traceOrMark);
 
     // Trace helper thread roots.
     HelperThreadState().trace(trc, session);
 
     // Trace the embedding's black and gray roots.
     if (!JS::CurrentThreadIsHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING);
@@ -422,17 +422,17 @@ js::gc::GCRuntime::finishRoots()
 
     if (rootsHash.ref().initialized())
         rootsHash.ref().clear();
 
     rt->finishPersistentRoots();
 
     rt->finishSelfHosting();
 
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->finishRoots();
 
 #ifdef DEBUG
     // The nsWrapperCache may not be empty before our shutdown GC, so we have
     // to skip that table when verifying that we are fully unrooted.
     auto prior = grayRootTracer;
     grayRootTracer = Callback<JSTraceDataOp>(nullptr, nullptr);
 
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -158,17 +158,17 @@ struct TraceIncomingFunctor {
     // reference.
     void operator()(JSString** tp) {}
 };
 } // namespace (anonymous)
 
 JS_PUBLIC_API(void)
 JS::TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments)
 {
-    for (js::CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
+    for (js::CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) {
         if (compartments.has(comp))
             continue;
 
         for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
             mozilla::DebugOnly<const CrossCompartmentKey> prior = e.front().key();
             e.front().mutableKey().applyToWrapped(TraceIncomingFunctor(trc, compartments));
             MOZ_ASSERT(e.front().key() == prior);
         }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1464872.js
@@ -0,0 +1,14 @@
+var g = newGlobal();
+var dbg = Debugger(g);
+dbg.onEnterFrame = function(frame) {};
+
+var g2 = newGlobal();
+g2[g] = g;
+g2.evaluate("grayRoot()")
+g2 = undefined;
+
+g = undefined;
+dbg = undefined;
+
+gc();
+startgc(100000);
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -1038,17 +1038,17 @@ BaselineFrame::deleteDebugModeOSRInfo()
     flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
 }
 
 JitCode*
 JitRuntime::getBaselineDebugModeOSRHandler(JSContext* cx)
 {
     if (!baselineDebugModeOSRHandler_) {
         AutoLockForExclusiveAccess lock(cx);
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         uint32_t offset;
         if (JitCode* code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
             baselineDebugModeOSRHandler_ = code;
             baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
         }
     }
 
     return baselineDebugModeOSRHandler_;
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -103,27 +103,29 @@ jit::MaybeGetJitContext()
     return CurrentJitContext();
 }
 
 JitContext::JitContext(CompileRuntime* rt, CompileRealm* realm, TempAllocator* temp)
   : cx(nullptr),
     temp(temp),
     runtime(rt),
     realm(realm),
+    zone(realm ? realm->zone() : nullptr),
     prev_(CurrentJitContext()),
     assemblerCount_(0)
 {
     SetJitContext(this);
 }
 
 JitContext::JitContext(JSContext* cx, TempAllocator* temp)
   : cx(cx),
     temp(temp),
     runtime(CompileRuntime::get(cx->runtime())),
     realm(CompileRealm::get(cx->realm())),
+    zone(CompileZone::get(cx->zone())),
     prev_(CurrentJitContext()),
     assemblerCount_(0)
 {
     SetJitContext(this);
 }
 
 JitContext::JitContext(TempAllocator* temp)
   : JitContext(nullptr, nullptr, temp)
@@ -206,23 +208,20 @@ JitRuntime::startTrampolineCode(MacroAss
     masm.haltingAlign(CodeAlignment);
     masm.setFramePushed(0);
     return masm.currentOffset();
 }
 
 bool
 JitRuntime::initialize(JSContext* cx, AutoLockForExclusiveAccess& lock)
 {
-    AutoAtomsRealm ar(cx, lock);
+    AutoAtomsZone az(cx, lock);
 
     JitContext jctx(cx, nullptr);
 
-    if (!cx->realm()->ensureJitRealmExists(cx))
-        return false;
-
     functionWrappers_ = cx->new_<VMWrapperMap>(cx);
     if (!functionWrappers_ || !functionWrappers_->init())
         return false;
 
     StackMacroAssembler masm;
 
     Label bailoutTail;
     JitSpew(JitSpew_Codegen, "# Emitting bailout tail stub");
@@ -334,17 +333,17 @@ JitRuntime::initialize(JSContext* cx, Au
 
 JitCode*
 JitRuntime::debugTrapHandler(JSContext* cx)
 {
     if (!debugTrapHandler_) {
         // JitRuntime code stubs are shared across compartments and have to
         // be allocated in the atoms zone.
         AutoLockForExclusiveAccess lock(cx);
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         debugTrapHandler_ = generateDebugTrapHandler(cx);
     }
     return debugTrapHandler_;
 }
 
 JitRuntime::IonBuilderList&
 JitRuntime::ionLazyLinkList(JSRuntime* rt)
 {
@@ -587,17 +586,17 @@ JitRuntime::Trace(JSTracer* trc, AutoLoc
 {
     MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
 
     // Shared stubs are allocated in the atoms zone, so do not iterate
     // them after the atoms heap after it has been "finished."
     if (trc->runtime()->atomsAreFinished())
         return;
 
-    Zone* zone = trc->runtime()->atomsRealm(lock)->zone();
+    Zone* zone = trc->runtime()->atomsZone(lock);
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
 JitRuntime::TraceJitcodeGlobalTableForMinorGC(JSTracer* trc)
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -67,16 +67,17 @@ class JitContext
 
     // Allocator for temporary memory during compilation.
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/realm for use
     // during compilation.
     CompileRuntime* runtime;
     CompileRealm* realm;
+    CompileZone* zone;
 
     int getNextAssemblerId() {
         return assemblerCount_++;
     }
   private:
     JitContext* prev_;
     int assemblerCount_;
 };
--- a/js/src/jit/JSJitFrameIter.h
+++ b/js/src/jit/JSJitFrameIter.h
@@ -341,19 +341,17 @@ class RInstructionResults
     RInstructionResults(RInstructionResults&& src);
 
     RInstructionResults& operator=(RInstructionResults&& rhs);
 
     ~RInstructionResults();
 
     MOZ_MUST_USE bool init(JSContext* cx, uint32_t numResults);
     bool isInitialized() const;
-#ifdef DEBUG
     size_t length() const;
-#endif
 
     JitFrameLayout* frame() const;
 
     HeapPtr<Value>& operator[](size_t index);
 
     void trace(JSTracer* trc);
 };
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1489,23 +1489,21 @@ RInstructionResults::init(JSContext* cx,
 }
 
 bool
 RInstructionResults::isInitialized() const
 {
     return initialized_;
 }
 
-#ifdef DEBUG
 size_t
 RInstructionResults::length() const
 {
     return results_->length();
 }
-#endif
 
 JitFrameLayout*
 RInstructionResults::frame() const
 {
     MOZ_ASSERT(fp_);
     return fp_;
 }
 
@@ -1948,17 +1946,17 @@ SnapshotIterator::initInstructionResults
             // If the evaluation failed because of OOMs, then we discard the
             // current set of result that we collected so far.
             fallback.activation->removeIonFrameRecovery(fp);
             return false;
         }
     }
 
     MOZ_ASSERT(results->isInitialized());
-    MOZ_ASSERT(results->length() == recover_.numInstructions() - 1);
+    MOZ_RELEASE_ASSERT(results->length() == recover_.numInstructions() - 1);
     instructionResults_ = results;
     return true;
 }
 
 bool
 SnapshotIterator::computeInstructionResults(JSContext* cx, RInstructionResults* results) const
 {
     MOZ_ASSERT(!results->isInitialized());
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -152,18 +152,18 @@ struct Imm64
     inline Imm32 firstHalf() const;
     inline Imm32 secondHalf() const;
 };
 
 #ifdef DEBUG
 static inline bool
 IsCompilingWasm()
 {
-    // wasm compilation pushes a JitContext with a null Realm.
-    return GetJitContext()->realm == nullptr;
+    // wasm compilation pushes a JitContext with a null Zone.
+    return GetJitContext()->zone == nullptr;
 }
 #endif
 
 // Pointer to be embedded as an immediate in an instruction.
 struct ImmPtr
 {
     void* value;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -792,17 +792,17 @@ JS_WrapValue(JSContext* cx, MutableHandl
     return cx->compartment()->wrap(cx, vp);
 }
 
 static void
 ReleaseAssertObjectHasNoWrappers(JSContext* cx, HandleObject target)
 {
     RootedValue origv(cx, ObjectValue(*target));
 
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         if (c->lookupWrapper(origv))
             MOZ_CRASH("wrapper found for target object");
     }
 }
 
 /*
  * Brain transplants. Not for beginners or the squeamish.
  *
@@ -949,17 +949,17 @@ JS_PUBLIC_API(bool)
 JS_RefreshCrossCompartmentWrappers(JSContext* cx, HandleObject obj)
 {
     return RemapAllWrappersForObject(cx, obj, obj);
 }
 
 JS_PUBLIC_API(bool)
 JS_InitStandardClasses(JSContext* cx, HandleObject obj)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     assertSameCompartment(cx, obj);
 
     Rooted<GlobalObject*> global(cx, &obj->global());
     return GlobalObject::initStandardClasses(cx, global);
 }
@@ -1969,34 +1969,34 @@ JS_FireOnNewGlobalObject(JSContext* cx, 
     assertSameCompartment(cx, global);
     Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
     Debugger::onNewGlobalObject(cx, globalObject);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObject(JSContext* cx, const JSClass* jsclasp)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
     MOZ_ASSERT(clasp != &JSFunction::class_);
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     return NewObjectWithClassProto(cx, clasp, nullptr);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectWithGivenProto(JSContext* cx, const JSClass* jsclasp, HandleObject proto)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto);
 
     const Class* clasp = Valueify(jsclasp);
     if (!clasp)
         clasp = &PlainObject::class_;    /* default class is Object */
 
@@ -2004,17 +2004,17 @@ JS_NewObjectWithGivenProto(JSContext* cx
     MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
 
     return NewObjectWithGivenProto(cx, clasp, proto);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewPlainObject(JSContext* cx)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return NewBuiltinClassInstance<PlainObject>(cx);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewObjectForConstructor(JSContext* cx, const JSClass* clasp, const CallArgs& args)
@@ -2904,34 +2904,34 @@ JS::IsConstructor(JSObject* obj)
 {
     return obj->isConstructor();
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args,
                      MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fval, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
     RootedValue thisv(cx, ObjectOrNullValue(obj));
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
                 MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fun, args);
 
     InvokeArgs iargs(cx);
     if (!FillArgumentsFromArraylike(cx, iargs, args))
         return false;
 
@@ -2939,17 +2939,17 @@ JS_CallFunction(JSContext* cx, HandleObj
     RootedValue thisv(cx, ObjectOrNullValue(obj));
     return Call(cx, fval, thisv, iargs, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args,
                     MutableHandleValue rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, args);
 
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
@@ -3373,28 +3373,28 @@ JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject* obj, uint32_t index, const Value& value)
 {
     obj->as<NativeObject>().setReservedSlot(index, value);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     assertSameCompartment(cx, contents);
     return NewDenseCopiedArray(cx, contents.length(), contents.begin());
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, size_t length)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return NewDenseFullyAllocatedArray(cx, length);
 }
 
 inline bool
 IsGivenTypeObject(JSContext* cx, JS::HandleObject obj, const ESClass& typeClass, bool* isType)
@@ -3507,17 +3507,17 @@ JS_InitReadPrincipalsCallback(JSContext*
     MOZ_ASSERT(!cx->runtime()->readPrincipals);
     cx->runtime()->readPrincipals = read;
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                const char* name)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedAtom atom(cx);
     if (name) {
         atom = Atomize(cx, name, strlen(name));
         if (!atom)
@@ -3527,17 +3527,17 @@ JS_NewFunction(JSContext* cx, JSNative n
     return (flags & JSFUN_CONSTRUCTOR)
            ? NewNativeConstructor(cx, native, nargs, atom)
            : NewNativeFunction(cx, native, nargs, atom);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS::GetSelfHostedFunction(JSContext* cx, const char* selfHostedName, HandleId id, unsigned nargs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, id);
 
     RootedAtom name(cx, IdToFunctionName(cx, id));
     if (!name)
         return nullptr;
 
@@ -3783,60 +3783,60 @@ extern JS_PUBLIC_API(bool)
 JS_IsConstructor(JSFunction* fun)
 {
     return fun->isConstructor();
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     return DefineFunctions(cx, obj, fs, NotIntrinsic);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineFunction(JSContext* cx, HandleObject obj, const char* name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction*)
 JS_DefineUCFunction(JSContext* cx, HandleObject obj,
                     const char16_t* name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction*)
 JS_DefineFunctionById(JSContext* cx, HandleObject obj, HandleId id, JSNative call,
                       unsigned nargs, unsigned attrs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id);
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 /* Use the fastest available getc. */
 #if defined(HAVE_GETC_UNLOCKED)
@@ -4077,17 +4077,17 @@ JS::CompileOptions::CompileOptions(JSCon
 }
 
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
         SourceBufferHolder& srcBuf, MutableHandleScript script)
 {
     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
 
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     script.set(frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind, options, srcBuf));
     return !!script;
 }
 
 static bool
@@ -4218,17 +4218,17 @@ JS::CompileForNonSyntacticScope(JSContex
 }
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
                  const uint8_t* buf, size_t length)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     return frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options, buf, length);
 }
 
 JSScript*
 JS::DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options, FILE* file)
@@ -4542,17 +4542,17 @@ JS_GetFunctionScript(JSContext* cx, Hand
  */
 static bool
 CompileFunction(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
                 HandleAtom name, bool isInvalidName,
                 SourceBufferHolder& srcBuf, uint32_t parameterListEnd,
                 HandleObject enclosingEnv, HandleScope enclosingScope,
                 MutableHandleFunction fun)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, enclosingEnv);
     RootedAtom funAtom(cx);
 
     fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
                                 isInvalidName ? nullptr : name,
                                 /* proto = */ nullptr,
@@ -4711,17 +4711,17 @@ JS::ExposeScriptToDebugger(JSContext* cx
     MOZ_ASSERT(script->hideScriptFromDebugger());
     script->clearHideScriptFromDebugger();
     Debugger::onNewScript(cx, script);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileScript(JSContext* cx, HandleScript script)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     script->ensureNonLazyCanonicalFunction();
     RootedFunction fun(cx, script->functionNonDelazifying());
     if (fun)
         return JS_DecompileFunction(cx, fun);
     bool haveSource = script->scriptSource()->hasSourceData();
@@ -4729,27 +4729,27 @@ JS_DecompileScript(JSContext* cx, Handle
         return nullptr;
     return haveSource ? JSScript::sourceData(cx, script)
                       : NewStringCopyZ<CanGC>(cx, "[no source]");
 }
 
 JS_PUBLIC_API(JSString*)
 JS_DecompileFunction(JSContext* cx, HandleFunction fun)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, fun);
     return FunctionToString(cx, fun, /* isToSource = */ false);
 }
 
 MOZ_NEVER_INLINE static bool
 ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, scope, script);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(scope), script->hasNonSyntacticScope());
     return Execute(cx, script, *scope, rval);
 }
 
 static bool
@@ -4833,17 +4833,17 @@ JS::CloneAndExecuteScript(JSContext* cx,
 }
 
 static bool
 Evaluate(JSContext* cx, ScopeKind scopeKind, HandleObject env,
          const ReadOnlyCompileOptions& optionsArg,
          SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
     CompileOptions options(cx, optionsArg);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, env);
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic);
 
     options.setIsRunOnce(true);
     RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(),
                                                           scopeKind, options, srcBuf));
@@ -4974,17 +4974,17 @@ JS::SetModuleMetadataHook(JSRuntime* rt,
     AssertHeapIsIdle();
     rt->moduleMetadataHook = func;
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf, JS::MutableHandleObject module)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     module.set(frontend::CompileModule(cx, options, srcBuf));
     return !!module;
 }
 
 JS_PUBLIC_API(void)
@@ -5132,17 +5132,17 @@ JS::SetPromiseRejectionTrackerCallback(J
 {
     cx->promiseRejectionTrackerCallback = callback;
     cx->promiseRejectionTrackerCallbackData = data;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, executor, proto);
 
     if (!executor)
         return PromiseObject::createSkippingExecutor(cx);
 
     MOZ_ASSERT(IsCallable(executor));
@@ -5386,17 +5386,17 @@ JS::GetWaitForAllPromise(JSContext* cx, 
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableDefaultStreamObject(JSContext* cx,
                                    JS::HandleObject underlyingSource /* = nullptr */,
                                    JS::HandleFunction size /* = nullptr */,
                                    double highWaterMark /* = 1 */,
                                    JS::HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
@@ -5408,17 +5408,17 @@ JS::NewReadableDefaultStreamObject(JSCon
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableByteStreamObject(JSContext* cx,
                                 JS::HandleObject underlyingSource /* = nullptr */,
                                 double highWaterMark /* = 1 */,
                                 JS::HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
     RootedObject source(cx, underlyingSource);
     if (!source) {
         source = NewBuiltinClassInstance<PlainObject>(cx);
         if (!source)
             return nullptr;
@@ -5467,17 +5467,17 @@ JS::HasReadableStreamCallbacks(JSContext
     return cx->runtime()->readableStreamDataRequestCallback;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
                                           uint8_t flags /* = 0 */,
                                           HandleObject proto /* = nullptr */)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
 #ifdef DEBUG
     JSRuntime* rt = cx->runtime();
     MOZ_ASSERT(rt->readableStreamDataRequestCallback);
     MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
     MOZ_ASSERT(rt->readableStreamCancelCallback);
@@ -7622,19 +7622,18 @@ GetScriptedCallerGlobal(JSContext* cx)
 
     // If the caller is hidden, the embedding wants us to return null here so
     // that it can check its own stack (see HideScriptedCaller).
     if (activation->scriptedCallerIsHidden())
         return nullptr;
 
     GlobalObject* global = realm->maybeGlobal();
 
-    // No one should be running code in the atoms realm or running code in a
-    // realm without any live objects, so there should definitely be a live
-    // global.
+    // No one should be running code in a realm without any live objects, so
+    // there should definitely be a live global.
     MOZ_ASSERT(global);
 
     return global;
 }
 
 JS_PUBLIC_API(void)
 HideScriptedCaller(JSContext* cx)
 {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -157,21 +157,26 @@ JS_GetIsSecureContext(JSCompartment* com
 
 JS_FRIEND_API(JSPrincipals*)
 JS_GetCompartmentPrincipals(JSCompartment* compartment)
 {
     Realm* realm = JS::GetRealmForCompartment(compartment);
     return realm->principals();
 }
 
+JS_FRIEND_API(JSPrincipals*)
+JS::GetRealmPrincipals(JS::Realm* realm)
+{
+    return realm->principals();
+}
+
 JS_FRIEND_API(void)
-JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals)
+JS::SetRealmPrincipals(JS::Realm* realm, JSPrincipals* principals)
 {
     // Short circuit if there's no change.
-    Realm* realm = JS::GetRealmForCompartment(compartment);
     if (principals == realm->principals())
         return;
 
     // Any realm with the trusted principals -- and there can be
     // multiple -- is a system realm.
     const JSPrincipals* trusted = realm->runtimeFromMainThread()->trustedPrincipals();
     bool isSystem = principals && principals == trusted;
 
@@ -241,17 +246,17 @@ DefineHelpProperty(JSContext* cx, Handle
     if (!atom)
         return false;
     return JS_DefineProperty(cx, obj, prop, atom, JSPROP_READONLY | JSPROP_PERMANENT);
 }
 
 JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext* cx, HandleObject obj, const JSFunctionSpecWithHelp* fs)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     for (; fs->name; fs++) {
         JSAtom* atom = Atomize(cx, fs->name, strlen(fs->name));
         if (!atom)
             return false;
 
@@ -347,22 +352,16 @@ js::IsSystemCompartment(JSCompartment* c
 
 JS_FRIEND_API(bool)
 js::IsSystemZone(Zone* zone)
 {
     return zone->isSystem;
 }
 
 JS_FRIEND_API(bool)
-js::IsAtomsRealm(JS::Realm* realm)
-{
-    return realm->isAtomsRealm();
-}
-
-JS_FRIEND_API(bool)
 js::IsAtomsZone(JS::Zone* zone)
 {
     return zone->runtimeFromAnyThread()->isAtomsZone(zone);
 }
 
 JS_FRIEND_API(bool)
 js::IsFunctionObject(JSObject* obj)
 {
@@ -428,31 +427,31 @@ js::RunningWithTrustedPrincipals(JSConte
     return cx->runningWithTrustedPrincipals();
 }
 
 JS_FRIEND_API(JSFunction*)
 js::DefineFunctionWithReserved(JSContext* cx, JSObject* objArg, const char* name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return nullptr;
     Rooted<jsid> id(cx, AtomToId(atom));
     return DefineFunction(cx, obj, id, call, nargs, attrs, gc::AllocKind::FUNCTION_EXTENDED);
 }
 
 JS_FRIEND_API(JSFunction*)
 js::NewFunctionWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                             const char* name)
 {
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     CHECK_REQUEST(cx);
 
     RootedAtom atom(cx);
     if (name) {
         atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return nullptr;
@@ -463,17 +462,17 @@ js::NewFunctionWithReserved(JSContext* c
         NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED);
 }
 
 JS_FRIEND_API(JSFunction*)
 js::NewFunctionByIdWithReserved(JSContext* cx, JSNative native, unsigned nargs, unsigned flags,
                                 jsid id)
 {
     MOZ_ASSERT(JSID_IS_STRING(id));
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, id);
 
     RootedAtom atom(cx, JSID_TO_ATOM(id));
     return (flags & JSFUN_CONSTRUCTOR) ?
         NewNativeConstructor(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED) :
         NewNativeFunction(cx, native, nargs, atom, gc::AllocKind::FUNCTION_EXTENDED);
 }
@@ -1270,16 +1269,19 @@ JS_FRIEND_API(void)
 JS::NotifyGCRootsRemoved(JSContext* cx)
 {
     cx->runtime()->gc.notifyRootsRemoved();
 }
 
 JS_FRIEND_API(JS::Realm*)
 js::GetAnyRealmInZone(JS::Zone* zone)
 {
+    if (zone->isAtomsZone())
+        return nullptr;
+
     RealmsInZoneIter realm(zone);
     MOZ_ASSERT(!realm.done());
     return realm.get();
 }
 
 void
 JS::ObjectPtr::finalize(JSRuntime* rt)
 {
@@ -1538,18 +1540,19 @@ AutoAssertNoContentJS::~AutoAssertNoCont
 
 JS_FRIEND_API(void)
 js::EnableAccessValidation(JSContext* cx, bool enabled)
 {
     cx->enableAccessValidation = enabled;
 }
 
 JS_FRIEND_API(void)
-js::SetCompartmentValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp)
+js::SetRealmValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp)
 {
+    MOZ_ASSERT(global->is<GlobalObject>());
     global->realm()->setValidAccessPtr(accessp);
 }
 
 JS_FRIEND_API(bool)
 js::SystemZoneAvailable(JSContext* cx)
 {
     return true;
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -185,19 +185,16 @@ extern JS_FRIEND_API(void)
 JS_SetSetUseCounterCallback(JSContext* cx, JSSetUseCounterCallback callback);
 
 extern JS_FRIEND_API(bool)
 JS_GetIsSecureContext(JSCompartment* compartment);
 
 extern JS_FRIEND_API(JSPrincipals*)
 JS_GetCompartmentPrincipals(JSCompartment* compartment);
 
-extern JS_FRIEND_API(void)
-JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals);
-
 extern JS_FRIEND_API(JSPrincipals*)
 JS_GetScriptPrincipals(JSScript* script);
 
 namespace js {
 extern JS_FRIEND_API(JSCompartment*)
 GetScriptCompartment(JSScript* script);
 } /* namespace js */
 
@@ -320,16 +317,22 @@ ForceLexicalInitialization(JSContext *cx
 /**
  * Whether we are poisoning unused/released data for error detection. Governed
  * by the JS_GC_POISONING #ifdef as well as the $JSGC_DISABLE_POISONING
  * environment variable.
  */
 extern JS_FRIEND_API(int)
 IsGCPoisoning();
 
+extern JS_FRIEND_API(JSPrincipals*)
+GetRealmPrincipals(JS::Realm* realm);
+
+extern JS_FRIEND_API(void)
+SetRealmPrincipals(JS::Realm* realm, JSPrincipals* principals);
+
 } // namespace JS
 
 /**
  * Copies all own properties from |obj| to |target|. |obj| must be a "native"
  * object (that is to say, normal-ish - not an Array or a Proxy).
  *
  * This function immediately enters a compartment, and does not impose any
  * restrictions on the compartment of |cx|.
@@ -479,19 +482,16 @@ JS_FRIEND_API(bool) obj_defineSetter(JSC
 
 extern JS_FRIEND_API(bool)
 IsSystemCompartment(JSCompartment* comp);
 
 extern JS_FRIEND_API(bool)
 IsSystemZone(JS::Zone* zone);
 
 extern JS_FRIEND_API(bool)
-IsAtomsRealm(JS::Realm* realm);
-
-extern JS_FRIEND_API(bool)
 IsAtomsZone(JS::Zone* zone);
 
 struct WeakMapTracer
 {
     JSRuntime* runtime;
 
     explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {}
 
@@ -548,31 +548,32 @@ extern JS_FRIEND_API(bool)
 CheckGrayMarkingState(JSRuntime* rt);
 #endif
 
 #ifdef JS_HAS_CTYPES
 extern JS_FRIEND_API(size_t)
 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj);
 #endif
 
+// Note: this returns nullptr iff |zone| is the atoms zone.
 extern JS_FRIEND_API(JS::Realm*)
 GetAnyRealmInZone(JS::Zone* zone);
 
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
  */
 namespace shadow {
 
 struct ObjectGroup {
     const Class* clasp;
-    JSObject*   proto;
-    JSCompartment* compartment;
+    JSObject* proto;
+    JS::Realm* realm;
 };
 
 struct BaseShape {
     const js::Class* clasp_;
     JSObject* parent;
 };
 
 class Shape {
@@ -665,20 +666,33 @@ InheritanceProtoKeyForStandardClass(JSPr
 
     // Otherwise, we inherit [Object].
     return JSProto_Object;
 }
 
 JS_FRIEND_API(bool)
 IsFunctionObject(JSObject* obj);
 
+JS_FRIEND_API(bool)
+IsCrossCompartmentWrapper(JSObject* obj);
+
 static MOZ_ALWAYS_INLINE JSCompartment*
 GetObjectCompartment(JSObject* obj)
 {
-    return reinterpret_cast<shadow::Object*>(obj)->group->compartment;
+    JS::Realm* realm = reinterpret_cast<shadow::Object*>(obj)->group->realm;
+    return JS::GetCompartmentForRealm(realm);
+}
+
+// CrossCompartmentWrappers are shared by all realms within the compartment, so
+// getting a wrapper's realm usually doesn't make sense.
+static MOZ_ALWAYS_INLINE JS::Realm*
+GetNonCCWObjectRealm(JSObject* obj)
+{
+    MOZ_ASSERT(!js::IsCrossCompartmentWrapper(obj));
+    return reinterpret_cast<shadow::Object*>(obj)->group->realm;
 }
 
 JS_FRIEND_API(JSObject*)
 GetGlobalForObjectCrossCompartment(JSObject* obj);
 
 JS_FRIEND_API(JSObject*)
 GetPrototypeNoProxy(JSObject* obj);
 
@@ -3088,28 +3102,28 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Auto
     ~AutoAssertNoContentJS();
 
   private:
     JSContext* context_;
     bool prevAllowContentJS_;
 };
 
 // Turn on assertions so that we assert that
-//     !comp->validAccessPtr || *comp->validAccessPtr
-// is true for every |comp| that we run JS code in. The compartment's validAccessPtr
-// is set via SetCompartmentValidAccessPtr.
+//     !realm->validAccessPtr || *realm->validAccessPtr
+// is true for every |realm| that we run JS code in. The realm's validAccessPtr
+// is set via SetRealmValidAccessPtr.
 extern JS_FRIEND_API(void)
 EnableAccessValidation(JSContext* cx, bool enabled);
 
 // See EnableAccessValidation above. The caller must guarantee that accessp will
 // live at least as long as |global| is alive. The JS engine reads accessp from
 // threads that are allowed to run code on |global|, so all changes to *accessp
 // should be made from whichever thread owns |global| at a given time.
 extern JS_FRIEND_API(void)
-SetCompartmentValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp);
+SetRealmValidAccessPtr(JSContext* cx, JS::HandleObject global, bool* accessp);
 
 // Returns true if the system zone is available (i.e., if no cooperative contexts
 // are using it now).
 extern JS_FRIEND_API(bool)
 SystemZoneAvailable(JSContext* cx);
 
 typedef void
 (* LogCtorDtor)(void* self, const char* type, uint32_t sz);
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -80,16 +80,17 @@ class JS_PUBLIC_API(JSTracer);
 class JSFlatString;
 
 template<typename T> struct JSConstScalarSpec;
 typedef JSConstScalarSpec<double> JSConstDoubleSpec;
 typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
 
 namespace js {
 
+inline JS::Realm* GetContextRealm(const JSContext* cx);
 inline JSCompartment* GetContextCompartment(const JSContext* cx);
 inline JS::Zone* GetContextZone(const JSContext* cx);
 
 // Whether the current thread is permitted access to any part of the specified
 // runtime or zone.
 JS_FRIEND_API(bool)
 CurrentThreadCanAccessRuntime(const JSRuntime* rt);
 
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -532,17 +532,17 @@ js::NukeCrossCompartmentWrappers(JSConte
                                  const CompartmentFilter& sourceFilter,
                                  JSCompartment* target,
                                  js::NukeReferencesToWindow nukeReferencesToWindow,
                                  js::NukeReferencesFromTarget nukeReferencesFromTarget)
 {
     CHECK_REQUEST(cx);
     JSRuntime* rt = cx->runtime();
 
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(rt); !c.done(); c.next()) {
         if (!sourceFilter.match(c))
             continue;
 
         // If the compartment matches both the source and target filter, we may
         // want to cut both incoming and outgoing wrappers.
         bool nukeAll = (nukeReferencesFromTarget == NukeAllReferences &&
                         target == c.get());
 
@@ -676,17 +676,17 @@ js::RemapAllWrappersForObject(JSContext*
 
     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     RootedObject newTarget(cx, newTargetArg);
 
     AutoWrapperVector toTransplant(cx);
     if (!toTransplant.reserve(cx->runtime()->numCompartments))
         return false;
 
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
             // We found a wrapper. Remember and root it.
             toTransplant.infallibleAppend(WrapperValue(wp));
         }
     }
 
     for (const WrapperValue& v : toTransplant)
         RemapWrapper(cx, &v.toObject(), newTarget);
@@ -696,17 +696,17 @@ js::RemapAllWrappersForObject(JSContext*
 
 JS_FRIEND_API(bool)
 js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
                       const CompartmentFilter& targetFilter)
 {
     bool evictedNursery = false;
 
     AutoWrapperVector toRecompute(cx);
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next()) {
         // Filter by source compartment.
         if (!sourceFilter.match(c))
             continue;
 
         if (!evictedNursery && c->hasNurseryAllocatedWrapperEntries(targetFilter)) {
             cx->runtime()->gc.evictNursery();
             evictedNursery = true;
         }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -8904,17 +8904,17 @@ Shell(JSContext* cx, OptionParser* op, c
             JS_free(cx, const_cast<char*>(jsCacheDir));
         }
     }
 
     /*
      * Dump remaining type inference results while we still have a context.
      * This printing depends on atoms still existing.
      */
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next())
+    for (CompartmentsIter c(cx->runtime()); !c.done(); c.next())
         PrintTypes(cx, c, false);
 
     return result;
 }
 
 static void
 SetOutputFile(const char* const envVar,
               RCFile* defaultOut,
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3059,17 +3059,17 @@ Debugger::markIteratively(GCMarker* mark
 {
     bool markedAny = false;
 
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
     JSRuntime* rt = marker->runtime();
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next()) {
+    for (RealmsIter r(rt); !r.done(); r.next()) {
         if (r->isDebuggee()) {
             GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal();
             if (!IsMarkedUnbarriered(rt, &global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
              * getDebuggers can't return nullptr.
@@ -3256,36 +3256,49 @@ Debugger::detachAllDebuggersFromGlobal(F
     MOZ_ASSERT(!debuggers->empty());
     while (!debuggers->empty())
         debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr);
 }
 
 /* static */ void
 Debugger::findZoneEdges(Zone* zone, js::gc::ZoneComponentFinder& finder)
 {
-    /*
-     * For debugger cross compartment wrappers, add edges in the opposite
-     * direction to those already added by JSCompartment::findOutgoingEdges.
-     * This ensure that debuggers and their debuggees are finalized in the same
-     * group.
-     */
     JSRuntime* rt = zone->runtimeFromMainThread();
     for (Debugger* dbg : rt->debuggerList()) {
-        Zone* w = dbg->object->zone();
-        if (w == zone || !w->isGCMarking())
+        Zone* debuggerZone = dbg->object->zone();
+        if (!debuggerZone->isGCMarking())
             continue;
-        if (dbg->debuggeeZones.has(zone) ||
-            dbg->scripts.hasKeyInZone(zone) ||
-            dbg->sources.hasKeyInZone(zone) ||
-            dbg->objects.hasKeyInZone(zone) ||
-            dbg->environments.hasKeyInZone(zone) ||
-            dbg->wasmInstanceScripts.hasKeyInZone(zone) ||
-            dbg->wasmInstanceSources.hasKeyInZone(zone))
-        {
-            finder.addEdgeTo(w);
+
+        if (debuggerZone == zone) {
+            /*
+             * Add edges to debuggee zones. These are weak references that are
+             * not in the cross compartment wrapper map.
+             */
+            for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) {
+                Zone* debuggeeZone = e.front();
+                if (debuggeeZone->isGCMarking())
+                    finder.addEdgeTo(debuggeeZone);
+            }
+        } else {
+            /*
+             * For debugger cross compartment wrappers, add edges in the
+             * opposite direction to those already added by
+             * JSCompartment::findOutgoingEdges and above.  This ensure that
+             * debuggers and their debuggees are finalized in the same group.
+             */
+            if (dbg->debuggeeZones.has(zone) ||
+                dbg->scripts.hasKeyInZone(zone) ||
+                dbg->sources.hasKeyInZone(zone) ||
+                dbg->objects.hasKeyInZone(zone) ||
+                dbg->environments.hasKeyInZone(zone) ||
+                dbg->wasmInstanceScripts.hasKeyInZone(zone) ||
+                dbg->wasmInstanceSources.hasKeyInZone(zone))
+            {
+                finder.addEdgeTo(debuggerZone);
+            }
         }
     }
 }
 
 const ClassOps Debugger::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* enumerate   */
@@ -4952,17 +4965,17 @@ Debugger::findAllGlobals(JSContext* cx, 
 
     AutoObjectVector globals(cx);
 
     {
         // Accumulate the list of globals before wrapping them, because
         // wrapping can GC and collect realms from under us, while iterating.
         JS::AutoCheckCannotGC nogc;
 
-        for (RealmsIter r(cx->runtime(), SkipAtoms); !r.done(); r.next()) {
+        for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
             if (r->creationOptions().invisibleToDebugger())
                 continue;
 
             r->compartment()->scheduledForDestruction = false;
 
             GlobalObject* global = r->maybeGlobal();
 
             if (cx->runtime()->isSelfHostingGlobal(global))
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -147,17 +147,17 @@ GeckoProfilerRuntime::enable(bool enable
                 jitActivation = jitActivation->prevJitActivation();
             }
         }
     }
 
     // WebAssembly code does not need to be released, but profiling string
     // labels have to be generated so that they are available during async
     // profiling stack iteration.
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->wasm.ensureProfilingLabels(enabled);
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 GeckoProfilerRuntime::profileString(JSScript* script, JSFunction* maybeFun)
 {
     auto locked = strings.lock();
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -499,17 +499,17 @@ GlobalObject::createInternal(JSContext* 
 }
 
 /* static */ GlobalObject*
 GlobalObject::new_(JSContext* cx, const Class* clasp, JSPrincipals* principals,
                    JS::OnNewGlobalHookOption hookOption,
                    const JS::RealmOptions& options)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
-    MOZ_ASSERT_IF(cx->realm(), !cx->realm()->isAtomsRealm());
+    MOZ_ASSERT_IF(cx->zone(), !cx->zone()->isAtomsZone());
 
     Realm* realm = NewRealm(cx, principals, options);
     if (!realm)
         return nullptr;
 
     Rooted<GlobalObject*> global(cx);
     {
         AutoRealmUnchecked ar(cx, realm);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -342,17 +342,17 @@ js::CancelOffThreadIonCompile(const Comp
 }
 
 #ifdef DEBUG
 bool
 js::HasOffThreadIonCompile(Realm* realm)
 {
     AutoLockHelperThreadState lock;
 
-    if (!HelperThreadState().threads || realm->isAtomsRealm())
+    if (!HelperThreadState().threads)
         return false;
 
     GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist(lock);
     for (size_t i = 0; i < worklist.length(); i++) {
         jit::IonBuilder* builder = worklist[i];
         if (builder->script()->realm() == realm)
             return true;
     }
@@ -748,17 +748,17 @@ CreateGlobalForOffThreadParse(JSContext*
 
     JSObject* obj = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
                                        JS::DontFireOnNewGlobalHook, realmOptions);
     if (!obj)
         return nullptr;
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
-    JS_SetCompartmentPrincipals(global->compartment(), currentRealm->principals());
+    JS::SetRealmPrincipals(global->realm(), currentRealm->principals());
 
     return global;
 }
 
 static bool
 QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
 {
     AutoLockHelperThreadState lock;
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -446,17 +446,17 @@ AtomizeAndCopyCharsInner(JSContext* cx, 
             atom->setPinned();
             p->setPinned(true);
         }
         return atom;
     }
 
     JSAtom* atom;
     {
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
 
         JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
         if (!flat) {
             // Grudgingly forgo last-ditch GC. The alternative would be to release
             // the lock, manually GC here, and retry from the top. If you fix this,
             // please also fix or comment the similar case in Symbol::new_.
             ReportOutOfMemory(cx);
             return nullptr;
--- a/js/src/vm/JSCompartment-inl.h
+++ b/js/src/vm/JSCompartment-inl.h
@@ -47,54 +47,46 @@ JS::Realm::globalIsAboutToBeFinalized()
 js::ObjectRealm::get(const JSObject* obj)
 {
     return obj->realm()->objects_;
 }
 
 template <typename T>
 js::AutoRealm::AutoRealm(JSContext* cx, const T& target)
   : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(nullptr)
+    origin_(cx->realm())
 {
     cx_->enterRealmOf(target);
 }
 
-// Protected constructor that bypasses assertions in enterCompartmentOf. Used
-// only for entering the atoms realm.
-js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target,
-                         js::AutoLockForExclusiveAccess& lock)
-  : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(&lock)
-{
-    MOZ_ASSERT(target->isAtomsRealm());
-    cx_->enterAtomsRealm(target, lock);
-}
-
-// Protected constructor that bypasses assertions in enterCompartmentOf. Should
-// not be used to enter the atoms realm.
+// Protected constructor that bypasses assertions in enterRealmOf.
 js::AutoRealm::AutoRealm(JSContext* cx, JS::Realm* target)
   : cx_(cx),
-    origin_(cx->realm()),
-    maybeLock_(nullptr)
+    origin_(cx->realm())
 {
-    MOZ_ASSERT(!target->isAtomsRealm());
-    cx_->enterNonAtomsRealm(target);
+    cx_->enterRealm(target);
 }
 
 js::AutoRealm::~AutoRealm()
 {
-    cx_->leaveRealm(origin_, maybeLock_);
+    cx_->leaveRealm(origin_);
 }
 
-js::AutoAtomsRealm::AutoAtomsRealm(JSContext* cx,
-                                   js::AutoLockForExclusiveAccess& lock)
-  : AutoRealm(cx, cx->atomsRealm(lock), lock)
-{}
+js::AutoAtomsZone::AutoAtomsZone(JSContext* cx, js::AutoLockForExclusiveAccess& lock)
+  : cx_(cx),
+    origin_(cx->realm()),
+    lock_(lock)
+{
+    cx_->enterAtomsZone(lock);
+}
+
+js::AutoAtomsZone::~AutoAtomsZone()
+{
+    cx_->leaveAtomsZone(origin_, lock_);
+}
 
 js::AutoRealmUnchecked::AutoRealmUnchecked(JSContext* cx, JS::Realm* target)
   : AutoRealm(cx, target)
 {}
 
 inline bool
 JSCompartment::wrap(JSContext* cx, JS::MutableHandleValue vp)
 {
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -87,72 +87,66 @@ Realm::~Realm()
 }
 
 JSCompartment::~JSCompartment()
 {
     runtime_->numCompartments--;
 }
 
 bool
-JSCompartment::init(JSContext* maybecx)
+JSCompartment::init(JSContext* cx)
 {
     if (!crossCompartmentWrappers.init(0)) {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 bool
-ObjectRealm::init(JSContext* maybecx)
+ObjectRealm::init(JSContext* cx)
 {
     if (!iteratorCache.init()) {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
-    NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(maybecx));
+    NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
     if (!sentinel)
         return false;
 
     iteratorSentinel_ = Move(sentinel);
     enumerators = iteratorSentinel_.get();
     return true;
 }
 
 bool
-Realm::init(JSContext* maybecx)
+Realm::init(JSContext* cx)
 {
     // Initialize JSCompartment. This is temporary until Realm and
     // JSCompartment are completely separated.
-    if (!JSCompartment::init(maybecx))
+    if (!JSCompartment::init(cx))
         return false;
 
     /*
-     * maybecx is null when called to create the atoms realm from
-     * JSRuntime::init().
-     *
      * As a hack, we clear our timezone cache every time we create a new realm.
      * This ensures that the cache is always relatively fresh, but shouldn't
      * interfere with benchmarks that create tons of date objects (unless they
      * also create tons of iframes, which seems unlikely).
      */
     JS::ResetTimeZone();
 
-    if (!objects_.init(maybecx))
+    if (!objects_.init(cx))
         return false;
 
     if (!savedStacks_.init() ||
         !varNames_.init())
     {
-        if (maybecx)
-            ReportOutOfMemory(maybecx);
+        ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 jit::JitRuntime*
 JSRuntime::createJitRuntime(JSContext* cx)
@@ -302,17 +296,16 @@ CopyStringPure(JSContext* cx, JSString* 
         return nullptr;
 
     return NewStringDontDeflate<CanGC>(cx, copiedChars.forget(), len);
 }
 
 bool
 JSCompartment::wrap(JSContext* cx, MutableHandleString strp)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
 
     /* If the string is already in this compartment, we are done. */
     JSString* str = strp;
     if (str->zoneFromAnyThread() == zone())
         return true;
 
     /*
@@ -449,17 +442,16 @@ JSCompartment::getOrCreateWrapper(JSCont
 
     obj.set(wrapper);
     return true;
 }
 
 bool
 JSCompartment::wrap(JSContext* cx, MutableHandleObject obj)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
 
     if (!obj)
         return true;
 
     AutoDisableProxyCheck adpc;
 
     // Anything we're wrapping has already escaped into script, so must have
@@ -481,17 +473,16 @@ JSCompartment::wrap(JSContext* cx, Mutab
     // Ensure that the wrapper is also exposed.
     ExposeObjectToActiveJS(obj);
     return true;
 }
 
 bool
 JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg)
 {
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this));
     MOZ_ASSERT(cx->compartment() == this);
     MOZ_ASSERT(obj);
     MOZ_ASSERT(existingArg);
     MOZ_ASSERT(existingArg->compartment() == cx->compartment());
     MOZ_ASSERT(IsDeadProxyObject(existingArg));
 
     AutoDisableProxyCheck adpc;
 
@@ -610,17 +601,16 @@ ObjectRealm::getNonSyntacticLexicalEnvir
         return nullptr;
     return &lexicalEnv->as<LexicalEnvironmentObject>();
 }
 
 bool
 Realm::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
 {
     MOZ_ASSERT(name);
-    MOZ_ASSERT(!isAtomsRealm());
 
     if (varNames_.put(name))
         return true;
 
     ReportOutOfMemory(cx);
     return false;
 }
 
@@ -644,17 +634,17 @@ JSCompartment::traceOutgoingCrossCompart
     }
 }
 
 /* static */ void
 JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
 {
     gcstats::AutoPhase ap(trc->runtime()->gc.stats(), gcstats::PhaseKind::MARK_CCWS);
     MOZ_ASSERT(JS::CurrentThreadIsHeapMajorCollecting());
-    for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
+    for (CompartmentsIter c(trc->runtime()); !c.done(); c.next()) {
         if (!c->zone()->isCollecting())
             c->traceOutgoingCrossCompartmentWrappers(trc);
     }
     Debugger::traceIncomingCrossCompartmentEdges(trc);
 }
 
 void
 Realm::traceGlobal(JSTracer* trc)
@@ -914,17 +904,17 @@ Realm::sweepTemplateObjects()
         iterResultTemplate_.set(nullptr);
 }
 
 /* static */ void
 JSCompartment::fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc)
 {
     MOZ_ASSERT(trc->runtime()->gc.isHeapCompacting());
 
-    for (CompartmentsIter comp(trc->runtime(), SkipAtoms); !comp.done(); comp.next()) {
+    for (CompartmentsIter comp(trc->runtime()); !comp.done(); comp.next()) {
         // Sweep the wrapper map to update keys (wrapped values) in other
         // compartments that may have been moved.
         comp->sweepCrossCompartmentWrappers();
         // Trace the wrappers in the map to update their cross-compartment edges
         // to wrapped values in other compartments that may have been moved.
         comp->traceOutgoingCrossCompartmentWrappers(trc);
     }
 }
@@ -1397,22 +1387,16 @@ Realm::addSizeOfIncludingThis(mozilla::M
     if (scriptCountsMap) {
         *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
         for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
             *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
         }
     }
 }
 
-HashNumber
-Realm::randomHashCode()
-{
-    return HashNumber(getOrCreateRandomNumberGenerator().next());
-}
-
 mozilla::HashCodeScrambler
 Realm::randomHashCodeScrambler()
 {
     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                       randomKeyGenerator_.next());
 }
 
 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -615,17 +615,17 @@ struct JSCompartment
   private:
     bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
     bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
 
   protected:
     explicit JSCompartment(JS::Zone* zone);
     ~JSCompartment();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    MOZ_MUST_USE bool init(JSContext* cx);
 
   public:
     MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);
 
     MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp);
 #ifdef ENABLE_BIGINT
     MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandle<JS::BigInt*> bi);
 #endif
@@ -726,17 +726,17 @@ class ObjectRealm
                                       js::SystemAllocPolicy>;
     IteratorCache iteratorCache;
 
     static inline ObjectRealm& get(const JSObject* obj);
 
     explicit ObjectRealm(JS::Zone* zone);
     ~ObjectRealm();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    MOZ_MUST_USE bool init(JSContext* cx);
 
     void finishRoots();
     void trace(JSTracer* trc);
     void sweepAfterMinorGC();
     void sweepNativeIterators();
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* innerViewsArg,
@@ -827,17 +827,16 @@ class JS::Realm : private JSCompartment
     static const unsigned DebuggerObservesMask = IsDebuggee |
                                                  DebuggerObservesAllExecution |
                                                  DebuggerObservesCoverage |
                                                  DebuggerObservesAsmJS |
                                                  DebuggerObservesBinarySource;
     unsigned debugModeBits_ = 0;
     friend class js::AutoRestoreRealmDebugMode;
 
-    bool isAtomsRealm_ = false;
     bool isSelfHostingRealm_ = false;
     bool marked_ = true;
     bool isSystem_ = false;
 
   public:
     // WebAssembly state for the realm.
     js::wasm::Realm wasm;
 
@@ -885,17 +884,17 @@ class JS::Realm : private JSCompartment
 
     Realm(const Realm&) = delete;
     void operator=(const Realm&) = delete;
 
   public:
     Realm(JS::Zone* zone, const JS::RealmOptions& options);
     ~Realm();
 
-    MOZ_MUST_USE bool init(JSContext* maybecx);
+    MOZ_MUST_USE bool init(JSContext* cx);
     void destroy(js::FreeOp* fop);
     void clearTables();
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* tiAllocationSiteTables,
                                 size_t* tiArrayTypeTables,
                                 size_t* tiObjectTypeTables,
                                 size_t* realmObject,
@@ -935,35 +934,28 @@ class JS::Realm : private JSCompartment
 
     const JS::RealmCreationOptions& creationOptions() const { return creationOptions_; }
     JS::RealmBehaviors& behaviors() { return behaviors_; }
     const JS::RealmBehaviors& behaviors() const { return behaviors_; }
 
     /* Whether to preserve JIT code on non-shrinking GCs. */
     bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
 
-    bool isAtomsRealm() const {
-        return isAtomsRealm_;
-    }
-    void setIsAtomsRealm() {
-        isAtomsRealm_ = true;
-    }
-
     bool isSelfHostingRealm() const {
         return isSelfHostingRealm_;
     }
     void setIsSelfHostingRealm() {
         isSelfHostingRealm_ = true;
     }
 
     /* The global object for this realm.
      *
-     * This returns nullptr if this is the atoms realm.  (The global_ field is
-     * also null briefly during GC, after the global object is collected; but
-     * when that happens the Realm is destroyed during the same GC.)
+     * Note: the global_ field is null briefly during GC, after the global
+     * object is collected; but when that happens the Realm is destroyed during
+     * the same GC.)
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
      * always marks its global as well.
      */
     inline js::GlobalObject* maybeGlobal() const;
 
     /* An unbarriered getter for use while tracing. */
     inline js::GlobalObject* unsafeUnbarrieredMaybeGlobal() const;
@@ -1260,18 +1252,16 @@ class JS::Realm : private JSCompartment
 
     // Initializes randomNumberGenerator if needed.
     mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();
 
     const void* addressOfRandomNumberGenerator() const {
         return randomNumberGenerator_.ptr();
     }
 
-    js::HashNumber randomHashCode();
-
     mozilla::HashCodeScrambler randomHashCodeScrambler();
 
     bool isAccessValid() const {
         return validAccessPtr_ ? *validAccessPtr_ : true;
     }
     void setValidAccessPtr(bool* accessp) {
         validAccessPtr_ = accessp;
     }
@@ -1356,42 +1346,45 @@ class MOZ_RAII AssertRealmUnchanged
     JS::Realm* const oldRealm;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class AutoRealm
 {
     JSContext* const cx_;
     JS::Realm* const origin_;
-    const AutoLockForExclusiveAccess* maybeLock_;
 
   public:
     template <typename T>
     inline AutoRealm(JSContext* cx, const T& target);
     inline ~AutoRealm();
 
     JSContext* context() const { return cx_; }
     JS::Realm* origin() const { return origin_; }
 
   protected:
     inline AutoRealm(JSContext* cx, JS::Realm* target);
 
-    // Used only for entering the atoms realm.
-    inline AutoRealm(JSContext* cx, JS::Realm* target,
-                     AutoLockForExclusiveAccess& lock);
-
   private:
     AutoRealm(const AutoRealm&) = delete;
     AutoRealm& operator=(const AutoRealm&) = delete;
 };
 
-class AutoAtomsRealm : protected AutoRealm
+class MOZ_RAII AutoAtomsZone
 {
+    JSContext* const cx_;
+    JS::Realm* const origin_;
+    const AutoLockForExclusiveAccess& lock_;
+
+    AutoAtomsZone(const AutoAtomsZone&) = delete;
+    AutoAtomsZone& operator=(const AutoAtomsZone&) = delete;
+
   public:
-    inline AutoAtomsRealm(JSContext* cx, AutoLockForExclusiveAccess& lock);
+    inline AutoAtomsZone(JSContext* cx, AutoLockForExclusiveAccess& lock);
+    inline ~AutoAtomsZone();
 };
 
 // Enter a realm directly. Only use this where there's no target GC thing
 // to pass to AutoRealm or where you need to avoid the assertions in
 // JS::Compartment::enterCompartmentOf().
 class AutoRealmUnchecked : protected AutoRealm
 {
   public:
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -39,29 +39,24 @@ class CompartmentChecker
         MOZ_CRASH();
     }
 
     static void fail(JS::Zone* z1, JS::Zone* z2) {
         printf("*** Zone mismatch %p vs. %p\n", (void*) z1, (void*) z2);
         MOZ_CRASH();
     }
 
-    /* Note: should only be used when neither c1 nor c2 may be the atoms compartment. */
     static void check(JSCompartment* c1, JSCompartment* c2) {
-        MOZ_ASSERT(!c1->runtimeFromAnyThread()->isAtomsCompartment(c1));
-        MOZ_ASSERT(!c2->runtimeFromAnyThread()->isAtomsCompartment(c2));
         if (c1 != c2)
             fail(c1, c2);
     }
 
     void check(JSCompartment* c) {
-        if (c && !compartment->runtimeFromAnyThread()->isAtomsCompartment(c)) {
-            if (c != compartment)
-                fail(compartment, c);
-        }
+        if (c && c != compartment)
+            fail(compartment, c);
     }
 
     void checkZone(JS::Zone* z) {
         if (compartment && z != compartment->zone())
             fail(compartment->zone(), z);
     }
 
     void check(JSObject* obj) {
@@ -458,88 +453,91 @@ JSContext::setPendingException(const js:
 
 inline bool
 JSContext::runningWithTrustedPrincipals()
 {
     return !realm() || realm()->principals() == runtime()->trustedPrincipals();
 }
 
 inline void
-JSContext::enterNonAtomsRealm(JS::Realm* realm)
+JSContext::enterRealm(JS::Realm* realm)
 {
-    enterRealmDepth_++;
+    // We should never enter a realm while in the atoms zone.
+    MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
 
-    MOZ_ASSERT(!realm->zone()->isAtomsZone());
-
+#ifdef DEBUG
+    enterRealmDepth_++;
+#endif
     realm->enter();
-    setRealm(realm, nullptr);
+    setRealm(realm);
 }
 
 inline void
-JSContext::enterAtomsRealm(JS::Realm* realm,
-                           const js::AutoLockForExclusiveAccess& lock)
+JSContext::enterAtomsZone(const js::AutoLockForExclusiveAccess& lock)
 {
-    enterRealmDepth_++;
+    // Only one thread can be in the atoms zone at a time.
+    MOZ_ASSERT(runtime_->currentThreadHasExclusiveAccess());
 
-    MOZ_ASSERT(realm->zone()->isAtomsZone());
-
-    realm->enter();
-    setRealm(realm, &lock);
+    realm_ = nullptr;
+    zone_ = runtime_->atomsZone(lock);
+    arenas_ = &zone_->arenas;
 }
 
 template <typename T>
 inline void
 JSContext::enterRealmOf(const T& target)
 {
     MOZ_ASSERT(JS::CellIsNotGray(target));
-    enterNonAtomsRealm(target->realm());
+    enterRealm(target->realm());
 }
 
 inline void
 JSContext::enterNullRealm()
 {
+    // We should never enter a realm while in the atoms zone.
+    MOZ_ASSERT_IF(zone(), !zone()->isAtomsZone());
+
+#ifdef DEBUG
     enterRealmDepth_++;
+#endif
     setRealm(nullptr);
 }
 
 inline void
-JSContext::leaveRealm(JS::Realm* oldRealm,
-                      const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
+JSContext::leaveRealm(JS::Realm* oldRealm)
 {
     MOZ_ASSERT(hasEnteredRealm());
+#ifdef DEBUG
     enterRealmDepth_--;
+#endif
 
     // Only call leave() after we've setRealm()-ed away from the current realm.
     JS::Realm* startingRealm = realm_;
-    setRealm(oldRealm, maybeLock);
+    setRealm(oldRealm);
     if (startingRealm)
         startingRealm->leave();
 }
 
 inline void
-JSContext::setRealm(JS::Realm* realm,
-                    const js::AutoLockForExclusiveAccess* maybeLock /* = nullptr */)
+JSContext::leaveAtomsZone(JS::Realm* oldRealm,
+                          const js::AutoLockForExclusiveAccess& lock)
 {
-    // Only one thread can be in the atoms realm at a time.
-    MOZ_ASSERT_IF(realm && realm->isAtomsRealm(), maybeLock != nullptr);
-    MOZ_ASSERT_IF((realm && realm->isAtomsRealm()) || (realm_ && realm_->isAtomsRealm()),
-                  runtime_->currentThreadHasExclusiveAccess());
+    setRealm(oldRealm);
+}
 
-    // Make sure that the atoms realm has its own zone.
-    MOZ_ASSERT_IF(realm && !realm->isAtomsRealm(),
-                  !realm->zone()->isAtomsZone());
-
+inline void
+JSContext::setRealm(JS::Realm* realm)
+{
     // Both the current and the new realm should be properly marked as
     // entered at this point.
     MOZ_ASSERT_IF(realm_, realm_->hasBeenEntered());
     MOZ_ASSERT_IF(realm, realm->hasBeenEntered());
 
     // This thread must have exclusive access to the zone.
-    MOZ_ASSERT_IF(realm && !realm->zone()->isAtomsZone(),
-                  CurrentThreadCanAccessZone(realm->zone()));
+    MOZ_ASSERT_IF(realm, CurrentThreadCanAccessZone(realm->zone()));
 
     realm_ = realm;
     zone_ = realm ? realm->zone() : nullptr;
     arenas_ = zone_ ? &zone_->arenas : nullptr;
 }
 
 inline JSScript*
 JSContext::currentScript(jsbytecode** ppc,
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1214,17 +1214,19 @@ JSContext::alreadyReportedError()
 }
 
 JSContext::JSContext(JSRuntime* runtime, const JS::ContextOptions& options)
   : runtime_(runtime),
     kind_(ContextKind::HelperThread),
     helperThread_(nullptr),
     options_(options),
     arenas_(nullptr),
+#ifdef DEBUG
     enterRealmDepth_(0),
+#endif
     jitActivation(nullptr),
     activation_(nullptr),
     profilingActivation_(nullptr),
     nativeStackBase(GetNativeStackBase()),
     entryMonitor(nullptr),
     noExecuteDebuggerTop(nullptr),
     activityCallback(nullptr),
     activityCallbackArg(nullptr),
@@ -1350,17 +1352,17 @@ JSContext::setRuntime(JSRuntime* rt)
     runtime_ = rt;
 }
 
 bool
 JSContext::getPendingException(MutableHandleValue rval)
 {
     MOZ_ASSERT(throwing);
     rval.set(unwrappedException());
-    if (realm()->isAtomsRealm())
+    if (zone()->isAtomsZone())
         return true;
     bool wasOverRecursed = overRecursed_;
     clearPendingException();
     if (!compartment()->wrap(this, rval))
         return false;
     assertSameCompartment(this, rval);
     setPendingException(rval);
     overRecursed_ = wasOverRecursed;
@@ -1471,16 +1473,24 @@ JSContext::sizeOfExcludingThis(mozilla::
     /*
      * There are other JSContext members that could be measured; the following
      * ones have been found by DMD to be worth measuring.  More stuff may be
      * added later.
      */
     return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
 }
 
+#ifdef DEBUG
+bool
+JSContext::inAtomsZone() const
+{
+    return zone_->isAtomsZone();
+}
+#endif
+
 void
 JSContext::trace(JSTracer* trc)
 {
     cycleDetectorVector().trace(trc);
     geckoProfiler().trace(trc);
 
     if (trc->isMarkingTracer() && realm_)
         realm_->mark();
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -20,16 +20,17 @@
 #include "vm/ErrorReporting.h"
 #include "vm/MallocProvider.h"
 #include "vm/Runtime.h"
 
 struct DtoaState;
 
 namespace js {
 
+class AutoAtomsZone;
 class AutoRealm;
 
 namespace jit {
 class JitContext;
 class DebugModeOSRVolatileJitFrameIter;
 } // namespace jit
 
 namespace gc {
@@ -194,66 +195,72 @@ struct JSContext : public JS::RootingCon
      * places in the VM cannot know that they were called from script (e.g.,
      * they may have been called through the JSAPI via JS_CallFunction) and thus
      * cannot expect there is a scripted caller.
      *
      * Realms should be entered/left in a LIFO fasion. The depth of this
      * enter/leave stack is maintained by enterRealmDepth_ and queried by
      * hasEnteredRealm.
      *
-     * To enter a compartment, code should prefer using AutoRealm over
+     * To enter a realm, code should prefer using AutoRealm over
      * manually calling cx->enterRealm/leaveRealm.
      */
   protected:
+#ifdef DEBUG
     js::ThreadData<unsigned> enterRealmDepth_;
+#endif
 
-    inline void setRealm(JS::Realm* realm,
-                         const js::AutoLockForExclusiveAccess* maybeLock = nullptr);
+    inline void setRealm(JS::Realm* realm);
   public:
+#ifdef DEBUG
     bool hasEnteredRealm() const {
         return enterRealmDepth_ > 0;
     }
-#ifdef DEBUG
     unsigned getEnterRealmDepth() const {
         return enterRealmDepth_;
     }
 #endif
 
   private:
-    // We distinguish between entering the atoms realm and all other realms.
-    // Entering the atoms realm requires a lock.
-    inline void enterNonAtomsRealm(JS::Realm* realm);
-    inline void enterAtomsRealm(JS::Realm* realm,
-                                const js::AutoLockForExclusiveAccess& lock);
+    inline void enterRealm(JS::Realm* realm);
+    inline void enterAtomsZone(const js::AutoLockForExclusiveAccess& lock);
 
+    friend class js::AutoAtomsZone;
     friend class js::AutoRealm;
 
   public:
     template <typename T>
     inline void enterRealmOf(const T& target);
     inline void enterNullRealm();
-    inline void leaveRealm(JS::Realm* oldRealm,
-                           const js::AutoLockForExclusiveAccess* maybeLock = nullptr);
+
+    inline void leaveRealm(JS::Realm* oldRealm);
+    inline void leaveAtomsZone(JS::Realm* oldRealm,
+                               const js::AutoLockForExclusiveAccess& lock);
 
     void setHelperThread(js::HelperThread* helperThread);
     js::HelperThread* helperThread() const { return helperThread_; }
 
     bool isNurseryAllocSuppressed() const {
         return nurserySuppressions_;
     }
 
     // Threads may freely access any data in their compartment and zone.
     JSCompartment* compartment() const {
         return JS::GetCompartmentForRealm(realm_);
     }
     JS::Realm* realm() const {
         return realm_;
     }
+
+#ifdef DEBUG
+    bool inAtomsZone() const;
+#endif
+
     JS::Zone* zone() const {
-        MOZ_ASSERT_IF(!realm(), !zone_);
+        MOZ_ASSERT_IF(!realm() && zone_, inAtomsZone());
         MOZ_ASSERT_IF(realm(), js::GetCompartmentZone(GetCompartmentForRealm(realm())) == zone_);
         return zoneRaw();
     }
 
     // For use when the context's zone is being read by another thread and the
     // compartment and zone pointers might not be in sync.
     JS::Zone* zoneRaw() const {
         return zone_;
@@ -270,18 +277,18 @@ struct JSContext : public JS::RootingCon
     // Current global. This is only safe to use within the scope of the
     // AutoRealm from which it's called.
     inline js::Handle<js::GlobalObject*> global() const;
 
     // Methods to access runtime data that must be protected by locks.
     js::AtomSet& atoms(js::AutoLockForExclusiveAccess& lock) {
         return runtime_->atoms(lock);
     }
-    JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) {
-        return runtime_->atomsRealm(lock);
+    const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) {
+        return runtime_->atomsZone(lock);
     }
     js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) {
         return runtime_->symbolRegistry(lock);
     }
     js::ScriptDataTable& scriptDataTable(js::AutoLockScriptData& lock) {
         return runtime_->scriptDataTable(lock);
     }
 
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -831,17 +831,17 @@ CollectRuntimeStatsHelper(JSContext* cx,
 #ifdef DEBUG
     // Check that the in-arena measurements look ok.
     size_t totalArenaSize = rtStats->zTotals.gcHeapArenaAdmin +
                             rtStats->zTotals.unusedGCThings.totalSize() +
                             rtStats->gcHeapGCThings;
     MOZ_ASSERT(totalArenaSize % gc::ArenaSize == 0);
 #endif
 
-    for (RealmsIter realm(rt, WithAtoms); !realm.done(); realm.next())
+    for (RealmsIter realm(rt); !realm.done(); realm.next())
         realm->nullRealmStats();
 
     size_t numDirtyChunks =
         (rtStats->gcHeapChunkTotal - rtStats->gcHeapUnusedChunks) / gc::ChunkSize;
     size_t perChunkAdmin =
         sizeof(gc::Chunk) - (sizeof(gc::Arena) * gc::ArenasPerChunk);
     rtStats->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
 
@@ -881,28 +881,28 @@ JS::CollectRuntimeStats(JSContext* cx, R
 {
     return CollectRuntimeStatsHelper(cx, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
 }
 
 JS_PUBLIC_API(size_t)
 JS::SystemRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) {
         if (realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::UserRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+    for (RealmsIter realm(cx->runtime()); !realm.done(); realm.next()) {
         if (!realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::PeakSizeOfTemporary(const JSContext* cx)
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -152,17 +152,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     thousandsSeparator(nullptr),
     decimalSeparator(nullptr),
     numGrouping(nullptr),
 #endif
     beingDestroyed_(false),
     allowContentJS_(true),
     atoms_(nullptr),
     atomsAddedWhileSweeping_(nullptr),
-    atomsRealm_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     jitSupportsFloatingPoint(false),
     jitSupportsUnalignedAccesses(false),
     jitSupportsSimd(false),
     offthreadIonCompilationEnabled_(true),
@@ -214,34 +213,18 @@ JSRuntime::init(JSContext* cx, uint32_t 
 
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
     ScopedJSDeletePtr<Zone> atomsZone(js_new<Zone>(this));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
-    JS::RealmOptions options;
-    ScopedJSDeletePtr<Realm> atomsRealm(js_new<Realm>(atomsZone.get(), options));
-    if (!atomsRealm || !atomsRealm->init(nullptr))
-        return false;
-
-    JSCompartment* atomsComp = atomsRealm->compartment();
-    if (!atomsComp->realms().append(atomsRealm))
-        return false;
-
     gc.atomsZone = atomsZone.get();
-    if (!atomsZone->compartments().append(atomsComp))
-        return false;
-
-    atomsRealm->setIsSystem(true);
-    atomsRealm->setIsAtomsRealm();
-
     atomsZone.forget();
-    this->atomsRealm_ = atomsRealm.forget();
 
     if (!symbolRegistry_.ref().init())
         return false;
 
     if (!scriptDataTable_.ref().init())
         return false;
 
     /* The garbage collector depends on everything before this point being initialized. */
@@ -328,17 +311,16 @@ JSRuntime::destroyRuntime()
      */
     FreeScriptData(this);
 
 #if !EXPOSE_INTL_API
     FinishRuntimeNumberState(this);
 #endif
 
     gc.finish();
-    atomsRealm_ = nullptr;
 
     js_delete(defaultFreeOp_.ref());
 
     js_free(defaultLocale);
     js_delete(jitRuntime_.ref());
 
 #ifdef DEBUG
     initialized_ = false;
@@ -432,17 +414,17 @@ JSRuntime::addSizeOfIncludingThis(mozill
 
     rtSizes->wasmRuntime += wasmInstances.lock()->sizeOfExcludingThis(mallocSizeOf);
 }
 
 static bool
 HandleInterrupt(JSContext* cx, bool invokeCallback)
 {
     MOZ_ASSERT(cx->requestDepth >= 1);
-    MOZ_ASSERT(!cx->realm()->isAtomsRealm());
+    MOZ_ASSERT(!cx->zone()->isAtomsZone());
 
     cx->runtime()->gc.gcIfRequested();
 
     // A worker thread may have requested an interrupt after finishing an Ion
     // compilation.
     jit::AttachFinishedCompilations(cx);
 
     // Don't call the interrupt callback if we only interrupted for GC or Ion.
@@ -711,16 +693,30 @@ JSRuntime::randomHashCodeScrambler()
 
 mozilla::non_crypto::XorShift128PlusRNG
 JSRuntime::forkRandomKeyGenerator()
 {
     auto& rng = randomKeyGenerator();
     return mozilla::non_crypto::XorShift128PlusRNG(rng.next(), rng.next());
 }
 
+js::HashNumber
+JSRuntime::randomHashCode()
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(this));
+
+    if (randomHashCodeGenerator_.isNothing()) {
+        mozilla::Array<uint64_t, 2> seed;
+        GenerateXorShift128PlusSeed(seed);
+        randomHashCodeGenerator_.emplace(seed[0], seed[1]);
+    }
+
+    return HashNumber(randomHashCodeGenerator_->next());
+}
+
 void
 JSRuntime::updateMallocCounter(size_t nbytes)
 {
     gc.updateMallocCounter(nbytes);
 }
 
 JS_FRIEND_API(void*)
 JSRuntime::onOutOfMemory(AllocFunction allocFunc, size_t nbytes, void* reallocPtr, JSContext* maybecx)
@@ -765,17 +761,17 @@ JSRuntime::onOutOfMemoryCanGC(AllocFunct
     if (OnLargeAllocationFailure && bytes >= LARGE_ALLOCATION)
         OnLargeAllocationFailure();
     return onOutOfMemory(allocFunc, bytes, reallocPtr);
 }
 
 bool
 JSRuntime::activeGCInAtomsZone()
 {
-    Zone* zone = atomsRealm_->zone();
+    Zone* zone = unsafeAtomsZone();
     return (zone->needsIncrementalBarrier() && !gc.isVerifyPreBarriersEnabled()) ||
            zone->wasGCStarted();
 }
 
 bool
 JSRuntime::createAtomsAddedWhileSweepingTable()
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -563,20 +563,25 @@ struct JSRuntime : public js::MallocProv
         return !!jitRuntime_;
     }
 
   private:
     // Used to generate random keys for hash tables.
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomKeyGenerator_;
     mozilla::non_crypto::XorShift128PlusRNG& randomKeyGenerator();
 
+    // Used to generate random hash codes for symbols.
+    mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomHashCodeGenerator_;
+
   public:
     mozilla::HashCodeScrambler randomHashCodeScrambler();
     mozilla::non_crypto::XorShift128PlusRNG forkRandomKeyGenerator();
 
+    js::HashNumber randomHashCode();
+
     //-------------------------------------------------------------------------
     // Self-hosting support
     //-------------------------------------------------------------------------
 
     bool hasInitializedSelfHosting() const {
         return selfHostingGlobal_;
     }
 
@@ -694,21 +699,16 @@ struct JSRuntime : public js::MallocProv
     // Set of all atoms other than those in permanentAtoms and staticStrings.
     // Reading or writing this set requires the calling thread to use
     // AutoLockForExclusiveAccess.
     js::ExclusiveAccessLockOrGCTaskData<js::AtomSet*> atoms_;
 
     // Set of all atoms added while the main atoms table is being swept.
     js::ExclusiveAccessLockData<js::AtomSet*> atomsAddedWhileSweeping_;
 
-    // Realm and associated zone containing all atoms in the runtime, as
-    // well as runtime wide IonCode stubs. Modifying the contents of this
-    // zone requires the calling thread to use AutoLockForExclusiveAccess.
-    js::WriteOnceData<JS::Realm*> atomsRealm_;
-
     // Set of all live symbols produced by Symbol.for(). All such symbols are
     // allocated in the atomsZone. Reading or writing the symbol registry
     // requires the calling thread to use AutoLockForExclusiveAccess.
     js::ExclusiveAccessLockOrGCTaskData<js::SymbolRegistry> symbolRegistry_;
 
   public:
     bool initializeAtoms(JSContext* cx);
     void finishAtoms();
@@ -729,35 +729,26 @@ struct JSRuntime : public js::MallocProv
     }
 
     bool createAtomsAddedWhileSweepingTable();
     void destroyAtomsAddedWhileSweepingTable();
     js::AtomSet* atomsAddedWhileSweeping() {
         return atomsAddedWhileSweeping_;
     }
 
-    JS::Realm* atomsRealm(js::AutoLockForExclusiveAccess& lock) {
-        return atomsRealm_;
-    }
-    JS::Realm* unsafeAtomsRealm() {
-        return atomsRealm_;
+    const JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) const {
+        return gc.atomsZone;
     }
-
-    // Note: once JS::Realm and JSCompartment are completely unrelated, the
-    // atoms realm probably won't have a compartment so we can remove this
-    // then.
-    bool isAtomsCompartment(JSCompartment* comp) {
-        return JS::GetRealmForCompartment(comp) == atomsRealm_;
+    JS::Zone* atomsZone(const js::AutoLockForExclusiveAccess& lock) {
+        return gc.atomsZone;
     }
-
-    const JS::Zone* atomsZone(js::AutoLockForExclusiveAccess& lock) const {
+    JS::Zone* unsafeAtomsZone() {
         return gc.atomsZone;
     }
 
-    // The atoms realm is the only one in its zone.
     bool isAtomsZone(const JS::Zone* zone) const {
         return zone == gc.atomsZone;
     }
 
     bool activeGCInAtomsZone();
 
     js::SymbolRegistry& symbolRegistry(js::AutoLockForExclusiveAccess& lock) {
         return symbolRegistry_.ref();
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -189,17 +189,17 @@ PerformanceMonitoring::monotonicReadTime
     return 0;
 #endif // defined(MOZ_HAVE_RDTSC)
 }
 
 void
 PerformanceMonitoring::dispose(JSRuntime* rt)
 {
     reset();
-    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+    for (RealmsIter r(rt); !r.done(); r.next())
         r->performanceMonitoring.unlink();
 }
 
 PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -224,17 +224,17 @@ MOZ_ALWAYS_INLINE JSFlatString*
 JSFlatString::new_(JSContext* cx, const CharT* chars, size_t length)
 {
     MOZ_ASSERT(chars[length] == CharT(0));
 
     if (!validateLength(cx, length))
         return nullptr;
 
     JSFlatString* str;
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         str = js::Allocate<js::NormalAtom, allowGC>(cx);
     else
         str = js::Allocate<JSFlatString, allowGC>(cx, js::gc::DefaultHeap);
     if (!str)
         return nullptr;
 
     if (!str->isTenured()) {
         // The chars pointer is only considered to be handed over to this
@@ -268,27 +268,27 @@ JSFlatString::toPropertyName(JSContext* 
         return nullptr;
     return atom->asPropertyName();
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSThinInlineString*
 JSThinInlineString::new_(JSContext* cx)
 {
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx));
 
     return js::Allocate<JSThinInlineString, allowGC>(cx, js::gc::DefaultHeap);
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSFatInlineString*
 JSFatInlineString::new_(JSContext* cx)
 {
-    if (cx->realm()->isAtomsRealm())
+    if (cx->zone()->isAtomsZone())
         return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx));
 
     return js::Allocate<JSFatInlineString, allowGC>(cx, js::gc::DefaultHeap);
 }
 
 template<>
 MOZ_ALWAYS_INLINE JS::Latin1Char*
 JSThinInlineString::init<JS::Latin1Char>(size_t length)
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1082,17 +1082,17 @@ const StaticStrings::SmallChar StaticStr
 #undef R4
 #undef R6
 #undef R7
 
 bool
 StaticStrings::init(JSContext* cx)
 {
     AutoLockForExclusiveAccess lock(cx);
-    AutoAtomsRealm ar(cx, lock);
+    AutoAtomsZone az(cx, lock);
 
     static_assert(UNIT_STATIC_LIMIT - 1 <= JSString::MAX_LATIN1_CHAR,
                   "Unit strings must fit in Latin1Char.");
 
     using Latin1Range = mozilla::Range<const Latin1Char>;
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         Latin1Char buffer[] = { Latin1Char(i), '\0' };
--- a/js/src/vm/SymbolType.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -17,17 +17,17 @@
 
 using JS::Symbol;
 using namespace js;
 
 Symbol*
 Symbol::newInternal(JSContext* cx, JS::SymbolCode code, uint32_t hash, JSAtom* description,
                     AutoLockForExclusiveAccess& lock)
 {
-    MOZ_ASSERT(cx->realm() == cx->atomsRealm(lock));
+    MOZ_ASSERT(cx->zone() == cx->atomsZone(lock));
 
     // Following js::AtomizeString, we grudgingly forgo last-ditch GC here.
     Symbol* p = Allocate<JS::Symbol, NoGC>(cx);
     if (!p) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
     return new (p) Symbol(code, hash, description);
@@ -43,18 +43,18 @@ Symbol::new_(JSContext* cx, JS::SymbolCo
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
     // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     Symbol* sym;
     {
-        AutoAtomsRealm ar(cx, lock);
-        sym = newInternal(cx, code, cx->realm()->randomHashCode(), atom, lock);
+        AutoAtomsZone az(cx, lock);
+        sym = newInternal(cx, code, cx->runtime()->randomHashCode(), atom, lock);
     }
     if (sym)
         cx->markAtom(sym);
     return sym;
 }
 
 Symbol*
 Symbol::for_(JSContext* cx, HandleString description)
@@ -69,17 +69,17 @@ Symbol::for_(JSContext* cx, HandleString
     SymbolRegistry::AddPtr p = registry.lookupForAdd(atom);
     if (p) {
         cx->markAtom(*p);
         return *p;
     }
 
     Symbol* sym;
     {
-        AutoAtomsRealm ar(cx, lock);
+        AutoAtomsZone az(cx, lock);
         // Rehash the hash of the atom to give the corresponding symbol a hash
         // that is different than the hash of the corresponding atom.
         HashNumber hash = mozilla::HashGeneric(atom->hash());
         sym = newInternal(cx, SymbolCode::InSymbolRegistry, hash, atom, lock);
         if (!sym)
             return nullptr;
 
         // p is still valid here because we have held the lock since the
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -192,17 +192,17 @@ CompartmentPrivate::~CompartmentPrivate(
 
 void
 CompartmentPrivate::SystemIsBeingShutDown()
 {
     mWrappedJSMap->ShutdownMarker();
 }
 
 RealmPrivate::RealmPrivate(JS::Realm* realm)
-    : scriptability(JS::GetCompartmentForRealm(realm))
+    : scriptability(realm)
     , scope(nullptr)
 {
 }
 
 static bool
 TryParseLocationURICandidate(const nsACString& uristr,
                              CompartmentPrivate::LocationHint aLocationHint,
                              nsIURI** aURI)
@@ -362,21 +362,21 @@ PrincipalImmuneToScriptPolicy(nsIPrincip
                 return true;
             }
         }
     }
 
     return false;
 }
 
-Scriptability::Scriptability(JSCompartment* c) : mScriptBlocks(0)
+Scriptability::Scriptability(JS::Realm* realm) : mScriptBlocks(0)
                                                , mDocShellAllowsScript(true)
                                                , mScriptBlockedByPolicy(false)
 {
-    nsIPrincipal* prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
+    nsIPrincipal* prin = nsJSPrincipals::get(JS::GetRealmPrincipals(realm));
     mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
 
     // If we're not immune, we should have a real principal with a codebase URI.
     // Check the URI against the new-style domain policy.
     if (!mImmuneToScriptPolicy) {
         nsCOMPtr<nsIURI> codebase;
         nsresult rv = prin->GetURI(getter_AddRefs(codebase));
         bool policyAllows;
@@ -1072,22 +1072,21 @@ XPCJSRuntime::~XPCJSRuntime()
 }
 
 // If |*anonymizeID| is non-zero and this is a user compartment, the name will
 // be anonymized.
 static void
 GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
                    bool replaceSlashes)
 {
-    if (js::IsAtomsRealm(JS::GetRealmForCompartment(c))) {
-        name.AssignLiteral("atoms");
-    } else if (*anonymizeID && !js::IsSystemCompartment(c)) {
+    JS::Realm* realm = JS::GetRealmForCompartment(c);
+    if (*anonymizeID && !js::IsSystemCompartment(c)) {
         name.AppendPrintf("<anonymized-%d>", *anonymizeID);
         *anonymizeID += 1;
-    } else if (JSPrincipals* principals = JS_GetCompartmentPrincipals(c)) {
+    } else if (JSPrincipals* principals = JS::GetRealmPrincipals(realm)) {
         nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
         if (NS_FAILED(rv)) {
             name.AssignLiteral("(unknown)");
         }
 
         // If the compartment's location (name) differs from the principal's
         // script location, append the compartment's location to allow
         // differentiation of multiple compartments owned by the same principal
@@ -2158,30 +2157,33 @@ class XPCJSRuntimeStats : public JS::Run
         for (size_t i = 0; i != realmStatsVector.length(); ++i)
             delete static_cast<xpc::RealmStatsExtras*>(realmStatsVector[i].extra);
 
         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
             delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
     }
 
     virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override {
-        // Get some global in this zone.
         AutoSafeJSContext cx;
-        Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone));
         xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
         extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
-        RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
-        if (global) {
-            RefPtr<nsGlobalWindowInner> window;
-            if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
-                // The global is a |window| object.  Use the path prefix that
-                // we should have already created for it.
-                if (mTopWindowPaths->Get(window->WindowID(),
-                                         &extras->pathPrefix))
-                    extras->pathPrefix.AppendLiteral("/js-");
+
+        // Get some global in this zone.
+        Rooted<Realm*> realm(cx, js::GetAnyRealmInZone(zone));
+        if (realm) {
+            RootedObject global(cx, JS::GetRealmGlobalOrNull(realm));
+            if (global) {
+                RefPtr<nsGlobalWindowInner> window;
+                if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
+                    // The global is a |window| object.  Use the path prefix that
+                    // we should have already created for it.
+                    if (mTopWindowPaths->Get(window->WindowID(),
+                                             &extras->pathPrefix))
+                        extras->pathPrefix.AppendLiteral("/js-");
+                }
             }
         }
 
         extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
 
         MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
 
         zStats->extra = extras;
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -38,17 +38,17 @@ class Exception;
 }
 
 typedef void (* xpcGCCallback)(JSGCStatus status);
 
 namespace xpc {
 
 class Scriptability {
 public:
-    explicit Scriptability(JSCompartment* c);
+    explicit Scriptability(JS::Realm* realm);
     bool Allowed();
     bool IsImmuneToScriptPolicy();
 
     void Block();
     void Unblock();
     void SetDocShellAllowsScript(bool aAllowed);
 
     static Scriptability& Get(JSObject* aScope);
--- a/media/mtransport/fuzztest/stun_parser_libfuzz.cpp
+++ b/media/mtransport/fuzztest/stun_parser_libfuzz.cpp
@@ -21,18 +21,17 @@ int FuzzingInitStunParser(int *argc, cha
 }
 
 static int
 RunStunParserFuzzing(const uint8_t* data, size_t size) {
   nr_stun_message *req = 0;
 
   UCHAR* mes = (UCHAR*)data;
 
-  nr_stun_message_create2(&req, mes, size);
-
-  nr_stun_decode_message(req, nullptr, nullptr);
-
-  nr_stun_message_destroy(&req);
+  if (!nr_stun_message_create2(&req, mes, size)) {
+    nr_stun_decode_message(req, nullptr, nullptr);
+    nr_stun_message_destroy(&req);
+  }
 
   return 0;
 }
 
 MOZ_FUZZING_INTERFACE_RAW(FuzzingInitStunParser, RunStunParserFuzzing, StunParser);
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -1020,17 +1020,17 @@ std::vector<std::string> NrIceCtx::GetGl
   }
   RFREE(attrs);
 
   return ret;
 }
 
 nsresult NrIceCtx::ParseGlobalAttributes(std::vector<std::string> attrs) {
   std::vector<char *> attrs_in;
-
+  attrs_in.reserve(attrs.size());
   for (auto& attr : attrs) {
     attrs_in.push_back(const_cast<char *>(attr.c_str()));
   }
 
   int r = nr_ice_peer_ctx_parse_global_attributes(peer_,
                                                   attrs_in.empty() ?
                                                   nullptr : &attrs_in[0],
                                                   attrs_in.size());
--- a/media/mtransport/nricemediastream.cpp
+++ b/media/mtransport/nricemediastream.cpp
@@ -224,17 +224,17 @@ NrIceMediaStream::~NrIceMediaStream() {
 }
 
 nsresult NrIceMediaStream::ParseAttributes(std::vector<std::string>&
                                            attributes) {
   if (!stream_)
     return NS_ERROR_FAILURE;
 
   std::vector<char *> attributes_in;
-
+  attributes_in.reserve(attributes.size());
   for (auto& attribute : attributes) {
     attributes_in.push_back(const_cast<char *>(attribute.c_str()));
   }
 
   // Still need to call nr_ice_ctx_parse_stream_attributes.
   int r = nr_ice_peer_ctx_parse_stream_attributes(ctx_peer_,
                                                   stream_,
                                                   attributes_in.empty() ?
--- a/media/mtransport/test/test_nr_socket_ice_unittest.cpp
+++ b/media/mtransport/test/test_nr_socket_ice_unittest.cpp
@@ -183,17 +183,17 @@ public:
     }
     RFREE(attrs);
 
     return ret;
   }
 
   void ParseGlobalAttributes(std::vector<std::string> attrs) {
     std::vector<char *> attrs_in;
-
+    attrs_in.reserve(attrs.size());
     for (auto& attr : attrs) {
       attrs_in.push_back(const_cast<char *>(attr.c_str()));
     }
 
     int r = nr_ice_peer_ctx_parse_global_attributes(peer_ctx_,
                                                     attrs_in.empty() ?
                                                     nullptr : &attrs_in[0],
                                                     attrs_in.size());
@@ -203,16 +203,17 @@ public:
   void SetControlling(bool controlling) {
     peer_ctx_->controlling = controlling ? 1 : 0;
   }
 
   void SetRemoteAttributes(std::vector<std::string> attributes) {
     int r;
 
     std::vector<char*> attrs;
+    attrs.reserve(attributes.size());
     for (auto& attr: attributes) {
       attrs.push_back(const_cast<char*>(attr.c_str()));
     }
 
     if (!attrs.empty()) {
       r = nr_ice_peer_ctx_parse_stream_attributes(peer_ctx_, ice_media_stream_, &attrs[0], attrs.size());
       ASSERT_EQ(0, r);
     }
--- a/media/mtransport/third_party/nICEr/src/stun/stun_codec.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c
@@ -1410,19 +1410,16 @@ nr_stun_decode_message(nr_stun_message *
     int r,_status;
     int offset;
     int size;
     int padding_bytes;
     nr_stun_message_attribute *attr;
     nr_stun_attr_info *attr_info;
     Data *password;
 
-    if (!msg)
-        ABORT(R_BAD_ARGS);
-
     r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length);
 
     if (!TAILQ_EMPTY(&msg->attributes))
         ABORT(R_BAD_ARGS);
 
     if (sizeof(nr_stun_message_header) > msg->length) {
        r_log(NR_LOG_STUN, LOG_WARNING, "Message too small");
        ABORT(R_FAILED);
--- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp
@@ -164,16 +164,17 @@ protected:
       }
     }
   }
 
   std::vector<RefPtr<JsepTransceiver>>
   DeepCopy(const std::vector<RefPtr<JsepTransceiver>>& transceivers)
   {
     std::vector<RefPtr<JsepTransceiver>> copy;
+    copy.reserve(transceivers.size());
     for (const RefPtr<JsepTransceiver>& transceiver : transceivers) {
       copy.push_back(new JsepTransceiver(*transceiver));
     }
     return copy;
   }
 
   std::string
   CreateOffer(const Maybe<JsepOfferOptions>& options = Nothing())
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -3196,17 +3196,17 @@ PeerConnectionImpl::IceGatheringStateCha
   RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
   if (!pco) {
     return;
   }
   WrappableJSErrorResult rv;
   mThread->Dispatch(WrapRunnable(pco,
                                  &PeerConnectionObserver::OnStateChange,
                                  PCObserverStateType::IceGatheringState,
-                                 rv, static_cast<JSCompartment*>(nullptr)),
+                                 rv, static_cast<JS::Realm*>(nullptr)),
                     NS_DISPATCH_NORMAL);
 
   if (mIceGatheringState == PCImplIceGatheringState::Complete) {
     SendLocalIceCandidateToContent(0, "", "");
   }
 }
 
 void
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -861,16 +861,17 @@ pref("gfx.webrender.enabled", true);
 pref("gfx.webrender.enabled", false);
 #endif
 #endif
 
 #ifdef XP_WIN
 pref("gfx.webrender.force-angle", true);
 pref("gfx.webrender.dcomp-win.enabled", true);
 pref("gfx.webrender.program-binary", true);
+pref("gfx.webrender.program-binary-disk", true);
 #endif
 
 #ifdef XP_MACOSX
 pref("gfx.compositor.glcontext.opaque", false);
 #endif
 
 pref("gfx.webrender.highlight-painted-layers", false);
 pref("gfx.webrender.async-scene-build", true);
--- a/netwerk/base/nsIProtocolHandler.idl
+++ b/netwerk/base/nsIProtocolHandler.idl
@@ -297,20 +297,23 @@ interface nsIProtocolHandler : nsISuppor
 
     /**
      * Channels for this protocol don't need to spin the event loop to handle
      * Open() and reads on the resulting stream.
      */
     const unsigned long URI_SYNC_LOAD_IS_OK = (1<<17);
 
     /**
-     * URI is secure to load in an https page and should not be blocked
-     * by nsMixedContentBlocker
+     * All the origins whose URI has this scheme are considered potentially
+     * trustworthy.
+     * Per the SecureContext spec, https: and wss: should be considered
+     * a priori secure, and implementations may consider other,
+     * implementation-specific URI schemes as secure.
      */
-    const unsigned long URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT = (1<<18);
+    const unsigned long URI_IS_POTENTIALLY_TRUSTWORTHY = (1<<18);
 
     /**
      * This URI may be fetched and the contents are visible to anyone. This is
      * semantically equivalent to the resource being served with all-access CORS
      * headers.
      */
     const unsigned long URI_FETCHABLE_BY_ANYONE = (1 << 19);
 
--- a/netwerk/protocol/about/nsAboutProtocolHandler.cpp
+++ b/netwerk/protocol/about/nsAboutProtocolHandler.cpp
@@ -88,17 +88,17 @@ nsAboutProtocolHandler::GetFlagsForURI(n
     uint32_t aboutModuleFlags = 0;
     rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags);
     // This should never happen, so pass back the error:
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Secure (https) pages can load safe about pages without becoming
     // mixed content.
     if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) {
-        *aFlags |= URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+        *aFlags |= URI_IS_POTENTIALLY_TRUSTWORTHY;
         // about: pages can only be loaded by unprivileged principals
         // if they are marked as LINKABLE
         if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
             // Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE.
             *aFlags &= ~URI_DANGEROUS_TO_LOAD;
             *aFlags |= URI_LOADABLE_BY_ANYONE;
         }
     }
@@ -300,17 +300,17 @@ nsSafeAboutProtocolHandler::GetDefaultPo
 {
     *result = -1;        // no port for moz-safe-about: URLs
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
 {
-    *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+    *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_IS_POTENTIALLY_TRUSTWORTHY;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec,
                                    const char *aCharset, // ignore charset info
                                    nsIURI *aBaseURI,
                                    nsIURI **result)
--- a/netwerk/protocol/file/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp
@@ -148,17 +148,18 @@ nsFileProtocolHandler::GetDefaultPort(in
 {
     *result = -1;        // no port for file: URLs
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::GetProtocolFlags(uint32_t *result)
 {
-    *result = URI_NOAUTH | URI_IS_LOCAL_FILE | URI_IS_LOCAL_RESOURCE;
+    *result = URI_NOAUTH | URI_IS_LOCAL_FILE |
+              URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewURI(const nsACString &spec,
                               const char *charset,
                               nsIURI *aBaseURI,
                               nsIURI **result)
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -2708,17 +2708,17 @@ nsHttpsHandler::GetDefaultPort(int32_t *
 {
     *aPort = NS_HTTPS_DEFAULT_PORT;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpsHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
 {
-    *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
+    *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_IS_POTENTIALLY_TRUSTWORTHY;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpsHandler::NewURI(const nsACString &aSpec,
                        const char *aOriginCharset,
                        nsIURI *aBaseURI,
                        nsIURI **_retval)
--- a/netwerk/protocol/res/ExtensionProtocolHandler.cpp
+++ b/netwerk/protocol/res/ExtensionProtocolHandler.cpp
@@ -370,17 +370,19 @@ ExtensionProtocolHandler::GetFlagsForURI
   // subset are web-accessible (and cross-origin fetchable). Check that whitelist.
   bool loadableByAnyone = false;
 
   URLInfo url(aURI);
   if (auto* policy = EPS().GetByURL(url)) {
     loadableByAnyone = policy->IsPathWebAccessible(url.FilePath());
   }
 
-  *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE | URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD);
+  *aFlags = URI_STD | URI_IS_LOCAL_RESOURCE | URI_IS_POTENTIALLY_TRUSTWORTHY |
+    (loadableByAnyone ? (URI_LOADABLE_BY_ANYONE |
+                         URI_FETCHABLE_BY_ANYONE) : URI_DANGEROUS_TO_LOAD);
   return NS_OK;
 }
 
 bool
 ExtensionProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
                                               const nsACString& aPath,
                                               const nsACString& aPathname,
                                               nsACString& aResult)
--- a/netwerk/protocol/res/nsResProtocolHandler.h
+++ b/netwerk/protocol/res/nsResProtocolHandler.h
@@ -22,17 +22,20 @@ class nsResProtocolHandler final : publi
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIRESPROTOCOLHANDLER
 
     NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::net::SubstitutingProtocolHandler::)
 
     nsResProtocolHandler()
-      : mozilla::net::SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE,
+      : mozilla::net::SubstitutingProtocolHandler("resource", URI_STD |
+                                                  URI_IS_UI_RESOURCE |
+                                                  URI_IS_LOCAL_RESOURCE |
+                                                  URI_IS_POTENTIALLY_TRUSTWORTHY,
                                                   /* aEnforceFileOrJar = */ false)
     {}
 
     MOZ_MUST_USE nsresult Init();
 
     NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override;
     NS_IMETHOD SetSubstitutionWithFlags(const nsACString& aRoot, nsIURI* aBaseURI, uint32_t aFlags) override;
 
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
@@ -285,16 +285,19 @@ BaseWebSocketChannel::GetDefaultPort(int
 
 NS_IMETHODIMP
 BaseWebSocketChannel::GetProtocolFlags(uint32_t *aProtocolFlags)
 {
   LOG(("BaseWebSocketChannel::GetProtocolFlags() %p\n", this));
 
   *aProtocolFlags = URI_NORELATIVE | URI_NON_PERSISTABLE | ALLOWS_PROXY |
       ALLOWS_PROXY_HTTP | URI_DOES_NOT_RETURN_DATA | URI_DANGEROUS_TO_LOAD;
+  if (mEncrypted) {
+    *aProtocolFlags |= URI_IS_POTENTIALLY_TRUSTWORTHY;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BaseWebSocketChannel::NewURI(const nsACString & aSpec, const char *aOriginCharset,
                              nsIURI *aBaseURI, nsIURI **_retval)
 {
   LOG(("BaseWebSocketChannel::NewURI() %p\n", this));
--- a/testing/config/tooltool-manifests/linux64/ccov.manifest
+++ b/testing/config/tooltool-manifests/linux64/ccov.manifest
@@ -1,9 +1,9 @@
 [
   {
     "size": 2161039,
     "digest": "e441dcd0caa5c4e0ebad9545920a576a02dcaf3f14438f2b8eafc8b15d40c9de58bd782f4ef2b668ac4098d1b7461d12497f89f4a26f870fd0bbeb83d0a4a74c",
     "algorithm": "sha512",
-    "filename": "grcov-linux-standalone-x86_64.tar.bz2",
+    "filename": "grcov-linux-x86_64.tar.bz2",
     "unpack": false
   }
 ]
new file mode 100644
--- /dev/null
+++ b/testing/config/tooltool-manifests/macosx64/ccov.manifest
@@ -0,0 +1,9 @@
+[
+  {
+    "size": 874769,
+    "visibility": "public",
+    "digest": "edab0125906d6258f752cbebb2129e6f0719179c23eb910340f71efb2623f82b55edd525063d9ab39d338b9fa438316ca689de02cee4f284bc459df14b400bbe",
+    "algorithm": "sha512",
+    "filename": "grcov-osx-x86_64.tar.bz2"
+  }
+]
--- a/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
+++ b/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
@@ -15,21 +15,16 @@ import uuid
 import mozinfo
 from mozharness.base.script import (
     PreScriptAction,
     PostScriptAction,
 )
 from mozharness.mozilla.testing.per_test_base import SingleTestMixin
 
 
-_here = os.path.abspath(os.path.dirname(__file__))
-_tooltool_path = os.path.normpath(os.path.join(_here, '..', '..', '..',
-                                               'external_tools',
-                                               'tooltool.py'))
-
 code_coverage_config_options = [
     [["--code-coverage"],
      {"action": "store_true",
       "dest": "code_coverage",
       "default": False,
       "help": "Whether gcov c++ code coverage should be run."
       }],
     [["--per-test-coverage"],
@@ -98,50 +93,57 @@ class CodeCoverageMixin(SingleTestMixin)
         except (AttributeError, KeyError, TypeError):
             return False
 
     @PostScriptAction('download-and-extract')
     def setup_coverage_tools(self, action, success=None):
         if not self.code_coverage_enabled:
             return
 
-        if mozinfo.os == 'linux':
+        if mozinfo.os == 'linux' or mozinfo.os == 'mac':
             self.prefix = '/builds/worker/workspace/build/src/'
             strip_count = self.prefix.count('/')
         elif mozinfo.os == 'win':
             self.prefix = 'z:/build/build/src/'
             # Add 1 as on Windows the path where the compiler tries to write the
             # gcda files has an additional 'obj-firefox' component.
             strip_count = self.prefix.count('/') + 1
+        else:
+            raise Exception('Unexpected OS: {}'.format(mozinfo.os))
 
         os.environ['GCOV_PREFIX_STRIP'] = str(strip_count)
 
         # Install grcov on the test machine
         # Get the path to the build machines gcno files.
         self.url_to_gcno = self.query_build_dir_url('target.code-coverage-gcno.zip')
         self.url_to_chrome_map = self.query_build_dir_url('chrome-map.json')
         dirs = self.query_abs_dirs()
 
         # Create the grcov directory, get the tooltool manifest, and finally
         # download and unpack the grcov binary.
         self.grcov_dir = tempfile.mkdtemp()
 
         if mozinfo.os == 'linux':
             platform = 'linux64'
-            tar_file = 'grcov-linux-standalone-x86_64.tar.bz2'
+            tar_file = 'grcov-linux-x86_64.tar.bz2'
         elif mozinfo.os == 'win':
             platform = 'win32'
             tar_file = 'grcov-win-i686.tar.bz2'
+        elif mozinfo.os == 'mac':
+            platform = 'macosx64'
+            tar_file = 'grcov-osx-x86_64.tar.bz2'
 
         manifest = os.path.join(dirs.get('abs_test_install_dir', os.path.join(dirs['abs_work_dir'], 'tests')), \
             'config/tooltool-manifests/%s/ccov.manifest' % platform)
 
-        cmd = [sys.executable, _tooltool_path, '--url', 'https://tooltool.mozilla-releng.net/', 'fetch', \
-            '-m', manifest, '-o', '-c', '/builds/worker/tooltool-cache']
-        self.run_command(cmd, cwd=self.grcov_dir)
+        self.tooltool_fetch(
+            manifest=manifest,
+            output_dir=self.grcov_dir,
+            cache=self.config.get('tooltool_cache')
+        )
 
         with tarfile.open(os.path.join(self.grcov_dir, tar_file)) as tar:
             tar.extractall(self.grcov_dir)
 
         # Download the gcno archive from the build machine.
         self.download_file(self.url_to_gcno, parent_dir=self.grcov_dir)
 
         # Download the chrome-map.json file from the build machine.
@@ -274,17 +276,17 @@ class CodeCoverageMixin(SingleTestMixin)
         ]
 
         if 'coveralls' in output_format:
             grcov_command += ['--token', 'UNUSED', '--commit-sha', 'UNUSED']
 
         if merge:
             grcov_command += [jsvm_output_file]
 
-        if mozinfo.os == 'win':
+        if mozinfo.os == 'win' or mozinfo.os == 'mac':
             grcov_command += ['--llvm']
 
         if filter_covered:
             grcov_command += ['--filter', 'covered']
 
         # 'grcov_output' will be a tuple, the first variable is the path to the lcov output,
         # the other is the path to the standard error output.
         tmp_output_file, _ = self.get_output_from_command(
--- a/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini
+++ b/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_001.html.ini
@@ -1,9 +1,6 @@
 [drawFocusIfNeeded_001.html]
   disabled:
     if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1092458
   [drawFocusIfNeeded draws a focus ring.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
       if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
-
--- a/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini
+++ b/testing/web-platform/meta/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_005.html.ini
@@ -1,9 +1,6 @@
 [drawFocusIfNeeded_005.html]
   disabled:
     if os == "win": https://bugzilla.mozilla.org/show_bug.cgi?id=1092458
   [drawFocusIfNeeded does draw a focus ring if the element is in focus and the user activated a particular focus ring.]
     expected:
-      if not debug and (os == "win") and (version == "6.2.9200") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
       if not debug and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
-
--- a/testing/web-platform/meta/content-security-policy/generic/generic-0_10.html.ini
+++ b/testing/web-platform/meta/content-security-policy/generic/generic-0_10.html.ini
@@ -1,9 +1,4 @@
 [generic-0_10.html]
   disabled:
     if not debug and (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1131091
-  expected:
-    if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT
-  [Violation report status OK.]
-    expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): TIMEOUT
 
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-7_1.html.ini
+++ b/testing/web-platform/meta/content-security-policy/media-src/media-src-7_1.html.ini
@@ -1,12 +1,3 @@
 [media-src-7_1.html]
   disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1124091
-  [In-policy async video src]
-    expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
 
-  [In-policy async video source element]
-    expected:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-      if debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/content-security-policy/media-src/media-src-redir-bug.sub.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[media-src-redir-bug.sub.html]
-  expected:
-    if (os == "win") and (version == "5.1.2600"): TIMEOUT
-  [In-policy async video src]
-    expected:
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [in-policy async video src w/redir]
-    expected:
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [In-policy async video source element]
-    expected:
-      if (os == "win") and (version == "5.1.2600"): FAIL
-
-  [In-policy async video source element w/redir]
-    expected:
-      if (os == "win") and (version == "5.1.2600"): NOTRUN
-
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -42,17 +42,16 @@ skip-if = os == "linux"
 [test_1085291.js]
 [test_1105208.js]
 [test_1105866.js]
 [test_adaptive.js]
 [test_adaptive_bug527311.js]
 [test_annotations.js]
 [test_asyncExecuteLegacyQueries.js]
 [test_async_transactions.js]
-skip-if = (os == "win" && os_version == "5.1") # Bug 1158887
 [test_bookmarks_json.js]
 [test_bookmarks_json_corrupt.js]
 [test_bookmarks_html.js]
 [test_bookmarks_html_corrupt.js]
 [test_bookmarks_html_escape_entities.js]
 [test_bookmarks_html_import_tags.js]
 [test_bookmarks_html_singleframe.js]
 [test_bookmarks_restore_notification.js]
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -13779,10 +13779,30 @@
     "alert_emails": ["mnakano@mozilla.com"],
     "bug_numbers": [1452538,1449564],
     "expires_in_version": "65",
     "kind": "linear",
     "high": 50,
     "n_buckets": 20,
     "releaseChannelCollection": "opt-out",
     "description": "Number of HTML editors whose inline table editing UI is actually used by users."
+  },
+  "ACTIVE_DOCGROUPS_PER_TABGROUP": {
+    "record_in_processes": ["content"],
+    "alert_emails": ["farre@mozilla.com"],
+    "bug_numbers": [1441972],
+    "expires_in_version": "67",
+    "kind": "exponential",
+    "high": 50,
+    "n_buckets": 20,
+    "description": "Number of active doc groups per tab group. Collected at the point when the top level document of the tab group is unloaded."
+  },
+  "TOTAL_DOCGROUPS_PER_TABGROUP": {
+    "record_in_processes": ["content"],
+    "alert_emails": ["farre@mozilla.com"],
+    "bug_numbers": [1441972],
+    "expires_in_version": "67",
+    "kind": "exponential",
+    "high": 50,
+    "n_buckets": 20,
+    "description": "Total number of doc groups per tab group, including docgroups fully in bfcache. Collected at the point when the top level document of the tab group is unloaded."
   }
 }
--- a/toolkit/components/terminator/nsTerminator.cpp
+++ b/toolkit/components/terminator/nsTerminator.cpp
@@ -418,16 +418,35 @@ nsTerminator::StartWatchdog()
   // AsyncShutdown.
   if (crashAfterMS > INT32_MAX - ADDITIONAL_WAIT_BEFORE_CRASH_MS) {
     // Defend against overflow
     crashAfterMS = INT32_MAX;
   } else {
     crashAfterMS += ADDITIONAL_WAIT_BEFORE_CRASH_MS;
   }
 
+# ifdef MOZ_VALGRIND
+  // If we're running on Valgrind, we'll be making forward progress at a
+  // rate of somewhere between 1/25th and 1/50th of normal.  This can cause
+  // timeouts frequently enough to be a problem for the Valgrind runs on
+  // automation: see bug 1296819.  As an attempt to avoid the worst of this,
+  // scale up the presented timeout by a factor of three.  For a
+  // non-Valgrind-enabled build, or for an enabled build which isn't running
+  // on Valgrind, the timeout is unchanged.
+  if (RUNNING_ON_VALGRIND) {
+    const int32_t scaleUp = 3;
+    if (crashAfterMS >= (INT32_MAX / scaleUp) - 1) {
+      // Defend against overflow
+      crashAfterMS = INT32_MAX;
+    } else {
+      crashAfterMS *= scaleUp;
+    }
+  }
+# endif
+
   UniquePtr<Options> options(new Options());
   const PRIntervalTime ticksDuration = PR_MillisecondsToInterval(1000);
   options->crashAfterTicks = crashAfterMS / ticksDuration;
   // Handle systems where ticksDuration is greater than crashAfterMS.
   if (options->crashAfterTicks == 0) {
     options->crashAfterTicks = crashAfterMS / 1000;
   }
 
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3760,21 +3760,21 @@ namespace mozilla {
 
 static void SetShutdownChecks() {
   // Set default first. On debug builds we crash. On nightly and local
   // builds we record. Nightlies will then send the info via telemetry,
   // but it is usefull to have the data in about:telemetry in local builds
   // too.
 
 #ifdef DEBUG
-#if defined(MOZ_CODE_COVERAGE) && defined(XP_WIN)
+#if defined(MOZ_CODE_COVERAGE)
   gShutdownChecks = SCM_NOTHING;
 #else
   gShutdownChecks = SCM_CRASH;
-#endif // MOZ_CODE_COVERAGE && XP_WIN
+#endif // MOZ_CODE_COVERAGE
 #else
   const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL);
   if (strcmp(releaseChannel, "nightly") == 0 ||
       strcmp(releaseChannel, "default") == 0) {
     gShutdownChecks = SCM_RECORD;
   } else {
     gShutdownChecks = SCM_NOTHING;
   }
--- a/tools/code-coverage/CodeCoverageHandler.h
+++ b/tools/code-coverage/CodeCoverageHandler.h
@@ -18,17 +18,17 @@ public:
   static CodeCoverageHandler* Get();
   CrossProcessMutex* GetMutex();
   CrossProcessMutexHandle GetMutexHandle(int aProcId);
   static void DumpCounters(int);
   static void ResetCounters(int);
 
 private:
   CodeCoverageHandler();
-  CodeCoverageHandler(const CrossProcessMutexHandle& aHandle);
+  explicit CodeCoverageHandler(const CrossProcessMutexHandle& aHandle);
 
   static StaticAutoPtr<CodeCoverageHandler> instance;
   CrossProcessMutex mGcovLock;
 
   DISALLOW_COPY_AND_ASSIGN(CodeCoverageHandler);
 
   void SetSignalHandlers();
 };
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -106,19 +106,20 @@
 // Android builds use the ARM Exception Handling ABI to unwind.
 #if defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
 # define HAVE_NATIVE_UNWIND
 # define USE_EHABI_STACKWALK
 # include "EHABIStackWalk.h"
 #endif
 
 // Linux builds use LUL, which uses DWARF info to unwind stacks.
-#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
-    defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \
-    defined(GP_PLAT_arm64_android)
+#if defined(GP_PLAT_amd64_linux) || \
+    defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android) || \
+    defined(GP_PLAT_mips64_linux) || \
+    defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android)
 # define HAVE_NATIVE_UNWIND
 # define USE_LUL_STACKWALK
 # include "lul/LulMain.h"
 # include "lul/platform-linux-lul.h"
 
 // On linux we use LUL for periodic samples and synchronous samples, but we use
 // FramePointerStackWalk for backtrace samples when MOZ_PROFILING is enabled.
 // (See the comment at the top of the file for a definition of
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -8,30 +8,16 @@
 
 #include <stdint.h>
 
 #include "mozilla/BasicEvents.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "nsCOMPtr.h"
 
-/******************************************************************************
- * nsDragDropEventStatus
- ******************************************************************************/
-
-enum nsDragDropEventStatus
-{  
-  // The event is a enter
-  nsDragDropEventStatus_eDragEntered,
-  // The event is exit
-  nsDragDropEventStatus_eDragExited,
-  // The event is drop
-  nsDragDropEventStatus_eDrop
-};
-
 namespace mozilla {
 
 namespace dom {
   class PBrowserParent;
   class PBrowserChild;
 } // namespace dom
 
 class WidgetPointerEvent;
--- a/xpcom/ds/IncrementalTokenizer.h
+++ b/xpcom/ds/IncrementalTokenizer.h
@@ -11,17 +11,17 @@
 
 #include "nsError.h"
 #include <functional>
 
 class nsIInputStream;
 
 namespace mozilla {
 
-class IncrementalTokenizer : public TokenizerBase
+class IncrementalTokenizer : public TokenizerBase<char>
 {
 public:
   /**
    * The consumer callback.  The function is called for every single token
    * as found in the input.  Failure result returned by this callback stops
    * the tokenization immediately and bubbles to result of Feed/FinishInput.
    *
    * Fragment()s of consumed tokens are ensured to remain valid until next call to
--- a/xpcom/ds/Tokenizer.cpp
+++ b/xpcom/ds/Tokenizer.cpp
@@ -6,317 +6,353 @@
 
 #include "Tokenizer.h"
 
 #include "nsUnicharUtils.h"
 #include <algorithm>
 
 namespace mozilla {
 
-static const char sWhitespaces[] = " \t";
+template<>
+char const TokenizerBase<char>::sWhitespaces[] = { ' ', '\t', 0 };
+template<>
+char16_t const TokenizerBase<char16_t>::sWhitespaces[3] = { ' ', '\t', 0 };
 
-Tokenizer::Tokenizer(const nsACString& aSource,
-                     const char* aWhitespaces,
-                     const char* aAdditionalWordChars)
-  : TokenizerBase(aWhitespaces, aAdditionalWordChars)
+template<typename TChar>
+static bool
+contains(TChar const* const list, TChar const needle)
 {
-  mInputFinished = true;
-  aSource.BeginReading(mCursor);
-  mRecord = mRollback = mCursor;
-  aSource.EndReading(mEnd);
+  for (TChar const *c = list; *c; ++c) {
+    if (needle == *c) {
+      return true;
+    }
+  }
+  return false;
 }
 
-Tokenizer::Tokenizer(const char* aSource,
-                     const char* aWhitespaces,
-                     const char* aAdditionalWordChars)
-  : Tokenizer(nsDependentCString(aSource), aWhitespaces, aAdditionalWordChars)
+template<typename TChar>
+TTokenizer<TChar>::TTokenizer(const typename base::TAString& aSource,
+                              const TChar* aWhitespaces,
+                              const TChar* aAdditionalWordChars)
+  : TokenizerBase<TChar>(aWhitespaces, aAdditionalWordChars)
+{
+  base::mInputFinished = true;
+  aSource.BeginReading(base::mCursor);
+  mRecord = mRollback = base::mCursor;
+  aSource.EndReading(base::mEnd);
+}
+
+template<typename TChar>
+TTokenizer<TChar>::TTokenizer(const TChar* aSource,
+                              const TChar* aWhitespaces,
+                              const TChar* aAdditionalWordChars)
+  : TTokenizer(typename base::TDependentString(aSource), aWhitespaces, aAdditionalWordChars)
 {
 }
 
+template<typename TChar>
 bool
-Tokenizer::Next(Token& aToken)
+TTokenizer<TChar>::Next(typename base::Token& aToken)
 {
-  if (!HasInput()) {
-    mHasFailed = true;
+  if (!base::HasInput()) {
+    base::mHasFailed = true;
     return false;
   }
 
-  mRollback = mCursor;
-  mCursor = Parse(aToken);
+  mRollback = base::mCursor;
+  base::mCursor = base::Parse(aToken);
 
-  AssignFragment(aToken, mRollback, mCursor);
+  base::AssignFragment(aToken, mRollback, base::mCursor);
 
-  mPastEof = aToken.Type() == TOKEN_EOF;
-  mHasFailed = false;
+  base::mPastEof = aToken.Type() == base::TOKEN_EOF;
+  base::mHasFailed = false;
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::Check(const TokenType aTokenType, Token& aResult)
+TTokenizer<TChar>::Check(const typename base::TokenType aTokenType, typename base::Token& aResult)
 {
-  if (!HasInput()) {
-    mHasFailed = true;
+  if (!base::HasInput()) {
+    base::mHasFailed = true;
     return false;
   }
 
-  nsACString::const_char_iterator next = Parse(aResult);
+  typename base::TAString::const_char_iterator next = base::Parse(aResult);
   if (aTokenType != aResult.Type()) {
-    mHasFailed = true;
+    base::mHasFailed = true;
     return false;
   }
 
-  mRollback = mCursor;
-  mCursor = next;
+  mRollback = base::mCursor;
+  base::mCursor = next;
 
-  AssignFragment(aResult, mRollback, mCursor);
+  base::AssignFragment(aResult, mRollback, base::mCursor);
 
-  mPastEof = aResult.Type() == TOKEN_EOF;
-  mHasFailed = false;
+  base::mPastEof = aResult.Type() == base::TOKEN_EOF;
+  base::mHasFailed = false;
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::Check(const Token& aToken)
+TTokenizer<TChar>::Check(const typename base::Token& aToken)
 {
-  if (!HasInput()) {
-    mHasFailed = true;
+  if (!base::HasInput()) {
+    base::mHasFailed = true;
     return false;
   }
 
-  Token parsed;
-  nsACString::const_char_iterator next = Parse(parsed);
+  typename base::Token parsed;
+  typename base::TAString::const_char_iterator next = base::Parse(parsed);
   if (!aToken.Equals(parsed)) {
-    mHasFailed = true;
+    base::mHasFailed = true;
     return false;
   }
 
-  mRollback = mCursor;
-  mCursor = next;
-  mPastEof = parsed.Type() == TOKEN_EOF;
-  mHasFailed = false;
+  mRollback = base::mCursor;
+  base::mCursor = next;
+  base::mPastEof = parsed.Type() == base::TOKEN_EOF;
+  base::mHasFailed = false;
   return true;
 }
 
+template<typename TChar>
 void
-Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines)
+TTokenizer<TChar>::SkipWhites(WhiteSkipping aIncludeNewLines)
 {
   if (!CheckWhite() && (aIncludeNewLines == DONT_INCLUDE_NEW_LINE || !CheckEOL())) {
     return;
   }
 
-  nsACString::const_char_iterator rollback = mRollback;
+  typename base::TAString::const_char_iterator rollback = mRollback;
   while (CheckWhite() || (aIncludeNewLines == INCLUDE_NEW_LINE && CheckEOL())) {
   }
 
-  mHasFailed = false;
+  base::mHasFailed = false;
   mRollback = rollback;
 }
 
+template<typename TChar>
 void
-Tokenizer::SkipUntil(Token const& aToken)
+TTokenizer<TChar>::SkipUntil(typename base::Token const& aToken)
 {
-  nsACString::const_char_iterator rollback = mCursor;
-  const Token eof = Token::EndOfFile();
+  typename base::TAString::const_char_iterator rollback = base::mCursor;
+  const typename base::Token eof = base::Token::EndOfFile();
 
-  Token t;
+  typename base::Token t;
   while (Next(t)) {
     if (aToken.Equals(t) || eof.Equals(t)) {
       Rollback();
       break;
     }
   }
 
   mRollback = rollback;
 }
 
+template<typename TChar>
 bool
-Tokenizer::CheckChar(bool (*aClassifier)(const char aChar))
+TTokenizer<TChar>::CheckChar(bool (*aClassifier)(const TChar aChar))
 {
   if (!aClassifier) {
     MOZ_ASSERT(false);
     return false;
   }
 
-  if (!HasInput() || mCursor == mEnd) {
-    mHasFailed = true;
+  if (!base::HasInput() || base::mCursor == base::mEnd) {
+    base::mHasFailed = true;
     return false;
   }
 
-  if (!aClassifier(*mCursor)) {
-    mHasFailed = true;
+  if (!aClassifier(*base::mCursor)) {
+    base::mHasFailed = true;
     return false;
   }
 
-  mRollback = mCursor;
-  ++mCursor;
-  mHasFailed = false;
+  mRollback = base::mCursor;
+  ++base::mCursor;
+  base::mHasFailed = false;
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadChar(char* aValue)
+TTokenizer<TChar>::ReadChar(TChar* aValue)
 {
   MOZ_RELEASE_ASSERT(aValue);
 
-  Token t;
-  if (!Check(TOKEN_CHAR, t)) {
+  typename base::Token t;
+  if (!Check(base::TOKEN_CHAR, t)) {
     return false;
   }
 
   *aValue = t.AsChar();
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadChar(bool (*aClassifier)(const char aChar), char* aValue)
+TTokenizer<TChar>::ReadChar(bool (*aClassifier)(const TChar aChar), TChar* aValue)
 {
   MOZ_RELEASE_ASSERT(aValue);
 
   if (!CheckChar(aClassifier)) {
     return false;
   }
 
   *aValue = *mRollback;
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadWord(nsACString& aValue)
+TTokenizer<TChar>::ReadWord(typename base::TAString& aValue)
 {
-  Token t;
-  if (!Check(TOKEN_WORD, t)) {
+  typename base::Token t;
+  if (!Check(base::TOKEN_WORD, t)) {
     return false;
   }
 
   aValue.Assign(t.AsString());
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadWord(nsDependentCSubstring& aValue)
+TTokenizer<TChar>::ReadWord(typename base::TDependentSubstring& aValue)
 {
-  Token t;
-  if (!Check(TOKEN_WORD, t)) {
+  typename base::Token t;
+  if (!Check(base::TOKEN_WORD, t)) {
     return false;
   }
 
   aValue.Rebind(t.AsString().BeginReading(), t.AsString().Length());
   return true;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadUntil(Token const& aToken, nsACString& aResult, ClaimInclusion aInclude)
+TTokenizer<TChar>::ReadUntil(typename base::Token const& aToken, typename base::TAString& aResult, ClaimInclusion aInclude)
 {
-  nsDependentCSubstring substring;
+  typename base::TDependentSubstring substring;
   bool rv = ReadUntil(aToken, substring, aInclude);
   aResult.Assign(substring);
   return rv;
 }
 
+template<typename TChar>
 bool
-Tokenizer::ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, ClaimInclusion aInclude)
+TTokenizer<TChar>::ReadUntil(typename base::Token const& aToken, typename base::TDependentSubstring& aResult, ClaimInclusion aInclude)
 {
-  nsACString::const_char_iterator record = mRecord;
+  typename base::TAString::const_char_iterator record = mRecord;
   Record();
-  nsACString::const_char_iterator rollback = mRollback = mCursor;
+  typename base::TAString::const_char_iterator rollback = mRollback = base::mCursor;
 
   bool found = false;
-  Token t;
+  typename base::Token t;
   while (Next(t)) {
     if (aToken.Equals(t)) {
       found = true;
       break;
     }
-    if (t.Equals(Token::EndOfFile())) {
+    if (t.Equals(base::Token::EndOfFile())) {
       // We don't want to eat it.
       Rollback();
       break;
     }
   }
 
   Claim(aResult, aInclude);
   mRollback = rollback;
   mRecord = record;
   return found;
 }
 
+template<typename TChar>
 void
-Tokenizer::Rollback()
+TTokenizer<TChar>::Rollback()
 {
-  MOZ_ASSERT(mCursor > mRollback || mPastEof,
-             "Tokenizer::Rollback() cannot use twice or before any parsing");
+  MOZ_ASSERT(base::mCursor > mRollback || base::mPastEof, "TODO!!!");
 
-  mPastEof = false;
-  mHasFailed = false;
-  mCursor = mRollback;
+  base::mPastEof = false;
+  base::mHasFailed = false;
+  base::mCursor = mRollback;
 }
 
+template<typename TChar>
 void
-Tokenizer::Record(ClaimInclusion aInclude)
+TTokenizer<TChar>::Record(ClaimInclusion aInclude)
 {
   mRecord = aInclude == INCLUDE_LAST
     ? mRollback
-    : mCursor;
+    : base::mCursor;
 }
 
+template<typename TChar>
 void
-Tokenizer::Claim(nsACString& aResult, ClaimInclusion aInclusion)
+TTokenizer<TChar>::Claim(typename base::TAString& aResult, ClaimInclusion aInclusion)
 {
-  nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
+  typename base::TAString::const_char_iterator close = aInclusion == EXCLUDE_LAST
     ? mRollback
-    : mCursor;
+    : base::mCursor;
   aResult.Assign(Substring(mRecord, close));
 }
 
+template<typename TChar>
 void
-Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion)
+TTokenizer<TChar>::Claim(typename base::TDependentSubstring& aResult, ClaimInclusion aInclusion)
 {
-  nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
+  typename base::TAString::const_char_iterator close = aInclusion == EXCLUDE_LAST
     ? mRollback
-    : mCursor;
+    : base::mCursor;
 
   MOZ_RELEASE_ASSERT(close >= mRecord, "Overflow!");
   aResult.Rebind(mRecord, close - mRecord);
 }
 
 // TokenizerBase
 
-TokenizerBase::TokenizerBase(const char* aWhitespaces,
-                             const char* aAdditionalWordChars)
+template<typename TChar>
+TokenizerBase<TChar>::TokenizerBase(const TChar* aWhitespaces,
+                                    const TChar* aAdditionalWordChars)
   : mPastEof(false)
   , mHasFailed(false)
   , mInputFinished(true)
   , mMode(Mode::FULL)
   , mMinRawDelivery(1024)
   , mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
   , mAdditionalWordChars(aAdditionalWordChars)
   , mCursor(nullptr)
   , mEnd(nullptr)
   , mNextCustomTokenID(TOKEN_CUSTOM0)
 {
 }
 
-TokenizerBase::Token
-TokenizerBase::AddCustomToken(const nsACString & aValue,
-                              ECaseSensitivity aCaseInsensitivity, bool aEnabled)
+template<typename TChar>
+auto
+TokenizerBase<TChar>::AddCustomToken(const TAString & aValue,
+                                     ECaseSensitivity aCaseInsensitivity, bool aEnabled)
+  -> Token
 {
   MOZ_ASSERT(!aValue.IsEmpty());
 
   UniquePtr<Token>& t = *mCustomTokens.AppendElement();
   t = MakeUnique<Token>();
 
   t->mType = static_cast<TokenType>(++mNextCustomTokenID);
   t->mCustomCaseInsensitivity = aCaseInsensitivity;
   t->mCustomEnabled = aEnabled;
   t->mCustom.Assign(aValue);
   return *t;
 }
 
+template<typename TChar>
 void
-TokenizerBase::RemoveCustomToken(Token& aToken)
+TokenizerBase<TChar>::RemoveCustomToken(Token& aToken)
 {
   if (aToken.mType == TOKEN_UNKNOWN) {
     // Already removed
     return;
   }
 
   for (UniquePtr<Token> const& custom : mCustomTokens) {
     if (custom->mType == aToken.mType) {
@@ -324,18 +360,19 @@ TokenizerBase::RemoveCustomToken(Token& 
       aToken.mType = TOKEN_UNKNOWN;
       return;
     }
   }
 
   MOZ_ASSERT(false, "Token to remove not found");
 }
 
+template<typename TChar>
 void
-TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled)
+TokenizerBase<TChar>::EnableCustomToken(Token const& aToken, bool aEnabled)
 {
   if (aToken.mType == TOKEN_UNKNOWN) {
     // Already removed
     return;
   }
 
   for (UniquePtr<Token> const& custom : mCustomTokens) {
     if (custom->Type() == aToken.Type()) {
@@ -343,63 +380,68 @@ TokenizerBase::EnableCustomToken(Token c
       custom->mCustomEnabled = aEnabled;
       return;
     }
   }
 
   MOZ_ASSERT(false, "Token to change not found");
 }
 
+template<typename TChar>
 void
-TokenizerBase::SetTokenizingMode(Mode aMode)
+TokenizerBase<TChar>::SetTokenizingMode(Mode aMode)
 {
   mMode = aMode;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::HasFailed() const
+TokenizerBase<TChar>::HasFailed() const
 {
   return mHasFailed;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::HasInput() const
+TokenizerBase<TChar>::HasInput() const
 {
   return !mPastEof;
 }
 
-nsACString::const_char_iterator
-TokenizerBase::Parse(Token& aToken) const
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Parse(Token& aToken) const
+  -> typename TAString::const_char_iterator
 {
   if (mCursor == mEnd) {
     if (!mInputFinished) {
       return mCursor;
     }
 
     aToken = Token::EndOfFile();
     return mEnd;
   }
 
   MOZ_RELEASE_ASSERT(mEnd >= mCursor, "Overflow!");
-  nsACString::size_type available = mEnd - mCursor;
+  typename TAString::size_type available = mEnd - mCursor;
 
   uint32_t longestCustom = 0;
   for (UniquePtr<Token> const& custom : mCustomTokens) {
     if (IsCustom(mCursor, *custom, &longestCustom)) {
       aToken = *custom;
       return mCursor + custom->mCustom.Length();
     }
   }
 
   if (!mInputFinished && available < longestCustom) {
     // Not enough data to deterministically decide.
     return mCursor;
   }
 
-  nsACString::const_char_iterator next = mCursor;
+  typename TAString::const_char_iterator next = mCursor;
 
   if (mMode == Mode::CUSTOM_ONLY) {
     // We have to do a brute-force search for all of the enabled custom
     // tokens.
     while (next < mEnd) {
       ++next;
       for (UniquePtr<Token> const& custom : mCustomTokens) {
         if (IsCustom(next, *custom)) {
@@ -436,17 +478,17 @@ TokenizerBase::Parse(Token& aToken) cons
     PARSE_WS,
     PARSE_CHAR,
   } state;
 
   if (IsWordFirst(*next)) {
     state = PARSE_WORD;
   } else if (IsNumber(*next)) {
     state = PARSE_INTEGER;
-  } else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
+  } else if (contains(mWhitespaces, *next)) { // not UTF-8 friendly?
     state = PARSE_WS;
   } else if (*next == '\r') {
     state = PARSE_CRLF;
   } else if (*next == '\n') {
     state = PARSE_LF;
   } else {
     state = PARSE_CHAR;
   }
@@ -512,55 +554,69 @@ TokenizerBase::Parse(Token& aToken) cons
       return next;
     } // switch (state)
   } // while (next < end)
 
   MOZ_ASSERT(!mInputFinished);
   return mCursor;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const
+TokenizerBase<TChar>::IsEnd(const typename TAString::const_char_iterator& caret) const
 {
   return caret == mEnd;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const
+TokenizerBase<TChar>::IsPending(const typename TAString::const_char_iterator& caret) const
 {
   return IsEnd(caret) && !mInputFinished;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::IsWordFirst(const char aInput) const
+TokenizerBase<TChar>::IsWordFirst(const TChar aInput) const
 {
   // TODO: make this fully work with unicode
   return (ToLowerCase(static_cast<uint32_t>(aInput)) !=
           ToUpperCase(static_cast<uint32_t>(aInput))) ||
           '_' == aInput ||
-          (mAdditionalWordChars ? !!strchr(mAdditionalWordChars, aInput) : false);
+          (mAdditionalWordChars ? contains(mAdditionalWordChars, aInput) : false);
 }
 
+template<typename TChar>
 bool
-TokenizerBase::IsWord(const char aInput) const
+TokenizerBase<TChar>::IsWord(const TChar aInput) const
 {
   return IsWordFirst(aInput) || IsNumber(aInput);
 }
 
+template<typename TChar>
 bool
-TokenizerBase::IsNumber(const char aInput) const
+TokenizerBase<TChar>::IsNumber(const TChar aInput) const
 {
   // TODO: are there unicode numbers?
   return aInput >= '0' && aInput <= '9';
 }
 
+namespace {
+
+template<typename TChar> class TCharComparator;
+template<> class TCharComparator<char> final : public nsCaseInsensitiveUTF8StringComparator {};
+template<> class TCharComparator<char16_t> final : public nsCaseInsensitiveStringComparator {};
+
+}
+
+template<typename TChar>
 bool
-TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret,
-                        const Token & aCustomToken,
-                        uint32_t * aLongest) const
+TokenizerBase<TChar>::IsCustom(const typename TAString::const_char_iterator & caret,
+                               const Token & aCustomToken,
+                               uint32_t * aLongest) const
 {
   MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0);
   if (!aCustomToken.mCustomEnabled) {
     return false;
   }
 
   if (aLongest) {
     *aLongest = std::max(*aLongest, aCustomToken.mCustom.Length());
@@ -570,153 +626,169 @@ TokenizerBase::IsCustom(const nsACString
   // and since it's on a hot path, it's just a diagnostic assert,
   // not a release assert.
   MOZ_DIAGNOSTIC_ASSERT(mEnd >= caret, "Overflow?");
   uint32_t inputLength = mEnd - caret;
   if (aCustomToken.mCustom.Length() > inputLength) {
     return false;
   }
 
-  nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length());
+  TDependentSubstring inputFragment(caret, aCustomToken.mCustom.Length());
   if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) {
-    return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator());
+    return inputFragment.Equals(aCustomToken.mCustom, TCharComparator<TChar>());
   }
   return inputFragment.Equals(aCustomToken.mCustom);
 }
 
-void TokenizerBase::AssignFragment(Token& aToken,
-                                   nsACString::const_char_iterator begin,
-                                   nsACString::const_char_iterator end)
+template<typename TChar>
+void
+TokenizerBase<TChar>::AssignFragment(Token& aToken,
+                                     typename TAString::const_char_iterator begin,
+                                     typename TAString::const_char_iterator end)
 {
   aToken.AssignFragment(begin, end);
 }
 
 // TokenizerBase::Token
 
-TokenizerBase::Token::Token()
+template<typename TChar>
+TokenizerBase<TChar>::Token::Token()
   : mType(TOKEN_UNKNOWN)
   , mChar(0)
   , mInteger(0)
   , mCustomCaseInsensitivity(CASE_SENSITIVE)
   , mCustomEnabled(false)
 {
 }
 
-TokenizerBase::Token::Token(const Token& aOther)
+template<typename TChar>
+TokenizerBase<TChar>::Token::Token(const Token& aOther)
   : mType(aOther.mType)
   , mCustom(aOther.mCustom)
   , mChar(aOther.mChar)
   , mInteger(aOther.mInteger)
   , mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity)
   , mCustomEnabled(aOther.mCustomEnabled)
 {
   if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) {
     mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
   }
 }
 
-TokenizerBase::Token&
-TokenizerBase::Token::operator=(const Token& aOther)
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::operator=(const Token& aOther)
+  -> Token&
 {
   mType = aOther.mType;
   mCustom = aOther.mCustom;
   mChar = aOther.mChar;
   mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
   mInteger = aOther.mInteger;
   mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity;
   mCustomEnabled = aOther.mCustomEnabled;
   return *this;
 }
 
+template<typename TChar>
 void
-TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin,
-                                     nsACString::const_char_iterator end)
+TokenizerBase<TChar>::Token::AssignFragment(typename TAString::const_char_iterator begin,
+                                     typename TAString::const_char_iterator end)
 {
   MOZ_RELEASE_ASSERT(end >= begin, "Overflow!");
   mFragment.Rebind(begin, end - begin);
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Raw()
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Raw() -> Token
 {
   Token t;
   t.mType = TOKEN_RAW;
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Word(const nsACString& aValue)
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Word(TAString const& aValue) -> Token
 {
   Token t;
   t.mType = TOKEN_WORD;
   t.mWord.Rebind(aValue.BeginReading(), aValue.Length());
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Char(const char aValue)
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Char(TChar const aValue) -> Token
 {
   Token t;
   t.mType = TOKEN_CHAR;
   t.mChar = aValue;
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Number(const uint64_t aValue)
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Number(uint64_t const aValue) -> Token
 {
   Token t;
   t.mType = TOKEN_INTEGER;
   t.mInteger = aValue;
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Whitespace()
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Whitespace() -> Token
 {
   Token t;
   t.mType = TOKEN_WS;
   t.mChar = '\0';
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::NewLine()
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::NewLine() -> Token
 {
   Token t;
   t.mType = TOKEN_EOL;
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::EndOfFile()
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::EndOfFile() -> Token
 {
   Token t;
   t.mType = TOKEN_EOF;
   return t;
 }
 
 // static
-TokenizerBase::Token
-TokenizerBase::Token::Error()
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::Error() -> Token
 {
   Token t;
   t.mType = TOKEN_ERROR;
   return t;
 }
 
+template<typename TChar>
 bool
-TokenizerBase::Token::Equals(const Token& aOther) const
+TokenizerBase<TChar>::Token::Equals(const Token& aOther) const
 {
   if (mType != aOther.mType) {
     return false;
   }
 
   switch (mType) {
   case TOKEN_INTEGER:
     return AsInteger() == aOther.AsInteger();
@@ -724,30 +796,39 @@ TokenizerBase::Token::Equals(const Token
     return AsString() == aOther.AsString();
   case TOKEN_CHAR:
     return AsChar() == aOther.AsChar();
   default:
     return true;
   }
 }
 
-char
-TokenizerBase::Token::AsChar() const
+template<typename TChar>
+TChar
+TokenizerBase<TChar>::Token::AsChar() const
 {
   MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS);
   return mChar;
 }
 
-nsDependentCSubstring
-TokenizerBase::Token::AsString() const
+template<typename TChar>
+auto
+TokenizerBase<TChar>::Token::AsString() const -> TDependentSubstring
 {
   MOZ_ASSERT(mType == TOKEN_WORD);
   return mWord;
 }
 
+template<typename TChar>
 uint64_t
-TokenizerBase::Token::AsInteger() const
+TokenizerBase<TChar>::Token::AsInteger() const
 {
   MOZ_ASSERT(mType == TOKEN_INTEGER);
   return mInteger;
 }
 
+template class TokenizerBase<char>;
+template class TokenizerBase<char16_t>;
+
+template class TTokenizer<char>;
+template class TTokenizer<char16_t>;
+
 } // mozilla
--- a/xpcom/ds/Tokenizer.h
+++ b/xpcom/ds/Tokenizer.h
@@ -11,19 +11,27 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
+template <typename TChar>
 class TokenizerBase
 {
 public:
+  typedef nsTSubstring<TChar> TAString;
+  typedef nsTString<TChar> TString;
+  typedef nsTDependentString<TChar> TDependentString;
+  typedef nsTDependentSubstring<TChar> TDependentSubstring;
+
+  static TChar const sWhitespaces[];
+
   /**
    * The analyzer works with elements in the input cut to a sequence of token
    * where each token has an elementary type
    */
   enum TokenType : uint32_t
   {
     TOKEN_UNKNOWN,
     TOKEN_RAW,
@@ -40,77 +48,77 @@ public:
   enum ECaseSensitivity
   {
     CASE_SENSITIVE,
     CASE_INSENSITIVE
   };
 
   /**
    * Class holding the type and the value of a token.  It can be manually created
-   * to allow checks against it via methods of Tokenizer or are results of some of
-   * the Tokenizer's methods.
+   * to allow checks against it via methods of TTokenizer or are results of some of
+   * the TTokenizer's methods.
    */
   class Token
   {
     TokenType mType;
-    nsDependentCSubstring mWord;
-    nsCString mCustom;
-    char mChar;
+    TDependentSubstring mWord;
+    TString mCustom;
+    TChar mChar;
     uint64_t mInteger;
     ECaseSensitivity mCustomCaseInsensitivity;
     bool mCustomEnabled;
 
     // If this token is a result of the parsing process, this member is referencing
     // a sub-string in the input buffer.  If this is externally created Token this
     // member is left an empty string.
-    nsDependentCSubstring mFragment;
+    TDependentSubstring mFragment;
 
-    friend class TokenizerBase;
-    void AssignFragment(nsACString::const_char_iterator begin,
-                        nsACString::const_char_iterator end);
+    friend class TokenizerBase<TChar>;
+    void AssignFragment(typename TAString::const_char_iterator begin,
+                        typename TAString::const_char_iterator end);
 
     static Token Raw();
 
   public:
     Token();
     Token(const Token& aOther);
     Token& operator=(const Token& aOther);
 
     // Static constructors of tokens by type and value
-    static Token Word(const nsACString& aWord);
-    static Token Char(const char aChar);
-    static Token Number(const uint64_t aNumber);
+    static Token Word(TAString const& aWord);
+    static Token Char(TChar const aChar);
+    static Token Number(uint64_t const aNumber);
     static Token Whitespace();
     static Token NewLine();
     static Token EndOfFile();
     static Token Error();
 
     // Compares the two tokens, type must be identical and value
     // of one of the tokens must be 'any' or equal.
     bool Equals(const Token& aOther) const;
 
     TokenType Type() const { return mType; }
-    char AsChar() const;
-    nsDependentCSubstring AsString() const;
+    TChar AsChar() const;
+    TDependentSubstring AsString() const;
     uint64_t AsInteger() const;
 
-    nsDependentCSubstring Fragment() const { return mFragment; }
+    TDependentSubstring Fragment() const { return mFragment; }
   };
 
   /**
    * Consumers may register a custom string that, when found in the input, is considered
    * a token and returned by Next*() and accepted by Check*() methods.
    * AddCustomToken() returns a reference to a token that can then be comapred using
    * Token::Equals() againts the output from Next*() or be passed to Check*().
    */
-  Token AddCustomToken(const nsACString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true);
+  Token AddCustomToken(const TAString& aValue, ECaseSensitivity aCaseInsensitivity, bool aEnabled = true);
   template <uint32_t N>
-  Token AddCustomToken(const char(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true)
+  Token AddCustomToken(const TChar(&aValue)[N], ECaseSensitivity aCaseInsensitivity, bool aEnabled = true)
   {
-    return AddCustomToken(nsDependentCSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled);
+    return AddCustomToken(TDependentSubstring(aValue, N - 1), aCaseInsensitivity, aEnabled);
   }
   void RemoveCustomToken(Token& aToken);
   /**
    * Only applies to a custom type of a Token (see AddCustomToken above.)
    * This turns on and off token recognition.  When a custom token is disabled,
    * it's ignored as never added as a custom token.
    */
   void EnableCustomToken(Token const& aToken, bool aEnable);
@@ -131,138 +139,143 @@ public:
 
   /**
    * Return false iff the last Check*() call has returned false or when we've read past
    * the end of the input string.
    */
   MOZ_MUST_USE bool HasFailed() const;
 
 protected:
-  explicit TokenizerBase(const char* aWhitespaces = nullptr,
-                         const char* aAdditionalWordChars = nullptr);
+  explicit TokenizerBase(const TChar* aWhitespaces = nullptr,
+                         const TChar* aAdditionalWordChars = nullptr);
 
   // false if we have already read the EOF token.
   bool HasInput() const;
   // Main parsing function, it doesn't shift the read cursor, just returns the next
   // token position.
-  nsACString::const_char_iterator Parse(Token& aToken) const;
+  typename TAString::const_char_iterator Parse(Token& aToken) const;
   // Is read cursor at the end?
-  bool IsEnd(const nsACString::const_char_iterator& caret) const;
+  bool IsEnd(const typename TAString::const_char_iterator& caret) const;
   // True, when we are at the end of the input data, but it has not been marked
-  // as complete yet.  In that case we cannot proceed with providing a multi-char token.
-  bool IsPending(const nsACString::const_char_iterator & caret) const;
+  // as complete yet.  In that case we cannot proceed with providing a multi-TChar token.
+  bool IsPending(const typename TAString::const_char_iterator & caret) const;
   // Is read cursor on a character that is a word start?
-  bool IsWordFirst(const char aInput) const;
+  bool IsWordFirst(const TChar aInput) const;
   // Is read cursor on a character that is an in-word letter?
-  bool IsWord(const char aInput) const;
+  bool IsWord(const TChar aInput) const;
   // Is read cursor on a character that is a valid number?
   // TODO - support multiple radix
-  bool IsNumber(const char aInput) const;
+  bool IsNumber(const TChar aInput) const;
   // Is equal to the given custom token?
-  bool IsCustom(const nsACString::const_char_iterator& caret,
+  bool IsCustom(const typename TAString::const_char_iterator& caret,
                 const Token& aCustomToken, uint32_t* aLongest = nullptr) const;
 
   // Friendly helper to assign a fragment on a Token
   static void AssignFragment(Token& aToken,
-                             nsACString::const_char_iterator begin,
-                             nsACString::const_char_iterator end);
+                             typename TAString::const_char_iterator begin,
+                             typename TAString::const_char_iterator end);
 
   // true iff we have already read the EOF token
   bool mPastEof;
   // true iff the last Check*() call has returned false, reverts to true on Rollback() call
   bool mHasFailed;
   // true if the input string is final (finished), false when we expect more data
   // yet to be fed to the tokenizer (see IncrementalTokenizer derived class).
   bool mInputFinished;
   // custom only vs full tokenizing mode, see the Parse() method
   Mode mMode;
   // minimal raw data chunked delivery during incremental feed
   uint32_t mMinRawDelivery;
 
   // Customizable list of whitespaces
-  const char* mWhitespaces;
+  const TChar* mWhitespaces;
   // Additinal custom word characters
-  const char* mAdditionalWordChars;
+  const TChar* mAdditionalWordChars;
 
   // All these point to the original buffer passed to the constructor or to the incremental
   // buffer after FeedInput.
-  nsACString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
-  nsACString::const_char_iterator mEnd; // End of the input position
+  typename TAString::const_char_iterator mCursor; // Position of the current (actually next to read) token start
+  typename TAString::const_char_iterator mEnd; // End of the input position
 
   // This is the list of tokens user has registered with AddCustomToken()
   nsTArray<UniquePtr<Token>> mCustomTokens;
   uint32_t mNextCustomTokenID;
 
 private:
   TokenizerBase() = delete;
   TokenizerBase(const TokenizerBase&) = delete;
   TokenizerBase(TokenizerBase&&) = delete;
   TokenizerBase(const TokenizerBase&&) = delete;
   TokenizerBase &operator=(const TokenizerBase&) = delete;
 };
 
 /**
  * This is a simple implementation of a lexical analyzer or maybe better
- * called a tokenizer.  It doesn't allow any user dictionaries or
- * user define token types.
+ * called a tokenizer.
  *
- * It is limited only to ASCII input for now. UTF-8 or any other input
- * encoding must yet be implemented.
+ * Please use Tokenizer or Tokenizer16 classes, that are specializations
+ * of this template class.  Tokenizer is for ASCII input, Tokenizer16 may
+ * handle char16_t input, but doesn't recognize whitespaces or numbers
+ * other than standard `char` specialized Tokenizer class.
  */
-class Tokenizer : public TokenizerBase
+template <typename TChar>
+class TTokenizer : public TokenizerBase<TChar>
 {
 public:
+  typedef TokenizerBase<TChar> base;
+
   /**
    * @param aSource
    *    The string to parse.
-   *    IMPORTANT NOTE: Tokenizer doesn't ensure the input string buffer lifetime.
-   *    It's up to the consumer to make sure the string's buffer outlives the Tokenizer!
+   *    IMPORTANT NOTE: TTokenizer doesn't ensure the input string buffer
+   * lifetime. It's up to the consumer to make sure the string's buffer outlives
+   * the TTokenizer!
    * @param aWhitespaces
-   *    If non-null Tokenizer will use this custom set of whitespaces for CheckWhite()
-   *    and SkipWhites() calls.
-   *    By default the list consists of space and tab.
+   *    If non-null TTokenizer will use this custom set of whitespaces for
+   * CheckWhite() and SkipWhites() calls. By default the list consists of space
+   * and tab.
    * @param aAdditionalWordChars
-   *    If non-null it will be added to the list of characters that consist a word.
-   *    This is useful when you want to accept e.g. '-' in HTTP headers.
-   *    By default a word character is consider any character for which upper case
+   *    If non-null it will be added to the list of characters that consist a
+   * word. This is useful when you want to accept e.g. '-' in HTTP headers. By
+   * default a word character is consider any character for which upper case
    *    is different from lower case.
    *
-   * If there is an overlap between aWhitespaces and aAdditionalWordChars, the check for
-   * word characters is made first.
+   * If there is an overlap between aWhitespaces and aAdditionalWordChars, the
+   * check for word characters is made first.
    */
-  explicit Tokenizer(const nsACString& aSource,
-                     const char* aWhitespaces = nullptr,
-                     const char* aAdditionalWordChars = nullptr);
-  explicit Tokenizer(const char* aSource,
-                     const char* aWhitespaces = nullptr,
-                     const char* aAdditionalWordChars = nullptr);
+  explicit TTokenizer(const typename base::TAString& aSource,
+                      const TChar* aWhitespaces = nullptr,
+                      const TChar* aAdditionalWordChars = nullptr);
+  explicit TTokenizer(const TChar* aSource,
+                      const TChar* aWhitespaces = nullptr,
+                      const TChar* aAdditionalWordChars = nullptr);
 
   /**
    * When there is still anything to read from the input, tokenize it, store the token type
    * and value to aToken result and shift the cursor past this just parsed token.  Each call
    * to Next() reads another token from the input and shifts the cursor.
    * Returns false if we have passed the end of the input.
    */
   MOZ_MUST_USE
-  bool Next(Token& aToken);
+  bool Next(typename base::Token& aToken);
 
   /**
    * Parse the token on the input read cursor position, check its type is equal to aTokenType
    * and if so, put it into aResult, shift the cursor and return true.  Otherwise, leave
    * the input read cursor position intact and return false.
    */
   MOZ_MUST_USE
-  bool Check(const TokenType aTokenType, Token& aResult);
+  bool Check(const typename base::TokenType aTokenType, typename base::Token& aResult);
   /**
    * Same as above method, just compares both token type and token value passed in aToken.
    * When both the type and the value equals, shift the cursor and return true.  Otherwise
    * return false.
    */
   MOZ_MUST_USE
-  bool Check(const Token& aToken);
+  bool Check(const typename base::Token& aToken);
 
   /**
    * SkipWhites method (below) may also skip new line characters automatically.
    */
   enum WhiteSkipping {
     /**
      * SkipWhites will only skip what is defined as a white space (default).
      */
@@ -279,98 +292,102 @@ public:
    * optionally skip also new lines.
    */
   void SkipWhites(WhiteSkipping aIncludeNewLines = DONT_INCLUDE_NEW_LINE);
 
   /**
    * Skips all tokens until the given one is found or EOF is hit.  The token
    * or EOF are next to read.
    */
-  void SkipUntil(Token const& aToken);
+  void SkipUntil(typename base::Token const& aToken);
 
   // These are mostly shortcuts for the Check() methods above.
 
   /**
    * Check whitespace character is present.
    */
   MOZ_MUST_USE
-  bool CheckWhite() { return Check(Token::Whitespace()); }
+  bool CheckWhite() { return Check(base::Token::Whitespace()); }
   /**
    * Check there is a single character on the read cursor position.  If so, shift the read
    * cursor position and return true.  Otherwise false.
    */
   MOZ_MUST_USE
-  bool CheckChar(const char aChar) { return Check(Token::Char(aChar)); }
+  bool CheckChar(const TChar aChar) { return Check(base::Token::Char(aChar)); }
   /**
    * This is a customizable version of CheckChar.  aClassifier is a function called with
    * value of the character on the current input read position.  If this user function
    * returns true, read cursor is shifted and true returned.  Otherwise false.
    * The user classifiction function is not called when we are at or past the end and
    * false is immediately returned.
    */
   MOZ_MUST_USE
-  bool CheckChar(bool (*aClassifier)(const char aChar));
+  bool CheckChar(bool (*aClassifier)(const TChar aChar));
   /**
    * Check for a whole expected word.
    */
   MOZ_MUST_USE
-  bool CheckWord(const nsACString& aWord) { return Check(Token::Word(aWord)); }
+  bool CheckWord(const typename base::TAString& aWord) {
+    return Check(base::Token::Word(aWord));
+  }
   /**
    * Shortcut for literal const word check with compile time length calculation.
    */
   template <uint32_t N>
   MOZ_MUST_USE
-  bool CheckWord(const char (&aWord)[N]) { return Check(Token::Word(nsDependentCString(aWord, N - 1))); }
+  bool CheckWord(const TChar (&aWord)[N]) {
+    return Check(base::Token::Word(typename base::TDependentString(aWord, N - 1)));
+  }
   /**
    * Checks \r, \n or \r\n.
    */
   MOZ_MUST_USE
-  bool CheckEOL() { return Check(Token::NewLine()); }
+  bool CheckEOL() { return Check(base::Token::NewLine()); }
   /**
    * Checks we are at the end of the input string reading.  If so, shift past the end
    * and returns true.  Otherwise does nothing and returns false.
    */
   MOZ_MUST_USE
-  bool CheckEOF() { return Check(Token::EndOfFile()); }
+  bool CheckEOF() { return Check(base::Token::EndOfFile()); }
 
   /**
    * These are shortcuts to obtain the value immediately when the token type matches.
    */
-  MOZ_MUST_USE bool ReadChar(char* aValue);
-  MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const char aChar),
-                             char* aValue);
-  MOZ_MUST_USE bool ReadWord(nsACString& aValue);
-  MOZ_MUST_USE bool ReadWord(nsDependentCSubstring& aValue);
+  MOZ_MUST_USE bool ReadChar(TChar* aValue);
+  MOZ_MUST_USE bool ReadChar(bool (*aClassifier)(const TChar aChar),
+                             TChar* aValue);
+  MOZ_MUST_USE bool ReadWord(typename base::TAString& aValue);
+  MOZ_MUST_USE bool ReadWord(typename base::TDependentSubstring& aValue);
 
   /**
    * This is an integer read helper.  It returns false and doesn't move the read
    * cursor when any of the following happens:
    *  - the token at the read cursor is not an integer
    *  - the final number doesn't fit the T type
    * Otherwise true is returned, aValue is filled with the integral number
    * and the cursor is moved forward.
    */
   template <typename T>
   MOZ_MUST_USE bool ReadInteger(T *aValue)
   {
     MOZ_RELEASE_ASSERT(aValue);
 
-    nsACString::const_char_iterator rollback = mRollback;
-    nsACString::const_char_iterator cursor = mCursor;
-    Token t;
-    if (!Check(TOKEN_INTEGER, t)) {
+    typename base::TAString::const_char_iterator rollback = mRollback;
+    typename base::TAString::const_char_iterator cursor = base::mCursor;
+    typename base::Token t;
+    if (!Check(base::TOKEN_INTEGER, t)) {
       return false;
     }
 
     mozilla::CheckedInt<T> checked(t.AsInteger());
     if (!checked.isValid()) {
       // Move to a state as if Check() call has failed
       mRollback = rollback;
-      mCursor = cursor;
-      mHasFailed = true;
+      base::mCursor = cursor;
+      base::mHasFailed = true;
       return false;
     }
 
     *aValue = checked.value();
     return true;
   }
 
   /**
@@ -378,31 +395,31 @@ public:
    */
   template <typename T,
             typename V = typename EnableIf<IsSigned<typename RemovePointer<T>::Type>::value,
                                            typename RemovePointer<T>::Type>::Type>
   MOZ_MUST_USE bool ReadSignedInteger(T *aValue)
   {
     MOZ_RELEASE_ASSERT(aValue);
 
-    nsACString::const_char_iterator rollback = mRollback;
-    nsACString::const_char_iterator cursor = mCursor;
+    typename base::TAString::const_char_iterator rollback = mRollback;
+    typename base::TAString::const_char_iterator cursor = base::mCursor;
     auto revert = MakeScopeExit([&] {
       // Move to a state as if Check() call has failed
       mRollback = rollback;
-      mCursor = cursor;
-      mHasFailed = true;
+      base::mCursor = cursor;
+      base::mHasFailed = true;
     });
 
     // Using functional raw access because '-' could be part of the word set
     // making CheckChar('-') not work.
-    bool minus = CheckChar([](const char aChar) { return aChar == '-'; });
+    bool minus = CheckChar([](const TChar aChar) { return aChar == '-'; });
 
-    Token t;
-    if (!Check(TOKEN_INTEGER, t)) {
+    typename base::Token t;
+    if (!Check(base::TOKEN_INTEGER, t)) {
       return false;
     }
 
     mozilla::CheckedInt<T> checked(t.AsInteger());
     if (minus) {
       checked *= -1;
     }
 
@@ -412,28 +429,28 @@ public:
 
     *aValue = checked.value();
     revert.release();
     return true;
   }
 
   /**
    * Returns the read cursor position back as it was before the last call of any parsing
-   * method of Tokenizer (Next, Check*, Skip*, Read*) so that the last operation
+   * method of TTokenizer (Next, Check*, Skip*, Read*) so that the last operation
    * can be repeated.
    * Rollback cannot be used multiple times, it only reverts the last successfull parse
    * operation.  It also cannot be used before any parsing operation has been called
-   * on the Tokenizer.
+   * on the TTokenizer.
    */
   void Rollback();
 
   /**
    * Record() and Claim() are collecting the input as it is being parsed to obtain
    * a substring between particular syntax bounderies defined by any recursive
-   * descent parser or simple parser the Tokenizer is used to read the input for.
+   * descent parser or simple parser the TTokenizer is used to read the input for.
    * Inlucsion of a token that has just been parsed can be controlled using an arguemnt.
    */
   enum ClaimInclusion {
     /**
      * Include resulting (or passed) token of the last lexical analyzer operation in the result.
      */
     INCLUDE_LAST,
     /**
@@ -448,43 +465,46 @@ public:
    * parsed token (INCLUDE_LAST).
    */
   void Record(ClaimInclusion aInclude = EXCLUDE_LAST);
   /**
    * Claim result of the record started with Record() call before.  Depending on aInclude
    * the ending of the sub-string result includes or excludes the last parsed or checked
    * token.
    */
-  void Claim(nsACString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
-  void Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
+  void Claim(typename base::TAString& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
+  void Claim(typename base::TDependentSubstring& aResult, ClaimInclusion aInclude = EXCLUDE_LAST);
 
   /**
    * If aToken is found, aResult is set to the substring between the current
    * position and the position of aToken, potentially including aToken depending
    * on aInclude.
    * If aToken isn't found aResult is set to the substring between the current
    * position and the end of the string.
    * If aToken is found, the method returns true. Otherwise it returns false.
    *
    * Calling Rollback() after ReadUntil() will return the read cursor to the
    * position it had before ReadUntil was called.
    */
-  MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsDependentCSubstring& aResult,
+  MOZ_MUST_USE bool ReadUntil(typename base::Token const& aToken, typename base::TDependentSubstring& aResult,
                               ClaimInclusion aInclude = EXCLUDE_LAST);
-  MOZ_MUST_USE bool ReadUntil(Token const& aToken, nsACString& aResult,
+  MOZ_MUST_USE bool ReadUntil(typename base::Token const& aToken, typename base::TAString& aResult,
                               ClaimInclusion aInclude = EXCLUDE_LAST);
 
 protected:
-  // All these point to the original buffer passed to the Tokenizer's constructor
-  nsACString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is
-  nsACString::const_char_iterator mRollback; // Position of the previous token start
+  // All these point to the original buffer passed to the TTokenizer's constructor
+  typename base::TAString::const_char_iterator mRecord; // Position where the recorded sub-string for Claim() is
+  typename base::TAString::const_char_iterator mRollback; // Position of the previous token start
 
 private:
-  Tokenizer() = delete;
-  Tokenizer(const Tokenizer&) = delete;
-  Tokenizer(Tokenizer&&) = delete;
-  Tokenizer(const Tokenizer&&) = delete;
-  Tokenizer &operator=(const Tokenizer&) = delete;
+  TTokenizer() = delete;
+  TTokenizer(const TTokenizer&) = delete;
+  TTokenizer(TTokenizer&&) = delete;
+  TTokenizer(const TTokenizer&&) = delete;
+  TTokenizer &operator=(const TTokenizer&) = delete;
 };
 
+typedef TTokenizer<char> Tokenizer;
+typedef TTokenizer<char16_t> Tokenizer16;
+
 } // mozilla
 
 #endif // Tokenizer_h__
--- a/xpcom/io/InputStreamLengthHelper.cpp
+++ b/xpcom/io/InputStreamLengthHelper.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 "InputStreamLengthHelper.h"
 #include "mozilla/dom/WorkerCommon.h"
+#include "nsIAsyncInputStream.h"
 #include "nsIInputStream.h"
 #include "nsIStreamTransportService.h"
 
 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
 
 namespace mozilla {
 
 namespace {
@@ -45,17 +46,19 @@ public:
 
       nsCOMPtr<nsIRunnable> self(this); // overly cute
       mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
       mCallbackTarget = nullptr;
       return NS_OK;
     }
 
     // pong
-    mCallback(mSize);
+    std::function<void(int64_t aLength)> callback;
+    callback.swap(mCallback);
+    callback(mSize);
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIInputStream> mStream;
   std::function<void(int64_t aLength)> mCallback;
   nsCOMPtr<nsIEventTarget> mCallbackTarget;
 
@@ -95,16 +98,22 @@ InputStreamLengthHelper::GetSyncLength(n
 
   nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
     do_QueryInterface(aStream);
   if (asyncStreamLength) {
     // GetAsyncLength should be used.
     return false;
   }
 
+  // We cannot calculate the length of an async stream.
+  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
+  if (asyncStream) {
+    return false;
+  }
+
   // For main-thread only, we want to avoid calling ::Available() for blocking
   // streams.
   if (NS_IsMainThread()) {
     bool nonBlocking = false;
     if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) {
       // Let's return -1. There is nothing else we can do here.
       return true;
     }