Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 10 Jan 2017 17:56:46 -0800
changeset 328793 b079c9833e3ed047e1b984e26b8d62d739baa40b
parent 328750 dbc2084de15a8a3b71843c1c1000df54bc1b0beb (current diff)
parent 328792 6e6cb36f0e9bb299a68ed000b8e67fa868247599 (diff)
child 328794 2963cf6be7f830c0d2155e2968cfc53585868a76
push id31187
push userkwierso@gmail.com
push dateWed, 11 Jan 2017 01:56:54 +0000
treeherdermozilla-central@b079c9833e3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.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 inbound to central, a=merge MozReview-Commit-ID: FLWmryRuVoL
python/mozbuild/mozbuild/test/backend/data/rust-programs/Cargo.toml
toolkit/components/telemetry/tests/unit/test_nsITelemetry.js
--- a/addon-sdk/source/lib/sdk/ui/toolbar/view.js
+++ b/addon-sdk/source/lib/sdk/ui/toolbar/view.js
@@ -120,17 +120,17 @@ const addView = curry((options, {documen
   // a toolbar inside a toolbar.
   // This is should be a temporary hack, we should have a proper XBL for toolbar
   // instead. See:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=982005
   let toolbar = document.createElementNS(XUL_NS, "toolbar");
   toolbar.setAttribute("id", "inner-" + options.id);
   toolbar.setAttribute("defaultset", options.items.join(","));
   toolbar.setAttribute("customizable", "true");
-  toolbar.setAttribute("style", "-moz-appearance: none; overflow: hidden");
+  toolbar.setAttribute("style", "-moz-appearance: none; overflow: hidden; border: 0;");
   toolbar.setAttribute("mode", "icons");
   toolbar.setAttribute("iconsize", "small");
   toolbar.setAttribute("context", "toolbar-context-menu");
   toolbar.setAttribute("flex", "1");
 
   view.insertBefore(toolbar, closeButton);
 
   const observer = new document.defaultView.MutationObserver(attributesChanged);
--- a/browser/components/newtab/PlacesProvider.jsm
+++ b/browser/components/newtab/PlacesProvider.jsm
@@ -1,13 +1,13 @@
 /* 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/. */
 
-/* global XPCOMUtils, Services, PlacesUtils, gPrincipal, EventEmitter */
+/* global XPCOMUtils, Services, PlacesUtils, EventEmitter */
 /* global gLinks */
 /* exported PlacesProvider */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["PlacesProvider"];
 
 const {interfaces: Ci, utils: Cu} = Components;
@@ -17,62 +17,24 @@ Cu.import("resource://gre/modules/XPCOMU
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() {
   const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
   return EventEmitter;
 });
 
-XPCOMUtils.defineLazyGetter(this, "gPrincipal", function() {
-  let uri = Services.io.newURI("about:newtab");
-  return Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
-});
-
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
+                                  "resource://gre/modules/NewTabUtils.jsm");
 
 // The maximum number of results PlacesProvider retrieves from history.
 const HISTORY_RESULTS_LIMIT = 100;
 
-/**
- * Singleton that checks if a given link should be displayed on about:newtab
- * or if we should rather not do it for security reasons. URIs that inherit
- * their caller's principal will be filtered.
- */
-let LinkChecker = {
-  _cache: new Map(),
-
-  get flags() {
-    return Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL |
-           Ci.nsIScriptSecurityManager.DONT_REPORT_ERRORS;
-  },
-
-  checkLoadURI: function LinkChecker_checkLoadURI(aURI) {
-    if (!this._cache.has(aURI)) {
-      this._cache.set(aURI, this._doCheckLoadURI(aURI));
-    }
-
-    return this._cache.get(aURI);
-  },
-
-  _doCheckLoadURI: function LinkChecker_doCheckLoadURI(aURI) {
-    let result = false;
-    try {
-      Services.scriptSecurityManager.
-        checkLoadURIStrWithPrincipal(gPrincipal, aURI, this.flags);
-      result = true;
-    } catch (e) {
-      // We got a weird URI or one that would inherit the caller's principal.
-      Cu.reportError(e);
-    }
-    return result;
-  }
-};
-
 /* Queries history to retrieve the most visited sites. Emits events when the
  * history changes.
  * Implements the EventEmitter interface.
  */
 let Links = function Links() {
   EventEmitter.decorate(this);
 };
 
@@ -100,37 +62,40 @@ Links.prototype = {
     onClearHistory: function historyObserver_onClearHistory() {
       gLinks.emit("clearHistory");
     },
 
     onFrecencyChanged: function historyObserver_onFrecencyChanged(aURI,
                            aNewFrecency, aGUID, aHidden, aLastVisitDate) { // jshint ignore:line
       // The implementation of the query in getLinks excludes hidden and
       // unvisited pages, so it's important to exclude them here, too.
-      if (!aHidden && aLastVisitDate) {
+      if (!aHidden && aLastVisitDate &&
+          NewTabUtils.linkChecker.checkLoadURI(aURI.spec)) {
         gLinks.emit("linkChanged", {
           url: aURI.spec,
           frecency: aNewFrecency,
           lastVisitDate: aLastVisitDate,
           type: "history",
         });
       }
     },
 
     onManyFrecenciesChanged: function historyObserver_onManyFrecenciesChanged() {
       // Called when frecencies are invalidated and also when clearHistory is called
       // See toolkit/components/places/tests/unit/test_frecency_observers.js
       gLinks.emit("manyLinksChanged");
     },
 
     onTitleChanged: function historyObserver_onTitleChanged(aURI, aNewTitle) {
-      gLinks.emit("linkChanged", {
-        url: aURI.spec,
-        title: aNewTitle
-      });
+      if (NewTabUtils.linkChecker.checkLoadURI(aURI.spec)) {
+        gLinks.emit("linkChanged", {
+          url: aURI.spec,
+          title: aNewTitle
+        });
+      }
     },
 
     QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver,
                         Ci.nsISupportsWeakReference])
   },
 
   /**
    * Must be called before the provider is used.
@@ -179,17 +144,17 @@ Links.prototype = {
                    GROUP BY rev_host HAVING MAX(lastVisitDate)
                    ORDER BY frecency DESC, lastVisitDate DESC, url`;
 
     let links = yield this.executePlacesQuery(sqlQuery, {
                   columns: ["url", "title", "lastVisitDate", "frecency", "type"],
                   params: {limit: this.maxNumLinks}
                 });
 
-    return links.filter(link => LinkChecker.checkLoadURI(link.url));
+    return links.filter(link => NewTabUtils.linkChecker.checkLoadURI(link.url));
   }),
 
   /**
    * Executes arbitrary query against places database
    *
    * @param {String} aSql
    *        SQL query to execute
    * @param {Object} [optional] aOptions
@@ -242,11 +207,15 @@ Links.prototype = {
 };
 
 /**
  * Singleton that serves as the default link provider for the grid.
  */
 const gLinks = new Links(); // jshint ignore:line
 
 let PlacesProvider = {
-  LinkChecker,
   links: gLinks,
 };
+
+// Kept only for backwards-compatibility
+XPCOMUtils.defineLazyGetter(PlacesProvider, "LinkChecker",
+  () => NewTabUtils.linkChecker);
+
--- a/browser/components/newtab/tests/xpcshell/test_PlacesProvider.js
+++ b/browser/components/newtab/tests/xpcshell/test_PlacesProvider.js
@@ -54,23 +54,23 @@ function makeVisit(index, daysAgo, isTyp
   };
 }
 
 /** Test LinkChecker **/
 
 add_task(function test_LinkChecker_securityCheck() {
 
   let urls = [
-    {url: "file://home/file/image.png", expected: false},
-    {url: "resource:///modules/PlacesProvider.jsm", expected: false},
     {url: "javascript:alert('hello')", expected: false}, // jshint ignore:line
     {url: "data:image/png;base64,XXX", expected: false},
     {url: "about:newtab", expected: true},
     {url: "https://example.com", expected: true},
     {url: "ftp://example.com", expected: true},
+    {url: "file://home/file/image.png", expected: true},
+    {url: "resource:///modules/PlacesProvider.jsm", expected: true},
   ];
   for (let {url, expected} of urls) {
     let observed = PlacesProvider.LinkChecker.checkLoadURI(url);
     equal(observed, expected, `can load "${url}"?`);
   }
 });
 
 /** Test Provider **/
--- a/browser/config/tooltool-manifests/win32/clang.manifest
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -32,10 +32,18 @@
 },
 {
 "version": "clang 4.0pre/r286542",
 "size": 222604502,
 "digest": "cea6119131adb66e0b7ec5030b00922ac95e4e97249fcab9561a848ea60b7f80536c9171a07136afcb79decbcdb20099a5e7ee493013710b8ba5ae072ad40851",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"algorithm": "sha512",
+"visibility": "public",
+"filename": "makecab.tar.bz2",
+"unpack": true,
+"digest": "da1f7685e5bc49a5ffbe5b4a3678ac7a58a0e125031726d30e2bacbbffa53d6efb9fd9f00d6dff5b54cff9412a103efa564c2af6f8fccc63082f6939181769f8",
+"size": 296777
 }
 ]
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -24,10 +24,18 @@
 },
 {
 "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
 "size": 326656969,
 "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
 "algorithm": "sha512",
 "filename": "vs2015u3.zip",
 "unpack": true
+},
+{
+"algorithm": "sha512",
+"visibility": "public",
+"filename": "makecab.tar.bz2",
+"unpack": true,
+"digest": "da1f7685e5bc49a5ffbe5b4a3678ac7a58a0e125031726d30e2bacbbffa53d6efb9fd9f00d6dff5b54cff9412a103efa564c2af6f8fccc63082f6939181769f8",
+"size": 296777
 }
 ]
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -33,10 +33,18 @@
 },
 {
 "version": "clang 4.0pre/r286542",
 "size": 226755339,
 "digest": "3c598607c36e70788ca7dbdf0d835f9e44fbcaa7b1ed77ef9971d743a5a230bebc0ccd2bcdf97f63ed4546d1b83f4c3556f35c30589c755aaaefbd674f750e22",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"algorithm": "sha512",
+"visibility": "public",
+"filename": "makecab.tar.bz2",
+"unpack": true,
+"digest": "da1f7685e5bc49a5ffbe5b4a3678ac7a58a0e125031726d30e2bacbbffa53d6efb9fd9f00d6dff5b54cff9412a103efa564c2af6f8fccc63082f6939181769f8",
+"size": 296777
 }
 ]
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -25,10 +25,18 @@
 },
 {
 "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
 "size": 326656969,
 "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
 "algorithm": "sha512",
 "filename": "vs2015u3.zip",
 "unpack": true
+},
+{
+"algorithm": "sha512",
+"visibility": "public",
+"filename": "makecab.tar.bz2",
+"unpack": true,
+"digest": "da1f7685e5bc49a5ffbe5b4a3678ac7a58a0e125031726d30e2bacbbffa53d6efb9fd9f00d6dff5b54cff9412a103efa564c2af6f8fccc63082f6939181769f8",
+"size": 296777
 }
 ]
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -201,53 +201,50 @@ body:not(.compact) .newtab-site[type=spo
   background-color: #F2F2F2;
   font-size: 13px;
   line-height: 30px;
   border: 1px solid #fff;
   border-radius: 0 0 var(--cell-corner-radius) var(--cell-corner-radius);
 }
 
 body.compact .newtab-title {
-  background-color: hsla(0,0%,100%,.8);
+  background-color: hsla(0,0%,100%,.85);
   font-size: 12px;
   line-height: 21px;
+  border: 1px solid hsla(0,0%,80%,.8);
+  border-top-color: hsla(0,0%,0%,.1);
+  background-clip: padding-box;
 }
 
 .newtab-title,
 .newtab-suggested {
   color: #5c5c5c;
 }
 
 body.compact .newtab-title,
 body.compact .newtab-suggested {
   color: black;
 }
 
-body.compact .newtab-title {
-  border: 1px solid hsla(0,0%,80%,.8);
-  border-top-color: hsla(0,0%,0%,.1);
-  background-clip: padding-box;
-}
-
 .newtab-suggested[active] {
   background-color: rgba(51, 51, 51, 0.95);
   border: 0;
   color: white;
 }
 
 body:not(.compact) .newtab-site:hover .newtab-title {
   color: white;
   background-color: #333;
   border-color: #333;
   border-top-color: white;
 }
 
 body.compact .newtab-site:hover .newtab-title {
   color: white;
-  background-color: hsla(0,0%,20%,.8);
+  background-color: hsla(0,0%,20%,.85);
   border-color: hsla(0,0%,0%,.8);
   border-top-color: white;
 }
 
 .newtab-site[pinned] .newtab-title {
   padding-inline-start: 24px;
 }
 
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -64,17 +64,17 @@ if test -n "$USE_ICU"; then
     icudir="$_topsrcdir/intl/icu/source"
     if test ! -d "$icudir"; then
         icudir="$_topsrcdir/../../intl/icu/source"
         if test ! -d "$icudir"; then
             AC_MSG_ERROR([Cannot find the ICU directory])
         fi
     fi
 
-    version=`sed -n 's/^[[:space:]]*#[[:space:]]*define[[:space:]][[:space:]]*U_ICU_VERSION_MAJOR_NUM[[:space:]][[:space:]]*\([0-9][0-9]*\)[[:space:]]*$/\1/p' "$icudir/common/unicode/uvernum.h"`
+    version=`sed -n 's/^[[[:space:]]]*#[[:space:]]*define[[:space:]][[:space:]]*U_ICU_VERSION_MAJOR_NUM[[:space:]][[:space:]]*\([0-9][0-9]*\)[[:space:]]*$/\1/p' "$icudir/common/unicode/uvernum.h"`
     if test x"$version" = x; then
        AC_MSG_ERROR([cannot determine icu version number from uvernum.h header file $lineno])
     fi
     MOZ_ICU_VERSION="$version"
 
     # TODO: the l is actually endian-dependent
     # We could make this set as 'l' or 'b' for little or big, respectively,
     # but we'd need to check in a big-endian version of the file.
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -411,8 +411,11 @@ add_old_configure_assignment('LINK', lin
 @depends(sdk_bin_path)
 @imports('os')
 def alter_path(sdk_bin_path):
     path = os.pathsep.join(sdk_bin_path)
     os.environ['PATH'] = path
     return path
 
 set_config('PATH', alter_path)
+
+check_prog('MAKECAB', ('makecab.exe',))
+
--- a/build/mozconfig.win-common
+++ b/build/mozconfig.win-common
@@ -9,8 +9,9 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
   MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
 fi
 
 # Some builds (eg: Mulet) don't want the installer, so only set this if it
 # hasn't already been set.
 MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1}
 
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=c:/builds/crash-stats-api.token
+export MAKECAB=$topsrcdir/makecab.exe
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -56,8 +56,12 @@ LOCAL_INCLUDES += [
 ]
 
 if CONFIG['ENABLE_TESTS']:
     DIRS += ['tests/gtest']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Security: CAPS")
+
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -25,16 +25,18 @@ define(function (require, exports, modul
    * and the max number of rendered items depends on the current mode.
    */
   let ArrayRep = React.createClass({
     displayName: "ArrayRep",
 
     propTypes: {
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
+      object: React.PropTypes.array.isRequired,
     },
 
     getTitle: function (object, context) {
       return "[" + object.length + "]";
     },
 
     arrayIterator: function (array, max) {
       let items = [];
@@ -165,16 +167,22 @@ define(function (require, exports, modul
   });
 
   /**
    * Renders array item. Individual values are separated by a comma.
    */
   let ItemRep = React.createFactory(React.createClass({
     displayName: "ItemRep",
 
+    propTypes: {
+      object: React.PropTypes.any.isRequired,
+      delim: React.PropTypes.string.isRequired,
+      mode: React.PropTypes.symbol,
+    },
+
     render: wrapRender(function () {
       const { Rep } = createFactories(require("./rep"));
 
       let object = this.props.object;
       let delim = this.props.delim;
       let mode = this.props.mode;
       return (
         DOM.span({},
--- a/devtools/client/shared/components/reps/attribute.js
+++ b/devtools/client/shared/components/reps/attribute.js
@@ -24,17 +24,18 @@ define(function (require, exports, modul
 
   /**
    * Renders DOM attribute
    */
   let Attribute = React.createClass({
     displayName: "Attr",
 
     propTypes: {
-      object: React.PropTypes.object.isRequired
+      object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       return grip.preview.nodeName;
     },
 
     render: wrapRender(function () {
       let object = this.props.object;
--- a/devtools/client/shared/components/reps/caption.js
+++ b/devtools/client/shared/components/reps/caption.js
@@ -16,16 +16,20 @@ define(function (require, exports, modul
 
   /**
    * Renders a caption. This template is used by other components
    * that needs to distinguish between a simple text/value and a label.
    */
   const Caption = React.createClass({
     displayName: "Caption",
 
+    propTypes: {
+      object: React.PropTypes.object,
+    },
+
     render: wrapRender(function () {
       return (
         DOM.span({"className": "caption"}, this.props.object)
       );
     }),
   });
 
   // Exports from this module
--- a/devtools/client/shared/components/reps/date-time.js
+++ b/devtools/client/shared/components/reps/date-time.js
@@ -21,17 +21,18 @@ define(function (require, exports, modul
 
   /**
    * Used to render JS built-in Date() object.
    */
   let DateTime = React.createClass({
     displayName: "Date",
 
     propTypes: {
-      object: React.PropTypes.object.isRequired
+      object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: grip
         }, grip.class + " ");
       }
--- a/devtools/client/shared/components/reps/document.js
+++ b/devtools/client/shared/components/reps/document.js
@@ -22,17 +22,18 @@ define(function (require, exports, modul
 
   /**
    * Renders DOM document object.
    */
   let Document = React.createClass({
     displayName: "Document",
 
     propTypes: {
-      object: React.PropTypes.object.isRequired
+      object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getLocation: function (grip) {
       let location = grip.preview.location;
       return location ? getURLDisplayString(location) : "";
     },
 
     getTitle: function (grip) {
--- a/devtools/client/shared/components/reps/element-node.js
+++ b/devtools/client/shared/components/reps/element-node.js
@@ -26,16 +26,19 @@ define(function (require, exports, modul
    */
   const ElementNode = React.createClass({
     displayName: "ElementNode",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      onDOMNodeMouseOver: React.PropTypes.func,
+      onDOMNodeMouseOut: React.PropTypes.func,
+      objectLink: React.PropTypes.func,
     },
 
     getElements: function (grip, mode) {
       let {attributes, nodeName} = grip.preview;
       const nodeNameElement = span({
         className: "tag-name theme-fg-color3"
       }, nodeName);
 
--- a/devtools/client/shared/components/reps/error.js
+++ b/devtools/client/shared/components/reps/error.js
@@ -21,16 +21,17 @@ define(function (require, exports, modul
    */
   const ErrorRep = React.createClass({
     displayName: "Error",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
     },
 
     render: wrapRender(function () {
       let object = this.props.object;
       let preview = object.preview;
       let name = preview && preview.name
         ? preview.name
         : "Error";
--- a/devtools/client/shared/components/reps/function.js
+++ b/devtools/client/shared/components/reps/function.js
@@ -22,17 +22,18 @@ define(function (require, exports, modul
 
   /**
    * This component represents a template for Function objects.
    */
   let Func = React.createClass({
     displayName: "Func",
 
     propTypes: {
-      object: React.PropTypes.object.isRequired
+      object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: grip
         }, "function ");
       }
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -28,16 +28,17 @@ define(function (require, exports, modul
   let GripArray = React.createClass({
     displayName: "GripArray",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
       provider: React.PropTypes.object,
+      objectLink: React.PropTypes.func,
     },
 
     getLength: function (grip) {
       if (!grip.preview) {
         return 0;
       }
 
       return grip.preview.length || grip.preview.childNodesLength || 0;
--- a/devtools/client/shared/components/reps/grip-map.js
+++ b/devtools/client/shared/components/reps/grip-map.js
@@ -25,16 +25,18 @@ define(function (require, exports, modul
    */
   const GripMap = React.createClass({
     displayName: "GripMap",
 
     propTypes: {
       object: React.PropTypes.object,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
+      isInterestingEntry: React.PropTypes.func,
     },
 
     getTitle: function (object) {
       let title = object && object.class ? object.class : "Map";
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: object
         }, title);
--- a/devtools/client/shared/components/reps/grip.js
+++ b/devtools/client/shared/components/reps/grip.js
@@ -30,16 +30,17 @@ define(function (require, exports, modul
     displayName: "Grip",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
       isInterestingProp: React.PropTypes.func,
       title: React.PropTypes.string,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (object) {
       let title = this.props.title || object.class || "Object";
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: object
         }, title);
--- a/devtools/client/shared/components/reps/infinity.js
+++ b/devtools/client/shared/components/reps/infinity.js
@@ -17,16 +17,20 @@ define(function (require, exports, modul
   const { span } = React.DOM;
 
   /**
    * Renders a Infinity object
    */
   const InfinityRep = React.createClass({
     displayName: "Infinity",
 
+    propTypes: {
+      object: React.PropTypes.object.isRequired,
+    },
+
     render: wrapRender(function () {
       return (
         span({className: "objectBox objectBox-number"},
           this.props.object.type
         )
       );
     })
   });
--- a/devtools/client/shared/components/reps/long-string.js
+++ b/devtools/client/shared/components/reps/long-string.js
@@ -20,16 +20,19 @@ define(function (require, exports, modul
    * Renders a long string grip.
    */
   const LongStringRep = React.createClass({
     displayName: "LongStringRep",
 
     propTypes: {
       useQuotes: React.PropTypes.bool,
       style: React.PropTypes.object,
+      cropLimit: React.PropTypes.number.isRequired,
+      member: React.PropTypes.string,
+      object: React.PropTypes.object.isRequired,
     },
 
     getDefaultProps: function () {
       return {
         useQuotes: true,
       };
     },
 
--- a/devtools/client/shared/components/reps/number.js
+++ b/devtools/client/shared/components/reps/number.js
@@ -17,16 +17,23 @@ define(function (require, exports, modul
   const { span } = React.DOM;
 
   /**
    * Renders a number
    */
   const Number = React.createClass({
     displayName: "Number",
 
+    propTypes: {
+      object: React.PropTypes.oneOfType([
+        React.PropTypes.object,
+        React.PropTypes.number,
+      ]).isRequired
+    },
+
     stringify: function (object) {
       let isNegativeZero = Object.is(object, -0) ||
         (object.type && object.type == "-0");
 
       return (isNegativeZero ? "-0" : String(object));
     },
 
     render: wrapRender(function () {
--- a/devtools/client/shared/components/reps/object-with-text.js
+++ b/devtools/client/shared/components/reps/object-with-text.js
@@ -22,16 +22,17 @@ define(function (require, exports, modul
   /**
    * Renders a grip object with textual data.
    */
   let ObjectWithText = React.createClass({
     displayName: "ObjectWithText",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       if (this.props.objectLink) {
         return span({className: "objectBox"},
           this.props.objectLink({
             object: grip
           }, this.getType(grip) + " ")
--- a/devtools/client/shared/components/reps/object-with-url.js
+++ b/devtools/client/shared/components/reps/object-with-url.js
@@ -23,16 +23,17 @@ define(function (require, exports, modul
   /**
    * Renders a grip object with URL data.
    */
   let ObjectWithURL = React.createClass({
     displayName: "ObjectWithURL",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       if (this.props.objectLink) {
         return span({className: "objectBox"},
           this.props.objectLink({
             object: grip
           }, this.getType(grip) + " ")
--- a/devtools/client/shared/components/reps/object.js
+++ b/devtools/client/shared/components/reps/object.js
@@ -24,16 +24,17 @@ define(function (require, exports, modul
    */
   const Obj = React.createClass({
     displayName: "Obj",
 
     propTypes: {
       object: React.PropTypes.object,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (object) {
       let className = object && object.class ? object.class : "Object";
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: object
         }, className);
--- a/devtools/client/shared/components/reps/promise.js
+++ b/devtools/client/shared/components/reps/promise.js
@@ -26,16 +26,17 @@ define(function (require, exports, modul
    */
   const PromiseRep = React.createClass({
     displayName: "Promise",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (object) {
       const title = object.class;
       if (this.props.objectLink) {
         return this.props.objectLink({
           object: object
         }, title);
--- a/devtools/client/shared/components/reps/prop-rep.js
+++ b/devtools/client/shared/components/reps/prop-rep.js
@@ -32,16 +32,17 @@ define(function (require, exports, modul
         React.PropTypes.object,
       ]).isRequired,
       // Equal character rendered between property name and value.
       equal: React.PropTypes.string,
       // Delimiter character used to separate individual properties.
       delim: React.PropTypes.string,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
     },
 
     render: wrapRender(function () {
       const { Grip } = require("./grip");
       let { Rep } = createFactories(require("./rep"));
 
       let key;
       // The key can be a simple string, for plain objects,
--- a/devtools/client/shared/components/reps/regexp.js
+++ b/devtools/client/shared/components/reps/regexp.js
@@ -22,16 +22,17 @@ define(function (require, exports, modul
   /**
    * Renders a grip object with regular expression.
    */
   let RegExp = React.createClass({
     displayName: "regexp",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getSource: function (grip) {
       return grip.displayString;
     },
 
     render: wrapRender(function () {
       let grip = this.props.object;
--- a/devtools/client/shared/components/reps/string.js
+++ b/devtools/client/shared/components/reps/string.js
@@ -23,16 +23,19 @@ define(function (require, exports, modul
    * Renders a string. String value is enclosed within quotes.
    */
   const StringRep = React.createClass({
     displayName: "StringRep",
 
     propTypes: {
       useQuotes: React.PropTypes.bool,
       style: React.PropTypes.object,
+      object: React.PropTypes.string.isRequired,
+      member: React.PropTypes.any,
+      cropLimit: React.PropTypes.number,
     },
 
     getDefaultProps: function () {
       return {
         useQuotes: true,
       };
     },
 
--- a/devtools/client/shared/components/reps/stylesheet.js
+++ b/devtools/client/shared/components/reps/stylesheet.js
@@ -23,16 +23,17 @@ define(function (require, exports, modul
   /**
    * Renders a grip representing CSSStyleSheet
    */
   let StyleSheet = React.createClass({
     displayName: "object",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       let title = "StyleSheet ";
       if (this.props.objectLink) {
         return DOM.span({className: "objectBox"},
           this.props.objectLink({
             object: grip
--- a/devtools/client/shared/components/reps/text-node.js
+++ b/devtools/client/shared/components/reps/text-node.js
@@ -26,16 +26,19 @@ define(function (require, exports, modul
    */
   let TextNode = React.createClass({
     displayName: "TextNode",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
       // @TODO Change this to Object.values once it's supported in Node's version of V8
       mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+      objectLink: React.PropTypes.func,
+      onDOMNodeMouseOver: React.PropTypes.func,
+      onDOMNodeMouseOut: React.PropTypes.func,
     },
 
     getTextContent: function (grip) {
       return cropString(grip.preview.textContent);
     },
 
     getTitle: function (grip) {
       const title = "#text";
--- a/devtools/client/shared/components/reps/window.js
+++ b/devtools/client/shared/components/reps/window.js
@@ -23,16 +23,17 @@ define(function (require, exports, modul
   /**
    * Renders a grip representing a window.
    */
   let Window = React.createClass({
     displayName: "Window",
 
     propTypes: {
       object: React.PropTypes.object.isRequired,
+      objectLink: React.PropTypes.func,
     },
 
     getTitle: function (grip) {
       if (this.props.objectLink) {
         return DOM.span({className: "objectBox"},
           this.props.objectLink({
             object: grip
           }, grip.class + " ")
--- a/devtools/client/shared/components/splitter/split-box.js
+++ b/devtools/client/shared/components/splitter/split-box.js
@@ -16,30 +16,36 @@ const { DOM: dom, PropTypes } = React;
 const SplitBox = React.createClass({
   displayName: "SplitBox",
 
   propTypes: {
     // Custom class name. You can use more names separated by a space.
     className: PropTypes.string,
     // Initial size of controlled panel.
     initialSize: PropTypes.number,
+    // Initial width of controlled panel.
+    initialWidth: PropTypes.number,
+    // Initial height of controlled panel.
+    initialHeight: PropTypes.number,
     // Left/top panel
     startPanel: PropTypes.any,
     // Min panel size.
     minSize: PropTypes.number,
     // Max panel size.
     maxSize: PropTypes.number,
     // Right/bottom panel
     endPanel: PropTypes.any,
     // True if the right/bottom panel should be controlled.
     endPanelControl: PropTypes.bool,
     // Size of the splitter handle bar.
     splitterSize: PropTypes.number,
     // True if the splitter bar is vertical (default is vertical).
-    vert: PropTypes.bool
+    vert: PropTypes.bool,
+    // Style object.
+    style: PropTypes.object,
   },
 
   getDefaultProps() {
     return {
       splitterSize: 5,
       vert: true,
       endPanelControl: false
     };
--- a/devtools/client/shared/components/stack-trace.js
+++ b/devtools/client/shared/components/stack-trace.js
@@ -9,37 +9,37 @@ const { DOM: dom, createClass, createFac
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const Frame = createFactory(require("./frame"));
 
 const l10n = new LocalizationHelper("devtools/client/locales/webconsole.properties");
 
 const AsyncFrame = createFactory(createClass({
   displayName: "AsyncFrame",
 
-  PropTypes: {
+  propTypes: {
     asyncCause: PropTypes.string.isRequired
   },
 
   render() {
     let { asyncCause } = this.props;
 
     return dom.span(
       { className: "frame-link-async-cause" },
       l10n.getFormatStr("stacktrace.asyncStack", asyncCause)
     );
   }
 }));
 
 const StackTrace = createClass({
   displayName: "StackTrace",
 
-  PropTypes: {
+  propTypes: {
     stacktrace: PropTypes.array.isRequired,
     onViewSourceInDebugger: PropTypes.func.isRequired,
-    onViewSourceInScratchpad: PropTypes.func.isRequired,
+    onViewSourceInScratchpad: PropTypes.func,
   },
 
   render() {
     let {
       stacktrace,
       onViewSourceInDebugger,
       onViewSourceInScratchpad
     } = this.props;
--- a/devtools/client/shared/components/tree.js
+++ b/devtools/client/shared/components/tree.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint-env browser */
 "use strict";
 
-const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react");
+const React = require("devtools/client/shared/vendor/react");
+const { DOM: dom, createClass, createFactory, PropTypes } = React;
 
 const AUTO_EXPAND_DEPTH = 0;
 const NUMBER_OF_OFFSCREEN_ITEMS = 1;
 
 /**
  * A fast, generic, expandable and collapsible tree component.
  *
  * This tree component is fast: it can handle trees with *many* items. It only
@@ -625,16 +626,24 @@ module.exports = createClass({
 
 /**
  * An arrow that displays whether its node is expanded (▼) or collapsed
  * (▶). When its node has no children, it is hidden.
  */
 const ArrowExpander = createFactory(createClass({
   displayName: "ArrowExpander",
 
+  propTypes: {
+    item: PropTypes.any.isRequired,
+    visible: PropTypes.bool.isRequired,
+    expanded: PropTypes.bool.isRequired,
+    onCollapse: PropTypes.func.isRequired,
+    onExpand: PropTypes.func.isRequired,
+  },
+
   shouldComponentUpdate(nextProps, nextState) {
     return this.props.item !== nextProps.item
       || this.props.visible !== nextProps.visible
       || this.props.expanded !== nextProps.expanded;
   },
 
   render() {
     const attrs = {
@@ -654,16 +663,33 @@ const ArrowExpander = createFactory(crea
       };
     }
 
     return dom.div(attrs);
   }
 }));
 
 const TreeNode = createFactory(createClass({
+  propTypes: {
+    focused: PropTypes.bool.isRequired,
+    onFocusedNodeUnmount: PropTypes.func,
+    item: PropTypes.any.isRequired,
+    expanded: PropTypes.bool.isRequired,
+    hasChildren: PropTypes.bool.isRequired,
+    onExpand: PropTypes.func.isRequired,
+    index: PropTypes.number.isRequired,
+    first: PropTypes.bool,
+    last: PropTypes.bool,
+    onFocus: PropTypes.func,
+    onBlur: PropTypes.func,
+    onCollapse: PropTypes.func.isRequired,
+    depth: PropTypes.number.isRequired,
+    renderItem: PropTypes.func.isRequired,
+  },
+
   componentDidMount() {
     if (this.props.focused) {
       this.refs.button.focus();
     }
   },
 
   componentDidUpdate() {
     if (this.props.focused) {
--- a/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
@@ -16,19 +16,21 @@ add_task(function* () {
   let jar = getJar(getRootDirectory(gTestPath));
   let dir = jar ?
             extractJarToTmp(jar) :
             getChromeDir(getResolvedURI(gTestPath));
 
   dir.append(TEST_FILE);
   let uri = Services.io.newFileURI(dir);
 
-  // We need a file remote type to make sure we don't switch processes when we
-  // load the file:// URI.
-  let { browser } = yield loadTab("about:blank", E10SUtils.FILE_REMOTE_TYPE);
+  // Open tab with correct remote type so we don't switch processes when we load
+  // the file:// URI, otherwise we won't get the same web console.
+  let remoteType = E10SUtils.getRemoteTypeForURI(uri.spec,
+                                                 gMultiProcessBrowser);
+  let { browser } = yield loadTab("about:blank", remoteType);
 
   hud = yield openConsole();
   hud.jsterm.clearOutput();
 
   let loaded = loadBrowser(browser);
   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri.spec);
   yield loaded;
 
--- a/dom/base/OrderedTimeoutIterator.h
+++ b/dom/base/OrderedTimeoutIterator.h
@@ -73,18 +73,18 @@ public:
       // equal.) Otherwise, return whichever iterator has an item left,
       // preferring a non-tracking timeout again.  Note that in practice, even
       // if a web page calls setTimeout() twice in a row, it should get
       // different mWhen values, so in practice we shouldn't fall back to
       // comparing timeout IDs.
       if (mNormalIter && mTrackingIter &&
           mNormalIter != mNormalStopAt &&
           mTrackingIter != mTrackingStopAt &&
-          (mTrackingIter->mWhen < mNormalIter->mWhen ||
-           (mTrackingIter->mWhen == mNormalIter->mWhen &&
+          (mTrackingIter->When() < mNormalIter->When() ||
+           (mTrackingIter->When() == mNormalIter->When() &&
             mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
         timeout = mTrackingIter;
         mKind = Kind::Tracking;
       } else if (mNormalIter && mNormalIter != mNormalStopAt) {
         timeout = mNormalIter;
         mKind = Kind::Normal;
       } else if (mTrackingIter && mTrackingIter != mTrackingStopAt) {
         timeout = mTrackingIter;
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -103,10 +103,59 @@ Timeout::InitTimer(nsIEventTarget* aTarg
 #ifdef DEBUG
 bool
 Timeout::HasRefCnt(uint32_t aCount) const
 {
   return mRefCnt.get() == aCount;
 }
 #endif // DEBUG
 
+void
+Timeout::SetWhenOrTimeRemaining(const TimeStamp& aBaseTime,
+                                const TimeDuration& aDelay)
+{
+  // This must not be called on dummy timeouts.  Instead use SetDummyWhen().
+  MOZ_DIAGNOSTIC_ASSERT(mWindow);
+
+  // If we are frozen simply set mTimeRemaining to be the "time remaining" in
+  // the timeout (i.e., the interval itself).  This will be used to create a
+  // new mWhen time when the window is thawed.  The end effect is that time does
+  // not appear to pass for frozen windows.
+  if (mWindow->IsFrozen()) {
+    mWhen = TimeStamp();
+    mTimeRemaining = aDelay;
+    return;
+  }
+
+  // Since we are not frozen we must set a precise mWhen target wakeup
+  // time.  Even if we are suspended we want to use this target time so
+  // that it appears time passes while suspended.
+  mWhen = aBaseTime + aDelay;
+  mTimeRemaining = TimeDuration(0);
+}
+
+void
+Timeout::SetDummyWhen(const TimeStamp& aWhen)
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mWindow);
+  mWhen = aWhen;
+}
+
+const TimeStamp&
+Timeout::When() const
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mWhen.IsNull());
+  // Note, mWindow->IsFrozen() can be true here.  The Freeze() method calls
+  // When() to calculate the delay to populate mTimeRemaining.
+  return mWhen;
+}
+
+const TimeDuration&
+Timeout::TimeRemaining() const
+{
+  MOZ_DIAGNOSTIC_ASSERT(mWhen.IsNull());
+  // Note, mWindow->IsFrozen() can be false here.  The Thaw() method calls
+  // TimeRemaining() to calculate the new When() value.
+  return mTimeRemaining;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_timeout_h
 #define mozilla_dom_timeout_h
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsPIDOMWindow.h"
 
 class nsGlobalWindow;
 class nsIEventTarget;
 class nsIPrincipal;
 class nsITimeoutHandler;
 class nsITimer;
 class nsIEventTarget;
 
@@ -43,16 +42,27 @@ public:
   nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
 
   enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
 
 #ifdef DEBUG
   bool HasRefCnt(uint32_t aCount) const;
 #endif // DEBUG
 
+  void SetWhenOrTimeRemaining(const TimeStamp& aBaseTime,
+                              const TimeDuration& aDelay);
+
+  void SetDummyWhen(const TimeStamp& aWhen);
+
+  // Can only be called when not frozen.
+  const TimeStamp& When() const;
+
+  // Can only be called when frozen.
+  const TimeDuration& TimeRemaining() const;
+
   // Window for which this timeout fires
   RefPtr<nsGlobalWindow> mWindow;
 
   // The actual timer object
   nsCOMPtr<nsITimer> mTimer;
 
   // True if the timeout was cleared
   bool mCleared;
@@ -66,39 +76,39 @@ public:
   Reason mReason;
 
   // Returned as value of setTimeout()
   uint32_t mTimeoutId;
 
   // Interval in milliseconds
   uint32_t mInterval;
 
-  // mWhen and mTimeRemaining can't be in a union, sadly, because they
-  // have constructors.
-  // Nominal time to run this timeout.  Use only when timeouts are not
-  // suspended.
-  TimeStamp mWhen;
-  // Remaining time to wait.  Used only when timeouts are suspended.
-  TimeDuration mTimeRemaining;
-
   // Principal with which to execute
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   // stack depth at which timeout is firing
   uint32_t mFiringDepth;
 
   uint32_t mNestingLevel;
 
   // The popup state at timeout creation time if not created from
   // another timeout
   PopupControlState mPopupState;
 
   // The language-specific information about the callback.
   nsCOMPtr<nsITimeoutHandler> mScriptHandler;
 
 private:
+  // mWhen and mTimeRemaining can't be in a union, sadly, because they
+  // have constructors.
+  // Nominal time to run this timeout.  Use only when timeouts are not
+  // frozen.
+  TimeStamp mWhen;
+  // Remaining time to wait.  Used only when timeouts are frozen.
+  TimeDuration mTimeRemaining;
+
   ~Timeout();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_timeout_h
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -172,34 +172,24 @@ TimeoutManager::SetTimeout(nsITimeoutHan
   uint32_t realInterval = interval;
   if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL ||
       mBackPressureDelayMS > 0 || mWindow.IsBackgroundInternal()) {
     // Don't allow timeouts less than DOMMinTimeoutValue() from
     // now...
     realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
   }
 
-  TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
+  timeout->mWindow = &mWindow;
 
-  if (mWindow.IsFrozen()) {
-    // If we are frozen simply set timeout->mTimeRemaining to be the
-    // "time remaining" in the timeout (i.e., the interval itself).  This
-    // will be used to create a new mWhen time when the window is thawed.
-    // The end effect is that time does not appear to pass for frozen windows.
-    timeout->mTimeRemaining = delta;
-  } else {
-    // Since we are not frozen we must set a precise mWhen target wakeup
-    // time.  Even if we are suspended we want to use this target time so
-    // that it appears time passes while suspended.
-    timeout->mWhen = TimeStamp::Now() + delta;
-  }
+  TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
+  timeout->SetWhenOrTimeRemaining(TimeStamp::Now(), delta);
 
   // If we're not suspended, then set the timer.
   if (!mWindow.IsSuspended()) {
-    MOZ_ASSERT(!timeout->mWhen.IsNull());
+    MOZ_ASSERT(!timeout->When().IsNull());
 
     nsresult rv;
     timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     RefPtr<Timeout> copy = timeout;
@@ -209,18 +199,16 @@ TimeoutManager::SetTimeout(nsITimeoutHan
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // The timeout is now also held in the timer's closure.
     Unused << copy.forget();
   }
 
-  timeout->mWindow = &mWindow;
-
   if (!aIsInterval) {
     timeout->mNestingLevel = nestingLevel;
   }
 
   // No popups from timeouts by default
   timeout->mPopupState = openAbused;
 
   if (gRunningTimeoutDepth == 0 &&
@@ -330,44 +318,44 @@ TimeoutManager::RunTimeout(Timeout* aTim
   // the same as the lifetime of the containing nsGlobalWindow.
   Unused << windowKungFuDeathGrip;
 
   // A native timer has gone off. See which of our timeouts need
   // servicing
   TimeStamp now = TimeStamp::Now();
   TimeStamp deadline;
 
-  if (aTimeout && aTimeout->mWhen > now) {
+  if (aTimeout && aTimeout->When() > now) {
     // The OS timer fired early (which can happen due to the timers
     // having lower precision than TimeStamp does).  Set |deadline| to
     // be the time when the OS timer *should* have fired so that any
     // timers that *should* have fired before aTimeout *will* be fired
     // now.
 
-    deadline = aTimeout->mWhen;
+    deadline = aTimeout->When();
   } else {
     deadline = now;
   }
 
   // The timeout list is kept in deadline order. Discover the latest timeout
   // whose deadline has expired. On some platforms, native timeout events fire
-  // "early", but we handled that above by setting deadline to aTimeout->mWhen
+  // "early", but we handled that above by setting deadline to aTimeout->When()
   // if the timer fired early.  So we can stop walking if we get to timeouts
-  // whose mWhen is greater than deadline, since once that happens we know
+  // whose When() is greater than deadline, since once that happens we know
   // nothing past that point is expired.
   {
     // Use a nested scope in order to make sure the strong references held by
     // the iterator are freed after the loop.
     OrderedTimeoutIterator expiredIter(mNormalTimeouts,
                                        mTrackingTimeouts,
                                        nullptr,
                                        nullptr);
     while (true) {
       Timeout* timeout = expiredIter.Next();
-      if (!timeout || timeout->mWhen > deadline) {
+      if (!timeout || timeout->When() > deadline) {
         break;
       }
 
       if (timeout->mFiringDepth == 0) {
         // Mark any timeouts that are on the list to be fired with the
         // firing depth so that we can reentrantly run timeouts
         timeout->mFiringDepth = firingDepth;
         last_expired_timeout_is_normal = expiredIter.PickedNormalIter();
@@ -405,24 +393,24 @@ TimeoutManager::RunTimeout(Timeout* aTim
 
   // Insert a dummy timeout into the list of timeouts between the
   // portion of the list that we are about to process now and those
   // timeouts that will be processed in a future call to
   // win_run_timeout(). This dummy timeout serves as the head of the
   // list for any timeouts inserted as a result of running a timeout.
   RefPtr<Timeout> dummy_normal_timeout = new Timeout();
   dummy_normal_timeout->mFiringDepth = firingDepth;
-  dummy_normal_timeout->mWhen = now;
+  dummy_normal_timeout->SetDummyWhen(now);
   if (last_expired_timeout_is_normal) {
     last_expired_normal_timeout->setNext(dummy_normal_timeout);
   }
 
   RefPtr<Timeout> dummy_tracking_timeout = new Timeout();
   dummy_tracking_timeout->mFiringDepth = firingDepth;
-  dummy_tracking_timeout->mWhen = now;
+  dummy_tracking_timeout->SetDummyWhen(now);
   if (!last_expired_timeout_is_normal) {
     last_expired_tracking_timeout->setNext(dummy_tracking_timeout);
   }
 
   RefPtr<Timeout> timeoutExtraRef1(dummy_normal_timeout);
   RefPtr<Timeout> timeoutExtraRef2(dummy_tracking_timeout);
 
   // Now we need to search the normal and tracking timer list at the same
@@ -697,49 +685,36 @@ TimeoutManager::RescheduleTimeout(Timeou
 
   // If we're running pending timeouts, set the next interval to be
   // relative to "now", and not to when the timeout that was pending
   // should have fired.
   TimeStamp firingTime;
   if (aRunningPendingTimeouts) {
     firingTime = now + nextInterval;
   } else {
-    firingTime = aTimeout->mWhen + nextInterval;
+    firingTime = aTimeout->When() + nextInterval;
   }
 
   TimeStamp currentNow = TimeStamp::Now();
   TimeDuration delay = firingTime - currentNow;
 
   // And make sure delay is nonnegative; that might happen if the timer
   // thread is firing our timers somewhat early or if they're taking a long
   // time to run the callback.
   if (delay < TimeDuration(0)) {
     delay = TimeDuration(0);
   }
 
+  aTimeout->SetWhenOrTimeRemaining(currentNow, delay);
+
   if (!aTimeout->mTimer) {
-    if (mWindow.IsFrozen()) {
-      // If we are frozen simply set timeout->mTimeRemaining to be the
-      // "time remaining" in the timeout (i.e., the interval itself).  This
-      // will be used to create a new mWhen time when the window is thawed.
-      // The end effect is that time does not appear to pass for frozen windows.
-      aTimeout->mTimeRemaining = delay;
-    } else if (mWindow.IsSuspended()) {
-    // Since we are not frozen we must set a precise mWhen target wakeup
-    // time.  Even if we are suspended we want to use this target time so
-    // that it appears time passes while suspended.
-      aTimeout->mWhen = currentNow + delay;
-    } else {
-      MOZ_ASSERT_UNREACHABLE("Window should be frozen or suspended.");
-    }
+    MOZ_DIAGNOSTIC_ASSERT(mWindow.IsFrozen() || mWindow.IsSuspended());
     return true;
   }
 
-  aTimeout->mWhen = currentNow + delay;
-
   // Reschedule the OS timer. Don't bother returning any error codes if
   // this fails since the callers of this method don't care about them.
   nsresult rv = aTimeout->InitTimer(mWindow.EventTargetFor(TaskCategory::Timer),
                                     delay.ToMilliseconds());
 
   if (NS_FAILED(rv)) {
     NS_ERROR("Error initializing timer for DOM timeout!");
 
@@ -801,35 +776,35 @@ TimeoutManager::Timeouts::ResetTimersFor
                                                           int32_t aMinTimeoutValueMS,
                                                           SortBy aSortBy,
                                                           nsIEventTarget* aQueue)
 {
   TimeStamp now = TimeStamp::Now();
 
   // If insertion point is non-null, we're in the middle of firing timers and
   // the timers we're planning to fire all come before insertion point;
-  // insertion point itself is a dummy timeout with an mWhen that may be
+  // insertion point itself is a dummy timeout with an When() that may be
   // semi-bogus.  In that case, we don't need to do anything with insertion
   // point or anything before it, so should start at the timer after insertion
   // point, if there is one.
   // Otherwise, start at the beginning of the list.
   for (Timeout* timeout = InsertionPoint() ?
          InsertionPoint()->getNext() : GetFirst();
        timeout; ) {
     // It's important that this check be <= so that we guarantee that
     // taking std::max with |now| won't make a quantity equal to
-    // timeout->mWhen below.
-    if (timeout->mWhen <= now) {
+    // timeout->When() below.
+    if (timeout->When() <= now) {
       timeout = timeout->getNext();
       continue;
     }
 
-    if (timeout->mWhen - now >
+    if (timeout->When() - now >
         TimeDuration::FromMilliseconds(aPreviousThrottleDelayMS)) {
-      // No need to loop further.  Timeouts are sorted in mWhen order
+      // No need to loop further.  Timeouts are sorted in When() order
       // and the ones after this point were all set up for at least
       // gMinBackgroundTimeoutValue ms and hence were not clamped.
       break;
     }
 
     // We reduced our throttled delay. Re-init the timer appropriately.
     // Compute the interval the timer should have had if it had not been set in a
     // background window
@@ -837,36 +812,37 @@ TimeoutManager::Timeouts::ResetTimersFor
       TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
                                             uint32_t(aMinTimeoutValueMS)));
     uint32_t oldIntervalMillisecs = 0;
     timeout->mTimer->GetDelay(&oldIntervalMillisecs);
     TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
     if (oldInterval > interval) {
       // unclamp
       TimeStamp firingTime =
-        std::max(timeout->mWhen - oldInterval + interval, now);
+        std::max(timeout->When() - oldInterval + interval, now);
 
-      NS_ASSERTION(firingTime < timeout->mWhen,
+      NS_ASSERTION(firingTime < timeout->When(),
                    "Our firing time should strictly decrease!");
 
       TimeDuration delay = firingTime - now;
-      timeout->mWhen = firingTime;
+      timeout->SetWhenOrTimeRemaining(now, delay);
+      MOZ_DIAGNOSTIC_ASSERT(timeout->When() == firingTime);
 
-      // Since we reset mWhen we need to move |timeout| to the right
-      // place in the list so that it remains sorted by mWhen.
+      // Since we reset When() we need to move |timeout| to the right
+      // place in the list so that it remains sorted by When().
 
       // Get the pointer to the next timeout now, before we move the
       // current timeout in the list.
       Timeout* nextTimeout = timeout->getNext();
 
-      // It is safe to remove and re-insert because mWhen is now
+      // It is safe to remove and re-insert because When() is now
       // strictly smaller than it used to be, so we know we'll insert
       // |timeout| before nextTimeout.
       NS_ASSERTION(!nextTimeout ||
-                   timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
+                   timeout->When() < nextTimeout->When(), "How did that happen?");
       timeout->remove();
       // Insert() will addref |timeout| and reset mFiringDepth.  Make sure to
       // undo that after calling it.
       uint32_t firingDepth = timeout->mFiringDepth;
       Insert(timeout, aSortBy);
       timeout->mFiringDepth = firingDepth;
       timeout->Release();
 
@@ -932,20 +908,20 @@ void
 TimeoutManager::Timeouts::Insert(Timeout* aTimeout, SortBy aSortBy)
 {
   // Start at mLastTimeout and go backwards.  Don't go further than insertion
   // point, though.  This optimizes for the common case of insertion at the end.
   Timeout* prevSibling;
   for (prevSibling = GetLast();
        prevSibling && prevSibling != InsertionPoint() &&
          // This condition needs to match the one in SetTimeoutOrInterval that
-         // determines whether to set mWhen or mTimeRemaining.
+         // determines whether to set When() or TimeRemaining().
          (aSortBy == SortBy::TimeRemaining ?
-          prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
-          prevSibling->mWhen > aTimeout->mWhen);
+          prevSibling->TimeRemaining() > aTimeout->TimeRemaining() :
+          prevSibling->When() > aTimeout->When());
        prevSibling = prevSibling->getPrevious()) {
     /* Do nothing; just searching */
   }
 
   // Now link in aTimeout after prevSibling.
   if (prevSibling) {
     prevSibling->setNext(aTimeout);
   } else {
@@ -1023,25 +999,25 @@ TimeoutManager::Resume()
     if (!aTimeout->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       return;
     }
 
     MOZ_ASSERT(!aTimeout->mTimer);
 
-    // The timeout mWhen is set to the absolute time when the timer should
+    // The timeout When() is set to the absolute time when the timer should
     // fire.  Recalculate the delay from now until that deadline.  If the
     // the deadline has already passed or falls within our minimum delay
     // deadline, then clamp the resulting value to the minimum delay.  The
-    // mWhen will remain at its absolute time, but we won'aTimeout fire the OS
+    // When() will remain at its absolute time, but we won'aTimeout fire the OS
     // timer until our calculated delay has passed.
     int32_t remaining = 0;
-    if (aTimeout->mWhen > now) {
-      remaining = static_cast<int32_t>((aTimeout->mWhen - now).ToMilliseconds());
+    if (aTimeout->When() > now) {
+      remaining = static_cast<int32_t>((aTimeout->When() - now).ToMilliseconds());
     }
     uint32_t delay = std::max(remaining, DOMMinTimeoutValue());
 
     aTimeout->mTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (!aTimeout->mTimer) {
       aTimeout->remove();
       return;
     }
@@ -1063,21 +1039,22 @@ void
 TimeoutManager::Freeze()
 {
   TimeStamp now = TimeStamp::Now();
   ForEachUnorderedTimeout([&](Timeout* aTimeout) {
     // Save the current remaining time for this timeout.  We will
     // re-apply it when the window is Thaw()'d.  This effectively
     // shifts timers to the right as if time does not pass while
     // the window is frozen.
-    if (aTimeout->mWhen > now) {
-      aTimeout->mTimeRemaining = aTimeout->mWhen - now;
-    } else {
-      aTimeout->mTimeRemaining = TimeDuration(0);
+    TimeDuration delta(0);
+    if (aTimeout->When() > now) {
+      delta = aTimeout->When() - now;
     }
+    aTimeout->SetWhenOrTimeRemaining(now, delta);
+    MOZ_DIAGNOSTIC_ASSERT(aTimeout->TimeRemaining() == delta);
 
     // Since we are suspended there should be no OS timer set for
     // this timeout entry.
     MOZ_ASSERT(!aTimeout->mTimer);
   });
 }
 
 void
@@ -1091,18 +1068,19 @@ TimeoutManager::Thaw()
     // case we have a dummy timeout in the list that *must not* be resumed. It
     // can be identified by a null mWindow.
     if (!aTimeout->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       return;
     }
 
-    // Set mWhen back to the time when the timer is supposed to fire.
-    aTimeout->mWhen = now + aTimeout->mTimeRemaining;
+    // Set When() back to the time when the timer is supposed to fire.
+    aTimeout->SetWhenOrTimeRemaining(now, aTimeout->TimeRemaining());
+    MOZ_DIAGNOSTIC_ASSERT(!aTimeout->When().IsNull());
 
     MOZ_ASSERT(!aTimeout->mTimer);
   });
 }
 
 bool
 TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId)
 {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -869,19 +869,16 @@ nsOuterWindowProxy::getOwnPropertyDescri
     return false;
   }
   if (found) {
     FillPropertyDescriptor(desc, proxy, true);
     return true;
   }
   // else fall through to js::Wrapper
 
-  // When we change this to always claim the property is configurable (bug
-  // 1178639), update the comments in nsOuterWindowProxy::defineProperty
-  // accordingly.
   return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
 nsOuterWindowProxy::defineProperty(JSContext* cx,
                                    JS::Handle<JSObject*> proxy,
                                    JS::Handle<jsid> id,
                                    JS::Handle<JS::PropertyDescriptor> desc,
@@ -889,39 +886,16 @@ nsOuterWindowProxy::defineProperty(JSCon
 {
   if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
     // Spec says to Reject whether this is a supported index or not,
     // since we have no indexed setter or indexed creator.  It is up
     // to the caller to decide whether to throw a TypeError.
     return result.failCantDefineWindowElement();
   }
 
-#ifndef RELEASE_OR_BETA // To be turned on in bug 1178638.
-  // For now, allow chrome code to define non-configurable properties
-  // on windows, until we sort out what exactly the addon SDK is
-  // doing.  In the meantime, this still allows us to test web compat
-  // behavior.
-  if (desc.hasConfigurable() && !desc.configurable() &&
-      !nsContentUtils::IsCallerChrome()) {
-    return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
-  }
-
-  // Note that if hasConfigurable() is false we do NOT want to
-  // setConfigurable(true).  That would make this code:
-  //
-  //   var x;
-  //   window.x = 5;
-  //
-  // fail, because the JS engine ends up converting the assignment into a define
-  // with !hasConfigurable(), but the var actually declared a non-configurable
-  // property on our underlying Window object, so the set would fail if we
-  // forced setConfigurable(true) here.  What we want to do instead is change
-  // getOwnPropertyDescriptor to always claim configurable.  See bug 1178639.
-#endif
-
   return js::Wrapper::defineProperty(cx, proxy, id, desc, result);
 }
 
 bool
 nsOuterWindowProxy::ownPropertyKeys(JSContext *cx,
                                     JS::Handle<JSObject*> proxy,
                                     JS::AutoIdVector &props) const
 {
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -774,17 +774,16 @@ skip-if = toolkit == 'android'
 skip-if = toolkit == 'android'
 [test_websocket4.html]
 skip-if = toolkit == 'android'
 [test_websocket5.html]
 skip-if = toolkit == 'android'
 [test_window_constructor.html]
 [test_window_cross_origin_props.html]
 [test_window_define_nonconfigurable.html]
-skip-if = release_or_beta
 [test_window_define_symbol.html]
 [test_window_element_enumeration.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_window_named_frame_enumeration.html]
 [test_window_orientation.html]
 skip-if = true # bug 1312417
--- a/dom/base/test/test_window_define_nonconfigurable.html
+++ b/dom/base/test/test_window_define_nonconfigurable.html
@@ -5,41 +5,88 @@ https://bugzilla.mozilla.org/show_bug.cg
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1107443</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
-  /** Test for Bug 1107443 **/
-  try {
-    Object.defineProperty(window, "nosuchprop", { value: 5, configurable: false });
-    throw "didn't throw";
-  } catch (e) {
-    is(e instanceof TypeError, true,
-       "defineProperty(window) with a non-configurable property should " +
-       "throw a TypeError, instead got: " + e);
-    is(Object.getOwnPropertyDescriptor(window, "nosuchprop"), undefined,
-       'Window should not have property after an attempt to define it failed');
-  }
+  /**
+   * Test for Bug 1107443, modified when it was backed out in bug 1329323.
+   * This is now testing the _current_ behavior, not the desired one; expect
+   * failures in this test and needing to update it when bug 1329324 is
+   * fixed.
+   */
+  var retval = Object.defineProperty(window, "nosuchprop",
+                                     { value: 5, configurable: false });
+  todo_is(retval, false,
+          "Should return false when 'failing' to define non-configurable property via Object.defineProperty.")
+  var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop' now");
+  todo_is(desc.configurable, true,
+          "Property 'nosuchprop' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop' should be readonly");
+  is(desc.value, 5, "Property 'nosuchprop' should have the right value");
+
+  retval = Object.defineProperty(window, "nosuchprop2", { value: 6 });
+  is(retval, window,
+     "Should return object when succesfully defining 'nosuchprop2'");
+  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop2");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop2' now");
+  todo_is(desc.configurable, true,
+          "Property 'nosuchprop2' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop2' should be readonly");
+  is(desc.value, 6, "Property 'nosuchprop2' should have the right value");
 
-  Object.defineProperty(window, "nosuchprop", { value: 6 });
-  var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
-  is(typeof(desc), "object", "Should have a property now");
-  todo_is(desc.configurable, true, "Property should be configurable");
-  is(desc.writable, false, "Property should be readonly");
-  is(desc.value, 6, "Property should have the right value");
+  retval = Object.defineProperty(window, "nosuchprop3",
+                                 { value: 7, configurable: true });
+  is(retval, window,
+     "Should return object when succesfully defining 'nosuchprop3'");
+  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop3");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop3' now");
+  is(desc.configurable, true,
+          "Property 'nosuchprop3' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop3' should be readonly");
+  is(desc.value, 7, "Property 'nosuchprop3' should have the right value");
 
-  Object.defineProperty(window, "nosuchprop2", { value: 7, configurable: true });
-  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop2");
-  is(typeof(desc), "object", "Should have a property now");
-  is(desc.configurable, true, "Property should be configurable");
-  is(desc.writable, false, "Property should be readonly");
-  is(desc.value, 7, "Property should have the right value");
+  // XXXbz it's not actually entirely clear what behavior the
+  // Reflect.defineProperty bits should have.  Check it carefully once there's a
+  // spec.
+  retval = Reflect.defineProperty(window, "nosuchprop4",
+                                  { value: 8, configurable: false });
+  todo_is(retval, false,
+          "Should not be able to Reflect.defineProperty if non-configurable");
+  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop4");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop4' now");
+  todo_is(desc.configurable, true,
+          "Property 'nosuchprop4' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop4' should be readonly");
+  is(desc.value, 8, "Property 'nosuchprop4' should have the right value");
+
+  retval = Reflect.defineProperty(window, "nosuchprop5",
+                                  { value: 9 });
+  is(retval, true,
+     "Should be able to Reflect.defineProperty with default configurability");
+  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop5");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop5' now");
+  todo_is(desc.configurable, true,
+          "Property 'nosuchprop5' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop5' should be readonly");
+  is(desc.value, 9, "Property 'nosuchprop5' should have the right value");
+
+  retval = Reflect.defineProperty(window, "nosuchprop6",
+                                  { value: 10, configurable: true });
+  is(retval, true,
+     "Should be able to Reflect.defineProperty if configurable");
+  desc = Object.getOwnPropertyDescriptor(window, "nosuchprop6");
+  is(typeof(desc), "object", "Should have a property 'nosuchprop6' now");
+  is(desc.configurable, true, "Property 'nosuchprop6' should be configurable");
+  is(desc.writable, false, "Property 'nosuchprop6' should be readonly");
+  is(desc.value, 10, "Property 'nosuchprop6' should have the right value");
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107443">Mozilla Bug 1107443</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1370,20 +1370,26 @@ def UnionTypes(unionTypes, config):
                 elif f.isDictionary():
                     # For a dictionary, we need to see its declaration in
                     # UnionTypes.h so we have its sizeof and know how big to
                     # make our union.
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
                     # And if it needs rooting, we need RootedDictionary too
                     if typeNeedsRooting(f):
                         headers.add("mozilla/dom/RootedDictionary.h")
+                elif f.isFloat() and not f.isUnrestricted():
+                    # Restricted floats are tested for finiteness
+                    implheaders.add("mozilla/FloatingPoint.h")
+                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
                 elif f.isEnum():
                     # Need to see the actual definition of the enum,
                     # unfortunately.
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
+                elif f.isPrimitive():
+                    implheaders.add("mozilla/dom/PrimitiveConversions.h")
                 elif f.isCallback():
                     # Callbacks always use strong refs, so we need to include
                     # the right header to be able to Release() in our inlined
                     # code.
                     headers.add(CGHeaders.getDeclarationFilename(f.callback))
                 elif f.isMozMap():
                     headers.add("mozilla/dom/MozMap.h")
                     # And add headers for the type we're parametrized over
@@ -1441,16 +1447,20 @@ def UnionConversions(unionTypes, config)
                             typeDesc = config.getDescriptor(f.inner.identifier.name)
                         except NoSuchDescriptorError:
                             return
                         headers.add(typeDesc.headerFile)
                     else:
                         headers.add(CGHeaders.getDeclarationFilename(f.inner))
                 elif f.isDictionary():
                     headers.add(CGHeaders.getDeclarationFilename(f.inner))
+                elif f.isFloat() and not f.isUnrestricted():
+                    # Restricted floats are tested for finiteness
+                    headers.add("mozilla/FloatingPoint.h")
+                    headers.add("mozilla/dom/PrimitiveConversions.h")
                 elif f.isPrimitive():
                     headers.add("mozilla/dom/PrimitiveConversions.h")
                 elif f.isMozMap():
                     headers.add("mozilla/dom/MozMap.h")
                     # And the internal type of the MozMap
                     addHeadersForType(f.inner)
 
             # We plan to include UnionTypes.h no matter what, so it's
@@ -16781,17 +16791,16 @@ class GlobalGenRoots():
 
         includes.add("mozilla/OwningNonNull.h")
         includes.add("mozilla/dom/UnionMember.h")
         includes.add("mozilla/dom/BindingDeclarations.h")
         # BindingUtils.h is only needed for SetToObject.
         # If it stops being inlined or stops calling CallerSubsumes
         # both this bit and the bit in CGBindingRoot can be removed.
         includes.add("mozilla/dom/BindingUtils.h")
-        implincludes.add("mozilla/dom/PrimitiveConversions.h")
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'], unions)
 
         curr = CGWrapper(curr, post='\n')
 
         builder = ForwardDeclarationBuilder()
         for className, isStruct in declarations:
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -95,8 +95,9 @@ MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSE
 MSG_DEF(MSG_INVALID_SPACING_MODE_ERROR, 1, JSEXN_TYPEERR, "Invalid spacing '{0}'.")
 MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
 MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
 MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
 MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
 MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
 MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
 MSG_DEF(MSG_WORKER_THREAD_SHUTTING_DOWN, 0, JSEXN_TYPEERR, "The Worker thread is shutting down.")
+MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.")
--- a/dom/bindings/RootedOwningNonNull.h
+++ b/dom/bindings/RootedOwningNonNull.h
@@ -46,28 +46,19 @@ struct GCPolicy<mozilla::OwningNonNull<T
     if ((*tp).isInitialized()) {
       (*tp)->Trace(trc);
     }
   }
 };
 } // namespace JS
 
 namespace js {
-template<typename T>
-struct RootedBase<mozilla::OwningNonNull<T>>
+template<typename T, typename Wrapper>
+struct WrappedPtrOperations<mozilla::OwningNonNull<T>, Wrapper>
 {
-  typedef mozilla::OwningNonNull<T> SmartPtrType;
-
-  operator SmartPtrType& () const
-  {
-    auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
-    return self.get();
-  }
-
   operator T& () const
   {
-    auto& self = *static_cast<const JS::Rooted<SmartPtrType>*>(this);
-    return self.get();
+    return static_cast<const Wrapper*>(this)->get();
   }
 };
 } // namespace js
 
 #endif /* mozilla_RootedOwningNonNull_h__ */
--- a/dom/bindings/RootedRefPtr.h
+++ b/dom/bindings/RootedRefPtr.h
@@ -34,26 +34,19 @@ struct GCPolicy<RefPtr<T>>
     if (*tp) {
       (*tp)->Trace(trc);
     }
   }
 };
 } // namespace JS
 
 namespace js {
-template<typename T>
-struct RootedBase<RefPtr<T>>
+template<typename T, typename Wrapper>
+struct WrappedPtrOperations<RefPtr<T>, Wrapper>
 {
-  operator RefPtr<T>& () const
-  {
-    auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
-    return self.get();
-  }
-
   operator T*() const
   {
-    auto& self = *static_cast<const JS::Rooted<RefPtr<T>>*>(this);
-    return self.get();
+    return static_cast<const Wrapper*>(this)->get();
   }
 };
 } // namespace js
 
 #endif /* mozilla_RootedRefPtr_h__ */
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -150,16 +150,27 @@ CacheOpChild::Recv__delete__(const Error
     {
       mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
       break;
     }
     case CacheOpResult::TStorageOpenResult:
     {
       auto actor = static_cast<CacheChild*>(
         aResult.get_StorageOpenResult().actorChild());
+
+      // If we have a success status then we should have an actor.  Gracefully
+      // reject instead of crashing, though, if we get a nullptr here.
+      MOZ_DIAGNOSTIC_ASSERT(actor);
+      if (!actor) {
+        ErrorResult status;
+        status.ThrowTypeError<MSG_CACHE_OPEN_FAILED>();
+        mPromise->MaybeReject(status);
+        break;
+      }
+
       actor->SetWorkerHolder(GetWorkerHolder());
       RefPtr<Cache> cache = new Cache(mGlobal, actor);
       mPromise->MaybeResolve(cache);
       break;
     }
     case CacheOpResult::TStorageDeleteResult:
     {
       mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -496,49 +496,54 @@ CacheStorage::WrapObject(JSContext* aCon
 {
   return mozilla::dom::CacheStorageBinding::Wrap(aContext, this, aGivenProto);
 }
 
 void
 CacheStorage::ActorCreated(PBackgroundChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
+  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mStatus));
+  MOZ_DIAGNOSTIC_ASSERT(!mActor);
   MOZ_DIAGNOSTIC_ASSERT(aActor);
 
   if (NS_WARN_IF(mWorkerHolder && mWorkerHolder->Notified())) {
     ActorFailed();
     return;
   }
 
   // WorkerHolder ownership is passed to the CacheStorageChild actor and any
   // actors it may create.  The WorkerHolder will keep the worker thread alive
   // until the actors can gracefully shutdown.
-  CacheStorageChild* newActor = new CacheStorageChild(this, mWorkerHolder);
+  mActor = new CacheStorageChild(this, mWorkerHolder);
+  mWorkerHolder = nullptr;
+
+  // Pass the actor construction message to the parent.  Note, if this fails
+  // we can get DestroyInternal() and ActorFailed() called synchronously.  This
+  // will null out mActor and set an error mStatus.
   PCacheStorageChild* constructedActor =
-    aActor->SendPCacheStorageConstructor(newActor, mNamespace, *mPrincipalInfo);
+    aActor->SendPCacheStorageConstructor(mActor, mNamespace, *mPrincipalInfo);
 
-  if (NS_WARN_IF(!constructedActor)) {
-    ActorFailed();
+  if (NS_WARN_IF(NS_FAILED(mStatus))) {
+    MOZ_DIAGNOSTIC_ASSERT(!mActor);
     return;
   }
 
-  mWorkerHolder = nullptr;
-
-  MOZ_DIAGNOSTIC_ASSERT(constructedActor == newActor);
-  mActor = newActor;
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(constructedActor == mActor);
 
   MaybeRunPendingRequests();
   MOZ_DIAGNOSTIC_ASSERT(mPendingRequests.IsEmpty());
 }
 
 void
 CacheStorage::ActorFailed()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_DIAGNOSTIC_ASSERT(!NS_FAILED(mStatus));
+  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mStatus));
 
   mStatus = NS_ERROR_UNEXPECTED;
   mWorkerHolder = nullptr;
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
     nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
     entry->mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
   }
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -80,16 +80,21 @@ CacheStreamControlParent::AssertOwningTh
 }
 #endif
 
 void
 CacheStreamControlParent::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   CloseAllReadStreamsWithoutReporting();
+  // If the initial SendPStreamControlConstructor() fails we will
+  // be called before mStreamList is set.
+  if (!mStreamList) {
+    return;
+  }
   mStreamList->RemoveStreamControl(this);
   mStreamList->NoteClosedAll();
   mStreamList = nullptr;
 }
 
 mozilla::ipc::IPCResult
 CacheStreamControlParent::RecvNoteClosed(const nsID& aId)
 {
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -1210,34 +1210,37 @@ public:
                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     // Look for existing cache
     bool cacheFound;
     nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
                                         &cacheFound, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     if (cacheFound) {
+      MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
       return rv;
     }
 
     rv = db::CreateCacheId(aConn, &mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = db::StoragePutCache(aConn, mNamespace, mArgs.key(), mCacheId);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = trans.Commit();
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
+    MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
     return rv;
   }
 
   virtual void
   Complete(Listener* aListener, ErrorResult&& aRv) override
   {
+    MOZ_DIAGNOSTIC_ASSERT(aRv.Failed() || mCacheId != INVALID_CACHE_ID);
     aListener->OnOpComplete(Move(aRv), StorageOpenResult(), mCacheId);
   }
 
 private:
   const Namespace mNamespace;
   const StorageOpenArgs mArgs;
   CacheId mCacheId;
 };
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1287,16 +1287,17 @@ CrossProcessSafeEvent(const WidgetEvent&
   case eMouseEventClass:
     switch (aEvent.mMessage) {
     case eMouseDown:
     case eMouseUp:
     case eMouseMove:
     case eContextMenu:
     case eMouseEnterIntoWidget:
     case eMouseExitFromWidget:
+    case eMouseTouchDrag:
       return true;
     default:
       return false;
     }
   case eTouchEventClass:
     switch (aEvent.mMessage) {
     case eTouchStart:
     case eTouchMove:
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -188,16 +188,49 @@ MakeStorageVersion(uint32_t aMajorStorag
   return int32_t((aMajorStorageVersion << 16) + aMinorStorageVersion);
 }
 #endif
 
 uint32_t
 GetMajorStorageVersion(int32_t aStorageVersion)
 {
   return uint32_t(aStorageVersion >> 16);
+
+}
+
+nsresult
+CreateTables(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  // The database doesn't have any tables for now. It's only used for storage
+  // version checking.
+  // However, this is the place where any future tables should be created.
+
+  nsresult rv;
+
+#ifdef DEBUG
+  {
+    int32_t storageVersion;
+    rv = aConnection->GetSchemaVersion(&storageVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(storageVersion == 0);
+  }
+#endif
+
+  rv = aConnection->SetSchemaVersion(kStorageVersion);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
 }
 
 /******************************************************************************
  * Quota manager class declarations
  ******************************************************************************/
 
 } // namespace
 
@@ -4089,22 +4122,35 @@ QuotaManager::MaybeRemoveOldDirectories(
       return rv;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-QuotaManager::UpgradeStorageFrom0ToCurrent(mozIStorageConnection* aConnection)
+QuotaManager::UpgradeStorageFrom0_0To1_0(mozIStorageConnection* aConnection)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aConnection);
 
-  nsresult rv;
+  nsresult rv = MaybeUpgradeIndexedDBDirectory();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = MaybeUpgradePersistentStorageDirectory();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = MaybeRemoveOldDirectories();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   for (const PersistenceType persistenceType : kAllPersistenceTypes) {
     nsCOMPtr<nsIFile> directory =
       do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
@@ -4140,22 +4186,36 @@ QuotaManager::UpgradeStorageFrom0ToCurre
     return rv;
   }
 
   return NS_OK;
 }
 
 #if 0
 nsresult
-QuotaManager::UpgradeStorageFrom1To2(mozIStorageConnection* aConnection)
+QuotaManager::UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aConnection);
 
-  nsresult rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
+  nsresult rv;
+
+#ifdef DEBUG
+  {
+    int32_t storageVersion;
+    rv = aConnection->GetSchemaVersion(&storageVersion);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(storageVersion == MakeStorageVersion(1, 0));
+  }
+#endif
+
+  rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 #endif
 
@@ -4226,62 +4286,92 @@ QuotaManager::EnsureStorageIsInitialized
   if (GetMajorStorageVersion(storageVersion) > kMajorStorageVersion) {
     NS_WARNING("Unable to initialize storage, version is too high!");
     return NS_ERROR_FAILURE;
   }
 
   if (storageVersion < kStorageVersion) {
     const bool newDatabase = !storageVersion;
 
+    nsCOMPtr<nsIFile> storageDir =
+      do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = storageDir->InitWithPath(mStoragePath);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool exists;
+    rv = storageDir->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!exists) {
+      nsCOMPtr<nsIFile> indexedDBDir =
+        do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = indexedDBDir->InitWithPath(mIndexedDBPath);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
+      rv = indexedDBDir->Exists(&exists);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    const bool newDirectory = !exists;
+
     if (newDatabase) {
       // Set the page size first.
       if (kSQLitePageSizeOverride) {
         rv = connection->ExecuteSimpleSQL(
           nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
         );
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
 
     mozStorageTransaction transaction(connection, false,
                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
-    if (newDatabase) {
-      rv = MaybeUpgradeIndexedDBDirectory();
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = MaybeUpgradePersistentStorageDirectory();
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = MaybeRemoveOldDirectories();
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      rv = UpgradeStorageFrom0ToCurrent(connection);
+
+    // An upgrade method can upgrade the database, the storage or both.
+    // The upgrade loop below can only be avoided when there's no database and
+    // no storage yet (e.g. new profile).
+    if (newDatabase && newDirectory) {
+      rv = CreateTables(connection);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&storageVersion)));
       MOZ_ASSERT(storageVersion == kStorageVersion);
     } else {
       // This logic needs to change next time we change the storage!
       static_assert(kStorageVersion == int32_t((1 << 16) + 0),
                     "Upgrade function needed due to storage version increase.");
 
       while (storageVersion != kStorageVersion) {
-        /* if (storageVersion == MakeStorageVersion(1, 0)) {
-          rv = UpgradeStorageFrom1To2(connection);
-        } else */ {
+        if (storageVersion == 0) {
+          rv = UpgradeStorageFrom0_0To1_0(connection);
+#if 0
+        } else if (storageVersion == MakeStorageVersion(1, 0)) {
+          rv = UpgradeStorageFrom1_0To2_0(connection);
+#endif
+        } else {
           NS_WARNING("Unable to initialize storage, no upgrade path is "
                      "available!");
           return NS_ERROR_FAILURE;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -450,21 +450,21 @@ private:
 
   nsresult
   MaybeUpgradePersistentStorageDirectory();
 
   nsresult
   MaybeRemoveOldDirectories();
 
   nsresult
-  UpgradeStorageFrom0ToCurrent(mozIStorageConnection* aConnection);
+  UpgradeStorageFrom0_0To1_0(mozIStorageConnection* aConnection);
 
 #if 0
   nsresult
-  UpgradeStorageFrom1To2(mozIStorageConnection* aConnection);
+  UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection);
 #endif
 
   nsresult
   InitializeRepository(PersistenceType aPersistenceType);
 
   nsresult
   InitializeOrigin(PersistenceType aPersistenceType,
                    const nsACString& aGroup,
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1574,23 +1574,27 @@ CacheCreator::ResolvedCallback(JSContext
   }
 }
 
 void
 CacheCreator::DeleteCache()
 {
   AssertIsOnMainThread();
 
-  ErrorResult rv;
+  // This is called when the load is canceled which can occur before
+  // mCacheStorage is initialized.
+  if (!mCacheStorage) {
+    return;
+  }
 
   // It's safe to do this while Cache::Match() and Cache::Put() calls are
   // running.
+  IgnoredErrorResult rv;
   RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, rv);
   if (NS_WARN_IF(rv.Failed())) {
-    rv.SuppressException();
     return;
   }
 
   // We don't care to know the result of the promise object.
   FailLoaders(NS_ERROR_FAILURE);
 }
 
 void
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -122,25 +122,25 @@ struct BarrierMethods<nsXBLMaybeCompiled
 };
 
 template <class T>
 struct IsHeapConstructibleType<nsXBLMaybeCompiled<T>>
 { // Yes, this is the exception to the rule. Sorry.
   static constexpr bool value = true;
 };
 
-template <class UncompiledT>
-class HeapBase<nsXBLMaybeCompiled<UncompiledT>>
+template <class UncompiledT, class Wrapper>
+class HeapBase<nsXBLMaybeCompiled<UncompiledT>, Wrapper>
 {
-  const JS::Heap<nsXBLMaybeCompiled<UncompiledT>>& wrapper() const {
-    return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT>>*>(this);
+  const Wrapper& wrapper() const {
+    return *static_cast<const Wrapper*>(this);
   }
 
-  JS::Heap<nsXBLMaybeCompiled<UncompiledT>>& wrapper() {
-    return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT>>*>(this);
+  Wrapper& wrapper() {
+    return *static_cast<Wrapper*>(this);
   }
 
   const nsXBLMaybeCompiled<UncompiledT>* extract() const {
     return wrapper().address();
   }
 
   nsXBLMaybeCompiled<UncompiledT>* extract() {
     return wrapper().unsafeGet();
--- a/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
+++ b/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
@@ -46,18 +46,48 @@ class TokenLexer : public Lexer
 
  private:
     TokenVector mTokens;
     TokenVector::const_iterator mIter;
 };
 
 }  // anonymous namespace
 
+class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable
+{
+  public:
+    ScopedMacroReenabler(MacroExpander *expander);
+    ~ScopedMacroReenabler();
+
+  private:
+    MacroExpander *mExpander;
+};
+
+MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)
+    : mExpander(expander)
+{
+    mExpander->mDeferReenablingMacros = true;
+}
+
+MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
+{
+    mExpander->mDeferReenablingMacros = false;
+    for (auto *macro : mExpander->mMacrosToReenable)
+    {
+        macro->disabled = false;
+    }
+    mExpander->mMacrosToReenable.clear();
+}
+
 MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics)
-    : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mTotalTokensInContexts(0)
+    : mLexer(lexer),
+      mMacroSet(macroSet),
+      mDiagnostics(diagnostics),
+      mTotalTokensInContexts(0),
+      mDeferReenablingMacros(false)
 {
 }
 
 MacroExpander::~MacroExpander()
 {
     for (MacroContext *context : mContextStack)
     {
         delete context;
@@ -182,17 +212,24 @@ void MacroExpander::popMacro()
     ASSERT(!mContextStack.empty());
 
     MacroContext *context = mContextStack.back();
     mContextStack.pop_back();
 
     ASSERT(context->empty());
     ASSERT(context->macro->disabled);
     ASSERT(context->macro->expansionCount > 0);
-    context->macro->disabled = false;
+    if (mDeferReenablingMacros)
+    {
+        mMacrosToReenable.push_back(context->macro);
+    }
+    else
+    {
+        context->macro->disabled = false;
+    }
     context->macro->expansionCount--;
     mTotalTokensInContexts -= context->replacements.size();
     delete context;
 }
 
 bool MacroExpander::expandMacro(const Macro &macro,
                                 const Token &identifier,
                                 std::vector<Token> *replacements)
@@ -258,16 +295,21 @@ bool MacroExpander::collectMacroArgs(con
                                      SourceLocation *closingParenthesisLocation)
 {
     Token token;
     getToken(&token);
     ASSERT(token.type == '(');
 
     args->push_back(MacroArg());
 
+    // Defer reenabling macros until args collection is finished to avoid the possibility of
+    // infinite recursion. Otherwise infinite recursion might happen when expanding the args after
+    // macros have been popped from the context stack when parsing the args.
+    ScopedMacroReenabler deferReenablingMacros(this);
+
     int openParens = 1;
     while (openParens != 0)
     {
         getToken(&token);
 
         if (token.type == Token::LAST)
         {
             mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
--- a/gfx/angle/src/compiler/preprocessor/MacroExpander.h
+++ b/gfx/angle/src/compiler/preprocessor/MacroExpander.h
@@ -62,13 +62,18 @@ class MacroExpander : public Lexer
 
     Lexer *mLexer;
     MacroSet *mMacroSet;
     Diagnostics *mDiagnostics;
 
     std::unique_ptr<Token> mReserveToken;
     std::vector<MacroContext *> mContextStack;
     size_t mTotalTokensInContexts;
+
+    bool mDeferReenablingMacros;
+    std::vector<const Macro *> mMacrosToReenable;
+
+    class ScopedMacroReenabler;
 };
 
 }  // namespace pp
 
 #endif  // COMPILER_PREPROCESSOR_MACROEXPANDER_H_
--- a/gfx/angle/src/tests/preprocessor_tests/define_test.cpp
+++ b/gfx/angle/src/tests/preprocessor_tests/define_test.cpp
@@ -966,8 +966,23 @@ TEST_F(DefineTest, UndefineInInvocationP
         "(1, 2)\n";
     const char *expected = "\n\n\n1 2\n";
 
     EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED,
                                     pp::SourceLocation(0, 3), _));
 
     preprocess(input, expected);
 }
+
+// The name of the macro "a" is inside an incomplete macro invocation of macro "m()" in its own
+// expansion. This should not result in infinite recursion.
+TEST_F(DefineTest, RecursiveMacroNameInsideIncompleteMacroInvocationInMacroExpansion)
+{
+    const char *input =
+        "#define m(a)\n"
+        "#define a m((a)\n"
+        "a)\n";
+    const char *expected =
+        "\n"
+        "\n"
+        "\n";
+    preprocess(input, expected);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/1325159-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+#o_1 {
+  position: absolute;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background: red;
+}
+#o_2 {
+  height: 10000px;
+}
+</style>
+<script>
+function boom(){
+  let doc = document.documentElement;
+  o_2.style.MozBorderEndStyle = "dotted";
+  doc.style.MozPerspective = "24.5pt";
+  o_0.style.MozTransformStyle = "preserve-3d";
+  doc.style.overflow = "-moz-scrollbars-horizontal";
+  doc.style.textOverflow = "''";
+  o_0.style.offsetInlineStart = "calc(3*25px)";
+  doc.style.paddingTop = "calc(67108864%)";
+  doc.style.width = "3e-0%";
+  o_0.style.display = "-moz-stack";
+  o_0.style.position = "relative";
+}
+addEventListener("DOMContentLoaded", boom);
+</script>
+</head>
+<body id=o_0><div id=o_1></div><div id=o_2></div></body>
+</html>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -127,8 +127,9 @@ asserts-if(stylo,1) pref(security.fileur
 load 1034403-1.html
 load 1205900.html
 load 1134549-1.svg
 load balinese-letter-spacing.html
 load 1216832-1.html
 load 1225125-1.html
 asserts-if(stylo,2) load 1308394.html # bug 1324669
 load 1317403-1.html
+load 1325159-1.html
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -533,23 +533,16 @@ bool Channel::ChannelImpl::ProcessIncomi
                                                 &fds[fds_i], m.header()->num_fds);
         fds_i += m.header()->num_fds;
       }
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
       DLOG(INFO) << "received message on channel @" << this <<
         " with type " << m.type();
 #endif
 
-#ifdef MOZ_TASK_TRACER
-      AutoSaveCurTraceInfo saveCurTraceInfo;
-      SetCurTraceInfo(m.header()->source_event_id,
-                      m.header()->parent_task_id,
-                      m.header()->source_event_type);
-#endif
-
       if (m.routing_id() == MSG_ROUTING_NONE &&
           m.type() == HELLO_MESSAGE_TYPE) {
         // The Hello message contains only the process id.
         listener_->OnChannelConnected(MessageIterator(m).NextInt());
 #if defined(OS_MACOSX)
       } else if (m.routing_id() == MSG_ROUTING_NONE &&
                  m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
         DCHECK(m.fd_cookie() != 0);
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -8,17 +8,17 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
 
 #if defined(OS_POSIX)
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
 #ifdef MOZ_TASK_TRACER
-#include "GeckoTaskTracer.h"
+#include "GeckoTaskTracerImpl.h"
 #endif
 
 #include "mozilla/Move.h"
 
 #ifdef MOZ_TASK_TRACER
 using namespace mozilla::tasktracer;
 #endif
 
@@ -140,9 +140,44 @@ void Message::EnsureFileDescriptorSet() 
 }
 
 uint32_t Message::num_fds() const {
   return file_descriptor_set() ? file_descriptor_set()->size() : 0;
 }
 
 #endif
 
+#ifdef MOZ_TASK_TRACER
+void *MessageTask() {
+  return reinterpret_cast<void*>(&MessageTask);
+}
+
+void
+Message::TaskTracerDispatch() {
+  header()->task_id = GenNewUniqueTaskId();
+  uintptr_t* vtab = reinterpret_cast<uintptr_t*>(&MessageTask);
+  LogVirtualTablePtr(header()->task_id,
+                     header()->source_event_id,
+                     vtab);
+  LogDispatch(header()->task_id,
+              header()->parent_task_id,
+              header()->source_event_id,
+              header()->source_event_type);
+}
+
+Message::AutoTaskTracerRun::AutoTaskTracerRun(Message& aMsg)
+  : mMsg(aMsg)
+  , mTaskId(mMsg.header()->task_id)
+  , mSourceEventId(mMsg.header()->source_event_id) {
+  LogBegin(mMsg.header()->task_id,
+           mMsg.header()->source_event_id);
+  SetCurTraceInfo(mMsg.header()->source_event_id,
+                  mMsg.header()->task_id,
+                  mMsg.header()->source_event_type);
+}
+
+Message::AutoTaskTracerRun::~AutoTaskTracerRun() {
+  AddLabel("IPC Message %s", mMsg.name());
+  LogEnd(mTaskId, mSourceEventId);
+}
+#endif
+
 }  // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_message.h
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -267,16 +267,29 @@ class Message : public Pickle {
   void set_sync() {
     header()->flags |= SYNC_BIT;
   }
 
   void set_interrupt() {
     header()->flags |= INTERRUPT_BIT;
   }
 
+#ifdef MOZ_TASK_TRACER
+  void TaskTracerDispatch();
+  class AutoTaskTracerRun
+    : public mozilla::tasktracer::AutoSaveCurTraceInfo {
+    Message& mMsg;
+    uint64_t mTaskId;
+    uint64_t mSourceEventId;
+  public:
+    explicit AutoTaskTracerRun(Message& aMsg);
+    ~AutoTaskTracerRun();
+  };
+#endif
+
 #if !defined(OS_MACOSX)
  protected:
 #endif
 
   // flags
   enum {
     NESTED_MASK     = 0x0003,
     PRIO_BIT        = 0x0004,
@@ -306,16 +319,17 @@ class Message : public Pickle {
       // For RPC and Urgent messages, a transaction ID for message ordering.
       int32_t txid;
     };
     // The actual local stack depth.
     uint32_t interrupt_local_stack_depth;
     // Sequence number
     int32_t seqno;
 #ifdef MOZ_TASK_TRACER
+    uint64_t task_id;
     uint64_t source_event_id;
     uint64_t parent_task_id;
     mozilla::tasktracer::SourceEventType source_event_type;
 #endif
   };
 
   Header* header() {
     return headerT<Header>();
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -17,16 +17,21 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Logging.h"
 #include "nsAutoPtr.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
 #include "nsContentUtils.h"
 
+#ifdef MOZ_TASK_TRACER
+#include "GeckoTaskTracer.h"
+using namespace mozilla::tasktracer;
+#endif
+
 using mozilla::Move;
 
 // Undo the damage done by mozzconf.h
 #undef compress
 
 // Logging seems to be somewhat broken on b2g.
 #ifdef MOZ_B2G
 #define IPC_LOG(...)
@@ -974,16 +979,19 @@ MessageChannel::OnMessageReceivedFromLin
     //
     // (3) We are not waiting on a reply.
     //   - We post a task to the main event loop.
     //
     // Note that, we may notify the main thread even though the monitor is not
     // blocked. This is okay, since we always check for pending events before
     // blocking again.
 
+#ifdef MOZ_TASK_TRACER
+    aMsg.TaskTracerDispatch();
+#endif
     RefPtr<MessageTask> task = new MessageTask(this, Move(aMsg));
     mPending.insertBack(task);
 
     if (shouldWakeUp) {
         NotifyWorkerThread();
     }
 
     if (shouldPostTask) {
@@ -1074,16 +1082,19 @@ MessageChannel::Send(Message* aMsg, Mess
     // Sanity checks.
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, false);
     NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
+#ifdef MOZ_TASK_TRACER
+    AutoScopedLabel autolabel("sync message %s", aMsg->name());
+#endif
 
     CxxStackFrame f(*this, OUT_MESSAGE, msg);
 
     MonitorAutoLock lock(*mMonitor);
 
     if (mTimedOutMessageSeqno) {
         // Don't bother sending another sync message if a previous one timed out
         // and we haven't received a reply for it. Once the original timed-out
@@ -1266,16 +1277,19 @@ MessageChannel::Call(Message* aMsg, Mess
 {
     nsAutoPtr<Message> msg(aMsg);
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
 #endif
+#ifdef MOZ_TASK_TRACER
+    AutoScopedLabel autolabel("sync message %s", aMsg->name());
+#endif
 
     // This must come before MonitorAutoLock, as its destructor acquires the
     // monitor lock.
     CxxStackFrame cxxframe(*this, OUT_MESSAGE, msg);
 
     MonitorAutoLock lock(*mMonitor);
     if (!Connected()) {
         ReportConnectionError("MessageChannel::Call", msg);
@@ -1411,16 +1425,19 @@ MessageChannel::Call(Message* aMsg, Mess
 
             return !is_reply_error;
         }
 
         // Dispatch an Interrupt in-call. Snapshot the current stack depth while we
         // own the monitor.
         size_t stackDepth = InterruptStackDepth();
         {
+#ifdef MOZ_TASK_TRACER
+            Message::AutoTaskTracerRun tasktracerRun(recvd);
+#endif
             MonitorAutoUnlock unlock(*mMonitor);
 
             CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
             DispatchInterruptMessage(Move(recvd), stackDepth);
         }
         if (!Connected()) {
             ReportConnectionError("MessageChannel::DispatchInterruptMessage");
             return false;
@@ -1670,16 +1687,19 @@ MessageChannel::DispatchMessage(Message 
 
     {
         AutoEnterTransaction transaction(this, aMsg);
 
         int id = aMsg.transaction_id();
         MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == transaction.TransactionID());
 
         {
+#ifdef MOZ_TASK_TRACER
+            Message::AutoTaskTracerRun tasktracerRun(aMsg);
+#endif
             MonitorAutoUnlock unlock(*mMonitor);
             CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
 
             mListener->ArtificialSleep();
 
             if (aMsg.is_sync())
                 DispatchSyncMessage(aMsg, *getter_Transfers(reply));
             else if (aMsg.is_interrupt())
@@ -1706,16 +1726,19 @@ MessageChannel::DispatchMessage(Message 
 void
 MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
 {
     AssertWorkerThread();
 
     int nestedLevel = aMsg.nested_level();
 
     MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread());
+#ifdef MOZ_TASK_TRACER
+    AutoScopedLabel autolabel("sync message %s", aMsg.name());
+#endif
 
     MessageChannel* dummy;
     MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
 
     Result rv;
     {
         AutoSetValue<MessageChannel*> blocked(blockingVar, this);
         rv = mListener->OnMessageReceived(aMsg, aReply);
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -129,23 +129,23 @@ class GCRekeyableHashMap : public JS::GC
     // GCRekeyableHashMap is movable
     GCRekeyableHashMap(GCRekeyableHashMap&& rhs) : Base(mozilla::Move(rhs)) {}
     void operator=(GCRekeyableHashMap&& rhs) {
         MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
         Base::operator=(mozilla::Move(rhs));
     }
 };
 
-template <typename Outer, typename... Args>
-class GCHashMapOperations
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
 {
     using Map = JS::GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
 
-    const Map& map() const { return static_cast<const Outer*>(this)->get(); }
+    const Map& map() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Map::AddPtr;
     using Ptr = typename Map::Ptr;
     using Range = typename Map::Range;
 
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
@@ -158,28 +158,28 @@ class GCHashMapOperations
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return map().sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-template <typename Outer, typename... Args>
-class MutableGCHashMapOperations
-  : public GCHashMapOperations<Outer, Args...>
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
+  : public WrappedPtrOperations<JS::GCHashMap<Args...>, Wrapper>
 {
     using Map = JS::GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
 
-    Map& map() { return static_cast<Outer*>(this)->get(); }
+    Map& map() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Map::AddPtr;
-    struct Enum : public Map::Enum { explicit Enum(Outer& o) : Map::Enum(o.map()) {} };
+    struct Enum : public Map::Enum { explicit Enum(Wrapper& o) : Map::Enum(o.map()) {} };
     using Ptr = typename Map::Ptr;
     using Range = typename Map::Range;
 
     bool init(uint32_t len = 16) { return map().init(len); }
     void clear()                 { map().clear(); }
     void finish()                { map().finish(); }
     void remove(Ptr p)           { map().remove(p); }
 
@@ -206,36 +206,16 @@ class MutableGCHashMapOperations
     }
 
     template<typename KeyInput, typename ValueInput>
     bool putNew(KeyInput&& k, ValueInput&& v) {
         return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
     }
 };
 
-template <typename A, typename B, typename C, typename D, typename E>
-class RootedBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::Rooted<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class MutableHandleBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::MutableHandle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class HandleBase<JS::GCHashMap<A,B,C,D,E>>
-  : public GCHashMapOperations<JS::Handle<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
-template <typename A, typename B, typename C, typename D, typename E>
-class WeakCacheBase<JS::GCHashMap<A,B,C,D,E>>
-  : public MutableGCHashMapOperations<JS::WeakCache<JS::GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
-{};
-
 } // namespace js
 
 namespace JS {
 
 // A GCHashSet is a HashSet with an additional trace method that knows
 // be traced to be kept alive will generally want to use this GCHashSet
 // specialization in lieu of HashSet.
 //
@@ -287,23 +267,23 @@ class GCHashSet : public js::HashSet<T, 
     GCHashSet(const GCHashSet& hs) = delete;
     GCHashSet& operator=(const GCHashSet& hs) = delete;
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename... Args>
-class GCHashSetOperations
+template <typename Wrapper, typename... Args>
+class WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
 {
     using Set = JS::GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
 
-    const Set& set() const { return static_cast<const Outer*>(this)->get(); }
+    const Set& set() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Set::AddPtr;
     using Entry = typename Set::Entry;
     using Ptr = typename Set::Ptr;
     using Range = typename Set::Range;
 
     bool initialized() const                   { return set().initialized(); }
@@ -317,29 +297,29 @@ class GCHashSetOperations
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return set().sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-template <typename Outer, typename... Args>
-class MutableGCHashSetOperations
-  : public GCHashSetOperations<Outer, Args...>
+template <typename Wrapper, typename... Args>
+class MutableWrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
+  : public WrappedPtrOperations<JS::GCHashSet<Args...>, Wrapper>
 {
     using Set = JS::GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
 
-    Set& set() { return static_cast<Outer*>(this)->get(); }
+    Set& set() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     using AddPtr = typename Set::AddPtr;
     using Entry = typename Set::Entry;
-    struct Enum : public Set::Enum { explicit Enum(Outer& o) : Set::Enum(o.set()) {} };
+    struct Enum : public Set::Enum { explicit Enum(Wrapper& o) : Set::Enum(o.set()) {} };
     using Ptr = typename Set::Ptr;
     using Range = typename Set::Range;
 
     bool init(uint32_t len = 16) { return set().init(len); }
     void clear()                 { set().clear(); }
     void finish()                { set().finish(); }
     void remove(Ptr p)           { set().remove(p); }
     void remove(const Lookup& l) { set().remove(l); }
@@ -365,35 +345,11 @@ class MutableGCHashSetOperations
     }
 
     template<typename TInput>
     bool putNew(const Lookup& l, TInput&& t) {
         return set().putNew(l, mozilla::Forward<TInput>(t));
     }
 };
 
-template <typename T, typename HP, typename AP>
-class RootedBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::Rooted<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class MutableHandleBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::MutableHandle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class HandleBase<JS::GCHashSet<T, HP, AP>>
-  : public GCHashSetOperations<JS::Handle<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
-template <typename T, typename HP, typename AP>
-class WeakCacheBase<JS::GCHashSet<T, HP, AP>>
-  : public MutableGCHashSetOperations<JS::WeakCache<JS::GCHashSet<T, HP, AP>>, T, HP, AP>
-{
-};
-
 } /* namespace js */
 
 #endif /* GCHashTable_h */
--- a/js/public/GCVariant.h
+++ b/js/public/GCVariant.h
@@ -119,23 +119,23 @@ struct GCPolicy<mozilla::Variant<Ts...>>
         Impl::trace(trc, v, name);
     }
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename... Ts>
-class GCVariantOperations
+template <typename Wrapper, typename... Ts>
+class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
 {
     using Impl = JS::detail::GCVariantImplementation<Ts...>;
     using Variant = mozilla::Variant<Ts...>;
 
-    const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
+    const Variant& variant() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     template <typename T>
     bool is() const {
         return variant().template is<T>();
     }
 
     template <typename T>
@@ -145,54 +145,34 @@ class GCVariantOperations
 
     template <typename Matcher>
     typename Matcher::ReturnType
     match(Matcher& matcher) const {
         return Impl::match(matcher, JS::Handle<Variant>::fromMarkedLocation(&variant()));
     }
 };
 
-template <typename Outer, typename... Ts>
-class MutableGCVariantOperations
-  : public GCVariantOperations<Outer, Ts...>
+template <typename Wrapper, typename... Ts>
+class MutableWrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
+  : public WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper>
 {
     using Impl = JS::detail::GCVariantImplementation<Ts...>;
     using Variant = mozilla::Variant<Ts...>;
 
-    const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
-    Variant& variant() { return static_cast<Outer*>(this)->get(); }
+    const Variant& variant() const { return static_cast<const Wrapper*>(this)->get(); }
+    Variant& variant() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     template <typename T>
     JS::MutableHandle<T> as() {
         return JS::MutableHandle<T>::fromMarkedLocation(&variant().template as<T>());
     }
 
     template <typename Matcher>
     typename Matcher::ReturnType
     match(Matcher& matcher) {
         return Impl::match(matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant()));
     }
 };
 
-template <typename... Ts>
-class RootedBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::Rooted<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class MutableHandleBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::MutableHandle<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class HandleBase<mozilla::Variant<Ts...>>
-  : public GCVariantOperations<JS::Handle<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
-template <typename... Ts>
-class PersistentRootedBase<mozilla::Variant<Ts...>>
-  : public MutableGCVariantOperations<JS::PersistentRooted<mozilla::Variant<Ts...>>, Ts...>
-{ };
-
 } // namespace js
 
 #endif // js_GCVariant_h
--- a/js/public/GCVector.h
+++ b/js/public/GCVector.h
@@ -130,43 +130,43 @@ class GCVector
             GCPolicy<T>::trace(trc, &elem, "vector element");
     }
 };
 
 } // namespace JS
 
 namespace js {
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class GCVectorOperations
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
 {
     using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     size_t length() const { return vec().length(); }
     bool empty() const { return vec().empty(); }
     size_t capacity() const { return vec().capacity(); }
     const T* begin() const { return vec().begin(); }
     const T* end() const { return vec().end(); }
     const T& back() const { return vec().back(); }
 
     JS::Handle<T> operator[](size_t aIndex) const {
         return JS::Handle<T>::fromMarkedLocation(&vec().operator[](aIndex));
     }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class MutableGCVectorOperations
-  : public GCVectorOperations<Outer, T, Capacity, AllocPolicy>
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class MutableWrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
+  : public WrappedPtrOperations<JS::GCVector<T, Capacity, AllocPolicy>, Wrapper>
 {
     using Vec = JS::GCVector<T, Capacity, AllocPolicy>;
-    const Vec& vec() const { return static_cast<const Outer*>(this)->get(); }
-    Vec& vec() { return static_cast<Outer*>(this)->get(); }
+    const Vec& vec() const { return static_cast<const Wrapper*>(this)->get(); }
+    Vec& vec() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     const AllocPolicy& allocPolicy() const { return vec().allocPolicy(); }
     AllocPolicy& allocPolicy() { return vec().allocPolicy(); }
     const T* begin() const { return vec().begin(); }
     T* begin() { return vec().begin(); }
     const T* end() const { return vec().end(); }
     T* end() { return vec().end(); }
@@ -219,31 +219,11 @@ class MutableGCVectorOperations
     T popCopy() { return vec().popCopy(); }
     template<typename U> T* insert(T* aP, U&& aVal) {
         return vec().insert(aP, mozilla::Forward<U>(aVal));
     }
     void erase(T* aT) { vec().erase(aT); }
     void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
 };
 
-template <typename T, size_t N, typename AP>
-class RootedBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::Rooted<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class MutableHandleBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::MutableHandle<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class HandleBase<JS::GCVector<T,N,AP>>
-  : public GCVectorOperations<JS::Handle<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
-template <typename T, size_t N, typename AP>
-class PersistentRootedBase<JS::GCVector<T,N,AP>>
-  : public MutableGCVectorOperations<JS::PersistentRooted<JS::GCVector<T,N,AP>>, T,N,AP>
-{};
-
 } // namespace js
 
 #endif // js_GCVector_h
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -108,38 +108,44 @@
  */
 
 namespace js {
 
 template <typename T>
 struct BarrierMethods {
 };
 
-template <typename T>
-class RootedBase {};
+template <typename Element, typename Wrapper>
+class WrappedPtrOperations {};
 
-template <typename T>
-class HandleBase {};
+template <typename Element, typename Wrapper>
+class MutableWrappedPtrOperations : public WrappedPtrOperations<Element, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class RootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
-template <typename T>
-class MutableHandleBase {};
+template <typename T, typename Wrapper>
+class HandleBase : public WrappedPtrOperations<T, Wrapper> {};
 
-template <typename T>
-class HeapBase {};
+template <typename T, typename Wrapper>
+class MutableHandleBase : public MutableWrappedPtrOperations<T, Wrapper> {};
+
+template <typename T, typename Wrapper>
+class HeapBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
 // Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many macros into scope
 template <typename T> struct IsHeapConstructibleType    { static constexpr bool value = false; };
 #define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
     template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; };
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 #undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
 
-template <typename T>
-class PersistentRootedBase {};
+template <typename T, typename Wrapper>
+class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
 static void* const ConstNullValue = nullptr;
 
 namespace gc {
 struct Cell;
 template<typename T>
 struct PersistentRootedMarker;
 } /* namespace gc */
@@ -218,17 +224,17 @@ AssertGCThingIsNotAnObjectSubclass(js::g
  * keep the pointed-to GC thing alive.
  *
  * Heap<T> objects should only be used on the heap. GC references stored on the
  * C/C++ stack must use Rooted/Handle/MutableHandle instead.
  *
  * Type T must be a public GC pointer type.
  */
 template <typename T>
-class Heap : public js::HeapBase<T>
+class Heap : public js::HeapBase<T, Heap<T>>
 {
     // Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for legacy reasons.
     static_assert(js::IsHeapConstructibleType<T>::value,
                   "Type T must be a public GC pointer type");
   public:
     using ElementType = T;
 
     Heap() {
@@ -357,17 +363,17 @@ ScriptIsMarkedGray(const Heap<JSScript*>
  * The considerations to keep in mind when using a TenuredHeap<T> vs a normal
  * Heap<T> are:
  *
  *  - It is invalid for a TenuredHeap<T> to refer to a non-tenured thing.
  *  - It is however valid for a Heap<T> to refer to a tenured thing.
  *  - It is not possible to store flag bits in a Heap<T>.
  */
 template <typename T>
-class TenuredHeap : public js::HeapBase<T>
+class TenuredHeap : public js::HeapBase<T, TenuredHeap<T>>
 {
   public:
     using ElementType = T;
 
     TenuredHeap() : bits(0) {
         static_assert(sizeof(T) == sizeof(TenuredHeap<T>),
                       "TenuredHeap<T> must be binary compatible with T.");
     }
@@ -440,17 +446,17 @@ class TenuredHeap : public js::HeapBase<
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
  * rooted. See "Move GC Stack Rooting" above.
  *
  * If you want to add additional methods to Handle for a specific
  * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T>
+class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>>
 {
     friend class JS::MutableHandle<T>;
 
   public:
     using ElementType = T;
 
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
@@ -530,17 +536,17 @@ class MOZ_NONHEAP_CLASS Handle : public 
  * Similar to a handle, but the underlying storage can be changed. This is
  * useful for outparams.
  *
  * If you want to add additional methods to MutableHandle for a specific
  * specialization, define a MutableHandleBase<T> specialization containing
  * them.
  */
 template <typename T>
-class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
+class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T, MutableHandle<T>>
 {
   public:
     using ElementType = T;
 
     inline MOZ_IMPLICIT MutableHandle(Rooted<T>* root);
     inline MOZ_IMPLICIT MutableHandle(PersistentRooted<T>* root);
 
   private:
@@ -742,17 +748,17 @@ namespace JS {
  * Local variable of type T whose value is always rooted. This is typically
  * used for local variables, or for non-rooted values being passed to a
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
  *
  * If you want to add additional methods to Rooted for a specific
  * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_RAII Rooted : public js::RootedBase<T>
+class MOZ_RAII Rooted : public js::RootedBase<T, Rooted<T>>
 {
     inline void registerWithRootLists(js::RootedListHeads& roots) {
         this->stack = &roots[JS::MapTypeToRootKind<T>::kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
     inline js::RootedListHeads& rootLists(JS::RootingContext* cx) {
@@ -840,45 +846,45 @@ namespace js {
  * class-querying and downcasting operations.
  *
  * Given a Rooted<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
  * as an optimization of
  *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
  *   Handle<StringObject*> h = rooted;
  */
-template <>
-class RootedBase<JSObject*>
+template <typename Container>
+class RootedBase<JSObject*, Container> : public MutableWrappedPtrOperations<JSObject*, Container>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
 /**
  * Augment the generic Handle<T> interface when T = JSObject* with
  * downcasting operations.
  *
  * Given a Handle<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
  * as an optimization of
  *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
  *   Handle<StringObject*> h = rooted;
  */
-template <>
-class HandleBase<JSObject*>
+template <typename Container>
+class HandleBase<JSObject*, Container> : public WrappedPtrOperations<JSObject*, Container>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
 /** Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
-class MOZ_RAII FakeRooted : public RootedBase<T>
+class MOZ_RAII FakeRooted : public RootedBase<T, FakeRooted<T>>
 {
   public:
     using ElementType = T;
 
     template <typename CX>
     explicit FakeRooted(CX* cx) : ptr(JS::GCPolicy<T>::initial()) {}
 
     template <typename CX>
@@ -896,17 +902,17 @@ class MOZ_RAII FakeRooted : public Roote
         ptr = value;
     }
 
     FakeRooted(const FakeRooted&) = delete;
 };
 
 /** Interface substitute for MutableHandle<T> which is not required to point to rooted memory. */
 template <typename T>
-class FakeMutableHandle : public js::MutableHandleBase<T>
+class FakeMutableHandle : public js::MutableHandleBase<T, FakeMutableHandle<T>>
 {
   public:
     using ElementType = T;
 
     MOZ_IMPLICIT FakeMutableHandle(T* t) {
         ptr = t;
     }
 
@@ -1065,17 +1071,17 @@ MutableHandle<T>::MutableHandle(Persiste
  * In the context of Firefox, this is a severe restriction: almost everything in
  * Firefox is owned by some JS object or another, so using PersistentRooted in
  * such objects would introduce leaks. For these kinds of edges, Heap<T> or
  * TenuredHeap<T> would be better types. It's up to the implementor of the type
  * containing Heap<T> or TenuredHeap<T> members to make sure their referents get
  * marked when the object itself is marked.
  */
 template<typename T>
-class PersistentRooted : public js::PersistentRootedBase<T>,
+class PersistentRooted : public js::RootedBase<T, PersistentRooted<T>>,
                          private mozilla::LinkedListElement<PersistentRooted<T>>
 {
     using ListBase = mozilla::LinkedListElement<PersistentRooted<T>>;
 
     friend class mozilla::LinkedList<PersistentRooted>;
     friend class mozilla::LinkedListElement<PersistentRooted>;
 
     void registerWithRootLists(js::RootLists& roots) {
@@ -1225,54 +1231,35 @@ class JS_PUBLIC_API(ObjectPtr)
     explicit operator bool() const { return value.unbarrieredGet(); }
     explicit operator bool() { return value.unbarrieredGet(); }
 };
 
 } /* namespace JS */
 
 namespace js {
 
-template <typename Outer, typename T, typename D>
-class UniquePtrOperations
+template <typename T, typename D, typename Container>
+class WrappedPtrOperations<UniquePtr<T, D>, Container>
 {
-    const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Outer*>(this)->get(); }
+    const UniquePtr<T, D>& uniquePtr() const { return static_cast<const Container*>(this)->get(); }
 
   public:
     explicit operator bool() const { return !!uniquePtr(); }
 };
 
-template <typename Outer, typename T, typename D>
-class MutableUniquePtrOperations : public UniquePtrOperations<Outer, T, D>
+template <typename T, typename D, typename Container>
+class MutableWrappedPtrOperations<UniquePtr<T, D>, Container>
+  : public WrappedPtrOperations<UniquePtr<T, D>, Container>
 {
-    UniquePtr<T, D>& uniquePtr() { return static_cast<Outer*>(this)->get(); }
+    UniquePtr<T, D>& uniquePtr() { return static_cast<Container*>(this)->get(); }
 
   public:
     MOZ_MUST_USE typename UniquePtr<T, D>::Pointer release() { return uniquePtr().release(); }
 };
 
-template <typename T, typename D>
-class RootedBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::Rooted<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class MutableHandleBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::MutableHandle<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class HandleBase<UniquePtr<T, D>>
-  : public UniquePtrOperations<JS::Handle<UniquePtr<T, D>>, T, D>
-{ };
-
-template <typename T, typename D>
-class PersistentRootedBase<UniquePtr<T, D>>
-  : public MutableUniquePtrOperations<JS::PersistentRooted<UniquePtr<T, D>>, T, D>
-{ };
-
 namespace gc {
 
 template <typename T, typename TraceCallbacks>
 void
 CallTraceCallbackOnNonHeap(T* v, const TraceCallbacks& aCallbacks, const char* aName, void* aClosure)
 {
     static_assert(sizeof(T) == sizeof(JS::Heap<T>), "T and Heap<T> must be compatible.");
     MOZ_ASSERT(v);
--- a/js/public/SweepingAPI.h
+++ b/js/public/SweepingAPI.h
@@ -4,33 +4,28 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_SweepingAPI_h
 #define js_SweepingAPI_h
 
 #include "js/HeapAPI.h"
 
-namespace js {
-template <typename T>
-class WeakCacheBase {};
-} // namespace js
-
 namespace JS {
 template <typename T> class WeakCache;
 
 namespace shadow {
 JS_PUBLIC_API(void)
 RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
 } // namespace shadow
 
 // A WeakCache stores the given Sweepable container and links itself into a
 // list of such caches that are swept during each GC.
 template <typename T>
-class WeakCache : public js::WeakCacheBase<T>,
+class WeakCache : public js::MutableWrappedPtrOperations<T, WeakCache<T>>,
                   private mozilla::LinkedListElement<WeakCache<T>>
 {
     friend class mozilla::LinkedListElement<WeakCache<T>>;
     friend class mozilla::LinkedList<WeakCache<T>>;
 
     WeakCache() = delete;
     WeakCache(const WeakCache&) = delete;
 
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1283,30 +1283,28 @@ struct BarrierMethods<JS::Value>
     static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
         JS::HeapValuePostBarrier(v, prev, next);
     }
     static void exposeToJS(const JS::Value& v) {
         JS::ExposeValueToActiveJS(v);
     }
 };
 
-template <class Outer> class MutableValueOperations;
+template <class Wrapper> class MutableValueOperations;
 
 /**
  * A class designed for CRTP use in implementing the non-mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * ValueOperations<Outer> with a visible get() method returning a const
- * reference to the Value abstracted by Outer.
+ * Value interface in Value-like classes.  Wrapper must be a class inheriting
+ * ValueOperations<Wrapper> with a visible get() method returning a const
+ * reference to the Value abstracted by Wrapper.
  */
-template <class Outer>
-class ValueOperations
+template <class Wrapper>
+class WrappedPtrOperations<JS::Value, Wrapper>
 {
-    friend class MutableValueOperations<Outer>;
-
-    const JS::Value& value() const { return static_cast<const Outer*>(this)->get(); }
+    const JS::Value& value() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool isUndefined() const { return value().isUndefined(); }
     bool isNull() const { return value().isNull(); }
     bool isBoolean() const { return value().isBoolean(); }
     bool isTrue() const { return value().isTrue(); }
     bool isFalse() const { return value().isFalse(); }
     bool isNumber() const { return value().isNumber(); }
@@ -1341,24 +1339,24 @@ class ValueOperations
     JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
 
     JSWhyMagic whyMagic() const { return value().whyMagic(); }
     uint32_t magicUint32() const { return value().magicUint32(); }
 };
 
 /**
  * A class designed for CRTP use in implementing all the mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations<Outer> with visible get() methods returning const and
- * non-const references to the Value abstracted by Outer.
+ * Value interface in Value-like classes.  Wrapper must be a class inheriting
+ * MutableWrappedPtrOperations<Wrapper> with visible get() methods returning const and
+ * non-const references to the Value abstracted by Wrapper.
  */
-template <class Outer>
-class MutableValueOperations : public ValueOperations<Outer>
+template <class Wrapper>
+class MutableWrappedPtrOperations<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
 {
-    JS::Value& value() { return static_cast<Outer*>(this)->get(); }
+    JS::Value& value() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void setNull() { value().setNull(); }
     void setUndefined() { value().setUndefined(); }
     void setInt32(int32_t i) { value().setInt32(i); }
     void setDouble(double d) { value().setDouble(d); }
     void setNaN() { setDouble(JS::GenericNaN()); }
     void setBoolean(bool b) { value().setBoolean(b); }
@@ -1373,23 +1371,19 @@ class MutableValueOperations : public Va
     void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
     void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
 };
 
 /*
  * Augment the generic Heap<T> interface when T = Value with
  * type-querying, value-extracting, and mutating operations.
  */
-template <>
-class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
+template <typename Wrapper>
+class HeapBase<JS::Value, Wrapper> : public WrappedPtrOperations<JS::Value, Wrapper>
 {
-    typedef JS::Heap<JS::Value> Outer;
-
-    friend class ValueOperations<Outer>;
-
     void setBarriered(const JS::Value& v) {
         *static_cast<JS::Heap<JS::Value>*>(this) = v;
     }
 
   public:
     void setNull() { setBarriered(JS::NullValue()); }
     void setUndefined() { setBarriered(JS::UndefinedValue()); }
     void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); }
@@ -1426,32 +1420,16 @@ class HeapBase<JS::Value> : public Value
     void setObjectOrNull(JSObject* arg) {
         if (arg)
             setObject(*arg);
         else
             setNull();
     }
 };
 
-template <>
-class HandleBase<JS::Value> : public ValueOperations<JS::Handle<JS::Value> >
-{};
-
-template <>
-class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
-{};
-
-template <>
-class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
-{};
-
-template <>
-class PersistentRootedBase<JS::Value> : public MutableValueOperations<JS::PersistentRooted<JS::Value>>
-{};
-
 /*
  * If the Value is a GC pointer type, convert to that type and call |f| with
  * the pointer. If the Value is not a GC type, calls F::defaultValue.
  */
 template <typename F, typename... Args>
 auto
 DispatchTyped(F f, const JS::Value& val, Args&&... args)
   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -48,24 +48,32 @@ class HashableValue
     HashableValue trace(JSTracer* trc) const;
     Value get() const { return value.get(); }
 
     void trace(JSTracer* trc) {
         TraceEdge(trc, &value, "HashableValue");
     }
 };
 
-template <>
-class RootedBase<HashableValue> {
+template <typename Wrapper>
+class WrappedPtrOperations<HashableValue, Wrapper>
+{
+  public:
+    Value value() const {
+        return static_cast<const Wrapper*>(this)->get().get();
+    }
+};
+
+template <typename Wrapper>
+class MutableWrappedPtrOperations<HashableValue, Wrapper>
+  : public WrappedPtrOperations<HashableValue, Wrapper>
+{
   public:
     MOZ_MUST_USE bool setValue(JSContext* cx, HandleValue v) {
-        return static_cast<JS::Rooted<HashableValue>*>(this)->get().setValue(cx, v);
-    }
-    Value value() const {
-        return static_cast<const JS::Rooted<HashableValue>*>(this)->get().get();
+        return static_cast<Wrapper*>(this)->get().setValue(cx, v);
     }
 };
 
 template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashMap;
 
 template <class T, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashSet;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -781,17 +781,27 @@ ScheduleGC(JSContext* cx, unsigned argc,
         /* Schedule a GC to happen after |arg| allocations. */
         JS_ScheduleGC(cx, args[0].toInt32());
     } else if (args[0].isObject()) {
         /* Ensure that |zone| is collected during the next GC. */
         Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
         PrepareZoneForGC(zone);
     } else if (args[0].isString()) {
         /* This allows us to schedule the atoms zone for GC. */
-        PrepareZoneForGC(args[0].toString()->zone());
+        Zone* zone = args[0].toString()->zoneFromAnyThread();
+        if (!CurrentThreadCanAccessZone(zone)) {
+            RootedObject callee(cx, &args.callee());
+            ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
+            return false;
+        }
+        PrepareZoneForGC(zone);
+    } else {
+        RootedObject callee(cx, &args.callee());
+        ReportUsageErrorASCII(cx, callee, "Bad argument - expecting integer, object or string");
+        return false;
     }
 
     uint32_t zealBits;
     uint32_t freq;
     uint32_t next;
     JS_GetGCZealBits(cx, &zealBits, &freq, &next);
     args.rval().setInt32(next);
     return true;
@@ -4214,19 +4224,20 @@ JS_FN_HELP("rejectPromise", RejectPromis
 "  Preserve JIT code during garbage collections."),
 
 #ifdef JS_GC_ZEAL
     JS_FN_HELP("gczeal", GCZeal, 2, 0,
 "gczeal(level, [N])",
 gc::ZealModeHelpText),
 
     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
-"schedulegc([num | obj])",
+"schedulegc([num | obj | string])",
 "  If num is given, schedule a GC after num allocations.\n"
 "  If obj is given, schedule a GC of obj's zone.\n"
+"  If string is given, schedule a GC of the string's zone if possible.\n"
 "  Returns the number of allocations before the next trigger."),
 
     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
 "selectforgc(obj1, obj2, ...)",
 "  Schedule the given objects to be marked in the next GC slice."),
 
     JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
 "verifyprebarriers()",
--- a/js/src/ds/TraceableFifo.h
+++ b/js/src/ds/TraceableFifo.h
@@ -47,82 +47,42 @@ class TraceableFifo : public js::Fifo<T,
     void trace(JSTracer* trc) {
         for (size_t i = 0; i < this->front_.length(); ++i)
             JS::GCPolicy<T>::trace(trc, &this->front_[i], "fifo element");
         for (size_t i = 0; i < this->rear_.length(); ++i)
             JS::GCPolicy<T>::trace(trc, &this->rear_[i], "fifo element");
     }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class TraceableFifoOperations
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class WrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
 {
     using TF = TraceableFifo<T, Capacity, AllocPolicy>;
-    const TF& fifo() const { return static_cast<const Outer*>(this)->extract(); }
+    const TF& fifo() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     size_t length() const { return fifo().length(); }
     bool empty() const { return fifo().empty(); }
     const T& front() const { return fifo().front(); }
 };
 
-template <typename Outer, typename T, size_t Capacity, typename AllocPolicy>
-class MutableTraceableFifoOperations
-  : public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy>
+template <typename Wrapper, typename T, size_t Capacity, typename AllocPolicy>
+class MutableWrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
+  : public WrappedPtrOperations<TraceableFifo<T, Capacity, AllocPolicy>, Wrapper>
 {
     using TF = TraceableFifo<T, Capacity, AllocPolicy>;
-    TF& fifo() { return static_cast<Outer*>(this)->extract(); }
+    TF& fifo() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     T& front() { return fifo().front(); }
 
     template<typename U> bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward<U>(u)); }
     template<typename... Args> bool emplaceBack(Args&&... args) {
         return fifo().emplaceBack(mozilla::Forward<Args...>(args...));
     }
 
     bool popFront() { return fifo().popFront(); }
     void clear() { fifo().clear(); }
 };
 
-template <typename A, size_t B, typename C>
-class RootedBase<TraceableFifo<A,B,C>>
-  : public MutableTraceableFifoOperations<JS::Rooted<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::Rooted<TF>, A,B,C>;
-    const TF& extract() const { return *static_cast<const JS::Rooted<TF>*>(this)->address(); }
-
-    friend class MutableTraceableFifoOperations<JS::Rooted<TF>, A,B,C>;
-    TF& extract() { return *static_cast<JS::Rooted<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C>
-class MutableHandleBase<TraceableFifo<A,B,C>>
-  : public MutableTraceableFifoOperations<JS::MutableHandle<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::MutableHandle<TF>, A,B,C>;
-    const TF& extract() const {
-        return *static_cast<const JS::MutableHandle<TF>*>(this)->address();
-    }
-
-    friend class MutableTraceableFifoOperations<JS::MutableHandle<TF>, A,B,C>;
-    TF& extract() { return *static_cast<JS::MutableHandle<TF>*>(this)->address(); }
-};
-
-template <typename A, size_t B, typename C>
-class HandleBase<TraceableFifo<A,B,C>>
-  : public TraceableFifoOperations<JS::Handle<TraceableFifo<A,B,C>>, A,B,C>
-{
-    using TF = TraceableFifo<A,B,C>;
-
-    friend class TraceableFifoOperations<JS::Handle<TF>, A,B,C>;
-    const TF& extract() const {
-        return *static_cast<const JS::Handle<TF>*>(this)->address();
-    }
-};
-
 } // namespace js
 
 #endif // js_TraceableFifo_h
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -311,25 +311,19 @@ struct InternalBarrierMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_GCTHING(id); }
     static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); }
 
     static void preBarrier(jsid id) { DispatchTyped(PreBarrierFunctor<jsid>(), id); }
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 };
 
-// Barrier classes can use Mixins to add methods to a set of barrier
-// instantiations, to make the barriered thing look and feel more like the
-// thing itself.
-template <typename T>
-class BarrieredBaseMixins {};
-
 // Base class of all barrier types.
 template <typename T>
-class BarrieredBase : public BarrieredBaseMixins<T>
+class BarrieredBase
 {
   protected:
     // BarrieredBase is not directly instantiable.
     explicit BarrieredBase(const T& v) : value(v) {}
 
     // Storage for all barrier classes. |value| must be a GC thing reference
     // type: either a direct pointer to a GC thing or a supported tagged
     // pointer that can reference GC things, such as JS::Value or jsid. Nested
@@ -340,19 +334,22 @@ class BarrieredBase : public BarrieredBa
     // Note: this is public because C++ cannot friend to a specific template instantiation.
     // Friending to the generic template leads to a number of unintended consequences, including
     // template resolution ambiguity and a circular dependency with Tracing.h.
     T* unsafeUnbarrieredForTracing() { return &value; }
 };
 
 // Base class for barriered pointer types that intercept only writes.
 template <class T>
-class WriteBarrieredBase : public BarrieredBase<T>
+class WriteBarrieredBase : public BarrieredBase<T>,
+                           public WrappedPtrOperations<T, WriteBarrieredBase<T>>
 {
   protected:
+    using BarrieredBase<T>::value;
+
     // WriteBarrieredBase is not directly instantiable.
     explicit WriteBarrieredBase(const T& v) : BarrieredBase<T>(v) {}
 
   public:
     using ElementType = T;
 
     DECLARE_POINTER_CONSTREF_OPS(T);
 
@@ -561,18 +558,22 @@ class ReadBarrieredBase : public Barrier
 // Incremental GC requires that weak pointers have read barriers. See the block
 // comment at the top of Barrier.h for a complete discussion of why.
 //
 // Note that this class also has post-barriers, so is safe to use with nursery
 // pointers. However, when used as a hashtable key, care must still be taken to
 // insert manual post-barriers on the table for rekeying if the key is based in
 // any way on the address of the object.
 template <typename T>
-class ReadBarriered : public ReadBarrieredBase<T>
+class ReadBarriered : public ReadBarrieredBase<T>,
+                      public WrappedPtrOperations<T, ReadBarriered<T>>
 {
+  protected:
+    using ReadBarrieredBase<T>::value;
+
   public:
     ReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
 
     // It is okay to add barriers implicitly.
     MOZ_IMPLICIT ReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
@@ -629,22 +630,16 @@ class ReadBarriered : public ReadBarrier
     }
 };
 
 // A WeakRef pointer does not hold its target live and is automatically nulled
 // out when the GC discovers that it is not reachable from any other path.
 template <typename T>
 using WeakRef = ReadBarriered<T>;
 
-// Add Value operations to all Barrier types. Note, this must be defined before
-// HeapSlot for HeapSlot's base to get these operations.
-template <>
-class BarrieredBaseMixins<JS::Value> : public ValueOperations<WriteBarrieredBase<JS::Value>>
-{};
-
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
 // memory, but with substantially less overhead than a HeapPtr.
 class HeapSlot : public WriteBarrieredBase<Value>
 {
   public:
     enum Kind {
         Slot = 0,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1311060.js
@@ -0,0 +1,3 @@
+if (helperThreadCount() === 0)
+   quit();
+evalInWorker(`schedulegc("s1");`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-1324773-2.js
@@ -0,0 +1,15 @@
+if (!('gczeal' in this))
+    quit();
+var lfGlobal = newGlobal();
+lfGlobal.evaluate(`
+    for (var i = 0; i < 600; i++)
+        eval('function f' + i + '() {}');
+`);
+var lfGlobal = newGlobal();
+lfGlobal.evaluate(`
+    if (!('gczeal' in this))
+        quit();
+    var dbg = new Debugger();
+    gczeal(9, 10);
+    dbg.findScripts({});
+`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-1324773.js
@@ -0,0 +1,15 @@
+if (!('gczeal' in this))
+    quit();
+var lfGlobal = newGlobal();
+lfGlobal.evaluate(`
+    for (var i = 0; i < 600; i++)
+        eval('function f' + i + '() {}');
+`);
+var lfGlobal = newGlobal();
+lfGlobal.evaluate(`
+    if (!('gczeal' in this))
+        quit();
+    var dbg = new Debugger();
+    gczeal(9, 1);
+    dbg.findScripts({});
+`);
--- a/js/src/jit-test/tests/wasm/integer.js
+++ b/js/src/jit-test/tests/wasm/integer.js
@@ -202,43 +202,78 @@ assertEq(testTrunc(13.37), 1);
     testBinary64('rem_u', "0x8ff00ff00ff00ff0", "0x100000001", "0x80000001");
 
     testTrap64('div_s', 10, 0, /integer divide by zero/);
     testTrap64('div_s', "0x8000000000000000", -1, /integer overflow/);
     testTrap64('div_u', 0, 0, /integer divide by zero/);
     testTrap64('rem_s', 10, 0, /integer divide by zero/);
     testTrap64('rem_u', 10, 0, /integer divide by zero/);
 
-    testBinary64('and', 42, 6, 2);
+    // Bitops.
     testBinary64('or', 42, 6, 46);
-    testBinary64('xor', 42, 2, 40);
-    testBinary64('and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
     testBinary64('or', "0x8765432112345678", "0xffff0000ffff0000", "0xffff4321ffff5678");
+
+    testBinary64('xor', 42, 2, 40);
     testBinary64('xor', "0x8765432112345678", "0xffff0000ffff0000", "0x789a4321edcb5678");
-    testBinary64('shl', 40, 2, 160);
-    testBinary64('shr_s', -40, 2, -10);
-    testBinary64('shr_u', -40, 2, "0x3ffffffffffffff6");
-    testBinary64('shl', 0xff00ff, 28, "0xff00ff0000000");
+
+    testBinary64('shl', 0xff00ff, 28, "0x0ff00ff0000000");
+    testBinary64('shl', 0xff00ff, 30, "0x3fc03fc0000000");
+    testBinary64('shl', 0xff00ff, 31, "0x7f807f80000000");
+    testBinary64('shl', 0xff00ff, 32, "0xff00ff00000000");
     testBinary64('shl', 1, 63, "0x8000000000000000");
     testBinary64('shl', 1, 64, 1);
+    testBinary64('shl', 40, 2, 160);
+
+    testBinary64('shr_s', -40, 2, -10);
     testBinary64('shr_s', "0xff00ff0000000", 28, 0xff00ff);
+    testBinary64('shr_s', "0xff00ff0000000", 30, 0x3fc03f);
+    testBinary64('shr_s', "0xff00ff0000000", 31, 0x1fe01f);
+    testBinary64('shr_s', "0xff00ff0000000", 32, 0x0ff00f);
+
+    testBinary64('shr_u', -40, 2, "0x3ffffffffffffff6");
+    testBinary64('shr_u', "0x8ffff00ff0000000", 30, "0x23fffc03f");
+    testBinary64('shr_u', "0x8ffff00ff0000000", 31, "0x11fffe01f");
+    testBinary64('shr_u', "0x8ffff00ff0000000", 32, "0x08ffff00f");
     testBinary64('shr_u', "0x8ffff00ff0000000", 56, 0x8f);
-    testBinary64('rotl', 40, 2, 160);
-    testBinary64('rotr', 40, 2, 10);
+
+    testBinary64('and', 42, 0, 0);
+    testBinary64('and', 42, 6, 2);
+    testBinary64('and', "0x0000000012345678", "0xffff0000ffff0000", "0x0000000012340000");
+    testBinary64('and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
+
+    // Rotations.
+    testBinary64('rotl', 40, 0, 0x28);
+    testBinary64('rotl', 40, 2, 0xA0);
+    testBinary64('rotl', 40, 8, 0x2800);
+    testBinary64('rotl', 40, 30, "0xA00000000");
+    testBinary64('rotl', 40, 31, "0x1400000000");
+    testBinary64('rotl', 40, 32, "0x2800000000");
+
+    testBinary64('rotl', "0x1234567812345678", 4, "0x2345678123456781");
+    testBinary64('rotl', "0x1234567812345678", 30, "0x048D159E048D159E");
+    testBinary64('rotl', "0x1234567812345678", 31, "0x091A2B3C091A2B3C");
+    testBinary64('rotl', "0x1234567812345678", 32, "0x1234567812345678");
+
+    testBinary64('rotl', "0x0000000000001000", 127, "0x0000000000000800");
+
+    testBinary64('rotr', 40, 0, 0x28);
+    testBinary64('rotr', 40, 2, 0x0A);
+    testBinary64('rotr', 40, 30, "0xA000000000");
+    testBinary64('rotr', 40, 31, "0x5000000000");
+    testBinary64('rotr', 40, 32, "0x2800000000");
+
     testBinary64('rotr', "0x1234567812345678", 4, "0x8123456781234567");
-    testBinary64('rotl', "0x1234567812345678", 4, "0x2345678123456781");
-    testBinary64('rotl', "0x1234567812345678", 60, "0x8123456781234567");
+    testBinary64('rotr', "0x1234567812345678", 30, "0x48D159E048D159E0");
+    testBinary64('rotr', "0x1234567812345678", 31, "0x2468ACF02468ACF0");
+    testBinary64('rotr', "0x1234567812345678", 32, "0x1234567812345678");
     testBinary64('rotr', "0x1234567812345678", 60, "0x2345678123456781");
-    testBinary64('rotl', "0x0000000000001000", 127, "0x0000000000000800");
+
     testBinary64('rotr', "0x0000000000001000", 127, "0x0000000000002000");
-    testBinary64('rotr', 40, 0, 40);
-    testBinary64('rotl', 40, 0, 40);
-    testBinary64('and', 42, 0, 0);
-    testBinary64('and', "0x0000000012345678", "0xffff0000ffff0000", "0x0000000012340000");
 
+    // Comparisons.
     testComparison64('eq', 40, 40, 1);
     testComparison64('ne', 40, 40, 0);
     testComparison64('lt_s', 40, 40, 0);
     testComparison64('lt_u', 40, 40, 0);
     testComparison64('le_s', 40, 40, 1);
     testComparison64('le_u', 40, 40, 1);
     testComparison64('gt_s', 40, 40, 0);
     testComparison64('gt_u', 40, 40, 0);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/null-metadata-filename.js
@@ -0,0 +1,9 @@
+if (typeof enableSPSProfiling === 'undefined' || !isAsmJSCompilationAvailable())
+    quit();
+
+enableSPSProfiling();
+var code = evaluate("(function() { 'use asm'; function g() { return 43 } return g })", {
+    fileName: null
+});
+
+assertEq(code()(), 43);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4724,16 +4724,19 @@ IonBuilder::getSingletonPrototype(JSFunc
     HeapTypeSetKey protoProperty = targetKey->property(protoid);
 
     return protoProperty.singleton(constraints());
 }
 
 MDefinition*
 IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee)
 {
+    if (!target->hasScript())
+        return nullptr;
+
     // Get the singleton prototype (if exists)
     JSObject* proto = getSingletonPrototype(target);
     if (!proto)
         return nullptr;
 
     JSObject* templateObject = inspector->getTemplateObject(pc);
     if (!templateObject)
         return nullptr;
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -534,17 +534,17 @@ MacroAssembler::rotateRight64(Register c
 }
 
 void
 MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, Register temp)
 {
     MOZ_ASSERT(src == dest, "defineReuseInput");
 
     int32_t amount = count.value & 0x3f;
-    if (amount % 0x1f != 0) {
+    if ((amount & 0x1f) != 0) {
         movl(dest.high, temp);
         shldl(Imm32(amount & 0x1f), dest.low, dest.high);
         shldl(Imm32(amount & 0x1f), temp, dest.low);
     }
 
     if (!!(amount & 0x20))
         xchgl(dest.high, dest.low);
 }
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -52,29 +52,20 @@ struct MyContainer
     MyContainer() : obj(nullptr), str(nullptr) {}
     void trace(JSTracer* trc) {
         js::TraceNullableEdge(trc, &obj, "test container");
         js::TraceNullableEdge(trc, &str, "test container");
     }
 };
 
 namespace js {
-template <>
-struct RootedBase<MyContainer> {
-    HeapPtr<JSObject*>& obj() { return static_cast<Rooted<MyContainer>*>(this)->get().obj; }
-    HeapPtr<JSString*>& str() { return static_cast<Rooted<MyContainer>*>(this)->get().str; }
-};
-template <>
-struct PersistentRootedBase<MyContainer> {
-    HeapPtr<JSObject*>& obj() {
-        return static_cast<PersistentRooted<MyContainer>*>(this)->get().obj;
-    }
-    HeapPtr<JSString*>& str() {
-        return static_cast<PersistentRooted<MyContainer>*>(this)->get().str;
-    }
+template <typename Wrapper>
+struct MutableWrappedPtrOperations<MyContainer, Wrapper> {
+    HeapPtr<JSObject*>& obj() { return static_cast<Wrapper*>(this)->get().obj; }
+    HeapPtr<JSString*>& str() { return static_cast<Wrapper*>(this)->get().str; }
 };
 } // namespace js
 
 BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
 {
     JS::Rooted<MyContainer> container(cx);
     container.obj() = JS_NewObject(cx, nullptr);
     container.str() = JS_NewStringCopyZ(cx, "Hello");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2564,20 +2564,24 @@ struct JS_PUBLIC_API(PropertyDescriptor)
     PropertyDescriptor()
       : obj(nullptr), attrs(0), getter(nullptr), setter(nullptr), value(JS::UndefinedValue())
     {}
 
     static void trace(PropertyDescriptor* self, JSTracer* trc) { self->trace(trc); }
     void trace(JSTracer* trc);
 };
 
-template <typename Outer>
-class PropertyDescriptorOperations
+} // namespace JS
+
+namespace js {
+
+template <typename Wrapper>
+class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
 {
-    const PropertyDescriptor& desc() const { return static_cast<const Outer*>(this)->get(); }
+    const JS::PropertyDescriptor& desc() const { return static_cast<const Wrapper*>(this)->get(); }
 
     bool has(unsigned bit) const {
         MOZ_ASSERT(bit != 0);
         MOZ_ASSERT((bit & (bit - 1)) == 0);  // only a single bit
         return (desc().attrs & bit) != 0;
     }
 
     bool hasAny(unsigned bits) const {
@@ -2696,51 +2700,52 @@ class PropertyDescriptorOperations
     void assertCompleteIfFound() const {
 #ifdef DEBUG
         if (object())
             assertComplete();
 #endif
     }
 };
 
-template <typename Outer>
-class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
+    : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
 {
-    PropertyDescriptor& desc() { return static_cast<Outer*>(this)->get(); }
+    JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void clear() {
         object().set(nullptr);
         setAttributes(0);
         setGetter(nullptr);
         setSetter(nullptr);
         value().setUndefined();
     }
 
-    void initFields(HandleObject obj, HandleValue v, unsigned attrs,
+    void initFields(JS::HandleObject obj, JS::HandleValue v, unsigned attrs,
                     JSGetterOp getterOp, JSSetterOp setterOp) {
         MOZ_ASSERT(getterOp != JS_PropertyStub);
         MOZ_ASSERT(setterOp != JS_StrictPropertyStub);
 
         object().set(obj);
         value().set(v);
         setAttributes(attrs);
         setGetter(getterOp);
         setSetter(setterOp);
     }
 
-    void assign(PropertyDescriptor& other) {
+    void assign(JS::PropertyDescriptor& other) {
         object().set(other.obj);
         setAttributes(other.attrs);
         setGetter(other.getter);
         setSetter(other.setter);
         value().set(other.value);
     }
 
-    void setDataDescriptor(HandleValue v, unsigned attrs) {
+    void setDataDescriptor(JS::HandleValue v, unsigned attrs) {
         MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE |
                               JSPROP_PERMANENT |
                               JSPROP_READONLY |
                               JSPROP_IGNORE_ENUMERATE |
                               JSPROP_IGNORE_PERMANENT |
                               JSPROP_IGNORE_READONLY)) == 0);
         object().set(nullptr);
         setAttributes(attrs);
@@ -2805,36 +2810,17 @@ class MutablePropertyDescriptorOperation
     }
     JS::MutableHandleObject setterObject() {
         MOZ_ASSERT(this->hasSetterObject());
         return JS::MutableHandleObject::fromMarkedLocation(
                 reinterpret_cast<JSObject**>(&desc().setter));
     }
 };
 
-} /* namespace JS */
-
-namespace js {
-
-template <>
-class RootedBase<JS::PropertyDescriptor>
-  : public JS::MutablePropertyDescriptorOperations<JS::Rooted<JS::PropertyDescriptor>>
-{};
-
-template <>
-class HandleBase<JS::PropertyDescriptor>
-  : public JS::PropertyDescriptorOperations<JS::Handle<JS::PropertyDescriptor>>
-{};
-
-template <>
-class MutableHandleBase<JS::PropertyDescriptor>
-  : public JS::MutablePropertyDescriptorOperations<JS::MutableHandle<JS::PropertyDescriptor>>
-{};
-
-} /* namespace js */
+} // namespace js
 
 namespace JS {
 
 extern JS_PUBLIC_API(bool)
 ObjectToCompletePropertyDescriptor(JSContext* cx,
                                    JS::HandleObject obj,
                                    JS::HandleValue descriptor,
                                    JS::MutableHandle<PropertyDescriptor> desc);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -566,17 +566,20 @@ JSCompartment::getNonSyntacticLexicalEnv
     if (!lexicalEnv)
         return nullptr;
     return &lexicalEnv->as<LexicalEnvironmentObject>();
 }
 
 bool
 JSCompartment::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
 {
-    if (varNames_.put(name.get()))
+    MOZ_ASSERT(name);
+    MOZ_ASSERT(!isAtomsCompartment());
+
+    if (varNames_.put(name))
         return true;
 
     ReportOutOfMemory(cx);
     return false;
 }
 
 void
 JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
@@ -715,18 +718,19 @@ JSCompartment::finishRoots()
         nonSyntacticLexicalEnvironments_->clear();
 }
 
 void
 JSCompartment::sweepAfterMinorGC(JSTracer* trc)
 {
     globalWriteBarriered = 0;
 
-    if (innerViews.needsSweepAfterMinorGC())
-        innerViews.sweepAfterMinorGC();
+    InnerViewTable& table = innerViews.get();
+    if (table.needsSweepAfterMinorGC())
+        table.sweepAfterMinorGC();
 
     crossCompartmentWrappers.sweepAfterMinorGC(trc);
 }
 
 void
 JSCompartment::sweepSavedStacks()
 {
     savedStacks_.sweep();
@@ -798,16 +802,22 @@ JSCompartment::sweepNativeIterators()
  * markCrossCompartmentWrappers.
  */
 void
 JSCompartment::sweepCrossCompartmentWrappers()
 {
     crossCompartmentWrappers.sweep();
 }
 
+void
+JSCompartment::sweepVarNames()
+{
+    varNames_.sweep();
+}
+
 namespace {
 struct TraceRootFunctor {
     JSTracer* trc;
     const char* name;
     TraceRootFunctor(JSTracer* trc, const char* name) : trc(trc), name(name) {}
     template <class T> void operator()(T* t) { return TraceRoot(trc, t, name); }
 };
 struct NeedsSweepUnbarrieredFunctor {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -649,16 +649,17 @@ struct JSCompartment
     void sweepSavedStacks();
     void sweepGlobalObject(js::FreeOp* fop);
     void sweepSelfHostingScriptSource();
     void sweepJitCompartment(js::FreeOp* fop);
     void sweepRegExps();
     void sweepDebugEnvironments();
     void sweepNativeIterators();
     void sweepTemplateObjects();
+    void sweepVarNames();
 
     void purge();
     void clearTables();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupAfterMovingGC();
     void fixupGlobal();
     void fixupScriptMapsAfterMovingGC();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -4865,16 +4865,18 @@ MAKE_GC_SWEEP_TASK(SweepObjectGroupsTask
 MAKE_GC_SWEEP_TASK(SweepRegExpsTask);
 MAKE_GC_SWEEP_TASK(SweepMiscTask);
 #undef MAKE_GC_SWEEP_TASK
 
 /* virtual */ void
 SweepAtomsTask::run()
 {
     runtime->sweepAtoms();
+    for (CompartmentsIter comp(runtime, SkipAtoms); !comp.done(); comp.next())
+        comp->sweepVarNames();
 }
 
 /* virtual */ void
 SweepCCWrappersTask::run()
 {
     for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
         c->sweepCrossCompartmentWrappers();
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -587,31 +587,33 @@ class JSObject : public js::gc::Cell
     static const size_t MAX_BYTE_SIZE = 4 * sizeof(void*) + 16 * sizeof(JS::Value);
 
   private:
     JSObject() = delete;
     JSObject(const JSObject& other) = delete;
     void operator=(const JSObject& other) = delete;
 };
 
-template <class U>
+template <typename Wrapper>
+template <typename U>
 MOZ_ALWAYS_INLINE JS::Handle<U*>
-js::RootedBase<JSObject*>::as() const
+js::RootedBase<JSObject*, Wrapper>::as() const
 {
-    const JS::Rooted<JSObject*>& self = *static_cast<const JS::Rooted<JSObject*>*>(this);
-    MOZ_ASSERT(self->is<U>());
+    const Wrapper& self = *static_cast<const Wrapper*>(this);
+    MOZ_ASSERT(self->template is<U>());
     return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
 }
 
+template <typename Wrapper>
 template <class U>
 MOZ_ALWAYS_INLINE JS::Handle<U*>
-js::HandleBase<JSObject*>::as() const
+js::HandleBase<JSObject*, Wrapper>::as() const
 {
     const JS::Handle<JSObject*>& self = *static_cast<const JS::Handle<JSObject*>*>(this);
-    MOZ_ASSERT(self->is<U>());
+    MOZ_ASSERT(self->template is<U>());
     return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
 }
 
 /*
  * The only sensible way to compare JSObject with == is by identity. We use
  * const& instead of * as a syntactic way to assert non-null. This leads to an
  * abundance of address-of operators to identity. Hence this overload.
  */
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -345,17 +345,17 @@ ArrayBufferObject::detach(JSContext* cx,
             oomUnsafe.crash("ArrayBufferObject::detach");
         MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);
         cx->compartment()->detachedTypedObjects = 1;
     }
 
     // Update all views of the buffer to account for the buffer having been
     // detached, and clear the buffer's data and list of views.
 
-    auto& innerViews = cx->compartment()->innerViews;
+    auto& innerViews = cx->compartment()->innerViews.get();
     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(buffer)) {
         for (size_t i = 0; i < views->length(); i++)
             NoteViewBufferWasDetached((*views)[i], newContents, cx);
         innerViews.removeViews(buffer);
     }
     if (buffer->firstView()) {
         if (buffer->forInlineTypedObject()) {
             // The buffer points to inline data in its first view, so to keep
@@ -420,17 +420,17 @@ ArrayBufferObject::changeContents(JSCont
     MOZ_RELEASE_ASSERT(!isWasm());
     MOZ_ASSERT(!forInlineTypedObject());
 
     // Change buffer contents.
     uint8_t* oldDataPointer = dataPointer();
     setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
 
     // Update all views.
-    auto& innerViews = cx->compartment()->innerViews;
+    auto& innerViews = cx->compartment()->innerViews.get();
     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
         for (size_t i = 0; i < views->length(); i++)
             changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
     }
     if (firstView())
         changeViewContents(cx, firstView(), oldDataPointer, newContents);
 }
 
--- a/js/src/vm/ArrayBufferObject.h
+++ b/js/src/vm/ArrayBufferObject.h
@@ -550,17 +550,16 @@ template<> inline bool TypeIsUnsigned<ui
 // Per-compartment table that manages the relationship between array buffers
 // and the views that use their storage.
 class InnerViewTable
 {
   public:
     typedef Vector<ArrayBufferViewObject*, 1, SystemAllocPolicy> ViewVector;
 
     friend class ArrayBufferObject;
-    friend class WeakCacheBase<InnerViewTable>;
 
   private:
     struct MapGCPolicy {
         static bool needsSweep(JSObject** key, ViewVector* value) {
             return InnerViewTable::sweepEntry(key, *value);
         }
     };
 
@@ -611,33 +610,25 @@ class InnerViewTable
 
     bool needsSweepAfterMinorGC() const {
         return !nurseryKeys.empty() || !nurseryKeysValid;
     }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
-template <>
-class WeakCacheBase<InnerViewTable>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<InnerViewTable, Wrapper>
+    : public WrappedPtrOperations<InnerViewTable, Wrapper>
 {
     InnerViewTable& table() {
-        return static_cast<JS::WeakCache<InnerViewTable>*>(this)->get();
-    }
-    const InnerViewTable& table() const {
-        return static_cast<const JS::WeakCache<InnerViewTable>*>(this)->get();
+        return static_cast<Wrapper*>(this)->get();
     }
 
   public:
-    InnerViewTable::ViewVector* maybeViewsUnbarriered(ArrayBufferObject* obj) {
-        return table().maybeViewsUnbarriered(obj);
-    }
-    void removeViews(ArrayBufferObject* obj) { table().removeViews(obj); }
-    void sweepAfterMinorGC() { table().sweepAfterMinorGC(); }
-    bool needsSweepAfterMinorGC() const { return table().needsSweepAfterMinorGC(); }
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
         return table().sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
 } // namespace js
 
 template <>
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1533,21 +1533,17 @@ GetSuperEnvFunction(JSContext* cx, Inter
  * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
  * within a local scope, resetting the value to nullptr (or the appropriate
  * equivalent for T) at scope end. This avoids inserting/removing the Rooted
  * from the rooter list, while preventing stale values from being kept alive
  * unnecessarily.
  */
 
 template<typename T>
-class ReservedRootedBase {
-};
-
-template<typename T>
-class ReservedRooted : public ReservedRootedBase<T>
+class ReservedRooted : public RootedBase<T, ReservedRooted<T>>
 {
     Rooted<T>* savedRoot;
 
   public:
     ReservedRooted(Rooted<T>* root, const T& ptr) : savedRoot(root) {
         *root = ptr;
     }
 
@@ -1565,24 +1561,16 @@ class ReservedRooted : public ReservedRo
     MutableHandle<T> operator&() { return &*savedRoot; }
 
     DECLARE_NONPOINTER_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(savedRoot->get())
     DECLARE_POINTER_CONSTREF_OPS(T)
     DECLARE_POINTER_ASSIGN_OPS(ReservedRooted, T)
 };
 
-template <>
-class ReservedRootedBase<Value> : public ValueOperations<ReservedRooted<Value>>
-{};
-
-template <>
-class ReservedRootedBase<Scope*> : public ScopeCastOperation<ReservedRooted<Scope*>>
-{};
-
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
  * on compiler support.
  *
--- a/js/src/vm/JSONParser.h
+++ b/js/src/vm/JSONParser.h
@@ -251,18 +251,21 @@ class MOZ_STACK_CLASS JSONParser : publi
 
     void getTextPosition(uint32_t* column, uint32_t* line);
 
   private:
     JSONParser(const JSONParser& other) = delete;
     void operator=(const JSONParser& other) = delete;
 };
 
-template <typename CharT>
-struct RootedBase<JSONParser<CharT>> {
+template <typename CharT, typename Wrapper>
+class MutableWrappedPtrOperations<JSONParser<CharT>, Wrapper>
+  : public WrappedPtrOperations<JSONParser<CharT>, Wrapper>
+{
+  public:
     bool parse(MutableHandleValue vp) {
-        return static_cast<Rooted<JSONParser<CharT>>*>(this)->get().parse(vp);
+        return static_cast<Wrapper*>(this)->get().parse(vp);
     }
 };
 
 } /* namespace js */
 
 #endif /* vm_JSONParser_h */
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -261,34 +261,16 @@ class SavedStacks {
             return true;
         }
 
         HeapPtr<JSAtom*> source;
         size_t line;
         uint32_t column;
     };
 
-    template <typename Outer>
-    struct LocationValueOperations {
-        JSAtom* source() const { return loc().source; }
-        size_t line() const { return loc().line; }
-        uint32_t column() const { return loc().column; }
-      private:
-        const LocationValue& loc() const { return static_cast<const Outer*>(this)->get(); }
-    };
-
-    template <typename Outer>
-    struct MutableLocationValueOperations : public LocationValueOperations<Outer> {
-        void setSource(JSAtom* v) { loc().source = v; }
-        void setLine(size_t v) { loc().line = v; }
-        void setColumn(uint32_t v) { loc().column = v; }
-      private:
-        LocationValue& loc() { return static_cast<Outer*>(this)->get(); }
-    };
-
   private:
     struct PCLocationHasher : public DefaultHasher<PCKey> {
         using ScriptPtrHasher = DefaultHasher<JSScript*>;
         using BytecodePtrHasher = DefaultHasher<jsbytecode*>;
 
         static HashNumber hash(const PCKey& key) {
             return mozilla::AddToHash(ScriptPtrHasher::hash(key.script),
                                       BytecodePtrHasher::hash(key.pc));
@@ -309,24 +291,41 @@ class SavedStacks {
     // it is no longer held by the table.
     using PCLocationMap = GCHashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy>;
     PCLocationMap pcLocationMap;
 
     MOZ_MUST_USE bool getLocation(JSContext* cx, const FrameIter& iter,
                                   MutableHandle<LocationValue> locationp);
 };
 
-template <>
-class RootedBase<SavedStacks::LocationValue>
-  : public SavedStacks::MutableLocationValueOperations<JS::Rooted<SavedStacks::LocationValue>>
-{};
+template <typename Wrapper>
+struct WrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+{
+    JSAtom* source() const { return loc().source; }
+    size_t line() const { return loc().line; }
+    uint32_t column() const { return loc().column; }
+
+  private:
+    const SavedStacks::LocationValue& loc() const {
+        return static_cast<const Wrapper*>(this)->get();
+    }
+};
 
-template <>
-class MutableHandleBase<SavedStacks::LocationValue>
-  : public SavedStacks::MutableLocationValueOperations<JS::MutableHandle<SavedStacks::LocationValue>>
-{};
+template <typename Wrapper>
+struct MutableWrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+    : public WrappedPtrOperations<SavedStacks::LocationValue, Wrapper>
+{
+    void setSource(JSAtom* v) { loc().source = v; }
+    void setLine(size_t v) { loc().line = v; }
+    void setColumn(uint32_t v) { loc().column = v; }
+
+  private:
+    SavedStacks::LocationValue& loc() {
+        return static_cast<Wrapper*>(this)->get();
+    }
+};
 
 UTF8CharsZ
 BuildUTF8StackString(JSContext* cx, HandleObject stack);
 
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -17,16 +17,17 @@
 #include "gc/Policy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "vm/Xdr.h"
 
 namespace js {
 
 class ModuleObject;
+class Scope;
 
 enum class BindingKind : uint8_t
 {
     Import,
     FormalParameter,
     Var,
     Let,
     Const,
@@ -181,16 +182,31 @@ class BindingLocation
 
     uint16_t argumentSlot() const {
         MOZ_ASSERT(kind_ == Kind::Argument);
         return mozilla::AssertedCast<uint16_t>(slot_);
     }
 };
 
 //
+// Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>.
+//
+template <typename Wrapper>
+class WrappedPtrOperations<Scope*, Wrapper>
+{
+  public:
+    template <class U>
+    JS::Handle<U*> as() const {
+        const Wrapper& self = *static_cast<const Wrapper*>(this);
+        MOZ_ASSERT_IF(self, self->template is<U>());
+        return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
+    }
+};
+
+//
 // The base class of all Scopes.
 //
 class Scope : public js::gc::TenuredCell
 {
     friend class GCMarker;
 
     // The kind determines data_.
     ScopeKind kind_;
@@ -1323,20 +1339,20 @@ class MOZ_STACK_CLASS ScopeIter
             TraceRoot(trc, &scope_, "scope iter scope");
     }
 };
 
 //
 // Specializations of Rooted containers for the iterators.
 //
 
-template <typename Outer>
-class BindingIterOperations
+template <typename Wrapper>
+class WrappedPtrOperations<BindingIter, Wrapper>
 {
-    const BindingIter& iter() const { return static_cast<const Outer*>(this)->get(); }
+    const BindingIter& iter() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool done() const { return iter().done(); }
     explicit operator bool() const { return !done(); }
     bool isLast() const { return iter().isLast(); }
     bool canHaveArgumentSlots() const { return iter().canHaveArgumentSlots(); }
     bool canHaveFrameSlots() const { return iter().canHaveFrameSlots(); }
     bool canHaveEnvironmentSlots() const { return iter().canHaveEnvironmentSlots(); }
@@ -1346,102 +1362,50 @@ class BindingIterOperations
     BindingKind kind() const { return iter().kind(); }
     bool isTopLevelFunction() const { return iter().isTopLevelFunction(); }
     bool hasArgumentSlot() const { return iter().hasArgumentSlot(); }
     uint16_t argumentSlot() const { return iter().argumentSlot(); }
     uint32_t nextFrameSlot() const { return iter().nextFrameSlot(); }
     uint32_t nextEnvironmentSlot() const { return iter().nextEnvironmentSlot(); }
 };
 
-template <typename Outer>
-class MutableBindingIterOperations : public BindingIterOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<BindingIter, Wrapper>
+  : public WrappedPtrOperations<BindingIter, Wrapper>
 {
-    BindingIter& iter() { return static_cast<Outer*>(this)->get(); }
+    BindingIter& iter() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void operator++(int) { iter().operator++(1); }
 };
 
-template <typename Outer>
-class ScopeIterOperations
+template <typename Wrapper>
+class WrappedPtrOperations<ScopeIter, Wrapper>
 {
-    const ScopeIter& iter() const { return static_cast<const Outer*>(this)->get(); }
+    const ScopeIter& iter() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool done() const { return iter().done(); }
     explicit operator bool() const { return !done(); }
     Scope* scope() const { return iter().scope(); }
     ScopeKind kind() const { return iter().kind(); }
     Shape* environmentShape() const { return iter().environmentShape(); }
     bool hasSyntacticEnvironment() const { return iter().hasSyntacticEnvironment(); }
 };
 
-template <typename Outer>
-class MutableScopeIterOperations : public ScopeIterOperations<Outer>
+template <typename Wrapper>
+class MutableWrappedPtrOperations<ScopeIter, Wrapper>
+  : public WrappedPtrOperations<ScopeIter, Wrapper>
 {
-    ScopeIter& iter() { return static_cast<Outer*>(this)->get(); }
+    ScopeIter& iter() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void operator++(int) { iter().operator++(1); }
 };
 
-#define SPECIALIZE_ROOTING_CONTAINERS(Iter, BaseIter)                    \
-    template <>                                                          \
-    class RootedBase<Iter>                                               \
-      : public Mutable##BaseIter##Operations<JS::Rooted<Iter>>           \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class MutableHandleBase<Iter>                                        \
-      : public Mutable##BaseIter##Operations<JS::MutableHandle<Iter>>    \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class HandleBase<Iter>                                               \
-      : public BaseIter##Operations<JS::Handle<Iter>>                    \
-    { };                                                                 \
-                                                                         \
-    template <>                                                          \
-    class PersistentRootedBase<Iter>                                     \
-      : public Mutable##BaseIter##Operations<JS::PersistentRooted<Iter>> \
-    { }
-
-SPECIALIZE_ROOTING_CONTAINERS(BindingIter, BindingIter);
-SPECIALIZE_ROOTING_CONTAINERS(PositionalFormalParameterIter, BindingIter);
-SPECIALIZE_ROOTING_CONTAINERS(ScopeIter, ScopeIter);
-
-#undef SPECIALIZE_ROOTING_CONTAINERS
-
-//
-// Allow using is<T> and as<T> on Rooted<Scope*> and Handle<Scope*>.
-//
-
-template <typename Outer>
-struct ScopeCastOperation
-{
-    template <class U>
-    JS::Handle<U*> as() const {
-        const Outer& self = *static_cast<const Outer*>(this);
-        MOZ_ASSERT_IF(self, self->template is<U>());
-        return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
-    }
-};
-
-template <>
-class RootedBase<Scope*> : public ScopeCastOperation<JS::Rooted<Scope*>>
-{ };
-
-template <>
-class HandleBase<Scope*> : public ScopeCastOperation<JS::Handle<Scope*>>
-{ };
-
-template <>
-class MutableHandleBase<Scope*> : public ScopeCastOperation<JS::MutableHandle<Scope*>>
-{ };
-
 } // namespace js
 
 namespace JS {
 
 template <>
 struct GCPolicy<js::ScopeKind> : public IgnoreGCPolicy<js::ScopeKind>
 { };
 
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1237,19 +1237,20 @@ struct InitialShapeEntry
     inline InitialShapeEntry(Shape* shape, const Lookup::ShapeProto& proto);
 
     static inline HashNumber hash(const Lookup& lookup);
     static inline bool match(const InitialShapeEntry& key, const Lookup& lookup);
     static void rekey(InitialShapeEntry& k, const InitialShapeEntry& newKey) { k = newKey; }
 
     bool needsSweep() {
         Shape* ushape = shape.unbarrieredGet();
-        JSObject* protoObj = proto.proto().raw();
+        TaggedProto uproto = proto.proto().unbarrieredGet();
+        JSObject* protoObj = uproto.raw();
         return (gc::IsAboutToBeFinalizedUnbarriered(&ushape) ||
-                (proto.proto().isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
+                (uproto.isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
     }
 };
 
 using InitialShapeSet = JS::GCHashSet<InitialShapeEntry, InitialShapeEntry, SystemAllocPolicy>;
 
 struct StackShape
 {
     /* For performance, StackShape only roots when absolutely necessary. */
@@ -1329,56 +1330,46 @@ struct StackShape
         return hash;
     }
 
     // Traceable implementation.
     static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
     void trace(JSTracer* trc);
 };
 
-template <typename Outer>
-class StackShapeOperations {
-    const StackShape& ss() const { return static_cast<const Outer*>(this)->get(); }
+template <typename Wrapper>
+class WrappedPtrOperations<StackShape, Wrapper>
+{
+    const StackShape& ss() const { return static_cast<const Wrapper*>(this)->get(); }
 
   public:
     bool hasSlot() const { return ss().hasSlot(); }
     bool hasMissingSlot() const { return ss().hasMissingSlot(); }
     uint32_t slot() const { return ss().slot(); }
     uint32_t maybeSlot() const { return ss().maybeSlot(); }
     uint32_t slotSpan() const { return ss().slotSpan(); }
     bool isAccessorShape() const { return ss().isAccessorShape(); }
     uint8_t attrs() const { return ss().attrs; }
 };
 
-template <typename Outer>
-class MutableStackShapeOperations : public StackShapeOperations<Outer> {
-    StackShape& ss() { return static_cast<Outer*>(this)->get(); }
+template <typename Wrapper>
+class MutableWrappedPtrOperations<StackShape, Wrapper>
+  : public WrappedPtrOperations<StackShape, Wrapper>
+{
+    StackShape& ss() { return static_cast<Wrapper*>(this)->get(); }
 
   public:
     void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
         ss().updateGetterSetter(rawGetter, rawSetter);
     }
     void setSlot(uint32_t slot) { ss().setSlot(slot); }
     void setBase(UnownedBaseShape* base) { ss().base = base; }
     void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
 };
 
-template <>
-class RootedBase<StackShape> : public MutableStackShapeOperations<JS::Rooted<StackShape>>
-{};
-
-template <>
-class HandleBase<StackShape> : public StackShapeOperations<JS::Handle<StackShape>>
-{};
-
-template <>
-class MutableHandleBase<StackShape>
-  : public MutableStackShapeOperations<JS::MutableHandle<StackShape>>
-{};
-
 inline
 Shape::Shape(const StackShape& other, uint32_t nfixed)
   : base_(other.base),
     propid_(other.propid),
     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
     attrs(other.attrs),
     flags(other.flags),
     parent(nullptr)
--- a/js/src/vm/TaggedProto.h
+++ b/js/src/vm/TaggedProto.h
@@ -73,46 +73,34 @@ struct InternalBarrierMethods<TaggedProt
         return proto.isObject();
     }
 
     static bool isMarkable(const TaggedProto& proto) {
         return proto.isObject();
     }
 };
 
-template<class Outer>
-class TaggedProtoOperations
+template <class Wrapper>
+class WrappedPtrOperations<TaggedProto, Wrapper>
 {
     const TaggedProto& value() const {
-        return static_cast<const Outer*>(this)->get();
+        return static_cast<const Wrapper*>(this)->get();
     }
 
   public:
     uintptr_t toWord() const { return value().toWord(); }
     inline bool isDynamic() const { return value().isDynamic(); }
     inline bool isObject() const { return value().isObject(); }
     inline JSObject* toObject() const { return value().toObject(); }
     inline JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
     JSObject* raw() const { return value().raw(); }
     HashNumber hashCode() const { return value().hashCode(); }
     uint64_t uniqueId() const { return value().uniqueId(); }
 };
 
-template <>
-class HandleBase<TaggedProto> : public TaggedProtoOperations<Handle<TaggedProto>>
-{};
-
-template <>
-class RootedBase<TaggedProto> : public TaggedProtoOperations<Rooted<TaggedProto>>
-{};
-
-template <>
-class BarrieredBaseMixins<TaggedProto> : public TaggedProtoOperations<GCPtr<TaggedProto>>
-{};
-
 // If the TaggedProto is a JSObject pointer, convert to that type and call |f|
 // with the pointer. If the TaggedProto is lazy, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
 DispatchTyped(F f, const TaggedProto& proto, Args&&... args)
   -> decltype(f(static_cast<JSObject*>(nullptr), mozilla::Forward<Args>(args)...))
 {
     if (proto.isObject())
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -773,20 +773,28 @@ Code::ensureProfilingState(JSRuntime* rt
             if (!codeRange.isFunction())
                 continue;
 
             ToCStringBuf cbuf;
             const char* bytecodeStr = NumberToCString(nullptr, &cbuf, codeRange.funcLineOrBytecode());
             MOZ_ASSERT(bytecodeStr);
 
             UTF8Bytes name;
-            if (!getFuncName(codeRange.funcIndex(), &name) ||
-                !name.append(" (", 2) ||
-                !name.append(metadata_->filename.get(), strlen(metadata_->filename.get())) ||
-                !name.append(':') ||
+            if (!getFuncName(codeRange.funcIndex(), &name) || !name.append(" (", 2))
+                return false;
+
+            if (const char* filename = metadata_->filename.get()) {
+                if (!name.append(filename, strlen(filename)))
+                    return false;
+            } else {
+                if (!name.append('?'))
+                    return false;
+            }
+
+            if (!name.append(':') ||
                 !name.append(bytecodeStr, strlen(bytecodeStr)) ||
                 !name.append(")\0", 2))
             {
                 return false;
             }
 
             UniqueChars label(name.extractOrCopyRawBuffer());
             if (!label)
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -4445,16 +4445,19 @@ public:
   }
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override;
 
   virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override
   {
+    if (!mList.GetChildren()->GetTop()) {
+      return false;
+    }
     return mList.GetChildren()->GetTop()->ShouldBuildLayerEvenIfInvisible(aBuilder);
   }
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
 
   virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
@@ -4471,17 +4474,19 @@ public:
   }
 
   nsIFrame* TransformFrame() { return mTransformFrame; }
 
   virtual int32_t ZIndex() const override;
 
   virtual void
   DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
-    static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop())->DoUpdateBoundsPreserves3D(aBuilder);
+    if (mList.GetChildren()->GetTop()) {
+      static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop())->DoUpdateBoundsPreserves3D(aBuilder);
+    }
   }
 
 private:
   nsDisplayWrapList mList;
   nsIFrame* mTransformFrame;
   uint32_t mIndex;
 };
 
--- a/media/mtransport/test/runnable_utils_unittest.cpp
+++ b/media/mtransport/test/runnable_utils_unittest.cpp
@@ -7,17 +7,16 @@
 // Original author: ekr@rtfm.com
 #include <iostream>
 
 #include "prio.h"
 
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
 #include "nsXPCOM.h"
-#include "nsXPCOMGlue.h"
 
 #include "mozilla/RefPtr.h"
 #include "nsIComponentManager.h"
 #include "nsIComponentRegistrar.h"
 #include "nsIIOService.h"
 #include "nsIServiceManager.h"
 #include "nsISocketTransportService.h"
 
--- a/media/mtransport/test/sockettransportservice_unittest.cpp
+++ b/media/mtransport/test/sockettransportservice_unittest.cpp
@@ -7,17 +7,16 @@
 // Original author: ekr@rtfm.com
 #include <iostream>
 
 #include "prio.h"
 
 #include "nsCOMPtr.h"
 #include "nsNetCID.h"
 #include "nsXPCOM.h"
-#include "nsXPCOMGlue.h"
 
 #include "nsIComponentManager.h"
 #include "nsIComponentRegistrar.h"
 #include "nsIIOService.h"
 #include "nsIServiceManager.h"
 #include "nsISocketTransportService.h"
 
 #include "nsASocketHandler.h"
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -90,18 +90,19 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
 
   // if consumers pass both, aLoadingContext and aLoadingPrincipal
   // then the loadingPrincipal must be the same as the node's principal
   MOZ_ASSERT(!aLoadingContext || !aLoadingPrincipal ||
              aLoadingContext->NodePrincipal() == aLoadingPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
-    mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
-    mForceInheritPrincipalDropped = true;
+    mForceInheritPrincipalDropped =
+      (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
+    mSecurityFlags &= ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
   if (aLoadingContext) {
     nsCOMPtr<nsPIDOMWindowOuter> contextOuter = aLoadingContext->OwnerDoc()->GetWindow();
     if (contextOuter) {
       ComputeIsThirdPartyContext(contextOuter);
       mOuterWindowID = contextOuter->WindowID();
       nsCOMPtr<nsPIDOMWindowOuter> parent = contextOuter->GetScriptableParent();
@@ -240,18 +241,19 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* a
 {
   // Top-level loads are never third-party
   // Grab the information we can out of the window.
   MOZ_ASSERT(aOuterWindow);
   MOZ_ASSERT(mTriggeringPrincipal);
 
   // if the load is sandboxed, we can not also inherit the principal
   if (mSecurityFlags & nsILoadInfo::SEC_SANDBOXED) {
-    mSecurityFlags ^= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
-    mForceInheritPrincipalDropped = true;
+    mForceInheritPrincipalDropped =
+      (mSecurityFlags & nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL);
+    mSecurityFlags &= ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
   // NB: Ignore the current inner window since we're navigating away from it.
   mOuterWindowID = aOuterWindow->WindowID();
 
   // TODO We can have a parent without a frame element in some cases dealing
   // with the hidden window.
   nsCOMPtr<nsPIDOMWindowOuter> parent = aOuterWindow->GetScriptableParent();
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -58,45 +58,50 @@ interface nsILoadInfo : nsISupports
    *
    * Exactly one of these flags are required to be set in order to allow
    * the channel to perform the correct security checks (SOP, CORS, ...) and
    * return the correct result principal. If none or more than one of these
    * flags are set AsyncOpen2 will fail.
    */
 
   /*
-   * Enforce the same origin policy where data: loads inherit
-   * the principal.
+   * Enforce the same origin policy where data: loads inherit the principal.
+   * See the documentation for principalToInherit, which describes exactly what
+   * principal is inherited.
    */
   const unsigned long SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS = (1<<0);
 
   /*
    * Enforce the same origin policy but data: loads are blocked.
    */
   const unsigned long SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED = (1<<1);
 
   /**
-   * Allow loads from other origins. Loads from data: will inherit
-   * the principal of the origin that triggered the load.
+   * Allow loads from other origins. Loads from data: will inherit the
+   * principal.  See the documentation for principalToInherit, which describes
+   * exactly what principal is inherited.
+   *
    * Commonly used by plain <img>, <video>, <link rel=stylesheet> etc.
    */
    const unsigned long SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS = (1<<2);
 
   /**
    * Allow loads from other origins. Loads from data: will be allowed,
    * but the resulting resource will get a null principal.
    * Used in blink/webkit for <iframe>s. Likely also the mode
    * that should be used by most Chrome code.
    */
   const unsigned long SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL = (1<<3);
 
   /**
-   * Allow loads from any origin, but require CORS for cross-origin
-   * loads. Loads from data: are allowed and the result will inherit
-   * the principal of the origin that triggered the load.
+   * Allow loads from any origin, but require CORS for cross-origin loads.
+   * Loads from data: are allowed and the result will inherit the principal.
+   * See the documentation for principalToInherit, which describes exactly what
+   * principal is inherited.
+   *
    * Commonly used by <img crossorigin>, <video crossorigin>,
    * XHR, fetch(), etc.
    */
   const unsigned long SEC_REQUIRE_CORS_DATA_INHERITS = (1<<4);
 
   /**
    * Choose cookie policy. The default policy is equivalent to "INCLUDE" for
    * SEC_REQUIRE_SAME_ORIGIN_* and SEC_ALLOW_CROSS_ORIGIN_* modes, and
@@ -110,25 +115,26 @@ interface nsILoadInfo : nsISupports
    * will be blocked no matter which of these flags are set.
    */
   const unsigned long SEC_COOKIES_DEFAULT = (0 << 5);
   const unsigned long SEC_COOKIES_INCLUDE = (1 << 5);
   const unsigned long SEC_COOKIES_SAME_ORIGIN = (2 << 5);
   const unsigned long SEC_COOKIES_OMIT = (3 << 5);
 
   /**
-   * Force inheriting of the Principal. The resulting resource will use the
-   * principal of the document which is doing the load. Setting this flag
-   * will cause GetChannelResultPrincipal to return the same principal as
-   * the loading principal that's passed in when creating the channel.
+   * Force inheriting of the principal.  See the documentation for
+   * principalToInherit, which describes exactly what principal is inherited.
+   *
+   * Setting this flag will cause GetChannelResultPrincipal to return the
+   * principal to be inherited as the channel principal.
    *
    * This will happen independently of the scheme of the URI that the
    * channel is loading.
    *
-   * So if the loading document comes from "http://a.com/", and the channel
+   * So if the principal that gets inherited is "http://a.com/", and the channel
    * is loading the URI "http://b.com/whatever", GetChannelResultPrincipal
    * will return a principal from "http://a.com/".
    *
    * This flag can not be used together with SEC_SANDBOXED.  If both are passed
    * to the LoadInfo constructor then this flag will be dropped.  If you need
    * to know whether this flag would have been present but was dropped due to
    * sandboxing, check for the forceInheritPrincipalDropped flag.
    */
@@ -176,21 +182,23 @@ interface nsILoadInfo : nsISupports
 
   /**
    * Load an error page, it should be one of following : about:neterror,
    * about:certerror, about:blocked, or about:tabcrashed.
    */
   const unsigned long SEC_LOAD_ERROR_PAGE = (1<<13);
 
   /**
-   * Force inheriting of the principalToInherit, overruling any owner
-   * that might be set on the channel. (Please note that channel.owner
-   * is deprecated and will be removed within Bug 1286838).
+   * Force inheriting of the principal, overruling any owner that might be set
+   * on the channel. (Please note that channel.owner is deprecated and will be
+   * removed within Bug 1286838).  See the documentation for principalToInherit,
+   * which describes exactly what principal is inherited.
+   *
    * Setting this flag will cause GetChannelResultPrincipal to return the
-   * principalToInherit set in the loadInfo.
+   * principal to be inherited as the channel principal.
    *
    * This will happen independently of the scheme of the URI that the
    * channel is loading.
    */
   const unsigned long SEC_FORCE_INHERIT_PRINCIPAL_OVERRULE_OWNER = (1<<14);
 
   /**
    * This is the principal of the network request's caller/requester where
@@ -356,27 +364,36 @@ interface nsILoadInfo : nsISupports
    * SEC_COOKIES_DEFAULT, but will instead return what the policy resolves to.
    * I.e. SEC_COOKIES_SAME_ORIGIN for CORS mode, and SEC_COOKIES_INCLUDE
    * otherwise.
    */
   [infallible] readonly attribute unsigned long cookiePolicy;
 
   /**
    * If forceInheritPrincipal is true, the data coming from the channel should
-   * use loadingPrincipal for its principal, even when the data is loaded over
-   * http:// or another protocol that would normally use a URI-based principal.
+   * inherit its principal, even when the data is loaded over http:// or another
+   * protocol that would normally use a URI-based principal.
+   *
+   * See the documentation for principalToInherit, which describes exactly what
+   * principal is inherited.
+   *
    * This attribute will never be true when loadingSandboxed is true.
    */
   [infallible] readonly attribute boolean forceInheritPrincipal;
 
   /**
    * If forceInheritPrincipalOverruleOwner is true, the data coming from the
-   * channel should use principalToInherit for its principal, even when the
-   * data is loaded over http:// or another protocol that would normally use
-   * a URI-based principal.
+   * channel should inherit the principal, even when the data is loaded over
+   * http:// or another protocol that would normally use a URI-based principal
+   * and even if the channel's .owner is not null.  This last is the difference
+   * between forceInheritPrincipalOverruleOwner and forceInheritPrincipal: the
+   * latter does _not_ overrule the .owner setting.
+   *
+   * See the documentation for principalToInherit, which describes exactly what
+   * principal is inherited.
    */
   [infallible] readonly attribute boolean forceInheritPrincipalOverruleOwner;
 
   /**
    * If loadingSandboxed is true, the data coming from the channel is
    * being loaded sandboxed, so it should have a nonce origin and
    * hence should use a NullPrincipal.
    */
@@ -496,24 +513,24 @@ interface nsILoadInfo : nsISupports
    * the frameOuterWindowID is the outer window containing the
    * foo.html document.
    *
    * Note: For other cases, frameOuterWindowID is 0.
    */
   [infallible] readonly attribute unsigned long long frameOuterWindowID;
 
   /**
-   * For all loads of none TYPE_DOUCMENT this function resets the 
-   * LoadingPrincipal, the TriggeringPrincipal and the
-   * PrincipalToInherit to a freshly created NullPrincipal which inherits
+   * For all loads of type other than TYPE_DOCUMENT this function resets the
+   * loadingPrincipal, the triggeringPrincipal and the
+   * principalToInherit to a freshly created NullPrincipal which inherits
    * the current origin attributes from the loadinfo.
    * For loads of TYPE_DOCUMENT this function resets only the
    * TriggeringPrincipal as well as the PrincipalToInherit to a freshly
    * created NullPrincipal which inherits the origin attributes from
-   * the loadInfo. (Please note that the LoadingPrincipal for TYPE_DOCUMENT
+   * the loadInfo. (Please note that the loadingPrincipal for TYPE_DOCUMENT
    * loads is always null.)
    *
    * WARNING: Please only use that function if you know exactly what
    * you are doing!!!
    */
   void resetPrincipalsToNullPrincipal();
 
   /**
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -68,16 +68,17 @@ from ..frontend.data import (
     StaticLibrary,
     TestManifest,
     VariablePassthru,
     XPIDLFile,
 )
 from ..util import (
     ensureParentDir,
     FileAvoidWrite,
+    OrderedDefaultDict,
 )
 from ..makeutil import Makefile
 from mozbuild.shellutil import quote as shell_quote
 
 MOZBUILD_VARIABLES = [
     b'ANDROID_APK_NAME',
     b'ANDROID_APK_PACKAGE',
     b'ANDROID_ASSETS_DIRS',
@@ -391,17 +392,17 @@ class RecursiveMakeBackend(CommonBackend
         # even if they are empty, because the directories are still filled
         # by the build system itself, and the install manifests are only
         # used for a "magic" rm -rf.
         self._install_manifests['dist_public']
         self._install_manifests['dist_private']
         self._install_manifests['dist_sdk']
 
         self._traversal = RecursiveMakeTraversal()
-        self._compile_graph = defaultdict(set)
+        self._compile_graph = OrderedDefaultDict(set)
 
         self._no_skip = {
             'export': set(),
             'libs': set(),
             'misc': set(),
             'tools': set(),
         }
 
@@ -533,23 +534,26 @@ class RecursiveMakeBackend(CommonBackend
            script=obj.script,
            method=obj.method))
 
         elif isinstance(obj, JARManifest):
             self._no_skip['libs'].add(backend_file.relobjdir)
             backend_file.write('JAR_MANIFEST := %s\n' % obj.path.full_path)
 
         elif isinstance(obj, RustProgram):
-            # Note that for these and host Rust programs, we don't need to
-            # bother with linked libraries, because Cargo will take care of
-            # all of that for us.
             self._process_rust_program(obj, backend_file)
+            # Hook the program into the compile graph.
+            build_target = self._build_target_for_obj(obj)
+            self._compile_graph[build_target]
 
         elif isinstance(obj, HostRustProgram):
             self._process_host_rust_program(obj, backend_file)
+            # Hook the program into the compile graph.
+            build_target = self._build_target_for_obj(obj)
+            self._compile_graph[build_target]
 
         elif isinstance(obj, Program):
             self._process_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostProgram):
             self._process_host_program(obj.program, backend_file)
             self._process_linked_libraries(obj, backend_file)
deleted file mode 100644
--- a/python/mozbuild/mozbuild/test/backend/data/rust-programs/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-authors = ["nobody <nobody@mozilla.org>"]
-name = "testing"
-version = "0.0.1"
-
-[[bin]]
-name = "target"
-
-[[bin]]
-name = "host"
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/rust-programs/code/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+authors = ["nobody <nobody@mozilla.org>"]
+name = "testing"
+version = "0.0.1"
+
+[[bin]]
+name = "target"
+
+[[bin]]
+name = "host"
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/rust-programs/code/moz.build
@@ -0,0 +1,6 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+RUST_PROGRAMS += ['target']
+HOST_RUST_PROGRAMS += ['host']
--- a/python/mozbuild/mozbuild/test/backend/data/rust-programs/moz.build
+++ b/python/mozbuild/mozbuild/test/backend/data/rust-programs/moz.build
@@ -1,6 +1,5 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # Any copyright is dedicated to the Public Domain.
 # http://creativecommons.org/publicdomain/zero/1.0/
 
-RUST_PROGRAMS += ['target']
-HOST_RUST_PROGRAMS += ['host']
+DIRS += ['code']
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -763,29 +763,34 @@ class TestRecursiveMakeBackend(BackendTe
         ]
 
         self.assertEqual(lines, expected)
 
     def test_rust_programs(self):
         """Test that {HOST_,}RUST_PROGRAMS are written to backend.mk correctly."""
         env = self._consume('rust-programs', RecursiveMakeBackend)
 
-        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
+        backend_path = mozpath.join(env.topobjdir, 'code/backend.mk')
         lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
 
         expected = [
-            'CARGO_FILE := %s/Cargo.toml' % env.topsrcdir,
+            'CARGO_FILE := %s/code/Cargo.toml' % env.topsrcdir,
             'RUST_PROGRAMS += i686-pc-windows-msvc/release/target.exe',
             'RUST_CARGO_PROGRAMS += target',
             'HOST_RUST_PROGRAMS += i686-pc-windows-msvc/release/host.exe',
             'HOST_RUST_CARGO_PROGRAMS += host',
         ]
 
         self.assertEqual(lines, expected)
 
+        root_deps_path = mozpath.join(env.topobjdir, 'root-deps.mk')
+        lines = [l.strip() for l in open(root_deps_path, 'rt').readlines()]
+
+        self.assertTrue(any(l == 'recurse_compile: code/host code/target' for l in lines))
+
     def test_final_target(self):
         """Test that FINAL_TARGET is written to backend.mk correctly."""
         env = self._consume('final_target', RecursiveMakeBackend)
 
         final_target_rule = "FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin)$(DIST_SUBDIR:%=/%)"
         expected = dict()
         expected[env.topobjdir] = []
         expected[mozpath.join(env.topobjdir, 'both')] = [
--- a/toolkit/components/telemetry/Events.yaml
+++ b/toolkit/components/telemetry/Events.yaml
@@ -1,68 +1,69 @@
 navigation:
-- methods: ["search"]
-  objects: ["about_home", "about_newtab", "contextmenu", "oneoff",
-            "suggestion", "alias", "enter", "searchbar", "urlbar"]
-  release_channel_collection: opt-in
-  description: >
-    This is recorded on each search navigation.
-    The value field records the action used to trigger the search:
-      "enter", "oneoff", "suggestion", "alias", null (for contextmenu)
-  bug_numbers: [1316281]
-  notification_emails: ["past@mozilla.com"]
-  expiry_version: "58.0"
-  extra_keys:
-    engine: The id of the search engine used.
+  search:
+    objects: ["about_home", "about_newtab", "contextmenu", "oneoff",
+              "suggestion", "alias", "enter", "searchbar", "urlbar"]
+    release_channel_collection: opt-in
+    description: >
+      This is recorded on each search navigation.
+      The value field records the action used to trigger the search:
+        "enter", "oneoff", "suggestion", "alias", null (for contextmenu)
+    bug_numbers: [1316281]
+    notification_emails: ["past@mozilla.com"]
+    expiry_version: "58.0"
+    extra_keys:
+      engine: The id of the search engine used.
 
 # This category contains event entries used for Telemetry tests.
 # They will not be sent out with any pings.
 telemetry.test:
-- methods: ["test1", "test2"]
-  objects: ["object1", "object2"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is a test entry for Telemetry.
-  expiry_date: never
-  extra_keys:
-    key1: This is just a test description.
-    key2: This is another test description.
-- methods: ["optout"]
-  objects: ["object1", "object2"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is an opt-out test entry.
-  expiry_date: never
-  release_channel_collection: opt-out
-  extra_keys:
-    key1: This is just a test description.
-- methods: ["expired_version"]
-  objects: ["object1", "object2"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is a test entry with an expired version.
-  expiry_version: "3.6"
-- methods: ["expired_date"]
-  objects: ["object1", "object2"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is a test entry with an expired date.
-  expiry_date: 2014-01-28
-- methods: ["not_expired_optout"]
-  objects: ["object1"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is an opt-out test entry with unexpired date and version.
-  release_channel_collection: opt-out
-  expiry_date: 2099-01-01
-  expiry_version: "999.0"
+  test:
+    methods: ["test1", "test2"]
+    objects: ["object1", "object2"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is a test entry for Telemetry.
+    expiry_date: never
+    extra_keys:
+      key1: This is just a test description.
+      key2: This is another test description.
+  optout:
+    objects: ["object1", "object2"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is an opt-out test entry.
+    expiry_date: never
+    release_channel_collection: opt-out
+    extra_keys:
+      key1: This is just a test description.
+  expired_version:
+    objects: ["object1", "object2"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is a test entry with an expired version.
+    expiry_version: "3.6"
+  expired_date:
+    objects: ["object1", "object2"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is a test entry with an expired date.
+    expiry_date: 2014-01-28
+  not_expired_optout:
+    objects: ["object1"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is an opt-out test entry with unexpired date and version.
+    release_channel_collection: opt-out
+    expiry_date: 2099-01-01
+    expiry_version: "999.0"
 
 # This is a secondary category used for Telemetry tests.
 # The events here will not be sent out with any pings.
 telemetry.test.second:
-- methods: ["test"]
-  objects: ["object1", "object2", "object3"]
-  bug_numbers: [1286606]
-  notification_emails: ["telemetry-client-dev@mozilla.com"]
-  description: This is a test entry for Telemetry.
-  expiry_date: never
-  extra_keys:
-    key1: This is just a test description.
+  test:
+    objects: ["object1", "object2", "object3"]
+    bug_numbers: [1286606]
+    notification_emails: ["telemetry-client-dev@mozilla.com"]
+    description: This is a test entry for Telemetry.
+    expiry_date: never
+    extra_keys:
+      key1: This is just a test description.
--- a/toolkit/components/telemetry/parse_events.py
+++ b/toolkit/components/telemetry/parse_events.py
@@ -40,158 +40,181 @@ class TypeChecker:
         It supports:
         - atomic values, e.g.: TypeChecker(int)
         - list values, e.g.: TypeChecker(list, basestring)
         - dict values, e.g.: TypeChecker(dict, basestring, int)
         - atomic values that can have different types, e.g.: TypeChecker(OneOf, int, date)"""
         self._kind = kind
         self._args = args
 
-    def check(self, key, value):
+    def check(self, identifier, key, value):
         # Check fields that can be one of two different types.
         if self._kind is OneOf:
             if not isinstance(value, self._args[0]) and not isinstance(value, self._args[1]):
-                raise ValueError, "failed type check for %s - expected %s or %s, got %s" %\
-                                  (key,
+                raise ValueError, "%s: failed type check for %s - expected %s or %s, got %s" %\
+                                  (identifier, key,
                                    nice_type_name(self._args[0]),
                                    nice_type_name(self._args[1]),
                                    nice_type_name(type(value)))
             return
 
         # Check basic type of value.
         if not isinstance(value, self._kind):
-            raise ValueError, "failed type check for %s - expected %s, got %s" %\
-                              (key,
+            raise ValueError, "%s: failed type check for %s - expected %s, got %s" %\
+                              (identifier, key,
                                nice_type_name(self._kind),
                                nice_type_name(type(value)))
 
         # Check types of values in lists.
         if self._kind is list:
             if len(value) < 1:
-                raise ValueError, "failed check for %s - list should not be empty" % key
+                raise ValueError, "%s: failed check for %s - list should not be empty" % (identifier, key)
             for x in value:
                 if not isinstance(x, self._args[0]):
-                    raise ValueError, "failed type check for %s - expected list value type %s, got %s" %\
-                                      (key,
+                    raise ValueError, "%s: failed type check for %s - expected list value type %s, got %s" %\
+                                      (identifier, key,
                                        nice_type_name(self._args[0]),
                                        nice_type_name(type(x)))
+
         # Check types of keys and values in dictionaries.
         elif self._kind is dict:
             if len(value.keys()) < 1:
-                    raise ValueError, "failed check for %s - dict should not be empty" % key
+                    raise ValueError, "%s: failed check for %s - dict should not be empty" % (identifier, key)
             for x in value.iterkeys():
                 if not isinstance(x, self._args[0]):
-                    raise ValueError, "failed dict type check for %s - expected key type %s, got %s" %\
-                                      (key,
+                    raise ValueError, "%s: failed dict type check for %s - expected key type %s, got %s" %\
+                                      (identifier, key,
                                        nice_type_name(self._args[0]),
                                        nice_type_name(type(x)))
             for k,v in value.iteritems():
                 if not isinstance(x, self._args[1]):
-                    raise ValueError, "failed dict type check for %s - expected value type %s for key %s, got %s" %\
-                                      (key,
+                    raise ValueError, "%s: failed dict type check for %s - expected value type %s for key %s, got %s" %\
+                                      (identifier, key,
                                        nice_type_name(self._args[1]),
                                        k,
                                        nice_type_name(type(x)))
 
-def type_check_event_fields(category, definition):
+def type_check_event_fields(identifier, name, definition):
     """Perform a type/schema check on the event definition."""
     REQUIRED_FIELDS = {
-        'methods': TypeChecker(list, basestring),
         'objects': TypeChecker(list, basestring),
         'bug_numbers': TypeChecker(list, int),
         'notification_emails': TypeChecker(list, basestring),
         'description': TypeChecker(basestring),
     }
     OPTIONAL_FIELDS = {
+        'methods': TypeChecker(list, basestring),
         'release_channel_collection': TypeChecker(basestring),
         'expiry_date': TypeChecker(OneOf, basestring, datetime.date),
         'expiry_version': TypeChecker(basestring),
         'extra_keys': TypeChecker(dict, basestring, basestring),
     }
     ALL_FIELDS = REQUIRED_FIELDS.copy()
     ALL_FIELDS.update(OPTIONAL_FIELDS)
 
     # Check that all the required fields are available.
     missing_fields = [f for f in REQUIRED_FIELDS.keys() if f not in definition]
     if len(missing_fields) > 0:
-        raise KeyError(category + ' - missing required fields: ' + ', '.join(missing_fields))
+        raise KeyError(identifier + ' - missing required fields: ' + ', '.join(missing_fields))
 
     # Is there any unknown field?
     unknown_fields = [f for f in definition.keys() if f not in ALL_FIELDS]
     if len(unknown_fields) > 0:
-        raise KeyError(category + ' - unknown fields: ' + ', '.join(unknown_fields))
+        raise KeyError(identifier + ' - unknown fields: ' + ', '.join(unknown_fields))
 
     # Type-check fields.
     for k,v in definition.iteritems():
-        ALL_FIELDS[k].check(k, v)
+        ALL_FIELDS[k].check(identifier, k, v)
 
-def string_check(category, field_name, value, min_length, max_length, regex=None):
+def string_check(identifier, field, value, min_length=1, max_length=None, regex=None):
     # Length check.
-    if len(value) > max_length:
-        raise ValueError("Value '%s' for %s in %s exceeds maximum length of %d" %\
-                         (value, field_name, category, max_length))
+    if len(value) < min_length:
+        raise ValueError("%s: value '%s' for field %s is less than minimum length of %d" %\
+                         (identifier, value, field, min_length))
+    if max_length and len(value) > max_length:
+        raise ValueError("%s: value '%s' for field %s is greater than maximum length of %d" %\
+                         (identifier, value, field, max_length))
     # Regex check.
     if regex and not re.match(regex, value):
-        raise ValueError, 'String value for %s in %s is not matching pattern "%s": %s' % \
-                          (field_name, category, regex, value)
+        raise ValueError, '%s: string value "%s" for %s is not matching pattern "%s"' % \
+                          (identifier, value, field, regex)
 
 class EventData:
     """A class representing one event."""
 
-    def __init__(self, category, definition):
-        type_check_event_fields(category, definition)
+    def __init__(self, category, name, definition):
+        self._category = category
+        self._name = name
+        self._definition = definition
+
+        type_check_event_fields(self.identifier, name, definition)
 
-        string_check(category, 'methods', definition.get('methods')[0], 1, MAX_METHOD_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
-        string_check(category, 'objects', definition.get('objects')[0], 1, MAX_OBJECT_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
+        # Check method & object string patterns.
+        for method in self.methods:
+            string_check(self.identifier, field='methods', value=method,
+                         min_length=1, max_length=MAX_METHOD_NAME_LENGTH,
+                         regex=IDENTIFIER_PATTERN)
+        for obj in self.objects:
+            string_check(self.identifier, field='objects', value=obj,
+                         min_length=1, max_length=MAX_OBJECT_NAME_LENGTH,
+                         regex=IDENTIFIER_PATTERN)
 
         # Check release_channel_collection
         rcc_key = 'release_channel_collection'
         rcc = definition.get(rcc_key, 'opt-in')
         allowed_rcc = ["opt-in", "opt-out"]
         if not rcc in allowed_rcc:
-            raise ValueError, "Value for %s in %s should be one of: %s" %\
-                              (rcc_key, category, ", ".join(allowed_rcc))
+            raise ValueError, "%s: value for %s should be one of: %s" %\
+                              (self.identifier, rcc_key, ", ".join(allowed_rcc))
 
         # Check extra_keys.
         extra_keys = definition.get('extra_keys', {})
         if len(extra_keys.keys()) > MAX_EXTRA_KEYS_COUNT:
-            raise ValueError, "Number of extra_keys in %s exceeds limit %d" %\
-                              (category, MAX_EXTRA_KEYS_COUNT)
+            raise ValueError, "%s: number of extra_keys exceeds limit %d" %\
+                              (self.identifier, MAX_EXTRA_KEYS_COUNT)
         for key in extra_keys.iterkeys():
-            string_check(category, 'extra_keys', key, 1, MAX_EXTRA_KEY_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
+            string_check(self.identifier, field='extra_keys', value=key,
+                         min_length=1, max_length=MAX_EXTRA_KEY_NAME_LENGTH,
+                         regex=IDENTIFIER_PATTERN)
 
         # Check expiry.
         if not 'expiry_version' in definition and not 'expiry_date' in definition:
-            raise KeyError, "Event in %s is missing an expiration - either expiry_version or expiry_date is required" %\
-                            (category)
+            raise KeyError, "%s: event is missing an expiration - either expiry_version or expiry_date is required" %\
+                            (self.identifier)
         expiry_date = definition.get('expiry_date')
         if expiry_date and isinstance(expiry_date, basestring) and expiry_date != 'never':
             if not re.match(DATE_PATTERN, expiry_date):
-                raise ValueError, "Event in %s has invalid expiry_date, it should be either 'never' or match this format: %s" %\
-                                  (category, DATE_PATTERN)
+                raise ValueError, "%s: event has invalid expiry_date, it should be either 'never' or match this format: %s" %\
+                                  (self.identifier, DATE_PATTERN)
             # Parse into date.
             definition['expiry_date'] = datetime.datetime.strptime(expiry_date, '%Y-%m-%d')
 
         # Finish setup.
-        self._category = category
-        self._definition = definition
         definition['expiry_version'] = add_expiration_postfix(definition.get('expiry_version', 'never'))
 
     @property
     def category(self):
         return self._category
 
     @property
     def category_cpp(self):
         # Transform e.g. category.example into CategoryExample.
         return convert_to_cpp_identifier(self._category, ".")
 
     @property
+    def name(self):
+        return self._name
+
+    @property
+    def identifier(self):
+        return self.category + "#" + self.name
+
+    @property
     def methods(self):
-        return self._definition.get('methods')
+        return self._definition.get('methods', [self.name])
 
     @property
     def objects(self):
         return self._definition.get('objects')
 
     @property
     def expiry_version(self):
         return self._definition.get('expiry_version')
@@ -251,21 +274,31 @@ def load_events(filename):
     except IOError, e:
         raise Exception('Error opening ' + filename + ': ' + e.message)
     except ValueError, e:
         raise Exception('Error parsing events in ' + filename + ': ' + e.message)
 
     event_list = []
 
     # Events are defined in a fixed two-level hierarchy within the definition file.
-    # The first level contains the category (group name), while the second level contains the
-    # event definitions (e.g. "category.name: [<event definition>, ...], ...").
+    # The first level contains the category (group name), while the second level contains
+    # the event names and definitions, e.g.:
+    #   category.name:
+    #     event_name:
+    #       <event definition>
+    #      ...
+    #   ...
     for category_name,category in events.iteritems():
-        string_check('', 'category', category_name, 1, MAX_CATEGORY_NAME_LENGTH, regex=IDENTIFIER_PATTERN)
+        string_check("top level structure", field='category', value=category_name,
+                     min_length=1, max_length=MAX_CATEGORY_NAME_LENGTH,
+                     regex=IDENTIFIER_PATTERN)
 
         # Make sure that the category has at least one entry in it.
         if not category or len(category) == 0:
             raise ValueError(category_name + ' must contain at least one entry')
 
-        for entry in category:
-            event_list.append(EventData(category_name, entry))
+        for name,entry in category.iteritems():
+            string_check(category_name, field='event name', value=name,
+                         min_length=1, max_length=MAX_METHOD_NAME_LENGTH,
+                         regex=IDENTIFIER_PATTERN)
+            event_list.append(EventData(category_name, name, entry))
 
     return event_list
rename from toolkit/components/telemetry/tests/unit/test_nsITelemetry.js
rename to toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -20,17 +20,17 @@ generated-files =
   dictionary.xpi
   experiment.xpi
   extension.xpi
   extension-2.xpi
   system.xpi
   restartless.xpi
   theme.xpi
 
-[test_nsITelemetry.js]
+[test_TelemetryHistograms.js]
 [test_SubsessionChaining.js]
 tags = addons
 [test_TelemetryEnvironment.js]
 skip-if = os == "android"
 tags = addons
 [test_PingAPI.js]
 skip-if = os == "android"
 [test_TelemetryFlagClear.js]
--- a/toolkit/crashreporter/tools/symbolstore.py
+++ b/toolkit/crashreporter/tools/symbolstore.py
@@ -786,17 +786,18 @@ class Dumper_Win32(Dumper):
         # Cache the corrected version to avoid future filesystem hits.
         self.fixedFilenameCaseCache[file] = result
         return result
 
     def CopyDebug(self, file, debug_file, guid, code_file, code_id):
         def compress(path):
             compressed_file = path[:-1] + '_'
             # ignore makecab's output
-            success = subprocess.call(["makecab.exe", "/D",
+            makecab = buildconfig.substs['MAKECAB']
+            success = subprocess.call([makecab, "-D",
                                        "CompressionType=MSZIP",
                                        path, compressed_file],
                                       stdout=open(os.devnull, 'w'),
                                       stderr=subprocess.STDOUT)
             if success == 0 and os.path.exists(compressed_file):
                 os.unlink(path)
                 return True
             return False
--- a/toolkit/crashreporter/tools/unit-symbolstore.py
+++ b/toolkit/crashreporter/tools/unit-symbolstore.py
@@ -206,16 +206,17 @@ class TestCopyDebug(HelperMixin, unittes
                                                   symbol_path=self.symbol_dir,
                                                   copy_debug=True,
                                                   archs="abc xyz")
         d.CopyDebug = mock_copy_debug
         d.Process(self.test_dir)
         d.Finish(stop_pool=False)
         self.assertEqual(1, len(copied))
 
+    @patch.dict('buildconfig.substs', {'MAKECAB': 'makecab'})
     def test_copy_debug_copies_binaries(self):
         """
         Test that CopyDebug copies binaries as well on Windows.
         """
         test_file = os.path.join(self.test_dir, 'foo.pdb')
         write_pdb(test_file)
         code_file = 'foo.dll'
         code_id = 'abc123'
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -18,21 +18,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/PlacesUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource://gre/modules/PageThumbs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BinarySearch",
   "resource://gre/modules/BinarySearch.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "gPrincipal", function() {
-  let uri = Services.io.newURI("about:newtab");
-  return Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
-});
-
 XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function() {
   return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function() {
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                     .createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = 'utf8';
@@ -1346,18 +1341,22 @@ var LinkChecker = {
     if (!(aURI in this._cache))
       this._cache[aURI] = this._doCheckLoadURI(aURI);
 
     return this._cache[aURI];
   },
 
   _doCheckLoadURI: function Links_doCheckLoadURI(aURI) {
     try {
+      // about:newtab is currently privileged. In any case, it should be
+      // possible for tiles to point to pretty much everything - but not
+      // to stuff that inherits the system principal, so we check:
+      let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
       Services.scriptSecurityManager.
-        checkLoadURIStrWithPrincipal(gPrincipal, aURI, this.flags);
+        checkLoadURIStrWithPrincipal(systemPrincipal, aURI, this.flags);
       return true;
     } catch (e) {
       // We got a weird URI or one that would inherit the caller's principal.
       return false;
     }
   }
 };
 
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -1,14 +1,23 @@
 /* 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/. */
 
+#if _WIN32_WINNT < 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include <shlobj.h>
 #include <stdio.h>
+
+#include "mozilla/WindowsVersion.h"
 #include "nsWindowsDllInterceptor.h"
+#include "nsWindowsHelpers.h"
 
 using namespace mozilla;
 
 struct payload {
   UINT64 a;
   UINT64 b;
   UINT64 c;
 
@@ -71,16 +80,64 @@ bool TestDetour(const char *dll, const c
     printf("TEST-PASS | WindowsDllInterceptor | Could detour %s from %s\n", func, dll);
     return true;
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to detour %s from %s\n", func, dll);
     return false;
   }
 }
 
+bool MaybeTestHook(const bool cond, const char* dll, const char* func)
+{
+  if (!cond) {
+    return true;
+  }
+
+  return TestHook(dll, func);
+}
+
+bool ShouldTestTipTsf()
+{
+#if defined(_M_X64)
+  return false;
+#else
+  if (!IsWin8OrLater()) {
+    return false;
+  }
+
+  nsModuleHandle shell32(LoadLibraryW(L"shell32.dll"));
+  if (!shell32) {
+    return true;
+  }
+
+  auto pSHGetKnownFolderPath = reinterpret_cast<decltype(&SHGetKnownFolderPath)>(GetProcAddress(shell32, "SHGetKnownFolderPath"));
+  if (!pSHGetKnownFolderPath) {
+    return true;
+  }
+
+  PWSTR commonFilesPath = nullptr;
+  if (FAILED(pSHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, nullptr,
+                                   &commonFilesPath))) {
+    return true;
+  }
+
+  wchar_t fullPath[MAX_PATH + 1] = {};
+  wcscpy(fullPath, commonFilesPath);
+  wcscat(fullPath, L"\\Microsoft Shared\\Ink\\tiptsf.dll");
+  CoTaskMemFree(commonFilesPath);
+
+  if (!LoadLibraryW(fullPath)) {
+    return false;
+  }
+
+  // Leak the module so that it's loaded for the interceptor test
+  return true;
+#endif
+}
+
 int main()
 {
   payload initial = { 0x12345678, 0xfc4e9d31, 0x87654321 };
   payload p0, p1;
   ZeroMemory(&p0, sizeof(p0));
   ZeroMemory(&p1, sizeof(p1));
 
   p0 = rotatePayload(initial);
@@ -162,15 +219,16 @@ int main()
       TestDetour("user32.dll", "CreateWindowExW") &&
       TestHook("user32.dll", "InSendMessageEx") &&
       TestHook("imm32.dll", "ImmGetContext") &&
       TestHook("imm32.dll", "ImmGetCompositionStringW") &&
       TestHook("imm32.dll", "ImmSetCandidateWindow") &&
 #ifdef _M_X64
       TestHook("user32.dll", "GetKeyState") &&
 #endif
+      MaybeTestHook(ShouldTestTipTsf(), "tiptsf.dll", "ProcessCaretEvents") &&
       TestDetour("ntdll.dll", "LdrLoadDll")) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
     return 0;
   }
 
   return 1;
 }
--- a/toolkit/xre/test/win/moz.build
+++ b/toolkit/xre/test/win/moz.build
@@ -19,11 +19,12 @@ LOCAL_INCLUDES += [
     '/toolkit/xre',
 ]
 
 DISABLE_STL_WRAPPING = True
 USE_STATIC_LIBS = True
 
 OS_LIBS += [
     'comctl32',
-    'ws2_32',
+    'ole32',
     'shell32',
+    'ws2_32',
 ]
--- a/tools/profiler/tasktracer/GeckoTaskTracer.cpp
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.cpp
@@ -382,16 +382,39 @@ AutoSourceEvent::AutoSourceEvent(SourceE
   CreateSourceEvent(aType);
 }
 
 AutoSourceEvent::~AutoSourceEvent()
 {
   DestroySourceEvent();
 }
 
+AutoScopedLabel::AutoScopedLabel(const char* aFormat, ...)
+  : mLabel(nullptr)
+{
+  if (IsStartLogging()) {
+    // Optimization for when it is disabled.
+    nsCString label;
+    va_list args;
+    va_start(args, aFormat);
+    label.AppendPrintf(aFormat, args);
+    va_end(args);
+    mLabel = strdup(label.get());
+    AddLabel("Begin %s", mLabel);
+  }
+}
+
+AutoScopedLabel::~AutoScopedLabel()
+{
+  if (mLabel) {
+    AddLabel("End %s", mLabel);
+    free(mLabel);
+  }
+}
+
 void AddLabel(const char* aFormat, ...)
 {
   TraceInfo* info = GetOrCreateTraceInfo();
   ENSURE_TRUE_VOID(info);
 
   MutexAutoLock lock(info->mLogsMutex);
   // Log format:
   // [3 taskId "label"]
--- a/tools/profiler/tasktracer/GeckoTaskTracer.h
+++ b/tools/profiler/tasktracer/GeckoTaskTracer.h
@@ -88,12 +88,20 @@ CreateTracedRunnable(already_AddRefed<ns
 // where nsThread and base::thread release themselves.
 void FreeTraceInfo();
 
 const char* GetJSLabelPrefix();
 
 void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId,
                      SourceEventType* aOutSourceEventType);
 
+class AutoScopedLabel
+{
+  char* mLabel;
+public:
+  explicit AutoScopedLabel(const char* aFormat, ...);
+  ~AutoScopedLabel();
+};
+
 } // namespace tasktracer
 } // namespace mozilla.
 
 #endif
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -341,37 +341,43 @@ DWORD CurrentWindowsTimeGetter::sLastPos
  * MSAA, which then sends WM_GETOBJECT to us.
  *
  * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
  * hook. Since thread-local hooks are called ahead of global hooks, we will
  * see these registered messages before tiptsf does. At this point we can then
  * raise a flag that blocks a11y before invoking CallNextHookEx which will then
  * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
  * flag by calling TIPMessageHandler::IsA11yBlocked().
+ *
+ * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
+ * function that also calls into UIA.
  */
 class TIPMessageHandler
 {
 public:
   ~TIPMessageHandler()
   {
     if (mHook) {
       ::UnhookWindowsHookEx(mHook);
     }
   }
 
   static void Initialize()
   {
     MOZ_ASSERT(!sInstance);
+    if (!IsWin8OrLater()) {
+      return;
+    }
+
     sInstance = new TIPMessageHandler();
     ClearOnShutdown(&sInstance);
   }
 
   static bool IsA11yBlocked()
   {
-    MOZ_ASSERT(sInstance);
     if (!sInstance) {
       return false;
     }
 
     return sInstance->mA11yBlockCount > 0;
   }
 
 private:
@@ -388,31 +394,46 @@ private:
     mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed");
     mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility");
     mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden");
     mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo");
 
     mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr,
                                ::GetCurrentThreadId());
     MOZ_ASSERT(mHook);
+
+#if defined(_M_IX86)
+    if (!IsWin10OrLater()) {
+      // tiptsf loads when STA COM is first initialized, so it should be present
+      sTipTsfInterceptor.Init("tiptsf.dll");
+      DebugOnly<bool> ok = sTipTsfInterceptor.AddHook("ProcessCaretEvents",
+          reinterpret_cast<intptr_t>(&ProcessCaretEventsHook),
+          (void**) &sProcessCaretEventsStub);
+      MOZ_ASSERT(ok);
+    }
+#endif // defined(_M_IX86)
   }
 
   class MOZ_RAII A11yInstantiationBlocker
   {
   public:
     A11yInstantiationBlocker()
     {
-      MOZ_ASSERT(TIPMessageHandler::sInstance);
+      if (!TIPMessageHandler::sInstance) {
+        return;
+      }
       ++TIPMessageHandler::sInstance->mA11yBlockCount;
     }
 
     ~A11yInstantiationBlocker()
     {
-      MOZ_ASSERT(TIPMessageHandler::sInstance &&
-                 TIPMessageHandler::sInstance->mA11yBlockCount > 0);
+      if (!TIPMessageHandler::sInstance) {
+        return;
+      }
+      MOZ_ASSERT(TIPMessageHandler::sInstance->mA11yBlockCount > 0);
       --TIPMessageHandler::sInstance->mA11yBlockCount;
     }
   };
 
   friend class A11yInstantiationBlocker;
 
   static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam)
   {
@@ -428,23 +449,44 @@ private:
         A11yInstantiationBlocker block;
         return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
       }
     }
 
     return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
   }
 
+#if defined(_M_IX86)
+  static void CALLBACK ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,
+                                              DWORD aEvent, HWND aHwnd,
+                                              LONG aObjectId, LONG aChildId,
+                                              DWORD aGeneratingTid,
+                                              DWORD aEventTime)
+  {
+    A11yInstantiationBlocker block;
+    sProcessCaretEventsStub(aWinEventHook, aEvent, aHwnd, aObjectId, aChildId,
+                            aGeneratingTid, aEventTime);
+  }
+
+  static WindowsDllInterceptor sTipTsfInterceptor;
+  static WINEVENTPROC sProcessCaretEventsStub;
+#endif // defined(_M_IX86)
+
   static StaticAutoPtr<TIPMessageHandler> sInstance;
 
-  HHOOK     mHook;
-  UINT      mMessages[7];
-  uint32_t  mA11yBlockCount;
+  HHOOK                 mHook;
+  UINT                  mMessages[7];
+  uint32_t              mA11yBlockCount;
 };
 
+#if defined(_M_IX86)
+WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
+WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
+#endif // defined(_M_IX86)
+
 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
 
 #endif // defined(ACCESSIBILITY)
 
 } // namespace mozilla
 
 /**************************************************************
  *