Merge mozilla-central to autoland. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Fri, 01 Jun 2018 00:58:09 +0300
changeset 420813 f364ab92b59ebeafe26ef6d579328c35a4bb99a0
parent 420812 dfb4c974e3b03eed033cc8beb77cd2cf19b52bd4 (current diff)
parent 420745 42880a726964a0bd66e2f636931e8322eae86ef7 (diff)
child 420814 e8eba439b33ea5acdc140c4d11ffd3650dff09b7
push id103894
push usercsabou@mozilla.com
push dateFri, 01 Jun 2018 09:46:36 +0000
treeherdermozilla-inbound@e99ff79303ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
dom/base/nsJSEnvironment.cpp
dom/bindings/Codegen.py
dom/workers/WorkerPrivate.cpp
js/src/gc/GC.cpp
js/xpconnect/src/XPCJSRuntime.cpp
testing/web-platform/meta/content-security-policy/media-src/media-src-redir-bug.sub.html.ini
tools/profiler/core/platform.cpp
--- 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
@@ -194,17 +194,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)
@@ -364,21 +364,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;
@@ -1074,22 +1074,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
@@ -2160,30 +2159,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;
     }