Merge inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Wed, 28 Mar 2018 12:49:56 +0300
changeset 410372 5bf126434fac78a31256c994b9dbf4b1031b0350
parent 410316 5adc5494450ba08d4b3ae6cc39b8ee3dc3f16acb (current diff)
parent 410371 1c8dd93fbeb071dcc9f54eae22b19da1fc0f4f90 (diff)
child 410373 a456475502b80a1264642d9eaee9394a8fad8315
child 410393 eb65d036d8069e24cf4297c05361d86979f277a7
child 410466 f4f8e10c031ae5a93eb9b230cfc0984f16906234
push id33725
push usercsabou@mozilla.com
push dateWed, 28 Mar 2018 09:50:28 +0000
treeherdermozilla-central@5bf126434fac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
5bf126434fac / 61.0a1 / 20180328100134 / files
nightly linux64
5bf126434fac / 61.0a1 / 20180328100134 / files
nightly mac
5bf126434fac / 61.0a1 / 20180328100134 / files
nightly win32
5bf126434fac / 61.0a1 / 20180328100134 / files
nightly win64
5bf126434fac / 61.0a1 / 20180328100134 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/components/extensions/ext-bookmarks.js
browser/components/extensions/ext-browser.js
browser/components/extensions/ext-browserAction.js
browser/components/extensions/ext-browsingData.js
browser/components/extensions/ext-c-browser.js
browser/components/extensions/ext-c-devtools-inspectedWindow.js
browser/components/extensions/ext-c-devtools-network.js
browser/components/extensions/ext-c-devtools-panels.js
browser/components/extensions/ext-c-devtools.js
browser/components/extensions/ext-c-menus.js
browser/components/extensions/ext-c-omnibox.js
browser/components/extensions/ext-c-tabs.js
browser/components/extensions/ext-chrome-settings-overrides.js
browser/components/extensions/ext-commands.js
browser/components/extensions/ext-devtools-inspectedWindow.js
browser/components/extensions/ext-devtools-network.js
browser/components/extensions/ext-devtools-panels.js
browser/components/extensions/ext-devtools.js
browser/components/extensions/ext-find.js
browser/components/extensions/ext-geckoProfiler.js
browser/components/extensions/ext-history.js
browser/components/extensions/ext-menus.js
browser/components/extensions/ext-omnibox.js
browser/components/extensions/ext-pageAction.js
browser/components/extensions/ext-pkcs11.js
browser/components/extensions/ext-sessions.js
browser/components/extensions/ext-sidebarAction.js
browser/components/extensions/ext-tabs.js
browser/components/extensions/ext-url-overrides.js
browser/components/extensions/ext-windows.js
config/external/icu/stubdata/moz.build
layout/style/ServoBindings.cpp
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseGeckoViewTest.java
modules/libpref/init/all.js
toolkit/components/extensions/ext-alarms.js
toolkit/components/extensions/ext-backgroundPage.js
toolkit/components/extensions/ext-browserSettings.js
toolkit/components/extensions/ext-c-backgroundPage.js
toolkit/components/extensions/ext-c-contentScripts.js
toolkit/components/extensions/ext-c-extension.js
toolkit/components/extensions/ext-c-identity.js
toolkit/components/extensions/ext-c-runtime.js
toolkit/components/extensions/ext-c-storage.js
toolkit/components/extensions/ext-c-test.js
toolkit/components/extensions/ext-c-toolkit.js
toolkit/components/extensions/ext-c-webRequest.js
toolkit/components/extensions/ext-clipboard.js
toolkit/components/extensions/ext-contentScripts.js
toolkit/components/extensions/ext-contextualIdentities.js
toolkit/components/extensions/ext-cookies.js
toolkit/components/extensions/ext-dns.js
toolkit/components/extensions/ext-downloads.js
toolkit/components/extensions/ext-extension.js
toolkit/components/extensions/ext-i18n.js
toolkit/components/extensions/ext-identity.js
toolkit/components/extensions/ext-idle.js
toolkit/components/extensions/ext-management.js
toolkit/components/extensions/ext-notifications.js
toolkit/components/extensions/ext-permissions.js
toolkit/components/extensions/ext-privacy.js
toolkit/components/extensions/ext-protocolHandlers.js
toolkit/components/extensions/ext-proxy.js
toolkit/components/extensions/ext-runtime.js
toolkit/components/extensions/ext-storage.js
toolkit/components/extensions/ext-tabs-base.js
toolkit/components/extensions/ext-theme.js
toolkit/components/extensions/ext-toolkit.js
toolkit/components/extensions/ext-topSites.js
toolkit/components/extensions/ext-webNavigation.js
toolkit/components/extensions/ext-webRequest.js
toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf
toolkit/mozapps/extensions/test/xpcshell/test_bug521905.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Historically updating ICU has required a CLOBBER.  Bug 1445524 is a fairly notable ICU-related change, so play it safe and force a full rebuild, even if no problem along these lines has actually been observed.
+More ICU-build-related touching for bug 1447475, trying a CLOBBER after a previous build failure just in case.
--- a/browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js
+++ b/browser/base/content/test/siteIdentity/browser_no_mcb_for_onions.js
@@ -18,17 +18,18 @@ add_task(async function allowOnionMixedC
   registerCleanupFunction(function() {
     gBrowser.removeCurrentTab();
   });
 
   await SpecialPowers.pushPrefEnv({set: [[PREF_BLOCK_DISPLAY, true]]});
   await SpecialPowers.pushPrefEnv({set: [[PREF_BLOCK_ACTIVE, true]]});
   await SpecialPowers.pushPrefEnv({set: [[PREF_ONION_WHITELIST, true]]});
 
-  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL).
+    catch(Cu.reportError);
   const browser = gBrowser.getBrowserForTab(tab);
 
   await ContentTask.spawn(browser, null, function() {
     is(docShell.hasMixedDisplayContentBlocked, false, "hasMixedDisplayContentBlocked not set");
     is(docShell.hasMixedActiveContentBlocked, false, "hasMixedActiveContentBlocked not set");
   });
 
   await assertMixedContentBlockingState(browser, {
--- a/browser/components/extensions/ext-devtools-inspectedWindow.js
+++ b/browser/components/extensions/ext-devtools-inspectedWindow.js
@@ -10,18 +10,19 @@ var {
   SpreadArgs,
 } = ExtensionCommon;
 
 this.devtools_inspectedWindow = class extends ExtensionAPI {
   getAPI(context) {
     // Lazily retrieved inspectedWindow actor front per child context.
     let waitForInspectedWindowFront;
 
-    // TODO(rpl): retrive a more detailed callerInfo object, like the filename and
-    // lineNumber of the actual extension called, in the child process.
+    // TODO - Bug 1448878: retrive a more detailed callerInfo object,
+    // like the filename and lineNumber of the actual extension called
+    // in the child process.
     const callerInfo = {
       addonId: context.extension.id,
       url: context.extension.baseURI.spec,
     };
 
     return {
       devtools: {
         inspectedWindow: {
--- a/browser/components/extensions/ext-devtools-panels.js
+++ b/browser/components/extensions/ext-devtools-panels.js
@@ -504,18 +504,19 @@ class ParentDevToolsInspectorSidebar {
 const sidebarsById = new Map();
 
 this.devtools_panels = class extends ExtensionAPI {
   getAPI(context) {
     // Lazily retrieved inspectedWindow actor front per child context
     // (used by Sidebar.setExpression).
     let waitForInspectedWindowFront;
 
-    // TODO(rpl): retrive a more detailed callerInfo object, like the filename and
-    // lineNumber of the actual extension called, in the child process.
+    // TODO - Bug 1448878: retrive a more detailed callerInfo object,
+    // like the filename and lineNumber of the actual extension called
+    // in the child process.
     const callerInfo = {
       addonId: context.extension.id,
       url: context.extension.baseURI.spec,
     };
 
     // An incremental "per context" id used in the generated devtools panel id.
     let nextPanelId = 0;
 
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -127,25 +127,19 @@ DEFINES += -DRESPATH='$(RESPATH)'
 LPROJ_ROOT = $(firstword $(subst -, ,$(AB_CD)))
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifeq (zh-TW,$(AB_CD))
 LPROJ_ROOT := $(subst -,_,$(AB_CD))
 endif
 endif
 DEFINES += -DLPROJ_ROOT=$(LPROJ_ROOT)
 
-DEFINES += -DMOZ_ICU_VERSION=$(MOZ_ICU_VERSION)
 ifdef MOZ_SYSTEM_ICU
 DEFINES += -DMOZ_SYSTEM_ICU
 endif
-ifdef MOZ_ICU_DATA_ARCHIVE
-DEFINES += -DMOZ_ICU_DATA_ARCHIVE
-endif
-DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
-DEFINES += -DICU_DATA_FILE=$(ICU_DATA_FILE)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
 ifdef CLANG_CL
 DEFINES += -DCLANG_CL
 endif
 
 ifdef LLVM_SYMBOLIZER
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -92,19 +92,16 @@
 @BINPATH@/@MSVC_C_RUNTIME_DLL@
 @BINPATH@/@MSVC_CXX_RUNTIME_DLL@
 #endif
 #if MOZ_PACKAGE_WIN_UCRT_DLLS
 @BINPATH@/api-ms-win-*.dll
 @BINPATH@/ucrtbase.dll
 #endif
 #endif
-#ifdef MOZ_ICU_DATA_ARCHIVE
-@RESPATH@/@ICU_DATA_FILE@
-#endif
 #ifdef MOZ_GTK3
 @BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 @BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
 #ifdef MOZ_WAYLAND
 @BINPATH@/@DLL_PREFIX@mozwayland@DLL_SUFFIX@
 #endif
 #endif
 
--- a/browser/modules/BrowserErrorReporter.jsm
+++ b/browser/modules/BrowserErrorReporter.jsm
@@ -86,34 +86,33 @@ class BrowserErrorReporter {
     this.unregisterListener = (
       options.unregisterListener || (() => Services.console.unregisterListener(this))
     );
 
     // Values that don't change between error reports.
     this.requestBodyTemplate = {
       logger: "javascript",
       platform: "javascript",
-      release: Services.appinfo.version,
+      release: Services.appinfo.appBuildID,
       environment: UpdateUtils.getUpdateChannel(false),
       contexts: {
         os: {
           name: PLATFORM_NAMES[AppConstants.platform],
           version: (
             Cc["@mozilla.org/network/protocol;1?name=http"]
             .getService(Ci.nsIHttpProtocolHandler)
             .oscpu
           ),
         },
         browser: {
           name: "Firefox",
           version: Services.appinfo.version,
         },
       },
       tags: {
-        appBuildID: Services.appinfo.appBuildID,
         changeset: AppConstants.SOURCE_REVISION_URL,
       },
       sdk: {
         name: SDK_NAME,
         version: SDK_VERSION,
       },
     };
 
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -2,17 +2,16 @@ dnl This Source Code Form is subject to 
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 dnl Set the MOZ_ICU_VERSION variable to denote the current version of the
 dnl ICU library, as well as a few other things.
 
 AC_DEFUN([MOZ_CONFIG_ICU], [
 
-ICU_LIB_NAMES=
 MOZ_SYSTEM_ICU=
 MOZ_ARG_WITH_BOOL(system-icu,
 [  --with-system-icu
                           Use system ICU (located with pkgconfig)],
     MOZ_SYSTEM_ICU=1)
 
 if test -n "$MOZ_SYSTEM_ICU"; then
     PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 59.1)
@@ -74,25 +73,22 @@ if test -n "$USE_ICU"; 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.
     ICU_DATA_FILE="icudt${version}l.dat"
-
-    MOZ_ICU_DATA_ARCHIVE=
 fi
 
 AC_SUBST(MOZ_ICU_VERSION)
 AC_SUBST(ENABLE_INTL_API)
 AC_SUBST(USE_ICU)
 AC_SUBST(ICU_DATA_FILE)
-AC_SUBST(MOZ_ICU_DATA_ARCHIVE)
 
 if test -n "$USE_ICU"; then
     dnl Source files that use ICU should have control over which parts of the ICU
     dnl namespace they want to use.
     AC_DEFINE(U_USING_ICU_NAMESPACE,0)
 
     if test -z "$MOZ_SYSTEM_ICU"; then
         if test -z "$YASM" -a -z "$GNU_AS" -a "$COMPILE_ENVIRONMENT"; then
--- a/build/build-clang/clang-win32-st-an.json
+++ b/build/build-clang/clang-win32-st-an.json
@@ -1,16 +1,17 @@
 {
     "llvm_revision": "317840",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "patches": [
       "r318309.patch",
       "msvc-host-x64.patch",
--- a/build/build-clang/clang-win64-st-an.json
+++ b/build/build-clang/clang-win64-st-an.json
@@ -1,16 +1,17 @@
 {
     "llvm_revision": "317840",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "ml": "ml64.exe",
     "patches": [
       "r318309.patch",
--- a/config/external/icu/data/moz.build
+++ b/config/external/icu/data/moz.build
@@ -1,18 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-    # Copy the pre-built ICU data file to dist/bin.
-    FINAL_TARGET_FILES += [CONFIG['ICU_DATA_FILE']]
-
 # Build a library containing the ICU data for use in the JS shell, so that
 # JSAPI consumers don't have to deal with setting ICU's data path.
 Library('icudata')
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     if CONFIG['CPU_ARCH'] == 'x86':
         ASFLAGS += ['-DPREFIX']
 elif CONFIG['OS_ARCH'] == 'Darwin':
--- a/config/external/icu/moz.build
+++ b/config/external/icu/moz.build
@@ -9,13 +9,9 @@ Library('icu')
 if CONFIG['MOZ_SYSTEM_ICU']:
     OS_LIBS += CONFIG['MOZ_ICU_LIBS']
 else:
     DIRS += [
         'common',
         'data',
         'i18n',
     ]
-    if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-        DIRS += ['stubdata']
-        USE_LIBS += ['icustubdata']
-    else:
-        USE_LIBS += ['icudata']
+    USE_LIBS += ['icudata']
deleted file mode 100644
--- a/config/external/icu/stubdata/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# This builds the ICU stubdata library, since we are shipping ICU
-# data in a separate data file. ICU needs a data symbol to link against
-# even if you're loading its data from a file.
-
-Library('icustubdata')
-
-SOURCES += ['/intl/icu/source/stubdata/stubdata.cpp']
--- a/devtools/client/inspector/extensions/test/browser_inspector_extension_sidebar.js
+++ b/devtools/client/inspector/extensions/test/browser_inspector_extension_sidebar.js
@@ -8,28 +8,41 @@ ChromeUtils.defineModuleGetter(this, "Co
                                "resource://testing-common/ContentTaskUtils.jsm");
 
 loader.lazyGetter(this, "WebExtensionInspectedWindowFront", () => {
   return require(
     "devtools/shared/fronts/webextension-inspected-window"
   ).WebExtensionInspectedWindowFront;
 }, true);
 
-const FAKE_CALLER_INFO = {
-  url: "moz-extension://fake-webextension-uuid/fake-caller-script.js",
-  lineNumber: 1,
-  addonId: "fake-webextension-uuid",
-};
 const SIDEBAR_ID = "an-extension-sidebar";
 const SIDEBAR_TITLE = "Sidebar Title";
 
+let extension;
+let fakeExtCallerInfo;
+
 let toolbox;
 let inspector;
 
 add_task(async function setupExtensionSidebar() {
+  extension = ExtensionTestUtils.loadExtension({
+    background() {
+      // This is just an empty extension used to ensure that the caller extension uuid
+      // actually exists.
+    }
+  });
+
+  await extension.startup();
+
+  fakeExtCallerInfo = {
+    url: WebExtensionPolicy.getByID(extension.id).getURL("fake-caller-script.js"),
+    lineNumber: 1,
+    addonId: extension.id,
+  };
+
   const res = await openInspectorForURL("about:blank");
   inspector = res.inspector;
   toolbox = res.toolbox;
 
   const onceSidebarCreated = toolbox.once(`extension-sidebar-created-${SIDEBAR_ID}`);
   toolbox.registerInspectorExtensionSidebar(SIDEBAR_ID, {title: SIDEBAR_TITLE});
 
   const sidebar = await onceSidebarCreated;
@@ -112,17 +125,17 @@ add_task(async function testSidebarSetOb
   let expression = `
     var obj = Object.create(null);
     obj.prop1 = 123;
     obj[Symbol('sym1')] = 456;
     obj.cyclic = obj;
     obj;
   `;
 
-  let evalResult = await inspectedWindowFront.eval(FAKE_CALLER_INFO, expression, {
+  let evalResult = await inspectedWindowFront.eval(fakeExtCallerInfo, expression, {
     evalResultAsGrip: true,
     toolboxConsoleActorID: toolbox.target.form.consoleActor
   });
 
   sidebar.setObjectValueGrip(evalResult.valueGrip, "Expected Root Title");
 
   // Wait the ObjectInspector component to be rendered and test its content.
   await testSetExpressionSidebarPanel(sidebarPanelContent, {
@@ -149,17 +162,17 @@ add_task(async function testSidebarDOMNo
     toolbox.target.client, toolbox.target.form
   );
 
   const sidebar = inspector.getPanel(SIDEBAR_ID);
   const sidebarPanelContent = inspector.sidebar.getTabPanel(SIDEBAR_ID);
 
   let expression = "({ body: document.body })";
 
-  let evalResult = await inspectedWindowFront.eval(FAKE_CALLER_INFO, expression, {
+  let evalResult = await inspectedWindowFront.eval(fakeExtCallerInfo, expression, {
     evalResultAsGrip: true,
     toolboxConsoleActorID: toolbox.target.form.consoleActor
   });
 
   sidebar.setObjectValueGrip(evalResult.valueGrip);
 
   // Wait the DOM node to be rendered inside the component.
   await waitForObjectInspector(sidebarPanelContent, "node");
@@ -231,11 +244,14 @@ add_task(async function teardownExtensio
 
   let inspectorStoreState = inspector.store.getState();
 
   Assert.deepEqual(inspectorStoreState.extensionsSidebar, {},
                    "The extensions sidebar Redux store data has been cleared");
 
   await toolbox.destroy();
 
+  await extension.unload();
+
   toolbox = null;
   inspector = null;
+  extension = null;
 });
--- a/devtools/server/actors/webextension-inspected-window.js
+++ b/devtools/server/actors/webextension-inspected-window.js
@@ -1,31 +1,105 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const protocol = require("devtools/shared/protocol");
 
-const {Ci, Cu, Cr} = require("chrome");
+const {Cc, Ci, Cu, Cr} = require("chrome");
 
 const {DebuggerServer} = require("devtools/server/main");
 const Services = require("Services");
 
 loader.lazyGetter(this, "NodeActor", () => require("devtools/server/actors/inspector/node").NodeActor, true);
 
 const {
   XPCOMUtils,
 } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 const {
   webExtensionInspectedWindowSpec,
 } = require("devtools/shared/specs/webextension-inspected-window");
 
+const {WebExtensionPolicy} = Cu.getGlobalForObject(XPCOMUtils);
+
+// A weak set of the documents for which a warning message has been
+// already logged (so that we don't keep emitting the same warning if an
+// extension keeps calling the devtools.inspectedWindow.eval API method
+// when it fails to retrieve a result, but we do log the warning message
+// if the user reloads the window):
+//
+// WeakSet<Document>
+const deniedWarningDocuments = new WeakSet();
+
+function isSystemPrincipalWindow(window) {
+  return window.document.nodePrincipal.isSystemPrincipal;
+}
+
+// Create the exceptionInfo property in the format expected by a
+// WebExtension inspectedWindow.eval API calls.
+function createExceptionInfoResult(props) {
+  return {
+    exceptionInfo: {
+      isError: true,
+      code: "E_PROTOCOLERROR",
+      description: "Unknown Inspector protocol error",
+
+      // Apply the passed properties.
+      ...props,
+    }
+  };
+}
+
+// Show a warning message in the webconsole when an extension
+// eval request has been denied, so that the user knows about it
+// even if the extension doesn't report the error itself.
+function logAccessDeniedWarning(window, callerInfo, extensionPolicy) {
+  // Do not log the same warning multiple times for the same document.
+  if (deniedWarningDocuments.has(window.document)) {
+    return;
+  }
+
+  deniedWarningDocuments.add(window.document);
+
+  const {name} = extensionPolicy;
+
+  // System principals have a null nodePrincipal.URI and so we use
+  // the url from window.location.href.
+  const reportedURI = isSystemPrincipalWindow(window) ?
+    Services.io.newURI(window.location.href) : window.document.nodePrincipal.URI;
+
+  const error = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
+
+  const msg = `The extension "${name}" is not allowed to access ${reportedURI.spec}`;
+
+  const innerWindowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+
+  const errorFlag = 0;
+
+  let {url, lineNumber} = callerInfo;
+
+  let callerURI = callerInfo.url && Services.io.newURI(callerInfo.url);
+
+  // callerInfo.url is not the full path to the file that called the WebExtensions
+  // API yet (Bug 1448878), and so we associate the error to the url of the extension
+  // manifest.json file as a fallback.
+  if (callerURI.filePath === "/") {
+    url = extensionPolicy.getURL("/manifest.json");
+    lineNumber = null;
+  }
+
+  error.initWithWindowID(msg, url, lineNumber, 0, 0, errorFlag, "webExtensions",
+                         innerWindowId);
+  Services.console.logMessage(error);
+}
+
 function CustomizedReload(params) {
   this.docShell = params.tabActor.window
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDocShell);
   this.docShell.QueryInterface(Ci.nsIWebProgress);
 
   this.inspectedWindowEval = params.inspectedWindowEval;
   this.callerInfo = params.callerInfo;
@@ -209,21 +283,16 @@ var WebExtensionInspectedWindowActor = p
       }
 
       if (this._dbg) {
         this._dbg.enabled = false;
         delete this._dbg;
       }
     },
 
-    isSystemPrincipal(window) {
-      const principal = window.document.nodePrincipal;
-      return Services.scriptSecurityManager.isSystemPrincipal(principal);
-    },
-
     get dbg() {
       if (this._dbg) {
         return this._dbg;
       }
 
       this._dbg = this.tabActor.makeDebugger();
       return this._dbg;
     },
@@ -304,17 +373,17 @@ var WebExtensionInspectedWindowActor = p
      * @param {string|undefined}        options.userAgent
      *   customize the userAgent during the page reload.
      * @param {string|undefined}        options.injectedScript
      *   evaluate the provided javascript code in the top level and every sub-frame
      *   created during the page reload, before any other script in the page has been
      *   executed.
      */
     reload(callerInfo, {ignoreCache, userAgent, injectedScript}) {
-      if (this.isSystemPrincipal(this.window)) {
+      if (isSystemPrincipalWindow(this.window)) {
         console.error("Ignored inspectedWindow.reload on system principal target for " +
                       `${callerInfo.url}:${callerInfo.lineNumber}`);
         return {};
       }
 
       const delayedReload = () => {
         // This won't work while the browser is shutting down and we don't really
         // care.
@@ -404,58 +473,99 @@ var WebExtensionInspectedWindowActor = p
      *   NOTE: this parameter is not part of the RDP protocol exposed by this actor, when
      *   it is called over the remote debugging protocol the target window is always
      *   `tabActor.window`.
      */
     eval(callerInfo, expression, options, customTargetWindow) {
       const window = customTargetWindow || this.window;
       options = options || {};
 
+      const extensionPolicy = WebExtensionPolicy.getByID(callerInfo.addonId);
+
+      if (!extensionPolicy) {
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s %s",
+          details: [
+            "Caller extension not found for",
+            callerInfo.url
+          ],
+        });
+      }
+
       if (!window) {
-        return {
-          exceptionInfo: {
-            isError: true,
-            code: "E_PROTOCOLERROR",
-            description: "Inspector protocol error: %s",
-            details: [
-              "The target window is not defined. inspectedWindow.eval not executed.",
-            ],
-          },
-        };
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s",
+          details: [
+            "The target window is not defined. inspectedWindow.eval not executed.",
+          ],
+        });
       }
 
-      if (this.isSystemPrincipal(window)) {
-        // On denied JS evaluation, report it using the same data format
+      // Log the error for the user to know that the extension request has been denied
+      // (the extension may not warn the user at all).
+      const logEvalDenied = () => {
+        logAccessDeniedWarning(window, callerInfo, extensionPolicy);
+      };
+
+      if (isSystemPrincipalWindow(window)) {
+        logEvalDenied();
+
+        // On denied JS evaluation, report it to the extension using the same data format
         // used in the corresponding chrome API method to report issues that are
         // not exceptions raised in the evaluated javascript code.
-        return {
-          exceptionInfo: {
-            isError: true,
-            code: "E_PROTOCOLERROR",
-            description: "Inspector protocol error: %s",
-            details: [
-              "This target has a system principal. inspectedWindow.eval denied.",
-            ],
-          },
-        };
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s",
+          details: [
+            "This target has a system principal. inspectedWindow.eval denied.",
+          ],
+        });
+      }
+
+      let docPrincipalURI = window.document.nodePrincipal.URI;
+
+      // Deny on document principals listed as restricted or
+      // related to the about: pages (only about:blank and about:srcdoc are
+      // allowed and their are expected to not have their about URI associated
+      // to the principal).
+      if (WebExtensionPolicy.isRestrictedURI(docPrincipalURI) ||
+          docPrincipalURI.schemeIs("about")) {
+        logEvalDenied();
+
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s %s",
+          details: [
+            "This extension is not allowed on the current inspected window origin",
+            docPrincipalURI.spec,
+          ],
+        });
+      }
+
+      const windowAddonId = window.document.nodePrincipal.addonId;
+
+      if (windowAddonId && extensionPolicy.id !== windowAddonId) {
+        logEvalDenied();
+
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s on %s",
+          details: [
+            "This extension is not allowed to access this extension page.",
+            window.document.location.origin,
+          ],
+        });
       }
 
       // Raise an error on the unsupported options.
       if (options.frameURL || options.contextSecurityOrigin ||
           options.useContentScriptContext) {
-        return {
-          exceptionInfo: {
-            isError: true,
-            code: "E_PROTOCOLERROR",
-            description: "Inspector protocol error: %s",
-            details: [
-              "The inspectedWindow.eval options are currently not supported",
-            ],
-          },
-        };
+        return createExceptionInfoResult({
+          description: "Inspector protocol error: %s",
+          details: [
+            "The inspectedWindow.eval options are currently not supported",
+          ],
+        });
       }
 
       const dbgWindow = this.dbg.makeGlobalObjectReference(window);
 
       let evalCalledFrom = callerInfo.url;
       if (callerInfo.lineNumber) {
         evalCalledFrom += `:${callerInfo.lineNumber}`;
       }
@@ -500,27 +610,23 @@ var WebExtensionInspectedWindowActor = p
       }
 
       if (evalResult) {
         try {
           // Return the evalResult as a grip (used by the WebExtensions
           // devtools inspector's sidebar.setExpression API method).
           if (options.evalResultAsGrip) {
             if (!options.toolboxConsoleActorID) {
-              return {
-                exceptionInfo: {
-                  isError: true,
-                  code: "E_PROTOCOLERROR",
-                  description: "Inspector protocol error: %s - %s",
-                  details: [
-                    "Unexpected invalid sidebar panel expression request",
-                    "missing toolboxConsoleActorID",
-                  ],
-                },
-              };
+              return createExceptionInfoResult({
+                description: "Inspector protocol error: %s - %s",
+                details: [
+                  "Unexpected invalid sidebar panel expression request",
+                  "missing toolboxConsoleActorID",
+                ],
+              });
             }
 
             let consoleActor = DebuggerServer.searchAllConnectionsForActor(
               options.toolboxConsoleActorID
             );
 
             return {valueGrip: consoleActor.createValueGrip(evalResult)};
           }
@@ -528,26 +634,22 @@ var WebExtensionInspectedWindowActor = p
           if (evalResult && typeof evalResult === "object") {
             evalResult = evalResult.unsafeDereference();
           }
           evalResult = JSON.parse(JSON.stringify(evalResult));
         } catch (err) {
           // The evaluation result cannot be sent over the RDP Protocol,
           // report it as with the same data format used in the corresponding
           // chrome API method.
-          return {
-            exceptionInfo: {
-              isError: true,
-              code: "E_PROTOCOLERROR",
-              description: "Inspector protocol error: %s",
-              details: [
-                String(err),
-              ],
-            },
-          };
+          return createExceptionInfoResult({
+            description: "Inspector protocol error: %s",
+            details: [
+              String(err),
+            ],
+          });
         }
       }
 
       return {value: evalResult};
     }
   }
 );
 
--- a/devtools/server/tests/browser/browser_webextension_inspected_window.js
+++ b/devtools/server/tests/browser/browser_webextension_inspected_window.js
@@ -5,46 +5,57 @@
 "use strict";
 
 const {
   WebExtensionInspectedWindowFront
 } = require("devtools/shared/fronts/webextension-inspected-window");
 
 const TEST_RELOAD_URL = `${MAIN_DOMAIN}/inspectedwindow-reload-target.sjs`;
 
-const FAKE_CALLER_INFO = {
-  url: "moz-extension://fake-webextension-uuid/fake-caller-script.js",
-  lineNumber: 1,
-  addonId: "fake-webextension-uuid",
-};
+async function setup(pageUrl) {
+  const extension = ExtensionTestUtils.loadExtension({
+    background() {
+      // This is just an empty extension used to ensure that the caller extension uuid
+      // actually exists.
+    }
+  });
 
-async function setup(pageUrl) {
+  await extension.startup();
+
+  const fakeExtCallerInfo = {
+    url: WebExtensionPolicy.getByID(extension.id).getURL("fake-caller-script.js"),
+    lineNumber: 1,
+    addonId: extension.id,
+  };
+
   await addTab(pageUrl);
   initDebuggerServer();
 
   const client = new DebuggerClient(DebuggerServer.connectPipe());
   const form = await connectDebuggerClient(client);
 
   const [, tabClient] = await client.attachTab(form.actor);
 
   const [, consoleClient] = await client.attachConsole(form.consoleActor, []);
 
   const inspectedWindowFront = new WebExtensionInspectedWindowFront(client, form);
 
   return {
     client, form,
     tabClient, consoleClient,
     inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   };
 }
 
-async function teardown({client}) {
+async function teardown({client, extension}) {
   await client.close();
   DebuggerServer.destroy();
   gBrowser.removeCurrentTab();
+  await extension.unload();
 }
 
 function waitForNextTabNavigated(client) {
   return new Promise(resolve => {
     client.addListener("tabNavigated", function tabNavigatedListener(evt, pkt) {
       if (pkt.state == "stop" && !pkt.isFrameSwitching) {
         client.removeListener("tabNavigated", tabNavigatedListener);
         resolve();
@@ -82,43 +93,52 @@ function collectEvalResults() {
     }
     const iframe = iframeDoc.querySelector("iframe");
     iframeDoc = iframe ? iframe.contentDocument : null;
   }
   return JSON.stringify(results);
 }
 
 add_task(async function test_successfull_inspectedWindowEval_result() {
-  const {client, inspectedWindowFront} = await setup(MAIN_DOMAIN);
-  const result = await inspectedWindowFront.eval(FAKE_CALLER_INFO, "window.location", {});
+  const {
+    client, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
+  } = await setup(MAIN_DOMAIN);
+
+  const result = await inspectedWindowFront.eval(fakeExtCallerInfo,
+                                                 "window.location", {});
 
   ok(result.value, "Got a result from inspectedWindow eval");
   is(result.value.href, MAIN_DOMAIN,
      "Got the expected window.location.href property value");
   is(result.value.protocol, "http:",
      "Got the expected window.location.protocol property value");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_successfull_inspectedWindowEval_resultAsGrip() {
-  const {client, inspectedWindowFront, form} = await setup(MAIN_DOMAIN);
-  let result = await inspectedWindowFront.eval(FAKE_CALLER_INFO, "window", {
+  const {
+    client, inspectedWindowFront, form,
+    extension, fakeExtCallerInfo,
+  } = await setup(MAIN_DOMAIN);
+
+  let result = await inspectedWindowFront.eval(fakeExtCallerInfo, "window", {
     evalResultAsGrip: true,
     toolboxConsoleActorID: form.consoleActor
   });
 
   ok(result.valueGrip, "Got a result from inspectedWindow eval");
   ok(result.valueGrip.actor, "Got a object actor as expected");
   is(result.valueGrip.type, "object", "Got a value grip of type object");
   is(result.valueGrip.class, "Window", "Got a value grip which is instanceof Location");
 
   // Test invalid evalResultAsGrip request.
   result = await inspectedWindowFront.eval(
-    FAKE_CALLER_INFO, "window", {evalResultAsGrip: true}
+    fakeExtCallerInfo, "window", {evalResultAsGrip: true}
   );
 
   ok(!result.value && !result.valueGrip,
      "Got a null result from the invalid inspectedWindow eval call");
   ok(result.exceptionInfo.isError, "Got an API Error result from inspectedWindow eval");
   ok(!result.exceptionInfo.isException, "An error isException is false as expected");
   is(result.exceptionInfo.code, "E_PROTOCOLERROR",
      "Got the expected 'code' property in the error result");
@@ -128,244 +148,261 @@ add_task(async function test_successfull
      "The 'details' array property should contains 1 element");
   is(result.exceptionInfo.details[0],
      "Unexpected invalid sidebar panel expression request",
      "Got the expected content in the error results's details");
   is(result.exceptionInfo.details[1],
      "missing toolboxConsoleActorID",
      "Got the expected content in the error results's details");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_error_inspectedWindowEval_result() {
-  const {client, inspectedWindowFront} = await setup(MAIN_DOMAIN);
-  const result = await inspectedWindowFront.eval(FAKE_CALLER_INFO, "window", {});
+  const {
+    client, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
+  } = await setup(MAIN_DOMAIN);
+
+  const result = await inspectedWindowFront.eval(fakeExtCallerInfo, "window", {});
 
   ok(!result.value, "Got a null result from inspectedWindow eval");
   ok(result.exceptionInfo.isError, "Got an API Error result from inspectedWindow eval");
   ok(!result.exceptionInfo.isException, "An error isException is false as expected");
   is(result.exceptionInfo.code, "E_PROTOCOLERROR",
      "Got the expected 'code' property in the error result");
   is(result.exceptionInfo.description, "Inspector protocol error: %s",
      "Got the expected 'description' property in the error result");
   is(result.exceptionInfo.details.length, 1,
      "The 'details' array property should contains 1 element");
   ok(result.exceptionInfo.details[0].includes("cyclic object value"),
      "Got the expected content in the error results's details");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_system_principal_denied_error_inspectedWindowEval_result() {
-  const {client, inspectedWindowFront} = await setup("about:addons");
-  const result = await inspectedWindowFront.eval(FAKE_CALLER_INFO, "window", {});
+  const {
+    client, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
+  } = await setup("about:addons");
+
+  const result = await inspectedWindowFront.eval(fakeExtCallerInfo, "window", {});
 
   ok(!result.value, "Got a null result from inspectedWindow eval");
   ok(result.exceptionInfo.isError,
      "Got an API Error result from inspectedWindow eval on a system principal page");
   is(result.exceptionInfo.code, "E_PROTOCOLERROR",
      "Got the expected 'code' property in the error result");
   is(result.exceptionInfo.description, "Inspector protocol error: %s",
      "Got the expected 'description' property in the error result");
   is(result.exceptionInfo.details.length, 1,
      "The 'details' array property should contains 1 element");
   is(result.exceptionInfo.details[0],
      "This target has a system principal. inspectedWindow.eval denied.",
      "Got the expected content in the error results's details");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowEval_result() {
-  const {client, inspectedWindowFront} = await setup(MAIN_DOMAIN);
+  const {
+    client, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
+  } = await setup(MAIN_DOMAIN);
+
   const result = await inspectedWindowFront.eval(
-    FAKE_CALLER_INFO, "throw Error('fake eval error');", {});
+    fakeExtCallerInfo, "throw Error('fake eval error');", {});
 
   ok(result.exceptionInfo.isException, "Got an exception as expected");
   ok(!result.value, "Got an undefined eval value");
   ok(!result.exceptionInfo.isError, "An exception should not be isError=true");
   ok(result.exceptionInfo.value.includes("Error: fake eval error"),
      "Got the expected exception message");
 
   const expectedCallerInfo =
-    `called from ${FAKE_CALLER_INFO.url}:${FAKE_CALLER_INFO.lineNumber}`;
+    `called from ${fakeExtCallerInfo.url}:${fakeExtCallerInfo.lineNumber}`;
   ok(result.exceptionInfo.value.includes(expectedCallerInfo),
      "Got the expected caller info in the exception message");
 
   const expectedStack = `eval code:1:7`;
   ok(result.exceptionInfo.value.includes(expectedStack),
      "Got the expected stack trace in the exception message");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowReload() {
   const {
     client, consoleClient, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   } = await setup(`${TEST_RELOAD_URL}?test=cache`);
 
   // Test reload with bypassCache=false.
 
   const waitForNoBypassCacheReload = waitForNextTabNavigated(client);
-  const reloadResult = await inspectedWindowFront.reload(FAKE_CALLER_INFO,
+  const reloadResult = await inspectedWindowFront.reload(fakeExtCallerInfo,
                                                          {ignoreCache: false});
 
   ok(!reloadResult, "Got the expected undefined result from inspectedWindow reload");
 
   await waitForNoBypassCacheReload;
 
   const noBypassCacheEval = await consoleEvalJS(consoleClient,
                                                 "document.body.textContent");
 
   is(noBypassCacheEval.result, "empty cache headers",
      "Got the expected result with reload forceBypassCache=false");
 
   // Test reload with bypassCache=true.
 
   const waitForForceBypassCacheReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO, {ignoreCache: true});
+  await inspectedWindowFront.reload(fakeExtCallerInfo, {ignoreCache: true});
 
   await waitForForceBypassCacheReload;
 
   const forceBypassCacheEval = await consoleEvalJS(consoleClient,
                                                    "document.body.textContent");
 
   is(forceBypassCacheEval.result, "no-cache:no-cache",
      "Got the expected result with reload forceBypassCache=true");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowReload_customUserAgent() {
   const {
     client, consoleClient, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   } = await setup(`${TEST_RELOAD_URL}?test=user-agent`);
 
   // Test reload with custom userAgent.
 
   const waitForCustomUserAgentReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO,
+  await inspectedWindowFront.reload(fakeExtCallerInfo,
                                     {userAgent: "Customized User Agent"});
 
   await waitForCustomUserAgentReload;
 
   const customUserAgentEval = await consoleEvalJS(consoleClient,
                                                   "document.body.textContent");
 
   is(customUserAgentEval.result, "Customized User Agent",
      "Got the expected result on reload with a customized userAgent");
 
   // Test reload with no custom userAgent.
 
   const waitForNoCustomUserAgentReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO, {});
+  await inspectedWindowFront.reload(fakeExtCallerInfo, {});
 
   await waitForNoCustomUserAgentReload;
 
   const noCustomUserAgentEval = await consoleEvalJS(consoleClient,
                                                     "document.body.textContent");
 
   is(noCustomUserAgentEval.result, window.navigator.userAgent,
      "Got the expected result with reload without a customized userAgent");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowReload_injectedScript() {
   const {
     client, consoleClient, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   } = await setup(`${TEST_RELOAD_URL}?test=injected-script&frames=3`);
 
   // Test reload with an injectedScript.
 
   const waitForInjectedScriptReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO,
+  await inspectedWindowFront.reload(fakeExtCallerInfo,
                                     {injectedScript: `new ${injectedScript}`});
   await waitForInjectedScriptReload;
 
   const injectedScriptEval = await consoleEvalJS(consoleClient,
                                                  `(${collectEvalResults})()`);
 
-  const expectedResult = (new Array(4)).fill("injected script executed first");
+  const expectedResult = (new Array(5)).fill("injected script executed first");
 
   SimpleTest.isDeeply(JSON.parse(injectedScriptEval.result), expectedResult,
      "Got the expected result on reload with an injected script");
 
   // Test reload without an injectedScript.
 
   const waitForNoInjectedScriptReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO, {});
+  await inspectedWindowFront.reload(fakeExtCallerInfo, {});
   await waitForNoInjectedScriptReload;
 
   const noInjectedScriptEval = await consoleEvalJS(consoleClient,
                                                    `(${collectEvalResults})()`);
 
-  const newExpectedResult = (new Array(4)).fill("injected script NOT executed");
+  const newExpectedResult = (new Array(5)).fill("injected script NOT executed");
 
   SimpleTest.isDeeply(JSON.parse(noInjectedScriptEval.result), newExpectedResult,
                       "Got the expected result on reload with no injected script");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowReload_multiple_calls() {
   const {
     client, consoleClient, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   } = await setup(`${TEST_RELOAD_URL}?test=user-agent`);
 
   // Test reload with custom userAgent three times (and then
   // check that only the first one has affected the page reload.
 
   const waitForCustomUserAgentReload = waitForNextTabNavigated(client);
 
-  inspectedWindowFront.reload(FAKE_CALLER_INFO, {userAgent: "Customized User Agent 1"});
-  inspectedWindowFront.reload(FAKE_CALLER_INFO, {userAgent: "Customized User Agent 2"});
+  inspectedWindowFront.reload(fakeExtCallerInfo, {userAgent: "Customized User Agent 1"});
+  inspectedWindowFront.reload(fakeExtCallerInfo, {userAgent: "Customized User Agent 2"});
 
   await waitForCustomUserAgentReload;
 
   const customUserAgentEval = await consoleEvalJS(consoleClient,
                                                   "document.body.textContent");
 
   is(customUserAgentEval.result, "Customized User Agent 1",
      "Got the expected result on reload with a customized userAgent");
 
   // Test reload with no custom userAgent.
 
   const waitForNoCustomUserAgentReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO, {});
+  await inspectedWindowFront.reload(fakeExtCallerInfo, {});
 
   await waitForNoCustomUserAgentReload;
 
   const noCustomUserAgentEval = await consoleEvalJS(consoleClient,
                                                     "document.body.textContent");
 
   is(noCustomUserAgentEval.result, window.navigator.userAgent,
      "Got the expected result with reload without a customized userAgent");
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 add_task(async function test_exception_inspectedWindowReload_stopped() {
   const {
     client, consoleClient, inspectedWindowFront,
+    extension, fakeExtCallerInfo,
   } = await setup(`${TEST_RELOAD_URL}?test=injected-script&frames=3`);
 
   // Test reload on a page that calls window.stop() immediately during the page loading
 
   const waitForPageLoad = waitForNextTabNavigated(client);
-  await inspectedWindowFront.eval(FAKE_CALLER_INFO,
+  await inspectedWindowFront.eval(fakeExtCallerInfo,
                                   "window.location += '&stop=windowStop'");
 
   info("Load a webpage that calls 'window.stop()' while is still loading");
   await waitForPageLoad;
 
   info("Starting a reload with an injectedScript");
   const waitForInjectedScriptReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO,
+  await inspectedWindowFront.reload(fakeExtCallerInfo,
                                     {injectedScript: `new ${injectedScript}`});
   await waitForInjectedScriptReload;
 
   const injectedScriptEval = await consoleEvalJS(consoleClient,
                                                  `(${collectEvalResults})()`);
 
   // The page should have stopped during the reload and only one injected script
   // is expected.
@@ -373,28 +410,28 @@ add_task(async function test_exception_i
 
   SimpleTest.isDeeply(JSON.parse(injectedScriptEval.result), expectedResult,
      "The injected script has been executed on the 'stopped' page reload");
 
   // Reload again with no options.
 
   info("Reload the tab again without any reload options");
   const waitForNoInjectedScriptReload = waitForNextTabNavigated(client);
-  await inspectedWindowFront.reload(FAKE_CALLER_INFO, {});
+  await inspectedWindowFront.reload(fakeExtCallerInfo, {});
   await waitForNoInjectedScriptReload;
 
   const noInjectedScriptEval = await consoleEvalJS(consoleClient,
                                                    `(${collectEvalResults})()`);
 
   // The page should have stopped during the reload and no injected script should
   // have been executed during this second reload (or it would mean that the previous
   // customized reload was still pending and has wrongly affected the second reload)
   const newExpectedResult = (new Array(1)).fill("injected script NOT executed");
 
   SimpleTest.isDeeply(
     JSON.parse(noInjectedScriptEval.result), newExpectedResult,
     "No injectedScript should have been evaluated during the second reload"
   );
 
-  await teardown({client});
+  await teardown({client, extension});
 });
 
 // TODO: check eval with $0 binding once implemented (Bug 1300590)
--- a/devtools/server/tests/browser/inspectedwindow-reload-target.sjs
+++ b/devtools/server/tests/browser/inspectedwindow-reload-target.sjs
@@ -43,16 +43,24 @@ function handleInjectedScriptTestRequest
 
   const frames = parseInt(params.get("frames"));
   let content = "";
 
   if (frames > 0) {
     // Output an iframe in seamless mode, so that there is an higher chance that in case
     // of test failures we get a screenshot where the nested iframes are all visible.
     content = `<iframe seamless src="?test=injected-script&frames=${frames - 1}"></iframe>`;
+  } else {
+    // Output an about:srcdoc frame to be sure that inspectedWindow.eval is able to
+    // evaluate js code into it.
+    let srcdoc = `
+      <pre>injected script NOT executed</pre>
+      <script>window.pageScriptExecutedFirst = true</script>
+    `;
+    content = `<iframe style="height: 30px;" srcdoc="${srcdoc}"></iframe>`;
   }
 
   if (params.get("stop") == "windowStop") {
     content = "<script>window.stop();</script>" + content;
   }
 
   response.write(`<!DOCTYPE html>
     <html>
--- a/dom/base/CharacterData.cpp
+++ b/dom/base/CharacterData.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
 #include "nsTextNode.h"
 #include "nsBidiUtils.h"
 #include "PLDHashTable.h"
 #include "mozilla/Sprintf.h"
+#include "nsWindowSizes.h"
 #include "nsWrapperCacheInlines.h"
 
 namespace mozilla {
 namespace dom {
 
 CharacterData::CharacterData(already_AddRefed<dom::NodeInfo>& aNodeInfo)
   : nsIContent(aNodeInfo)
 {
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -133,17 +133,17 @@ private:
   virtual ~CustomElementData() {}
 
   // Custom element type, for <button is="x-button"> or <x-button>
   // this would be x-button.
   RefPtr<nsAtom> mType;
   RefPtr<CustomElementDefinition> mCustomElementDefinition;
 };
 
-#define ALEADY_CONSTRUCTED_MARKER nullptr
+#define ALREADY_CONSTRUCTED_MARKER nullptr
 
 // The required information for a custom element as defined in:
 // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
 struct CustomElementDefinition
 {
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -74,16 +74,17 @@
 #include "mozilla/TextEditor.h"
 #include "mozilla/TextEvents.h"
 #include "nsNodeUtils.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsDocument.h"
 #include "nsAttrValueOrString.h"
 #include "nsAttrValueInlines.h"
 #include "nsCSSPseudoElements.h"
+#include "nsWindowSizes.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif /* MOZ_XUL */
 #include "nsSVGElement.h"
 #include "nsFrameSelection.h"
 #ifdef DEBUG
 #include "nsRange.h"
 #endif
@@ -4356,17 +4357,17 @@ MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(Serv
 
 void
 Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
 {
   FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
 
   if (HasServoData()) {
     // Measure the ElementData object itself.
-    aSizes.mLayoutServoElementDataObjects +=
+    aSizes.mLayoutElementDataObjects +=
       aSizes.mState.mMallocSizeOf(mServoData.Get());
 
     // Measure mServoData, excluding the ComputedValues. This measurement
     // counts towards the element's size. We use ServoElementMallocSizeOf and
     // ServoElementMallocEnclosingSizeOf rather than |aState.mMallocSizeOf| to
     // better distinguish in DMD's output the memory measured within Servo
     // code.
     *aNodeSize +=
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -79,16 +79,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsPIBoxObject.h"
 #include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsTextFragment.h"
 #include "nsContentCID.h"
+#include "nsWindowSizes.h"
 
 #include "nsIDOMEventListener.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
 #include "nsNodeInfoManager.h"
 #include "nsICategoryManager.h"
--- a/dom/base/Link.h
+++ b/dom/base/Link.h
@@ -13,16 +13,17 @@
 
 #include "mozilla/MemoryReporting.h"
 #include "nsIContent.h" // for nsLinkState
 #include "nsIContentPolicy.h"
 
 namespace mozilla {
 
 class EventStates;
+class SizeOfState;
 
 namespace dom {
 
 class Element;
 
 #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID               \
 { 0xb25edee6, 0xdd35, 0x4f8b,                             \
   { 0xab, 0x90, 0x66, 0xd0, 0xbd, 0x3c, 0x22, 0xd5 } }
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2400,16 +2400,23 @@ nsINode::AddSizeOfExcludingThis(nsWindow
   // - mNodeInfo
   // - mSlots
   //
   // The following members are not measured:
   // - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
   //   non-owning
 }
 
+void
+nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
+{
+  *aNodeSize += aSizes.mState.mMallocSizeOf(this);
+  AddSizeOfExcludingThis(aSizes, aNodeSize);
+}
+
 #define EVENT(name_, id_, type_, struct_)                                    \
   EventHandlerNonNull* nsINode::GetOn##name_() {                             \
     EventListenerManager *elm = GetExistingListenerManager();                \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())   \
                : nullptr;                                                    \
   }                                                                          \
   void nsINode::SetOn##name_(EventHandlerNonNull* handler)                   \
   {                                                                          \
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -12,17 +12,16 @@
 #include "nsCOMPtr.h"               // for member, local
 #include "nsGkAtoms.h"              // for nsGkAtoms::baseURIProperty
 #include "nsIDOMNode.h"
 #include "mozilla/dom/NodeInfo.h"            // member (in nsCOMPtr)
 #include "nsIVariant.h"             // for use in GetUserData()
 #include "nsNodeInfoManager.h"      // for use in NodePrincipal()
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
-#include "nsWindowSizes.h"          // for nsStyleSizes
 #include "mozilla/ErrorResult.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/NodeBinding.h"
@@ -51,16 +50,17 @@ class nsINode;
 class nsINodeList;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
 class nsNodeWeakReference;
 class nsDOMMutationObserver;
 class nsRange;
+class nsWindowSizes;
 struct RawServoSelectorList;
 
 namespace mozilla {
 class EventListenerManager;
 class TextEditor;
 namespace dom {
 /**
  * @return true if aChar is what the WHATWG defines as a 'ascii whitespace'.
@@ -354,21 +354,17 @@ public:
   virtual void AddSizeOfExcludingThis(nsWindowSizes& aSizes,
                                       size_t* aNodeSize) const;
 
   // SizeOfIncludingThis doesn't need to be overridden by sub-classes because
   // sub-classes of nsINode are guaranteed to be laid out in memory in such a
   // way that |this| points to the start of the allocated object, even in
   // methods of nsINode's sub-classes, so aSizes.mState.mMallocSizeOf(this) is
   // always safe to call no matter which object it was invoked on.
-  virtual void AddSizeOfIncludingThis(nsWindowSizes& aSizes,
-                                      size_t* aNodeSize) const {
-    *aNodeSize += aSizes.mState.mMallocSizeOf(this);
-    AddSizeOfExcludingThis(aSizes, aNodeSize);
-  }
+  void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const;
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
   friend class nsAttrAndChildArray;
 
 #ifdef MOZILLA_INTERNAL_API
   explicit nsINode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -353,51 +353,46 @@ CollectWindowReports(nsGlobalWindowInner
   REPORT_SIZE("/layout/style-sheets", mLayoutStyleSheetsSize,
               "Memory used by style sheets within a window.");
 
   REPORT_SIZE("/layout/pres-shell", mLayoutPresShellSize,
               "Memory used by layout's PresShell, along with any structures "
               "allocated in its arena and not measured elsewhere, "
               "within a window.");
 
-  REPORT_SIZE("/layout/gecko-style-sets", mLayoutGeckoStyleSets,
-              "Memory used by Gecko style sets within a window.");
+  REPORT_SIZE("/layout/style-sets/stylist/rule-tree",
+              mLayoutStyleSetsStylistRuleTree,
+              "Memory used by rule trees within style sets within a window.");
 
-  REPORT_SIZE("/layout/servo-style-sets/stylist/rule-tree",
-              mLayoutServoStyleSetsStylistRuleTree,
-              "Memory used by rule trees within Servo style sets within a "
-              "window.");
-
-  REPORT_SIZE("/layout/servo-style-sets/stylist/element-and-pseudos-maps",
-              mLayoutServoStyleSetsStylistElementAndPseudosMaps,
-              "Memory used by element and pseudos maps within Servo style "
+  REPORT_SIZE("/layout/style-sets/stylist/element-and-pseudos-maps",
+              mLayoutStyleSetsStylistElementAndPseudosMaps,
+              "Memory used by element and pseudos maps within style "
               "sets within a window.");
 
-  REPORT_SIZE("/layout/servo-style-sets/stylist/invalidation-map",
-              mLayoutServoStyleSetsStylistInvalidationMap,
-              "Memory used by invalidation maps within Servo style sets "
+  REPORT_SIZE("/layout/style-sets/stylist/invalidation-map",
+              mLayoutStyleSetsStylistInvalidationMap,
+              "Memory used by invalidation maps within style sets "
               "within a window.");
 
-  REPORT_SIZE("/layout/servo-style-sets/stylist/revalidation-selectors",
-              mLayoutServoStyleSetsStylistRevalidationSelectors,
-              "Memory used by selectors for cache revalidation within Servo "
+  REPORT_SIZE("/layout/style-sets/stylist/revalidation-selectors",
+              mLayoutStyleSetsStylistRevalidationSelectors,
+              "Memory used by selectors for cache revalidation within "
               "style sets within a window.");
 
-  REPORT_SIZE("/layout/servo-style-sets/stylist/other",
-              mLayoutServoStyleSetsStylistOther,
-              "Memory used by other Stylist data within Servo style sets "
+  REPORT_SIZE("/layout/style-sets/stylist/other",
+              mLayoutStyleSetsStylistOther,
+              "Memory used by other Stylist data within style sets "
               "within a window.");
 
-  REPORT_SIZE("/layout/servo-style-sets/other", mLayoutServoStyleSetsOther,
-              "Memory used by other parts of Servo style sets within a "
-              "window.");
+  REPORT_SIZE("/layout/style-sets/other", mLayoutStyleSetsOther,
+              "Memory used by other parts of style sets within a window.");
 
-  REPORT_SIZE("/layout/servo-element-data-objects",
-              mLayoutServoElementDataObjects,
-              "Memory used for Servo ElementData objects, but not the things"
+  REPORT_SIZE("/layout/element-data-objects",
+              mLayoutElementDataObjects,
+              "Memory used for ElementData objects, but not the things"
               "hanging off them.");
 
   REPORT_SIZE("/layout/text-runs", mLayoutTextRunsSize,
               "Memory used for text-runs (glyph layout) in the PresShell's "
               "frame tree, within a window.");
 
   REPORT_SIZE("/layout/pres-contexts", mLayoutPresContextSize,
               "Memory used for the PresContext in the PresShell's frame "
@@ -446,44 +441,16 @@ CollectWindowReports(nsGlobalWindowInner
               "Memory used by ComputedStyles within a window.");
 
   // There are many different kinds of style structs, but it is likely that
   // only a few matter. Implement a cutoff so we don't bloat about:memory with
   // many uninteresting entries.
   const size_t STYLE_SUNDRIES_THRESHOLD =
     js::MemoryReportingSundriesThreshold();
 
-  // This is the Gecko style structs, which are in the nsPresArena.
-  size_t geckoStyleSundriesSize = 0;
-#define STYLE_STRUCT(name_) \
-  { \
-    size_t size = \
-      windowSizes.mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
-    if (size < STYLE_SUNDRIES_THRESHOLD) { \
-      geckoStyleSundriesSize += size; \
-    } else { \
-      REPORT_SUM_SIZE( \
-        "/layout/gecko-style-structs/" # name_, size, \
-        "Memory used by the " #name_ " Gecko style structs within a window."); \
-    } \
-    aWindowTotalSizes->mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \
-      size; \
-  }
-#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
-
-  if (geckoStyleSundriesSize > 0) {
-    REPORT_SUM_SIZE(
-      "/layout/gecko-style-structs/sundries", geckoStyleSundriesSize,
-      "The sum of all memory used by Gecko style structs which were too small "
-      "to be shown individually.");
-  }
-
   // There are many different kinds of frames, but it is very likely
   // that only a few matter.  Implement a cutoff so we don't bloat
   // about:memory with many uninteresting entries.
   const size_t FRAME_SUNDRIES_THRESHOLD =
     js::MemoryReportingSundriesThreshold();
 
   size_t frameSundriesSize = 0;
 #define FRAME_ID(classname, ...) \
@@ -505,39 +472,39 @@ CollectWindowReports(nsGlobalWindowInner
 
   if (frameSundriesSize > 0) {
     REPORT_SUM_SIZE(
       "/layout/frames/sundries", frameSundriesSize,
       "The sum of all memory used by frames which were too small to be shown "
       "individually.");
   }
 
-  // This is the Servo style structs.
-  size_t servoStyleSundriesSize = 0;
+  // This is the style structs.
+  size_t styleSundriesSize = 0;
 #define STYLE_STRUCT(name_) \
   { \
-    size_t size = windowSizes.mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
+    size_t size = windowSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_); \
     if (size < STYLE_SUNDRIES_THRESHOLD) { \
-      servoStyleSundriesSize += size; \
+      styleSundriesSize += size; \
     } else { \
       REPORT_SUM_SIZE( \
-        "/layout/servo-style-structs/" # name_, size, \
-        "Memory used by the " #name_ " Servo style structs within a window."); \
+        "/layout/style-structs/" # name_, size, \
+        "Memory used by the " #name_ " style structs within a window."); \
     } \
-    aWindowTotalSizes->mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += size; \
+    aWindowTotalSizes->mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += size; \
   }
 #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
-  if (servoStyleSundriesSize > 0) {
+  if (styleSundriesSize > 0) {
     REPORT_SUM_SIZE(
-      "/layout/servo-style-structs/sundries", servoStyleSundriesSize,
-      "The sum of all memory used by Servo style structs which were too "
+      "/layout/style-structs/sundries", styleSundriesSize,
+      "The sum of all memory used by style structs which were too "
       "small to be shown individually.");
   }
 
 #undef REPORT_SIZE
 #undef REPORT_SUM_SIZE
 #undef REPORT_COUNT
 }
 
@@ -655,32 +622,28 @@ nsWindowMemoryReporter::CollectReports(n
   REPORT("window-objects/layout/style-sheets",
          windowTotalSizes.mLayoutStyleSheetsSize,
          "This is the sum of all windows' 'layout/style-sheets' numbers.");
 
   REPORT("window-objects/layout/pres-shell",
          windowTotalSizes.mLayoutPresShellSize,
          "This is the sum of all windows' 'layout/arenas' numbers.");
 
-  REPORT("window-objects/layout/gecko-style-sets",
-         windowTotalSizes.mLayoutGeckoStyleSets,
-         "This is the sum of all windows' 'layout/gecko-style-sets' numbers.");
+  REPORT("window-objects/layout/style-sets",
+         windowTotalSizes.mLayoutStyleSetsStylistRuleTree +
+         windowTotalSizes.mLayoutStyleSetsStylistElementAndPseudosMaps +
+         windowTotalSizes.mLayoutStyleSetsStylistInvalidationMap +
+         windowTotalSizes.mLayoutStyleSetsStylistRevalidationSelectors +
+         windowTotalSizes.mLayoutStyleSetsStylistOther +
+         windowTotalSizes.mLayoutStyleSetsOther,
+         "This is the sum of all windows' 'layout/style-sets/' numbers.");
 
-  REPORT("window-objects/layout/servo-style-sets",
-         windowTotalSizes.mLayoutServoStyleSetsStylistRuleTree +
-         windowTotalSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps +
-         windowTotalSizes.mLayoutServoStyleSetsStylistInvalidationMap +
-         windowTotalSizes.mLayoutServoStyleSetsStylistRevalidationSelectors +
-         windowTotalSizes.mLayoutServoStyleSetsStylistOther +
-         windowTotalSizes.mLayoutServoStyleSetsOther,
-         "This is the sum of all windows' 'layout/servo-style-sets/' numbers.");
-
-  REPORT("window-objects/layout/servo-element-data-objects",
-         windowTotalSizes.mLayoutServoElementDataObjects,
-         "This is the sum of all windows' 'layout/servo-element-data-objects' "
+  REPORT("window-objects/layout/element-data-objects",
+         windowTotalSizes.mLayoutElementDataObjects,
+         "This is the sum of all windows' 'layout/element-data-objects' "
          "numbers.");
 
   REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRunsSize,
          "This is the sum of all windows' 'layout/text-runs' numbers.");
 
   REPORT("window-objects/layout/pres-contexts",
          windowTotalSizes.mLayoutPresContextSize,
          "This is the sum of all windows' 'layout/pres-contexts' numbers.");
@@ -706,53 +669,40 @@ nsWindowMemoryReporter::CollectReports(n
   REPORT("window-objects/layout/rule-nodes",
          windowTotalSizes.mArenaSizes.mRuleNodes,
          "This is the sum of all windows' 'layout/rule-nodes' numbers.");
 
   REPORT("window-objects/layout/style-contexts",
          windowTotalSizes.mArenaSizes.mComputedStyles,
          "This is the sum of all windows' 'layout/style-contexts' numbers.");
 
-  size_t geckoStyleTotal = 0;
-#define STYLE_STRUCT(name_) \
-  geckoStyleTotal += \
-    windowTotalSizes.mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_);
-#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
-
-  REPORT("window-objects/layout/gecko-style-structs", geckoStyleTotal,
-         "Memory used for style structs within windows. This is the sum of "
-         "all windows' 'layout/gecko-style-structs/' numbers.");
-
   size_t frameTotal = 0;
 #define FRAME_ID(classname, ...) \
   frameTotal += windowTotalSizes.mArenaSizes.NS_ARENA_SIZES_FIELD(classname);
 #define ABSTRACT_FRAME_ID(...)
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
 
   REPORT("window-objects/layout/frames", frameTotal,
          "Memory used for layout frames within windows. "
          "This is the sum of all windows' 'layout/frames/' numbers.");
 
-  size_t servoStyleTotal = 0;
+  size_t styleTotal = 0;
 #define STYLE_STRUCT(name_) \
-  servoStyleTotal += \
-    windowTotalSizes.mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_);
+  styleTotal += \
+    windowTotalSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_);
 #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
-  REPORT("window-objects/layout/servo-style-structs", servoStyleTotal,
+  REPORT("window-objects/layout/style-structs", styleTotal,
          "Memory used for style structs within windows. This is the sum of "
-         "all windows' 'layout/servo-style-structs/' numbers.");
+         "all windows' 'layout/style-structs/' numbers.");
 
 #undef REPORT
 
   return NS_OK;
 }
 
 uint32_t
 nsWindowMemoryReporter::GetGhostTimeout()
--- a/dom/base/nsWindowSizes.h
+++ b/dom/base/nsWindowSizes.h
@@ -111,63 +111,58 @@ struct nsArenaSizes {
 
       #define FRAME_ID(classname, ...) \
         NS_ARENA_SIZES_FIELD(classname)(0),
       #define ABSTRACT_FRAME_ID(...)
       #include "nsFrameIdList.h"
       #undef FRAME_ID
       #undef ABSTRACT_FRAME_ID
 
-      mGeckoStyleSizes()
+      dummy()
   {}
 
   void addToTabSizes(nsTabSizes* aSizes) const
   {
     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
 
     #define FRAME_ID(classname, ...) \
       aSizes->add(nsTabSizes::Other, NS_ARENA_SIZES_FIELD(classname));
     #define ABSTRACT_FRAME_ID(...)
     #include "nsFrameIdList.h"
     #undef FRAME_ID
     #undef ABSTRACT_FRAME_ID
-
-    mGeckoStyleSizes.addToTabSizes(aSizes);
   }
 
   size_t getTotalSize() const
   {
     size_t total = 0;
 
     FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
 
     #define FRAME_ID(classname, ...) \
       total += NS_ARENA_SIZES_FIELD(classname);
     #define ABSTRACT_FRAME_ID(...)
     #include "nsFrameIdList.h"
     #undef FRAME_ID
     #undef ABSTRACT_FRAME_ID
 
-    total += mGeckoStyleSizes.getTotalSize();
-
     return total;
   }
 
   FOR_EACH_SIZE(DECL_SIZE)
 
   #define FRAME_ID(classname, ...) \
     size_t NS_ARENA_SIZES_FIELD(classname);
   #define ABSTRACT_FRAME_ID(...)
   #include "nsFrameIdList.h"
   #undef FRAME_ID
   #undef ABSTRACT_FRAME_ID
 
-  // This is Gecko-only because in Stylo these style structs are stored outside
-  // the nsPresArena, and so measured elsewhere.
-  nsStyleSizes mGeckoStyleSizes;
+  // Present just to absorb the trailing comma in the constructor.
+  int dummy;
 
 #undef FOR_EACH_SIZE
 };
 
 class nsWindowSizes
 {
 #define FOR_EACH_SIZE(macro) \
   macro(DOM,   mDOMElementNodesSize) \
@@ -175,24 +170,23 @@ class nsWindowSizes
   macro(DOM,   mDOMCDATANodesSize) \
   macro(DOM,   mDOMCommentNodesSize) \
   macro(DOM,   mDOMEventTargetsSize) \
   macro(DOM,   mDOMPerformanceUserEntries) \
   macro(DOM,   mDOMPerformanceResourceEntries) \
   macro(DOM,   mDOMOtherSize) \
   macro(Style, mLayoutStyleSheetsSize) \
   macro(Other, mLayoutPresShellSize) \
-  macro(Style, mLayoutGeckoStyleSets) \
-  macro(Style, mLayoutServoStyleSetsStylistRuleTree) \
-  macro(Style, mLayoutServoStyleSetsStylistElementAndPseudosMaps) \
-  macro(Style, mLayoutServoStyleSetsStylistInvalidationMap) \
-  macro(Style, mLayoutServoStyleSetsStylistRevalidationSelectors) \
-  macro(Style, mLayoutServoStyleSetsStylistOther) \
-  macro(Style, mLayoutServoStyleSetsOther) \
-  macro(Style, mLayoutServoElementDataObjects) \
+  macro(Style, mLayoutStyleSetsStylistRuleTree) \
+  macro(Style, mLayoutStyleSetsStylistElementAndPseudosMaps) \
+  macro(Style, mLayoutStyleSetsStylistInvalidationMap) \
+  macro(Style, mLayoutStyleSetsStylistRevalidationSelectors) \
+  macro(Style, mLayoutStyleSetsStylistOther) \
+  macro(Style, mLayoutStyleSetsOther) \
+  macro(Style, mLayoutElementDataObjects) \
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Style, mLayoutComputedValuesDom) \
   macro(Style, mLayoutComputedValuesNonDom) \
   macro(Style, mLayoutComputedValuesVisited) \
   macro(Style, mLayoutComputedValuesStale) \
   macro(Other, mPropertyTablesSize) \
@@ -200,47 +194,45 @@ class nsWindowSizes
 
 public:
   explicit nsWindowSizes(mozilla::SizeOfState& aState)
     :
       FOR_EACH_SIZE(ZERO_SIZE)
       mDOMEventTargetsCount(0),
       mDOMEventListenersCount(0),
       mArenaSizes(),
-      mServoStyleSizes(),
+      mStyleSizes(),
       mState(aState)
   {}
 
   void addToTabSizes(nsTabSizes* aSizes) const {
     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
     mArenaSizes.addToTabSizes(aSizes);
-    mServoStyleSizes.addToTabSizes(aSizes);
+    mStyleSizes.addToTabSizes(aSizes);
   }
 
   size_t getTotalSize() const
   {
     size_t total = 0;
 
     FOR_EACH_SIZE(ADD_TO_TOTAL_SIZE)
     total += mArenaSizes.getTotalSize();
-    total += mServoStyleSizes.getTotalSize();
+    total += mStyleSizes.getTotalSize();
 
     return total;
   }
 
   FOR_EACH_SIZE(DECL_SIZE);
 
   uint32_t mDOMEventTargetsCount;
   uint32_t mDOMEventListenersCount;
 
   nsArenaSizes mArenaSizes;
 
-  // This is Stylo-only because in Gecko these style structs are stored in the
-  // nsPresArena, and so are measured as part of that.
-  nsStyleSizes mServoStyleSizes;
+  nsStyleSizes mStyleSizes;
 
   mozilla::SizeOfState& mState;
 
 #undef FOR_EACH_SIZE
 };
 
 #undef ZERO_SIZE
 #undef ADD_TO_TAB_SIZES
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3506,191 +3506,278 @@ GetDesiredProto(JSContext* aCx, const JS
     return true;
   }
 
   aDesiredProto.set(&protoVal.toObject());
   return true;
 }
 
 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
-already_AddRefed<Element>
-CreateXULOrHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
-                       JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
+namespace binding_detail {
+bool
+HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
+                constructors::id::ID aConstructorId,
+                prototypes::id::ID aProtoId,
+                CreateInterfaceObjectsMethod aCreator)
 {
+  JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+
+  // Per spec, this is technically part of step 3, but doing the check
+  // directly lets us provide a better error message.  And then in
+  // step 2 we can work with newTarget in a simpler way because we
+  // know it's an object.
+  if (!args.isConstructing()) {
+    return ThrowConstructorWithoutNew(aCx,
+                                      NamesOfInterfacesWithProtos(aProtoId));
+  }
+
+  JS::Rooted<JSObject*> callee(aCx, &args.callee());
+  // 'callee' is not a function here; it's either an Xray for our interface
+  // object or the interface object itself.  So caling XrayAwareCalleeGlobal on
+  // it is not safe.  But since in the Xray case it's a wrapper for our
+  // interface object, we can just construct our GlobalObject from it and end
+  // up with the right thing.
+  GlobalObject global(aCx, callee);
+  if (global.Failed()) {
+    return false;
+  }
+
+  // Now we start the [HTMLConstructor] algorithm steps from
+  // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
+
   // Step 1.
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
   if (!window) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+    // This means we ended up with an HTML Element interface object defined in
+    // a non-Window scope.  That's ... pretty unexpected.
+    return Throw(aCx, NS_ERROR_UNEXPECTED);
   }
-
+  RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
+
+  // Technically, per spec, a window always has a document.  In Gecko, a
+  // sufficiently torn-down window might not, so check for that case.  We're
+  // going to need a document to create an element.
   nsIDocument* doc = window->GetExtantDoc();
   if (!doc) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+    return Throw(aCx, NS_ERROR_UNEXPECTED);
+  }
+
+  // Step 2.
+
+  // The newTarget might be a cross-compartment wrapper. Get the underlying
+  // object so we can do the spec's object-identity checks.  If we ever stop
+  // unwrapping here, carefully audit uses of newTarget below!
+  JS::Rooted<JSObject*> newTarget(aCx, js::CheckedUnwrap(&args.newTarget().toObject()));
+  if (!newTarget) {
+    return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
   }
 
+  // Enter the compartment of our underlying newTarget object, so we end
+  // up comparing to the constructor object for our interface from that global.
+  // XXXbz This is not what the spec says to do, and it's not super-clear to me
+  // at this point why we're doing it.  Why not just compare |newTarget| and
+  // |callee| if the intent is just to prevent registration of HTML interface
+  // objects as constructors?  Of course it's not clear that the spec check
+  // makes sense to start with: https://github.com/whatwg/html/issues/3575
+  {
+    JSAutoCompartment ac(aCx, newTarget);
+    JS::Handle<JSObject*> constructor =
+      GetPerInterfaceObjectHandle(aCx, aConstructorId, aCreator,
+                                  true);
+    if (!constructor) {
+      return false;
+    }
+    if (newTarget == constructor) {
+      return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
+    }
+  }
+
+  // Step 3.
+  CustomElementDefinition* definition =
+    registry->LookupCustomElementDefinition(aCx, newTarget);
+  if (!definition) {
+    return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
+  }
+
+  // Steps 4 and 5 do some sanity checks on our callee.  We add to those a
+  // determination of what sort of element we're planning to construct.
+  // Technically, this should happen (implicitly) in step 8, but this
+  // determination is side-effect-free, so it's OK.
   int32_t ns = doc->GetDefaultNamespaceID();
   if (ns != kNameSpaceID_XUL) {
     ns = kNameSpaceID_XHTML;
   }
 
-  RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
-  if (!registry) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
-  }
-
-  // Step 2 is in the code output by CGClassConstructor.
-  // Step 3.
-  JSContext* cx = aGlobal.Context();
-  JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject());
-  CustomElementDefinition* definition =
-    registry->LookupCustomElementDefinition(cx, newTarget);
-  if (!definition) {
-    aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
-    return nullptr;
-  }
-
-  // The callee might be an Xray. Unwrap it to get actual callee.
-  JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee()));
-  if (!callee) {
-    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return nullptr;
-  }
-
-  // And the actual callee might be in different compartment, so enter its
-  // compartment before getting the standard constructor object to compare to,
-  // so we get it from the same global as callee itself.
-  JSAutoCompartment ac(cx, callee);
   int32_t tag = eHTMLTag_userdefined;
   if (!definition->IsCustomBuiltIn()) {
     // Step 4.
     // If the definition is for an autonomous custom element, the active
-    // function should be HTMLElement or XULElement
-    JS::Rooted<JSObject*> constructor(cx);
+    // function should be HTMLElement or XULElement.  We want to get the actual
+    // functions to compare to from our global's compartment, not the caller
+    // compartment.
+    JSAutoCompartment ac(aCx, global.Get());
+
+    JS::Rooted<JSObject*> constructor(aCx);
     if (ns == kNameSpaceID_XUL) {
-      constructor = XULElementBinding::GetConstructorObject(cx);
+      constructor = XULElementBinding::GetConstructorObject(aCx);
     } else {
-      constructor = HTMLElementBinding::GetConstructorObject(cx);
+      constructor = HTMLElementBinding::GetConstructorObject(aCx);
     }
 
     if (!constructor) {
-      aRv.NoteJSContextException(cx);
-      return nullptr;
+      return false;
     }
 
-    if (callee != constructor) {
-      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
-      return nullptr;
+    if (constructor != js::CheckedUnwrap(callee)) {
+      return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
     }
   } else {
     // Step 5.
     // If the definition is for a customized built-in element, the localName
-    // should be defined in the specification.
+    // should be one of the ones defined in the specification for this interface.
 
     // Customized built-in elements are not supported for XUL yet.
     if (ns == kNameSpaceID_XUL) {
-      aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return nullptr;
+      return Throw(aCx, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     }
 
     tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
     if (tag == eHTMLTag_userdefined) {
-      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
-      return nullptr;
+      return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
     }
 
     MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
 
     // If the definition is for a customized built-in element, the active
     // function should be the localname's element interface.
     constructorGetterCallback cb = sConstructorGetterCallback[tag];
     if (!cb) {
-      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
-      return nullptr;
+      return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
     }
 
-    JS::Rooted<JSObject*> constructor(cx, cb(cx));
+    // We want to get the constructor from our global's compartment, not the
+    // caller compartment.
+    JSAutoCompartment ac(aCx, global.Get());
+    JS::Rooted<JSObject*> constructor(aCx, cb(aCx));
     if (!constructor) {
-      aRv.NoteJSContextException(cx);
-      return nullptr;
+      return false;
     }
 
-    if (callee != constructor) {
-      aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
-      return nullptr;
+    if (constructor != js::CheckedUnwrap(callee)) {
+      return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
     }
   }
 
-  RefPtr<mozilla::dom::NodeInfo> nodeInfo =
-    doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
-                                        nullptr,
-                                        ns,
-                                        nsINode::ELEMENT_NODE);
-  if (!nodeInfo) {
-    aRv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+  // Step 6.
+  JS::Rooted<JSObject*> desiredProto(aCx);
+  if (!GetDesiredProto(aCx, args, &desiredProto)) {
+    return false;
   }
 
-  // Step 6 and Step 7 are in the code output by CGClassConstructor.
-  // Step 8.
+  // Step 7.
+  if (!desiredProto) {
+    // This fallback behavior is designed to match analogous behavior for the
+    // JavaScript built-ins. So we enter the compartment of our underlying
+    // newTarget object and fall back to the prototype object from that global.
+    // XXX The spec says to use GetFunctionRealm(), which is not actually
+    // the same thing as what we have here (e.g. in the case of scripted callable proxies
+    // whose target is not same-compartment with the proxy, or bound functions, etc).
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658
+    {
+      JSAutoCompartment ac(aCx, newTarget);
+      desiredProto = GetPerInterfaceObjectHandle(aCx, aProtoId, aCreator, true);
+      if (!desiredProto) {
+          return false;
+      }
+    }
+
+    // desiredProto is in the compartment of the underlying newTarget object.
+    // Wrap it into the context compartment.
+    if (!JS_WrapObject(aCx, &desiredProto)) {
+      return false;
+    }
+  }
+
+  // We need to do some work to actually return an Element, so we do step 8 on
+  // one branch and steps 9-12 on another branch, then common up the "return
+  // element" work.
+  RefPtr<Element> element;
   nsTArray<RefPtr<Element>>& constructionStack =
     definition->mConstructionStack;
   if (constructionStack.IsEmpty()) {
-    RefPtr<Element> newElement;
+    // Step 8.
+    // Now we go to construct an element.  We want to do this in global's
+    // compartment, not caller compartment (the normal constructor behavior),
+    // just in case those elements create JS things.
+    JSAutoCompartment ac(aCx, global.Get());
+
+    RefPtr<NodeInfo> nodeInfo =
+      doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
+                                          nullptr,
+                                          ns,
+                                          nsINode::ELEMENT_NODE);
+    MOZ_ASSERT(nodeInfo);
+
     if (ns == kNameSpaceID_XUL) {
-      newElement = new nsXULElement(nodeInfo.forget());
+      element = new nsXULElement(nodeInfo.forget());
     } else {
       if (tag == eHTMLTag_userdefined) {
         // Autonomous custom element.
-        newElement = NS_NewHTMLElement(nodeInfo.forget());
+        element = NS_NewHTMLElement(nodeInfo.forget());
       } else {
         // Customized built-in element.
-        newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
+        element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
       }
     }
 
-    newElement->SetCustomElementData(
+    element->SetCustomElementData(
       new CustomElementData(definition->mType, CustomElementData::State::eCustom));
 
-    newElement->SetCustomElementDefinition(definition);
-
-    return newElement.forget();
-  }
-
-  // Step 9.
-  RefPtr<Element>& element = constructionStack.LastElement();
-
-  // Step 10.
-  if (element == ALEADY_CONSTRUCTED_MARKER) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
+    element->SetCustomElementDefinition(definition);
+  } else {
+    // Step 9.
+    element = constructionStack.LastElement();
+
+    // Step 10.
+    if (element == ALREADY_CONSTRUCTED_MARKER) {
+      return Throw(aCx, NS_ERROR_DOM_INVALID_STATE_ERR);
+    }
+
+    // Step 11.
+    // Do prototype swizzling for upgrading a custom element here, for cases
+    // when we have a reflector already.  If we don't have one yet, we will
+    // create it with the right proto (by calling DoGetOrCreateDOMReflector with
+    // that proto).
+    JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper());
+    if (reflector) {
+      // reflector might be in different compartment.
+      JSAutoCompartment ac(aCx, reflector);
+      JS::Rooted<JSObject*> givenProto(aCx, desiredProto);
+      if (!JS_WrapObject(aCx, &givenProto) ||
+          !JS_SetPrototype(aCx, reflector, givenProto)) {
+        return false;
+      }
+    }
+
+    // Step 12.
+    constructionStack.LastElement() = ALREADY_CONSTRUCTED_MARKER;
   }
 
-  // Step 11.
-  // Do prototype swizzling for upgrading a custom element here, for cases when
-  // we have a reflector already.  If we don't have one yet, our caller will
-  // create it with the right proto (by calling DoGetOrCreateDOMReflector with
-  // that proto).
-  JS::Rooted<JSObject*> reflector(cx, element->GetWrapper());
-  if (reflector) {
-    // reflector might be in different compartment.
-    JSAutoCompartment ac(cx, reflector);
-    JS::Rooted<JSObject*> givenProto(cx, aGivenProto);
-    if (!JS_WrapObject(cx, &givenProto) ||
-        !JS_SetPrototype(cx, reflector, givenProto)) {
-      aRv.NoteJSContextException(cx);
-      return nullptr;
-    }
+  // Tail end of step 8 and step 13: returning the element.  We want to do this
+  // part in the global's compartment, though in practice it won't matter much
+  // because Element always knows which compartment it should be created in.
+  JSAutoCompartment ac(aCx, global.Get());
+  if (!js::IsObjectInContextCompartment(desiredProto, aCx) &&
+      !JS_WrapObject(aCx, &desiredProto)) {
+    return false;
   }
 
-  // Step 12 and Step 13.
-  return element.forget();
+  return GetOrCreateDOMReflector(aCx, element, args.rval(), desiredProto);
 }
+} // namespace binding_detail
 
 #ifdef DEBUG
 namespace binding_detail {
 void
 AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
                              JS::Handle<JSObject*> aGivenProto)
 {
   if (!aGivenProto) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -18,16 +18,17 @@
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/PrototypeList.h"
 #include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/SegmentedVector.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
@@ -3442,14 +3443,21 @@ namespace binding_detail {
 // environment controlled by a hostile adversary.  This is because in the worker
 // case the global is in fact the worker global, so it and its standard objects
 // are controlled by the worker script.  This is why this function is in the
 // binding_detail namespace.  Any use of this function MUST be very carefully
 // reviewed by someone who is sufficiently devious and has a very good
 // understanding of all the code that will run while we're using the return
 // value, including the SpiderMonkey parts.
 JSObject* UnprivilegedJunkScopeOrWorkerGlobal();
+
+// Implementation of the [HTMLConstructor] extended attribute.
+bool
+HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
+                constructors::id::ID aConstructorId,
+                prototypes::id::ID aProtoId,
+                CreateInterfaceObjectsMethod aCreator);
 } // namespace binding_detail
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1820,16 +1820,36 @@ class CGClassConstructor(CGAbstractStati
         if not self._ctor:
             return ""
         return CGAbstractStaticMethod.define(self)
 
     def definition_body(self):
         return self.generate_code()
 
     def generate_code(self):
+        if self._ctor.isHTMLConstructor():
+            # We better have a prototype object.  Otherwise our proto
+            # id won't make sense.
+            assert self.descriptor.interface.hasInterfacePrototypeObject()
+            # We also better have a constructor object, if this is
+            # getting called!
+            assert self.descriptor.interface.hasInterfaceObject()
+            # We can't just pass null for the CreateInterfaceObjects callback,
+            # because our newTarget might be in a different compartment, in
+            # which case we'll need to look up constructor objects in that
+            # compartment.
+            return fill(
+                """
+                return binding_detail::HTMLConstructor(cx, argc, vp,
+                                                       constructors::id::${name},
+                                                       prototypes::id::${name},
+                                                       CreateInterfaceObjects);
+                """,
+                name=self.descriptor.name)
+
         # [ChromeOnly] interfaces may only be constructed by chrome.
         chromeOnlyCheck = ""
         if isChromeOnly(self._ctor):
             chromeOnlyCheck = dedent("""
                 if (!nsContentUtils::ThreadsafeIsSystemCaller(cx)) {
                   return ThrowingConstructor(cx, argc, vp);
                 }
 
@@ -1843,126 +1863,40 @@ class CGClassConstructor(CGAbstractStati
         # the name JS sees is the interface name; for named constructors
         # identifier.name is the actual name.
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
 
-        # [HTMLConstructor] for custom element
-        # This needs to live in bindings code because it directly examines
-        # newtarget and the callee function to do HTMLConstructor specific things.
-        if self._ctor.isHTMLConstructor():
-            htmlConstructorSanityCheck = dedent("""
-                // The newTarget might be a cross-compartment wrapper. Get the underlying object
-                // so we can do the spec's object-identity checks.
-                JS::Rooted<JSObject*> newTarget(cx, js::CheckedUnwrap(&args.newTarget().toObject()));
-                if (!newTarget) {
-                  return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
-                }
-
-                // Step 2 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
-                // Enter the compartment of our underlying newTarget object, so we end
-                // up comparing to the constructor object for our interface from that global.
-                {
-                  JSAutoCompartment ac(cx, newTarget);
-                  JS::Handle<JSObject*> constructor(GetConstructorObjectHandle(cx));
-                  if (!constructor) {
-                    return false;
-                  }
-                  if (newTarget == constructor) {
-                    return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
-                  }
-                }
-
-                """)
-
-            # If we are unable to get desired prototype from newTarget, then we
-            # fall back to the interface prototype object from newTarget's realm.
-            htmlConstructorFallback = dedent("""
-                if (!desiredProto) {
-                  // Step 7 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
-                  // This fallback behavior is designed to match analogous behavior for the
-                  // JavaScript built-ins. So we enter the compartment of our underlying
-                  // newTarget object and fall back to the prototype object from that global.
-                  // XXX The spec says to use GetFunctionRealm(), which is not actually
-                  // the same thing as what we have here (e.g. in the case of scripted callable proxies
-                  // whose target is not same-compartment with the proxy, or bound functions, etc).
-                  // https://bugzilla.mozilla.org/show_bug.cgi?id=1317658
-                  {
-                    JSAutoCompartment ac(cx, newTarget);
-                    desiredProto = GetProtoObjectHandle(cx);
-                    if (!desiredProto) {
-                        return false;
-                    }
-                  }
-
-                  // desiredProto is in the compartment of the underlying newTarget object.
-                  // Wrap it into the context compartment.
-                  if (!JS_WrapObject(cx, &desiredProto)) {
-                    return false;
-                  }
-                }
-                """)
-        else:
-            htmlConstructorSanityCheck = ""
-            htmlConstructorFallback = ""
-
-
-        # If we're a constructor, "obj" may not be a function, so calling
-        # XrayAwareCalleeGlobal() on it is not safe.  Of course in the
-        # constructor case either "obj" is an Xray or we're already in the
-        # content compartment, not the Xray compartment, so just
-        # constructing the GlobalObject from "obj" is fine.
         preamble = fill(
             """
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             JS::Rooted<JSObject*> obj(cx, &args.callee());
             $*{chromeOnlyCheck}
             if (!args.isConstructing()) {
               // XXXbz wish I could get the name from the callee instead of
               // Adding more relocations
               return ThrowConstructorWithoutNew(cx, "${ctorName}");
             }
 
-            GlobalObject global(cx, obj);
-            if (global.Failed()) {
-              return false;
-            }
-
-            $*{htmlConstructorSanityCheck}
             JS::Rooted<JSObject*> desiredProto(cx);
             if (!GetDesiredProto(cx, args, &desiredProto)) {
               return false;
             }
-            $*{htmlConstructorFallback}
             """,
             chromeOnlyCheck=chromeOnlyCheck,
-            ctorName=ctorName,
-            htmlConstructorSanityCheck=htmlConstructorSanityCheck,
-            htmlConstructorFallback=htmlConstructorFallback)
-
-        if  self._ctor.isHTMLConstructor():
-            signatures = self._ctor.signatures()
-            assert len(signatures) == 1
-            # Given that HTMLConstructor takes no args, we can just codegen a
-            # call to CreateXULOrHTMLElement() in BindingUtils which reuses the
-            # factory thing in HTMLContentSink. Then we don't have to implement
-            # Constructor on all the HTML elements.
-            callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1],
-                                               "CreateXULOrHTMLElement", True,
-                                               self.descriptor, self._ctor,
-                                               isConstructor=True)
-        else:
-            name = self._ctor.identifier.name
-            nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
-            callGenerator = CGMethodCall(nativeName, True, self.descriptor,
-                                         self._ctor, isConstructor=True,
-                                         constructorName=ctorName)
+            ctorName=ctorName)
+
+        name = self._ctor.identifier.name
+        nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
+        callGenerator = CGMethodCall(nativeName, True, self.descriptor,
+                                     self._ctor, isConstructor=True,
+                                     constructorName=ctorName)
         return preamble + "\n" + callGenerator.define()
 
     def profiler_label_and_jscontext(self):
         name = self._ctor.identifier.name
         if name != "constructor":
             ctorName = name
         else:
             ctorName = self.descriptor.interface.identifier.name
@@ -7757,33 +7691,36 @@ class CGPerSignatureCall(CGThing):
             cgThings.append(CGGeneric(dedent(
                 """
                 bool foundNonFiniteFloat = false;
                 """)))
             lenientFloatCode = "foundNonFiniteFloat = true;\n"
 
         argsPre = []
         if idlNode.isStatic():
-            # If we're a constructor, the GlobalObject struct will be created in
-            # CGClassConstructor.
-            if not isConstructor:
-                cgThings.append(CGGeneric(dedent(
-                    """
-                    GlobalObject global(cx, xpc::XrayAwareCalleeGlobal(obj));
-                    if (global.Failed()) {
-                      return false;
-                    }
-
-                    """)))
-
+            # If we're a constructor, "obj" may not be a function, so calling
+            # XrayAwareCalleeGlobal() on it is not safe.  Of course in the
+            # constructor case either "obj" is an Xray or we're already in the
+            # content compartment, not the Xray compartment, so just
+            # constructing the GlobalObject from "obj" is fine.
+            if isConstructor:
+                objForGlobalObject = "obj"
+            else:
+                objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
+            cgThings.append(CGGeneric(fill(
+                """
+                GlobalObject global(cx, ${obj});
+                if (global.Failed()) {
+                  return false;
+                }
+
+                """,
+                obj=objForGlobalObject)))
             argsPre.append("global")
 
-        if isConstructor and idlNode.isHTMLConstructor():
-            argsPre.extend(["args", "desiredProto"])
-
         # For JS-implemented interfaces we do not want to base the
         # needsCx decision on the types involved, just on our extended
         # attributes. Also, JSContext is not needed for the static case
         # since GlobalObject already contains the context.
         needsCx = needCx(returnType, arguments, self.extendedAttributes,
                          not descriptor.interface.isJSImplemented(), static)
         if needsCx:
             argsPre.append("cx")
--- a/dom/chrome-webidl/WebExtensionPolicy.webidl
+++ b/dom/chrome-webidl/WebExtensionPolicy.webidl
@@ -153,16 +153,21 @@ interface WebExtensionPolicy {
   static WebExtensionPolicy? getByHostname(ByteString hostname);
 
   /**
    * Returns the currently-active policy for the extension extension URI, or
    * null if the URI is not an extension URI, or no policy is currently active
    * for it.
    */
   static WebExtensionPolicy? getByURI(URI uri);
+
+  /**
+   * Returns true if the URI is restricted for any extension.
+   */
+  static boolean isRestrictedURI(URI uri);
 };
 
 dictionary WebExtensionInit {
   required DOMString id;
 
   required ByteString mozExtensionHostname;
 
   required DOMString baseURL;
--- a/dom/fetch/FetchConsumer.cpp
+++ b/dom/fetch/FetchConsumer.cpp
@@ -82,23 +82,21 @@ public:
 // thread when already shutting down.
 template <class Derived>
 class AbortConsumeBodyControlRunnable final : public MainThreadWorkerControlRunnable
 {
   RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
 
 public:
   AbortConsumeBodyControlRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
-                                  WorkerPrivate* aWorkerPrivate,
-                                  uint8_t* aResult)
+                                  WorkerPrivate* aWorkerPrivate)
     : MainThreadWorkerControlRunnable(aWorkerPrivate)
     , mFetchBodyConsumer(aFetchBodyConsumer)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    free(aResult);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     mFetchBodyConsumer->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr,
                                             true /* shutting down */);
     return true;
@@ -126,18 +124,17 @@ public:
     if (!mBodyConsumer) {
       return;
     }
 
     // Web Worker
     if (mWorkerRef) {
       RefPtr<AbortConsumeBodyControlRunnable<Derived>> r =
         new AbortConsumeBodyControlRunnable<Derived>(mBodyConsumer,
-                                                     mWorkerRef->Private(),
-                                                     nullptr);
+                                                     mWorkerRef->Private());
       if (!r->Dispatch()) {
         MOZ_CRASH("We are going to leak");
       }
       return;
     }
 
     // Main-thread
     mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
@@ -259,24 +256,23 @@ public:
       }
     }
 
     // The worker is shutting down. Let's use a control runnable to complete the
     // shutting down procedure.
 
     RefPtr<AbortConsumeBodyControlRunnable<Derived>> r =
       new AbortConsumeBodyControlRunnable<Derived>(mFetchBodyConsumer,
-                                                   mWorkerRef->Private(),
-                                                   nonconstResult);
+                                                   mWorkerRef->Private());
     if (NS_WARN_IF(!r->Dispatch())) {
       return NS_ERROR_FAILURE;
     }
 
-    // FetchBody is responsible for data.
-    return NS_SUCCESS_ADOPTED_DATA;
+    // We haven't taken ownership of the data.
+    return NS_OK;
   }
 
   virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                                   Blob* aBlob,
                                   nsresult aRv) override
   {
     // On error.
     if (NS_FAILED(aRv)) {
--- a/dom/html/HTMLAnchorElement.cpp
+++ b/dom/html/HTMLAnchorElement.cpp
@@ -14,16 +14,17 @@
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsAttrValueOrString.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIURI.h"
+#include "nsWindowSizes.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor)
 
 namespace mozilla {
 namespace dom {
 
 #define ANCHOR_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
 
--- a/dom/html/HTMLAreaElement.cpp
+++ b/dom/html/HTMLAreaElement.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/HTMLAreaElement.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/HTMLAreaElementBinding.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
+#include "nsWindowSizes.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Area)
 
 namespace mozilla {
 namespace dom {
 
 HTMLAreaElement::HTMLAreaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -23,16 +23,17 @@
 #include "nsINode.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIURL.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 #include "nsStyleConsts.h"
 #include "nsStyleLinkElement.h"
 #include "nsUnicharUtils.h"
+#include "nsWindowSizes.h"
 
 #define LINK_ELEMENT_FLAG_BIT(n_) \
   NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
 
 // Link element specific bits
 enum {
   // Indicates that a DNS Prefetch has been requested from this Link element.
   HTML_LINK_DNS_PREFETCH_REQUESTED = LINK_ELEMENT_FLAG_BIT(0),
--- a/dom/svg/SVGPathElement.cpp
+++ b/dom/svg/SVGPathElement.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/SVGPathElementBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsComputedDOMStyle.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"
+#include "nsWindowSizes.h"
 #include "SVGContentUtils.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
--- a/dom/workers/WorkerRef.cpp
+++ b/dom/workers/WorkerRef.cpp
@@ -98,18 +98,16 @@ WorkerRef::~WorkerRef()
 }
 
 void
 WorkerRef::Notify()
 {
   MOZ_ASSERT(mHolder);
   NS_ASSERT_OWNINGTHREAD(WorkerRef);
 
-  mHolder = nullptr;
-
   if (!mCallback) {
     return;
   }
 
   std::function<void()> callback = mCallback;
   mCallback = nullptr;
 
   callback();
@@ -145,16 +143,18 @@ WeakWorkerRef::WeakWorkerRef(WorkerPriva
 {}
 
 WeakWorkerRef::~WeakWorkerRef() = default;
 
 void
 WeakWorkerRef::Notify()
 {
   WorkerRef::Notify();
+
+  mHolder = nullptr;
   mWorkerPrivate = nullptr;
 }
 
 WorkerPrivate*
 WeakWorkerRef::GetPrivate() const
 {
   NS_ASSERT_OWNINGTHREAD(WeakWorkerRef);
   return mWorkerPrivate;
@@ -191,26 +191,32 @@ StrongWorkerRef::Create(WorkerPrivate* a
 
   return ref.forget();
 }
 
 StrongWorkerRef::StrongWorkerRef(WorkerPrivate* aWorkerPrivate)
   : WorkerRef(aWorkerPrivate)
 {}
 
-StrongWorkerRef::~StrongWorkerRef() = default;
+StrongWorkerRef::~StrongWorkerRef()
+{
+  NS_ASSERT_OWNINGTHREAD(StrongWorkerRef);
+}
 
 WorkerPrivate*
 StrongWorkerRef::Private() const
 {
   MOZ_ASSERT(mHolder);
   NS_ASSERT_OWNINGTHREAD(StrongWorkerRef);
   return mWorkerPrivate;
 }
 
+// ----------------------------------------------------------------------------
+// ThreadSafeWorkerRef
+
 ThreadSafeWorkerRef::ThreadSafeWorkerRef(StrongWorkerRef* aRef)
   : mRef(aRef)
 {
   MOZ_ASSERT(aRef);
   aRef->Private()->AssertIsOnWorkerThread();
 }
 
 ThreadSafeWorkerRef::~ThreadSafeWorkerRef()
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -18,16 +18,17 @@
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIChromeRegistry.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
+#include "nsWindowSizes.h"
 #include "mozilla/Services.h"
 #include "xpcpublic.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/URL.h"
 
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1513,17 +1513,18 @@ public:
                                          Color& aColor,
                                          std::vector<Glyph>& aGlyphs) = 0;
 };
 
 class DrawEventRecorder : public RefCounted<DrawEventRecorder>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
-  virtual void Finish() = 0;
+  // returns true if there were any items in the recording
+  virtual bool Finish() = 0;
   virtual ~DrawEventRecorder() { }
 };
 
 struct Tile
 {
   RefPtr<DrawTarget> mDrawTarget;
   IntPoint mTileOrigin;
 };
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -97,40 +97,47 @@ void
 DrawEventRecorderMemory::FlushItem(IntRect aRect)
 {
   MOZ_RELEASE_ASSERT(!aRect.IsEmpty());
   // Detatching our existing resources will add some
   // destruction events to our stream so we need to do that
   // first.
   DetatchResources();
 
+  // See moz2d_renderer.rs for a description of the stream format
   WriteElement(mIndex, mOutputStream.mLength);
 
   // write out the fonts into the extra data section
   mSerializeCallback(mOutputStream, mUnscaledFonts);
   WriteElement(mIndex, mOutputStream.mLength);
 
   WriteElement(mIndex, aRect.x);
   WriteElement(mIndex, aRect.y);
   WriteElement(mIndex, aRect.XMost());
   WriteElement(mIndex, aRect.YMost());
   ClearResources();
+
+  // write out a new header for the next recording in the stream
   WriteHeader(mOutputStream);
 }
 
-void
+bool
 DrawEventRecorderMemory::Finish()
 {
+  // this length might be 0, and things should still work.
+  // for example if there are no items in a particular area
   size_t indexOffset = mOutputStream.mLength;
   // write out the index
   mOutputStream.write(mIndex.mData, mIndex.mLength);
+  bool hasItems = mIndex.mLength != 0;
   mIndex = MemStream();
   // write out the offset of the Index to the end of the output stream
   WriteElement(mOutputStream, indexOffset);
   ClearResources();
+  return hasItems;
 }
 
 
 size_t
 DrawEventRecorderMemory::RecordingSize()
 {
   return mOutputStream.mLength;
 }
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -23,17 +23,17 @@ class PathRecording;
 
 class DrawEventRecorderPrivate : public DrawEventRecorder
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate, override)
 
   DrawEventRecorderPrivate();
   virtual ~DrawEventRecorderPrivate() { }
-  virtual void Finish() override { ClearResources(); }
+  virtual bool Finish() override { ClearResources(); return true; }
   virtual void FlushItem(IntRect) { }
   void DetatchResources() {
     // The iteration is a bit awkward here because our iterator will
     // be invalidated by the removal
     for (auto font = mStoredFonts.begin(); font != mStoredFonts.end(); ) {
       auto oldFont = font++;
       (*oldFont)->RemoveUserData(reinterpret_cast<UserDataKey*>(this));
     }
@@ -184,17 +184,17 @@ public:
   size_t RecordingSize();
 
   /**
    * Wipes the internal recording buffer, but the recorder does NOT forget which
    * objects it has recorded. This can be used so that a recording can be copied
    * and processed in chunks, releasing memory as it goes.
    */
   void WipeRecording();
-  void Finish() override;
+  bool Finish() override;
   void FlushItem(IntRect) override;
 
   MemStream mOutputStream;
   /* The index stream is of the form:
    * ItemIndex { size_t dataEnd; size_t extraDataEnd; }
    * It gets concatenated to the end of mOutputStream in Finish()
    * The last size_t in the stream is offset of the begining of the
    * index.
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -359,40 +359,38 @@ WebRenderBridgeChild::GetFontKeyForUnsca
 
     mFontKeys.Put(aUnscaled, fontKey);
   }
 
   return fontKey;
 }
 
 void
-WebRenderBridgeChild::RemoveExpiredFontKeys()
+WebRenderBridgeChild::RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResourceUpdates)
 {
   uint32_t counter = gfx::ScaledFont::DeletionCounter();
-  wr::IpcResourceUpdateQueue resources(this);
   if (mFontInstanceKeysDeleted != counter) {
     mFontInstanceKeysDeleted = counter;
     for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
-        resources.DeleteFontInstance(iter.Data());
+        aResourceUpdates.DeleteFontInstance(iter.Data());
         iter.Remove();
       }
     }
   }
   counter = gfx::UnscaledFont::DeletionCounter();
   if (mFontKeysDeleted != counter) {
     mFontKeysDeleted = counter;
     for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
-        resources.DeleteFont(iter.Data());
+        aResourceUpdates.DeleteFont(iter.Data());
         iter.Remove();
       }
     }
   }
-  UpdateResources(resources);
 }
 
 CompositorBridgeChild*
 WebRenderBridgeChild::GetCompositorBridgeChild()
 {
   return static_cast<CompositorBridgeChild*>(Manager());
 }
 
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -135,17 +135,17 @@ public:
                   const StackingContextHelper& aSc,
                   const wr::LayerRect& aBounds, const wr::LayerRect& aClip,
                   bool aBackfaceVisible,
                   const wr::GlyphOptions* aGlyphOptions = nullptr);
 
   wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
   wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont);
 
-  void RemoveExpiredFontKeys();
+  void RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResources);
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
   ipc::IShmemAllocator* GetShmemAllocator();
 
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -12,28 +12,1075 @@
 #include "mozilla/gfx/DrawEventRecorder.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/UpdateImageHelper.h"
+#include "UnitTransforms.h"
 #include "gfxEnv.h"
 #include "nsDisplayListInvalidation.h"
 #include "WebRenderCanvasRenderer.h"
 #include "LayersLogging.h"
 #include "LayerTreeInvalidation.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
+static bool
+PaintByLayer(nsDisplayItem* aItem,
+             nsDisplayListBuilder* aDisplayListBuilder,
+             const RefPtr<BasicLayerManager>& aManager,
+             gfxContext* aContext,
+             const gfx::Size& aScale,
+             const std::function<void()>& aPaintFunc);
+static int sIndent;
+#include <stdarg.h>
+#include <stdio.h>
 
-void WebRenderCommandBuilder::Destroy()
+static void GP(const char *fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+#if 0
+    for (int i = 0; i < sIndent; i++) { printf(" "); }
+    vprintf(fmt, args);
+#endif
+    va_end(args);
+}
+
+//XXX: problems:
+// - How do we deal with scrolling while having only a single invalidation rect?
+// We can have a valid rect and an invalid rect. As we scroll the valid rect will move
+// and the invalid rect will be the new area
+
+struct BlobItemData;
+static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray);
+NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(BlobGroupDataProperty,
+                                    nsTArray<BlobItemData*>,
+                                    DestroyBlobGroupDataProperty);
+
+// These are currently manually allocated and ownership is help by the mDisplayItems
+// hash table in DIGroup
+struct BlobItemData
+{
+  // a weak pointer to the frame for this item.
+  // DisplayItemData has a mFrameList to deal with merged frames. Hopefully we
+  // don't need to worry about that.
+  nsIFrame* mFrame;
+
+  uint32_t  mDisplayItemKey;
+  nsTArray<BlobItemData*>* mArray; // a weak pointer to the array that's owned by the frame property
+
+  IntRect mRect;
+  // It would be nice to not need this. We need to be able to call ComputeInvalidationRegion.
+  // ComputeInvalidationRegion will sometimes reach into parent style structs to get information
+  // that can change the invalidation region
+  UniquePtr<nsDisplayItemGeometry> mGeometry;
+  DisplayItemClip mClip;
+  bool mUsed; // initialized near construction
+
+  // a weak pointer to the group that owns this item
+  // we use this to track whether group for a particular item has changed
+  struct DIGroup* mGroup;
+
+  // XXX: only used for debugging
+  bool mInvalid;
+  bool mEmpty;
+
+  // properties that are used to emulate layer tree invalidation
+  Matrix mMatrix; // updated to track the current transform to device space
+  Matrix4x4Flagged mTransform; // only used with nsDisplayTransform items to detect transform changes
+  float mOpacity; // only used with nsDisplayOpacity items to detect change to opacity
+
+  IntRect mImageRect;
+  IntPoint mGroupOffset;
+
+  BlobItemData(DIGroup* aGroup, nsDisplayItem *aItem)
+    : mGroup(aGroup)
+  {
+    mInvalid = false;
+    mEmpty = false;
+    mDisplayItemKey = aItem->GetPerFrameKey();
+    AddFrame(aItem->Frame());
+  }
+
+private:
+  void AddFrame(nsIFrame* aFrame)
+  {
+    mFrame = aFrame;
+
+    nsTArray<BlobItemData*>* array =
+      aFrame->GetProperty(BlobGroupDataProperty());
+    if (!array) {
+      array = new nsTArray<BlobItemData*>();
+      aFrame->SetProperty(BlobGroupDataProperty(), array);
+    }
+    array->AppendElement(this);
+    mArray = array;
+  }
+
+public:
+  void ClearFrame()
+  {
+    // Delete the weak pointer to this BlobItemData on the frame
+    MOZ_RELEASE_ASSERT(mFrame);
+    // the property may already be removed if WebRenderUserData got deleted
+    // first so we use our own mArray pointer.
+    mArray->RemoveElement(this);
+
+    // drop the entire property if nothing's left in the array
+    if (mArray->IsEmpty()) {
+      // If the frame is in the process of being destroyed this will fail
+      // but that's ok, because the the property will be removed then anyways
+      mFrame->DeleteProperty(BlobGroupDataProperty());
+    }
+    mFrame = nullptr;
+  }
+
+  ~BlobItemData()
+  {
+    if (mFrame) {
+      ClearFrame();
+    }
+  }
+};
+
+static BlobItemData*
+GetBlobItemData(nsDisplayItem* aItem)
+{
+  nsIFrame* frame = aItem->Frame();
+  uint32_t key = aItem->GetPerFrameKey();
+  const nsTArray<BlobItemData*>* array = frame->GetProperty(BlobGroupDataProperty());
+  if (array) {
+    for (BlobItemData* item : *array) {
+      if (item->mDisplayItemKey == key) {
+        return item;
+      }
+    }
+  }
+  return nullptr;
+}
+
+// We keep around the BlobItemData so that when we invalidate it get properly included in the rect
+static void
+DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray)
+{
+  for (BlobItemData* item : *aArray) {
+    GP("DestroyBlobGroupDataProperty: %p-%d\n", item->mFrame, item->mDisplayItemKey);
+    item->mFrame = nullptr;
+  }
+  delete aArray;
+}
+
+
+struct DIGroup;
+struct Grouper
+{
+  explicit Grouper(ScrollingLayersHelper& aScrollingHelper)
+   : mScrollingHelper(aScrollingHelper)
+  {}
+
+  int32_t mAppUnitsPerDevPixel;
+  std::vector<nsDisplayItem*> mItemStack;
+  nsDisplayListBuilder* mDisplayListBuilder;
+  ScrollingLayersHelper& mScrollingHelper;
+  Matrix mTransform;
+
+  // Paint the list of aChildren display items.
+  void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
+                          nsDisplayList* aChildren, gfxContext* aContext,
+                          gfx::DrawEventRecorderMemory* aRecorder);
+
+  // Builds groups of display items split based on 'layer activity'
+  void ConstructGroups(WebRenderCommandBuilder* aCommandBuilder,
+                       wr::DisplayListBuilder& aBuilder,
+                       wr::IpcResourceUpdateQueue& aResources,
+                       DIGroup* aGroup, nsDisplayList* aList,
+                       const StackingContextHelper& aSc);
+  // Builds a group of display items without promoting anything to active.
+  void ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
+                                       wr::DisplayListBuilder& aBuilder,
+                                       wr::IpcResourceUpdateQueue& aResources,
+                                       DIGroup* aGroup, nsDisplayList* aList,
+                                       const StackingContextHelper& aSc);
+  ~Grouper() {
+  }
+};
+
+// Returns whether this is an item for which complete invalidation was
+// reliant on LayerTreeInvalidation in the pre-webrender world.
+static bool
+IsContainerLayerItem(nsDisplayItem* aItem)
+{
+  switch (aItem->GetType()) {
+    case DisplayItemType::TYPE_TRANSFORM:
+    case DisplayItemType::TYPE_LAYER_EVENT_REGIONS:
+    case DisplayItemType::TYPE_OPACITY:
+    case DisplayItemType::TYPE_FILTER:
+    case DisplayItemType::TYPE_MASK: {
+      return true;
+    }
+    default: {
+      return false;
+    }
+  }
+}
+
+#include <sstream>
+
+bool
+UpdateContainerLayerPropertiesAndDetectChange(nsDisplayItem* aItem, BlobItemData* aData)
+{
+  bool changed = false;
+  switch (aItem->GetType()) {
+    case DisplayItemType::TYPE_TRANSFORM: {
+      auto transformItem = static_cast<nsDisplayTransform*>(aItem);
+      Matrix4x4Flagged trans = transformItem->GetTransform();
+      changed = aData->mTransform != trans;
+
+      if (changed) {
+        std::stringstream ss;
+        //ss << trans << ' ' << aData->mTransform;
+        //GP("UpdateContainerLayerPropertiesAndDetectChange Matrix %d %s\n", changed, ss.str().c_str());
+      }
+
+      aData->mTransform = trans;
+      break;
+    }
+    case DisplayItemType::TYPE_OPACITY: {
+      auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
+      float opacity = opacityItem->GetOpacity();
+      changed = aData->mOpacity != opacity;
+      aData->mOpacity = opacity;
+      GP("UpdateContainerLayerPropertiesAndDetectChange Opacity\n");
+      break;
+    }
+    default:
+      break;
+  }
+  return changed;
+}
+
+struct DIGroup
+{
+  // XXX: Storing owning pointers to the BlobItemData in a hash table is not
+  // a good choice. There are two better options:
+  //
+  // 1. We should just be using a linked list for this stuff.
+  //    That we can iterate over only the used items.
+  //    We remove from the unused list and add to the used list
+  //    when we see an item.
+  //
+  //    we allocate using a free list.
+  //
+  // 2. We can use a Vec and use SwapRemove().
+  //    We'll just need to be careful when iterating.
+  //    The advantage of a Vec is that everything stays compact
+  //    and we don't need to heap allocate the BlobItemData's
+  nsTHashtable<nsPtrHashKey<BlobItemData>> mDisplayItems;
+
+  nsPoint mAnimatedGeometryRootOrigin;
+  nsPoint mLastAnimatedGeometryRootOrigin;
+  IntRect mInvalidRect;
+  nsRect mGroupBounds;
+  int32_t mAppUnitsPerDevPixel;
+  gfx::Size mScale;
+  IntPoint mGroupOffset;
+  Maybe<wr::ImageKey> mKey;
+
+  void InvalidateRect(const IntRect& aRect)
+  {
+    // Empty rects get dropped
+    mInvalidRect = mInvalidRect.Union(aRect);
+  }
+
+  IntRect ItemBounds(nsDisplayItem* aItem)
+  {
+    BlobItemData* data = GetBlobItemData(aItem);
+    return data->mRect;
+  }
+
+  void ClearItems()
+  {
+    GP("items: %d\n", mDisplayItems.Count());
+    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+      BlobItemData* data = iter.Get()->GetKey();
+      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
+      iter.Remove();
+      delete data;
+    }
+  }
+
+  static IntRect
+  ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, IntPoint aOffset)
+  {
+    return RoundedOut(aMatrix.TransformBounds(ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel)))) - aOffset;
+  }
+
+  void ComputeGeometryChange(nsDisplayItem* aItem, BlobItemData* aData, Matrix& aMatrix, nsDisplayListBuilder* aBuilder)
+  {
+    // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to
+    // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
+    // If we do get an invalid rect, then we want to add this on top of the change areas.
+    nsRect invalid;
+    nsRegion combined;
+    const DisplayItemClip& clip = aItem->GetClip();
+
+    nsPoint shift = mAnimatedGeometryRootOrigin - mLastAnimatedGeometryRootOrigin;
+
+    if (shift.x != 0 || shift.y != 0)
+      GP("shift %d %d\n", shift.x, shift.y);
+    int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+    MOZ_RELEASE_ASSERT(mAppUnitsPerDevPixel == appUnitsPerDevPixel);
+    LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(mGroupBounds, appUnitsPerDevPixel);
+    LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft());
+    GP("\n");
+    GP("CGC offset %d %d\n", offset.x, offset.y);
+    IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, appUnitsPerDevPixel);
+    //MOZ_RELEASE_ASSERT(mGroupOffset.x == offset.x && mGroupOffset.y == offset.y);
+    IntRect imageRect(0, 0, size.width, size.height);
+    GP("imageSize: %d %d\n", size.width, size.height);
+    /*if (aItem->IsReused() && aData->mGeometry) {
+      return;
+    }*/
+
+    GP("pre mInvalidRect: %s %p-%d - inv: %d %d %d %d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey(),
+       mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
+    if (!aData->mGeometry) {
+      // This item is being added for the first time, invalidate its entire area.
+      UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+      combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+      aData->mGeometry = Move(geometry);
+      nsRect bounds = combined.GetBounds();
+
+      auto transBounds = nsLayoutUtils::MatrixTransformRect(bounds,
+                                                            Matrix4x4::From2D(aMatrix),
+                                                            float(appUnitsPerDevPixel));
+
+      IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+      ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+      aData->mRect = transformedRect.Intersect(imageRect);
+      GP("CGC %s %d %d %d %d\n", aItem->Name(), bounds.x, bounds.y, bounds.width, bounds.height);
+      GP("transBounds %d %d %d %d\n", transBounds.x, transBounds.y, transBounds.width, transBounds.height);
+      GP("%d %d,  %f %f\n", mGroupOffset.x, mGroupOffset.y, aMatrix._11, aMatrix._22);
+      GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y, aData->mRect.width, aData->mRect.height);
+      InvalidateRect(aData->mRect);
+      aData->mInvalid = true;
+    } else if (/*aData->mIsInvalid || XXX: handle image load invalidation */ (aItem->IsInvalid(invalid) && invalid.IsEmpty())) {
+      MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
+      MOZ_RELEASE_ASSERT(mGroupOffset == aData->mGroupOffset);
+      UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+      /* Instead of doing this dance, let's just invalidate the old rect and the
+       * new rect.
+      combined = aData->mClip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
+      combined.MoveBy(shift);
+      combined.Or(combined, clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion()));
+      aData->mGeometry = Move(geometry);
+      */
+      combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+      aData->mGeometry = Move(geometry);
+
+      GP("matrix: %f %f\n", aMatrix._31, aMatrix._32); 
+      GP("frame invalid invalidate: %s\n", aItem->Name());
+      GP("old rect: %d %d %d %d\n",
+             aData->mRect.x,
+             aData->mRect.y,
+             aData->mRect.width,
+             aData->mRect.height);
+      InvalidateRect(aData->mRect.Intersect(imageRect));
+      // We want to snap to outside pixels. When should we multiply by the matrix?
+      // XXX: TransformBounds is expensive. We should avoid doing it if we have no transform
+      IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+      aData->mRect = transformedRect.Intersect(imageRect);
+      InvalidateRect(aData->mRect);
+      GP("new rect: %d %d %d %d\n",
+             aData->mRect.x,
+             aData->mRect.y,
+             aData->mRect.width,
+             aData->mRect.height);
+      aData->mInvalid = true;
+    } else {
+      MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
+      MOZ_RELEASE_ASSERT(mGroupOffset == aData->mGroupOffset);
+      GP("else invalidate: %s\n", aItem->Name());
+      aData->mGeometry->MoveBy(shift);
+      // this includes situations like reflow changing the position
+      aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(), &combined);
+      if (!combined.IsEmpty()) {
+        // There might be no point in doing this elaborate tracking here to get
+        // smaller areas
+        InvalidateRect(aData->mRect.Intersect(imageRect)); // invalidate the old area -- in theory combined should take care of this
+        UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+        aData->mClip.AddOffsetAndComputeDifference(shift, aData->mGeometry->ComputeInvalidationRegion(), clip,
+                                                   geometry ? geometry->ComputeInvalidationRegion() :
+                                                   aData->mGeometry->ComputeInvalidationRegion(),
+                                                   &combined);
+        IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+        IntRect invalidRect = transformedRect.Intersect(imageRect);
+        GP("combined not empty: mRect %d %d %d %d\n", invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
+        // invalidate the invalidated area.
+        InvalidateRect(invalidRect);
+
+        aData->mGeometry = Move(geometry);
+
+        combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
+        transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+        aData->mRect = transformedRect.Intersect(imageRect);
+
+        aData->mInvalid = true;
+      } else {
+        if (aData->mClip != clip) {
+          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+          if (!IsContainerLayerItem(aItem)) {
+            // the bounds of layer items can change on us without ComputeInvalidationRegion
+            // returning any change. Other items shouldn't have any hidden
+            // geometry change.
+            MOZ_RELEASE_ASSERT(geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds));
+          } else {
+            aData->mGeometry = Move(geometry);
+          }
+          combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
+          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+          InvalidateRect(aData->mRect.Intersect(imageRect));
+          aData->mRect = transformedRect.Intersect(imageRect);
+          InvalidateRect(aData->mRect);
+
+          GP("ClipChange: %s %d %d %d %d\n", aItem->Name(),
+                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
+
+        } else if (!aMatrix.ExactlyEquals(aData->mMatrix)) {
+          // We haven't detected any changes so far. Unfortunately we don't
+          // currently have a good way of checking if the transform has changed
+          // so we just store it and see if it see if it has changed.
+          // If we want this to go faster, we can probably put a flag on the frame
+          // using the style sytem UpdateTransformLayer hint and check for that.
+
+          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+          if (!IsContainerLayerItem(aItem)) {
+            // the bounds of layer items can change on us
+            // other items shouldn't
+            MOZ_RELEASE_ASSERT(geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds));
+          } else {
+            aData->mGeometry = Move(geometry);
+          }
+          combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
+          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+          InvalidateRect(aData->mRect.Intersect(imageRect));
+          aData->mRect = transformedRect.Intersect(imageRect);
+          InvalidateRect(aData->mRect);
+
+          GP("TransformChange: %s %d %d %d %d\n", aItem->Name(),
+                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
+        } else if (IsContainerLayerItem(aItem)) {
+          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+          // we need to catch bounds changes of containers so that we continue to have the correct bounds rects in the recording
+          if (!geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds) ||
+              UpdateContainerLayerPropertiesAndDetectChange(aItem, aData)) {
+            combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+            aData->mGeometry = Move(geometry);
+            IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+            InvalidateRect(aData->mRect.Intersect(imageRect));
+            aData->mRect = transformedRect.Intersect(imageRect);
+            InvalidateRect(aData->mRect);
+            GP("UpdateContainerLayerPropertiesAndDetectChange change\n");
+          } else {
+            // XXX: this code can eventually be deleted/made debug only
+            combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+            IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+            auto rect = transformedRect.Intersect(imageRect);
+            MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
+            GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
+                   aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
+          }
+        } else {
+          // XXX: this code can eventually be deleted/made debug only
+          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
+          combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
+          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mGroupOffset);
+          auto rect = transformedRect.Intersect(imageRect);
+          MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
+          GP("NoChange: %s %d %d %d %d\n", aItem->Name(),
+                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
+        }
+      }
+    }
+    aData->mClip = clip;
+    aData->mMatrix = aMatrix;
+    aData->mGroupOffset = mGroupOffset;
+    aData->mImageRect = imageRect;
+    GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
+  }
+
+  void EndGroup(WebRenderLayerManager* aWrManager,
+                wr::DisplayListBuilder& aBuilder,
+                wr::IpcResourceUpdateQueue& aResources,
+                Grouper* aGrouper,
+                nsDisplayItem* aStartItem,
+                nsDisplayItem* aEndItem)
+  {
+    mLastAnimatedGeometryRootOrigin = mAnimatedGeometryRootOrigin;
+    GP("\n\n");
+    GP("Begin EndGroup\n");
+
+    // Invalidate any unused items
+    GP("mDisplayItems\n");
+    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+      BlobItemData* data = iter.Get()->GetKey();
+      GP("  : %p-%d\n", data->mFrame, data->mDisplayItemKey);
+      if (!data->mUsed) {
+        GP("Invalidate unused: %p-%d\n", data->mFrame, data->mDisplayItemKey);
+        InvalidateRect(data->mRect);
+        iter.Remove();
+        delete data;
+      } else {
+        data->mUsed = false;
+      }
+    }
+
+    LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(mGroupBounds, aGrouper->mAppUnitsPerDevPixel);
+    IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, aGrouper->mAppUnitsPerDevPixel);
+
+    if (mInvalidRect.IsEmpty()) {
+      GP("Not repainting group because it's empty\n");
+      GP("End EndGroup\n");
+      if (mKey) {
+        PushImage(aBuilder, bounds);
+      }
+      return;
+    }
+
+    gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+    RefPtr<gfx::DrawEventRecorderMemory> recorder =
+      MakeAndAddRef<gfx::DrawEventRecorderMemory>(
+        [&](MemStream& aStream, std::vector<RefPtr<UnscaledFont>>& aUnscaledFonts) {
+          size_t count = aUnscaledFonts.size();
+          aStream.write((const char*)&count, sizeof(count));
+          for (auto unscaled : aUnscaledFonts) {
+            wr::FontKey key = aWrManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
+            aStream.write((const char*)&key, sizeof(key));
+          }
+        });
+
+    RefPtr<gfx::DrawTarget> dummyDt =
+      gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
+
+    RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, size);
+    // Setup the gfxContext
+    RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
+    GP("ctx-offset %f %f\n", bounds.x, bounds.y);
+    context->SetMatrix(Matrix::Scaling(mScale.width, mScale.height).PreTranslate(-bounds.x, -bounds.y));
+
+    GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
+
+    bool empty = aStartItem == aEndItem;
+    if (empty) {
+      if (mKey) {
+        aWrManager->AddImageKeyForDiscard(mKey.value());
+        mKey = Nothing();
+      }
+      return;
+    }
+
+    PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
+
+    if (!mKey) {
+#if 0
+      context->SetMatrix(Matrix());
+      dt->FillRect(gfx::Rect(0, 0, size.width, size.height), gfx::ColorPattern(gfx::Color(0., 1., 0., 0.5)));
+      dt->FlushItem(IntRect(IntPoint(0, 0), size));
+#endif
+    }
+
+    // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
+    bool isOpaque = false;
+
+    bool hasItems = recorder->Finish();
+    GP("%d Finish\n", hasItems);
+    Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
+    if (!mKey) {
+      if (!hasItems) // we don't want to send a new image that doesn't have any items in it
+        return;
+      wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey();
+      GP("No previous key making new one %d\n", key.mHandle);
+      wr::ImageDescriptor descriptor(size, 0, dt->GetFormat(), isOpaque);
+      MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
+      if (!aResources.AddBlobImage(key, descriptor, bytes)) {
+        return;
+      }
+      mKey = Some(key);
+    } else {
+      wr::ImageDescriptor descriptor(size, 0, dt->GetFormat(), isOpaque);
+      auto bottomRight = mInvalidRect.BottomRight();
+      GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, size.width, size.height);
+      MOZ_RELEASE_ASSERT(bottomRight.x <= size.width && bottomRight.y <= size.height);
+      GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
+      if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
+        return;
+      }
+    }
+    mInvalidRect.SetEmpty();
+    PushImage(aBuilder, bounds);
+    GP("End EndGroup\n\n");
+  }
+
+  void PushImage(wr::DisplayListBuilder& aBuilder, const LayoutDeviceRect& bounds)
+  {
+    wr::LayoutRect dest = wr::ToLayoutRect(bounds);
+    GP("PushImage: %f %f %f %f\n", dest.origin.x, dest.origin.y, dest.size.width, dest.size.height);
+    gfx::SamplingFilter sampleFilter = gfx::SamplingFilter::LINEAR; //nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
+    bool backfaceHidden = false;
+    aBuilder.PushImage(dest, dest, !backfaceHidden,
+                       wr::ToImageRendering(sampleFilter),
+                       mKey.value());
+  }
+
+  void PaintItemRange(Grouper* aGrouper,
+                      nsDisplayItem* aStartItem,
+                      nsDisplayItem* aEndItem,
+                      gfxContext* aContext,
+                      gfx::DrawEventRecorderMemory* aRecorder) {
+    IntSize size = mGroupBounds.Size().ScaleToNearestPixels(mScale.width, mScale.height, aGrouper->mAppUnitsPerDevPixel);
+    for (nsDisplayItem* item = aStartItem; item != aEndItem; item = item->GetAbove()) {
+      IntRect bounds = ItemBounds(item);
+      auto bottomRight = bounds.BottomRight();
+
+      GP("Trying %s %p-%d %d %d %d %d\n", item->Name(), item->Frame(), item->GetPerFrameKey(), bounds.x, bounds.y, bounds.XMost(), bounds.YMost());
+      GP("paint check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, size.width, size.height);
+      // skip empty items
+      if (bounds.IsEmpty()) {
+          continue;
+      }
+
+      bool dirty = true;
+      if (!mInvalidRect.Contains(bounds)) {
+        GP("Passing\n");
+        dirty = false;
+      }
+
+      if (mInvalidRect.Contains(bounds)) {
+        GP("Wholely contained\n");
+        BlobItemData* data = GetBlobItemData(item);
+        data->mInvalid = false;
+      } else {
+        BlobItemData* data = GetBlobItemData(item);
+        // if the item is invalid it needs to be fully contained
+        MOZ_RELEASE_ASSERT(!data->mInvalid);
+      }
+
+      nsDisplayList* children = item->GetChildren();
+      if (children) {
+        GP("doing children in EndGroup\n");
+        aGrouper->PaintContainerItem(this, item, bounds, children, aContext, aRecorder);
+      } else {
+        if (dirty) {
+          // What should the clip settting strategy be? We can set the full clip everytime.
+          // this is probably easiest for now. An alternative would be to put the push and the pop
+          // into separate items and let invalidation handle it that way.
+          DisplayItemClip currentClip = item->GetClip();
+
+          if (currentClip.HasClip()) {
+            aContext->Save();
+            currentClip.ApplyTo(aContext, aGrouper->mAppUnitsPerDevPixel);
+          }
+          aContext->NewPath();
+          GP("painting %s %p-%d\n", item->Name(), item->Frame(), item->GetPerFrameKey());
+          item->Paint(aGrouper->mDisplayListBuilder, aContext);
+          if (currentClip.HasClip()) {
+            aContext->Restore();
+          }
+        }
+        aContext->GetDrawTarget()->FlushItem(bounds);
+      }
+    }
+  }
+
+  ~DIGroup()
+  {
+    GP("Group destruct\n");
+    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
+      BlobItemData* data = iter.Get()->GetKey();
+      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
+      iter.Remove();
+      delete data;
+    }
+  }
+};
+
+void
+Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
+                            nsDisplayList* aChildren, gfxContext* aContext,
+                            gfx::DrawEventRecorderMemory* aRecorder)
+{
+  mItemStack.push_back(aItem);
+  switch (aItem->GetType()) {
+    case DisplayItemType::TYPE_TRANSFORM: {
+      DisplayItemClip currentClip = aItem->GetClip();
+
+      gfx::Matrix matrix;
+      if (currentClip.HasClip()) {
+        aContext->Save();
+        currentClip.ApplyTo(aContext, this->mAppUnitsPerDevPixel);
+      } else {
+        matrix = aContext->CurrentMatrix();
+      }
+
+      auto transformItem = static_cast<nsDisplayTransform*>(aItem);
+      Matrix4x4Flagged trans = transformItem->GetTransform();
+      Matrix trans2d;
+      MOZ_RELEASE_ASSERT(trans.Is2D(&trans2d));
+      aContext->Multiply(ThebesMatrix(trans2d));
+      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
+
+      if (currentClip.HasClip()) {
+        aContext->Restore();
+      } else {
+        aContext->SetMatrix(matrix);
+      }
+      break;
+    }
+    case DisplayItemType::TYPE_OPACITY: {
+      auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
+      float opacity = opacityItem->GetOpacity();
+      if (opacity == 0.0f) {
+        return;
+      }
+
+      aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacityItem->GetOpacity());
+      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
+      aContext->PopGroupAndBlend();
+      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      break;
+    }
+    case DisplayItemType::TYPE_BLEND_MODE: {
+      auto blendItem = static_cast<nsDisplayBlendMode*>(aItem);
+      auto blendMode = blendItem->BlendMode();
+      aContext->GetDrawTarget()->PushLayerWithBlend(false, 1.0, nullptr, mozilla::gfx::Matrix(), IntRect(), false, blendMode);
+      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
+      aContext->GetDrawTarget()->PopLayer();
+      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      break;
+    }
+    case DisplayItemType::TYPE_MASK: {
+      GP("Paint Mask\n");
+      // We don't currently support doing invalidation inside nsDisplayMask
+      // for now just paint it as a single item
+      gfx::Size scale(1, 1);
+      RefPtr<BasicLayerManager> blm = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
+      PaintByLayer(aItem, mDisplayListBuilder, blm, aContext, scale, [&]() {
+                   static_cast<nsDisplayMask*>(aItem)->PaintAsLayer(mDisplayListBuilder,
+                                                                    aContext, blm);
+                   });
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      break;
+    }
+    case DisplayItemType::TYPE_FILTER: {
+      GP("Paint Filter\n");
+      // We don't currently support doing invalidation inside nsDisplayFilter
+      // for now just paint it as a single item
+      RefPtr<BasicLayerManager> blm = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
+      gfx::Size scale(1, 1);
+      PaintByLayer(aItem, mDisplayListBuilder, blm, aContext, scale, [&]() {
+                   static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(mDisplayListBuilder,
+                                                                      aContext, blm);
+                   });
+      aContext->GetDrawTarget()->FlushItem(aItemBounds);
+      break;
+    }
+
+    default:
+      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
+      break;
+  }
+}
+
+class WebRenderGroupData : public WebRenderUserData
+{
+public:
+  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  virtual ~WebRenderGroupData();
+
+  virtual WebRenderGroupData* AsGroupData() override { return this; }
+  virtual UserDataType GetType() override { return UserDataType::eGroup; }
+  static UserDataType Type() { return UserDataType::eGroup; }
+
+  DIGroup mSubGroup;
+  DIGroup mFollowingGroup;
+};
+
+static bool
+IsItemProbablyActive(nsDisplayItem* aItem, nsDisplayListBuilder* aDisplayListBuilder);
+
+static bool
+HasActiveChildren(const nsDisplayList& aList, nsDisplayListBuilder *aDisplayListBuilder)
+{
+  for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
+    if (IsItemProbablyActive(i, aDisplayListBuilder)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// This function decides whether we want to treat this item as "active", which means
+// that it's a container item which we will turn into a WebRender StackingContext, or
+// whether we treat it as "inactive" and include it inside the parent blob image.
+//
+// We can't easily use GetLayerState because it wants a bunch of layers related information.
+static bool
+IsItemProbablyActive(nsDisplayItem* aItem, nsDisplayListBuilder* aDisplayListBuilder)
+{
+  if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
+    nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(aItem);
+    Matrix4x4Flagged t = transformItem->GetTransform();
+    Matrix t2d;
+    bool is2D = t.Is2D(&t2d);
+    GP("active: %d\n", transformItem->MayBeAnimated(aDisplayListBuilder));
+    return transformItem->MayBeAnimated(aDisplayListBuilder) || !is2D || HasActiveChildren(*transformItem->GetChildren(), aDisplayListBuilder);
+  } else if (aItem->GetType() == DisplayItemType::TYPE_OPACITY) {
+    nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
+    bool active = opacityItem->NeedsActiveLayer(aDisplayListBuilder, opacityItem->Frame());
+    GP("active: %d\n", active);
+    return active || HasActiveChildren(*opacityItem->GetChildren(), aDisplayListBuilder);
+  }
+  // TODO: handle other items?
+  return false;
+}
+
+// If we have an item we need to make sure it matches the current group
+// otherwise it means the item switched groups and we need to invalidate
+// it and recreate the data.
+static BlobItemData*
+GetBlobItemDataForGroup(nsDisplayItem* aItem, DIGroup* aGroup)
+{
+  BlobItemData* data = GetBlobItemData(aItem);
+  if (data) {
+    MOZ_RELEASE_ASSERT(data->mGroup->mDisplayItems.Contains(data));
+    if (data->mGroup != aGroup) {
+      GP("group don't match %p %p\n", data->mGroup, aGroup);
+      data->ClearFrame();
+      // the item is for another group
+      // it should be cleared out as being unused at the end of this paint
+      data = nullptr;
+    }
+  }
+  if (!data) {
+    GP("Allocating blob data\n");
+    data = new BlobItemData(aGroup, aItem);
+    aGroup->mDisplayItems.PutEntry(data);
+  }
+  data->mUsed = true;
+  return data;
+}
+
+// This does a pass over the display lists and will join the display items
+// into groups as well as paint them
+void
+Grouper::ConstructGroups(WebRenderCommandBuilder* aCommandBuilder,
+                         wr::DisplayListBuilder& aBuilder,
+                         wr::IpcResourceUpdateQueue& aResources,
+                         DIGroup* aGroup, nsDisplayList* aList,
+                         const StackingContextHelper& aSc)
+{
+  DIGroup* currentGroup = aGroup;
+
+  nsDisplayItem* item = aList->GetBottom();
+  nsDisplayItem* startOfCurrentGroup = item;
+  while (item) {
+    nsDisplayList* children = item->GetChildren();
+    if (IsItemProbablyActive(item, mDisplayListBuilder)) {
+      currentGroup->EndGroup(aCommandBuilder->mManager, aBuilder, aResources, this, startOfCurrentGroup, item);
+      mScrollingHelper.BeginItem(item, aSc);
+      sIndent++;
+      // Note: this call to CreateWebRenderCommands can recurse back into
+      // this function.
+      bool createdWRCommands =
+        item->CreateWebRenderCommands(aBuilder, aResources, aSc, aCommandBuilder->mManager,
+                                      mDisplayListBuilder);
+      sIndent--;
+      MOZ_RELEASE_ASSERT(createdWRCommands, "active transforms should always succeed at creating WebRender commands");
+
+      RefPtr<WebRenderGroupData> groupData =
+        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item);
+
+      // Initialize groupData->mFollowingGroup
+      // TODO: compute the group bounds post-grouping, so that they can be
+      // tighter for just the sublist that made it into this group.
+      // We want to ensure the tight bounds are still clipped by area
+      // that we're building the display list for.
+      if (groupData->mFollowingGroup.mKey) {
+        if (!groupData->mFollowingGroup.mGroupBounds.IsEqualEdges(currentGroup->mGroupBounds) ||
+            groupData->mFollowingGroup.mScale != currentGroup->mScale ||
+            groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel) {
+          if (groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel) {
+            printf("app unit change following: %d %d\n", groupData->mFollowingGroup.mAppUnitsPerDevPixel, currentGroup->mAppUnitsPerDevPixel);
+          }
+          // The group changed size
+          GP("Inner group size change\n");
+          aCommandBuilder->mManager->AddImageKeyForDiscard(groupData->mFollowingGroup.mKey.value());
+          groupData->mFollowingGroup.mKey = Nothing();
+          groupData->mFollowingGroup.ClearItems();
+
+          IntSize size = currentGroup->mGroupBounds.Size().ScaleToNearestPixels(currentGroup->mScale.width, currentGroup->mScale.height, mAppUnitsPerDevPixel);
+          groupData->mFollowingGroup.mInvalidRect = IntRect(IntPoint(0, 0), size);
+        }
+      }
+      groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
+      groupData->mFollowingGroup.mAppUnitsPerDevPixel = currentGroup->mAppUnitsPerDevPixel;
+      groupData->mFollowingGroup.mGroupOffset = currentGroup->mGroupOffset;
+      groupData->mFollowingGroup.mScale = currentGroup->mScale;
+
+      currentGroup = &groupData->mFollowingGroup;
+
+      startOfCurrentGroup = item->GetAbove();
+    } else { // inactive item
+
+      if (item->GetType() == DisplayItemType::TYPE_TRANSFORM) {
+        nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(item);
+        Matrix4x4Flagged t = transformItem->GetTransform();
+        Matrix t2d;
+        bool is2D = t.Is2D(&t2d);
+        MOZ_RELEASE_ASSERT(is2D, "Non-2D transforms should be treated as active");
+
+        // Save the current transform.
+        Matrix m = mTransform;
+
+        GP("t2d: %f %f\n", t2d._31, t2d._32);
+        mTransform.PreMultiply(t2d);
+        GP("mTransform: %f %f\n", mTransform._31, mTransform._32);
+        ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, currentGroup, children, aSc);
+
+        mTransform = m; // restore it
+      } else if (children) {
+        ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, currentGroup, children, aSc);
+      }
+
+      GP("Including %s of %d\n", item->Name(), currentGroup->mDisplayItems.Count());
+
+      BlobItemData* data = GetBlobItemDataForGroup(item, currentGroup);
+      currentGroup->ComputeGeometryChange(item, data, mTransform, mDisplayListBuilder); // we compute the geometry change here because we have the transform around still
+    }
+
+    item = item->GetAbove();
+  }
+
+  currentGroup->EndGroup(aCommandBuilder->mManager, aBuilder, aResources, this, startOfCurrentGroup, nullptr);
+}
+
+// This does a pass over the display lists and will join the display items
+// into a single group.
+void
+Grouper::ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
+                                       wr::DisplayListBuilder& aBuilder,
+                                       wr::IpcResourceUpdateQueue& aResources,
+                                       DIGroup* aGroup, nsDisplayList* aList,
+                                       const StackingContextHelper& aSc)
+{
+  DIGroup* currentGroup = aGroup;
+
+  nsDisplayItem* item = aList->GetBottom();
+  while (item) {
+    nsDisplayList* children = item->GetChildren();
+
+    if (item->GetType() == DisplayItemType::TYPE_TRANSFORM) {
+      nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(item);
+      Matrix4x4Flagged t = transformItem->GetTransform();
+      Matrix t2d;
+      bool is2D = t.Is2D(&t2d);
+      MOZ_RELEASE_ASSERT(is2D, "Non-2D transforms should be treated as active");
+
+      Matrix m = mTransform;
+
+      GP("t2d: %f %f\n", t2d._31, t2d._32);
+      mTransform.PreMultiply(t2d);
+      GP("mTransform: %f %f\n", mTransform._31, mTransform._32);
+      ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, currentGroup, children, aSc);
+
+      mTransform = m;
+    } else if (children) {
+      ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, currentGroup, children, aSc);
+    }
+
+    GP("Including %s of %d\n", item->Name(), currentGroup->mDisplayItems.Count());
+
+    BlobItemData* data = GetBlobItemDataForGroup(item, currentGroup);
+    currentGroup->ComputeGeometryChange(item, data, mTransform, mDisplayListBuilder); // we compute the geometry change here because we have the transform around still
+
+    item = item->GetAbove();
+  }
+}
+
+void
+WebRenderCommandBuilder::DoGroupingForDisplayList(nsDisplayList* aList,
+                                                  nsDisplayItem* aWrappingItem,
+                                                  nsDisplayListBuilder* aDisplayListBuilder,
+                                                  const StackingContextHelper& aSc,
+                                                  wr::DisplayListBuilder& aBuilder,
+                                                  wr::IpcResourceUpdateQueue& aResources)
+{
+  if (!aList->GetBottom()) {
+    return;
+  }
+
+  mScrollingHelper.BeginList(aSc);
+  Grouper g(mScrollingHelper);
+  int32_t appUnitsPerDevPixel = aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+  GP("DoGroupingForDisplayList\n");
+
+  g.mDisplayListBuilder = aDisplayListBuilder;
+  RefPtr<WebRenderGroupData> groupData = CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
+  bool snapped;
+  nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
+  DIGroup& group = groupData->mSubGroup;
+  auto p = group.mGroupBounds;
+  auto q = groupBounds;
+  gfx::Size scale = aSc.GetInheritedScale();
+  GP("Inherrited scale %f %f\n", scale.width, scale.height);
+  GP("Bounds: %d %d %d %d vs %d %d %d %d\n", p.x, p.y, p.width, p.height, q.x, q.y, q.width, q.height);
+  if (!group.mGroupBounds.IsEqualEdges(groupBounds) ||
+      group.mAppUnitsPerDevPixel != appUnitsPerDevPixel ||
+      group.mScale != scale) {
+    if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel) {
+      printf("app unit %d %d\n", group.mAppUnitsPerDevPixel, appUnitsPerDevPixel);
+    }
+    // The bounds have changed so we need to discard the old image and add all
+    // the commands again.
+    auto p = group.mGroupBounds;
+    auto q = groupBounds;
+    GP("Bounds change: %d %d %d %d vs %d %d %d %d\n", p.x, p.y, p.width, p.height, q.x, q.y, q.width, q.height);
+
+    group.ClearItems();
+    if (group.mKey) {
+      IntSize size = groupBounds.Size().ScaleToNearestPixels(scale.width, scale.height, g.mAppUnitsPerDevPixel);
+      group.mInvalidRect = IntRect(IntPoint(0, 0), size);
+      mManager->AddImageKeyForDiscard(group.mKey.value());
+      group.mKey = Nothing();
+    }
+  }
+  g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
+  g.mTransform = Matrix::Scaling(scale.width, scale.height);
+  group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
+  group.mGroupBounds = groupBounds;
+  group.mScale = scale;
+  group.mGroupOffset = group.mGroupBounds.TopLeft().ScaleToNearestPixels(scale.width, scale.height, g.mAppUnitsPerDevPixel);
+  group.mAnimatedGeometryRootOrigin = group.mGroupBounds.TopLeft();
+  g.ConstructGroups(this, aBuilder, aResources, &group, aList, aSc);
+  mScrollingHelper.EndList(aSc);
+}
+
+void
+WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
   RemoveUnusedAndResetWebRenderUserData();
   // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
   // The should always be false upon return from BuildWebRenderCommands().
   MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
 }
 
@@ -72,17 +1119,17 @@ WebRenderCommandBuilder::BuildWebRenderC
     aScrollData = WebRenderScrollData(mManager);
     MOZ_ASSERT(mLayerScrollData.empty());
     mLastCanvasDatas.Clear();
     mLastAsr = nullptr;
     mScrollingHelper.BeginBuild(mManager, aBuilder);
 
     {
       StackingContextHelper pageRootSc(sc, aBuilder, aFilters);
-      CreateWebRenderCommandsFromDisplayList(aDisplayList, aDisplayListBuilder,
+      CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, aDisplayListBuilder,
                                              pageRootSc, aBuilder, aResourceUpdates);
     }
 
     // Make a "root" layer data that has everything else as descendants
     mLayerScrollData.emplace_back();
     mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1);
     auto callback = [&aScrollData](FrameMetrics::ViewID aScrollId) -> bool {
       return aScrollData.HasMetadataFor(aScrollId).isSome();
@@ -105,21 +1152,29 @@ WebRenderCommandBuilder::BuildWebRenderC
     RemoveUnusedAndResetWebRenderUserData();
   }
 
   mManager->WrBridge()->AddWebRenderParentCommands(mParentCommands);
 }
 
 void
 WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
+                                                                nsDisplayItem* aWrappingItem,
                                                                 nsDisplayListBuilder* aDisplayListBuilder,
                                                                 const StackingContextHelper& aSc,
                                                                 wr::DisplayListBuilder& aBuilder,
                                                                 wr::IpcResourceUpdateQueue& aResources)
 {
+  if (mDoGrouping) {
+    MOZ_RELEASE_ASSERT(aWrappingItem, "Only the root list should have a null wrapping item, and mDoGrouping should never be true for the root list.");
+    GP("actually entering the grouping code\n");
+    DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, aSc, aBuilder, aResources);
+    return;
+  }
+
   mScrollingHelper.BeginList(aSc);
 
   bool apzEnabled = mManager->AsyncPanZoomEnabled();
   EventRegions eventRegions;
 
   FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
   while (nsDisplayItem* i = iter.GetNext()) {
     nsDisplayItem* item = i;
@@ -216,22 +1271,40 @@ WebRenderCommandBuilder::CreateWebRender
       // ASR so that if we recurse into a sublist they will know where to stop
       // walking up their ASR chain when building scroll metadata.
       if (forceNewLayerData) {
         mAsrStack.push_back(asr);
       }
     }
 
     mScrollingHelper.BeginItem(item, aSc);
-    // Note: this call to CreateWebRenderCommands can recurse back into
-    // this function if the |item| is a wrapper for a sublist.
-    if (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
-        !item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
-                                       aDisplayListBuilder)) {
-      PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+
+    if (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
+      AutoRestore<bool> restoreDoGrouping(mDoGrouping);
+      if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
+        // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
+        // display items (like animated transforms / opacity) share the same
+        // animated geometry root, so we can combine subsequent items of that
+        // type into the same image.
+        mDoGrouping = true;
+        GP("attempting to enter the grouping code\n");
+      }/* else if (itemType == DisplayItemType::TYPE_FOREIGN_OBJECT) {
+        // We do not want to apply grouping inside <foreignObject>.
+        // TODO: TYPE_FOREIGN_OBJECT does not exist yet, make it exist
+        mDoGrouping = false;
+      }*/
+
+      // Note: this call to CreateWebRenderCommands can recurse back into
+      // this function if the |item| is a wrapper for a sublist.
+      bool createdWRCommands =
+        item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
+                                      aDisplayListBuilder);
+      if (!createdWRCommands) {
+        PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+      }
     }
 
     if (apzEnabled) {
       if (forceNewLayerData) {
         // Pop the thing we pushed before the recursion, so the topmost item on
         // the stack is enclosing display item's ASR (or the stack is empty)
         mAsrStack.pop_back();
         const ActiveScrolledRoot* stopAtAsr =
@@ -759,10 +1832,24 @@ void
 WebRenderCommandBuilder::ClearCachedResources()
 {
   for (auto iter = mWebRenderUserDatas.Iter(); !iter.Done(); iter.Next()) {
     WebRenderUserData* data = iter.Get()->GetKey();
     data->ClearCachedResources();
   }
 }
 
+
+
+WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
+  : WebRenderUserData(aWRManager, aItem)
+{
+  MOZ_COUNT_CTOR(WebRenderGroupData);
+}
+
+WebRenderGroupData::~WebRenderGroupData()
+{
+  MOZ_COUNT_DTOR(WebRenderGroupData);
+  GP("Group data destruct\n");
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -33,16 +33,17 @@ class WebRenderUserData;
 class WebRenderCommandBuilder {
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData>> WebRenderUserDataRefTable;
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderCanvasData>> CanvasDataSet;
 
 public:
   explicit WebRenderCommandBuilder(WebRenderLayerManager* aManager)
   : mManager(aManager)
   , mLastAsr(nullptr)
+  , mDoGrouping(false)
   {}
 
   void Destroy();
 
   void EmptyTransaction();
 
   bool NeedsEmptyTransaction();
 
@@ -80,21 +81,30 @@ public:
 
   bool PushItemAsImage(nsDisplayItem* aItem,
                        wr::DisplayListBuilder& aBuilder,
                        wr::IpcResourceUpdateQueue& aResources,
                        const StackingContextHelper& aSc,
                        nsDisplayListBuilder* aDisplayListBuilder);
 
   void CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
+                                              nsDisplayItem* aOuterItem,
                                               nsDisplayListBuilder* aDisplayListBuilder,
                                               const StackingContextHelper& aSc,
                                               wr::DisplayListBuilder& aBuilder,
                                               wr::IpcResourceUpdateQueue& aResources);
 
+  // aWrappingItem has to be non-null.
+  void DoGroupingForDisplayList(nsDisplayList* aDisplayList,
+                                nsDisplayItem* aWrappingItem,
+                                nsDisplayListBuilder* aDisplayListBuilder,
+                                const StackingContextHelper& aSc,
+                                wr::DisplayListBuilder& aBuilder,
+                                wr::IpcResourceUpdateQueue& aResources);
+
   already_AddRefed<WebRenderFallbackData> GenerateFallbackData(nsDisplayItem* aItem,
                                                                wr::DisplayListBuilder& aBuilder,
                                                                wr::IpcResourceUpdateQueue& aResources,
                                                                const StackingContextHelper& aSc,
                                                                nsDisplayListBuilder* aDisplayListBuilder,
                                                                LayoutDeviceRect& aImageRect);
 
   void RemoveUnusedAndResetWebRenderUserData();
@@ -143,18 +153,18 @@ public:
 
     if (T::Type() == WebRenderUserData::UserDataType::eCanvas) {
       mLastCanvasDatas.PutEntry(data->AsCanvasData());
     }
     RefPtr<T> res = static_cast<T*>(data.get());
     return res.forget();
   }
 
+  WebRenderLayerManager* mManager;
 private:
-  WebRenderLayerManager* mManager;
   ScrollingLayersHelper mScrollingHelper;
 
   // These fields are used to save a copy of the display list for
   // empty transactions in layers-free mode.
   nsTArray<WebRenderParentCommand> mParentCommands;
 
   // We use this as a temporary data structure while building the mScrollData
   // inside a layers-free transaction.
@@ -165,14 +175,18 @@ private:
   // tree don't duplicate scroll metadata that their ancestors already have.
   std::vector<const ActiveScrolledRoot*> mAsrStack;
   const ActiveScrolledRoot* mLastAsr;
 
   WebRenderUserDataRefTable mWebRenderUserDatas;
 
   // Store of WebRenderCanvasData objects for use in empty transactions
   CanvasDataSet mLastCanvasDatas;
+
+  // Whether consecutive inactive display items should be grouped into one
+  // blob image.
+  bool mDoGrouping;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_WEBRENDERCOMMANDBUILDER_H */
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -240,17 +240,16 @@ WebRenderLayerManager::EndTransaction(Dr
 }
 
 void
 WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList,
                                                   nsDisplayListBuilder* aDisplayListBuilder,
                                                   const nsTArray<wr::WrFilterOp>& aFilters)
 {
   MOZ_ASSERT(aDisplayList && aDisplayListBuilder);
-  WrBridge()->RemoveExpiredFontKeys();
 
   AUTO_PROFILER_TRACING("Paint", "RenderLayers");
 
 #if DUMP_LISTS
   // Useful for debugging, it dumps the display list *before* we try to build
   // WR commands from it
   if (XRE_IsContentProcess()) nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList);
 #endif
@@ -296,16 +295,18 @@ WebRenderLayerManager::EndTransactionWit
   TimeStamp transactionStart = mTransactionIdAllocator->GetTransactionStart();
 
   for (const auto& key : mImageKeysToDelete) {
     resourceUpdates.DeleteImage(key);
   }
   mImageKeysToDelete.Clear();
   mImageKeysToDelete.SwapElements(mImageKeysToDeleteLater);
 
+  WrBridge()->RemoveExpiredFontKeys(resourceUpdates);
+
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (WrBridge()->GetSyncObject() &&
         WrBridge()->GetSyncObject()->IsSyncObjectValid()) {
       WrBridge()->GetSyncObject()->Synchronize();
     }
   }
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -25,37 +25,40 @@ class CanvasLayer;
 class ImageClient;
 class ImageContainer;
 class WebRenderBridgeChild;
 class WebRenderCanvasData;
 class WebRenderCanvasRendererAsync;
 class WebRenderImageData;
 class WebRenderFallbackData;
 class WebRenderLayerManager;
+class WebRenderGroupData;
 
 class WebRenderUserData
 {
 public:
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData> > WebRenderUserDataRefTable;
 
   static bool SupportsAsyncUpdate(nsIFrame* aFrame);
 
   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData)
 
   WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
 
   virtual WebRenderImageData* AsImageData() { return nullptr; }
   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; }
   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; }
+  virtual WebRenderGroupData* AsGroupData() { return nullptr; }
 
   enum class UserDataType {
     eImage,
     eFallback,
     eAnimation,
     eCanvas,
+    eGroup,
   };
 
   virtual UserDataType GetType() = 0;
   bool IsUsed() { return mUsed; }
   void SetUsed(bool aUsed) { mUsed = aUsed; }
   nsIFrame* GetFrame() { return mFrame; }
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
   void RemoveFromTable();
@@ -224,14 +227,12 @@ GetWebRenderUserData(nsIFrame* aFrame, u
   if (data && (data->GetType() == T::Type())) {
     RefPtr<T> result = static_cast<T*>(data);
     return result.forget();
   }
 
   return nullptr;
 }
 
-
-
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_WEBRENDERUSERDATA_H */
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -503,16 +503,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.use-surfacetexture-textures",       UseSurfaceTextureTextures, bool, false);
 
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
 
   DECL_GFX_PREF(Once, "gfx.webrender.all",                     WebRenderAll, bool, false);
   DECL_GFX_PREF(Once, "gfx.webrender.enabled",                 WebRenderEnabledDoNotUseDirectly, bool, false);
   DECL_OVERRIDE_PREF(Live, "gfx.webrender.blob-images",        WebRenderBlobImages, gfxPrefs::WebRenderAll());
+  DECL_GFX_PREF(Live, "gfx.webrender.blob.invalidation",       WebRenderBlobInvalidation, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.hit-test",                WebRenderHitTest, bool, true);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
 
   DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Image.h"
 #include "Layers.h"               // for LayerManager
 #include "nsRefreshDriver.h"
+#include "mozilla/SizeOfState.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"        // for Tie
 
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // Memory Reporting
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -33,16 +33,17 @@
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Services.h"
+#include "mozilla/SizeOfState.h"
 #include <stdint.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/gfx/Scale.h"
 
 #include "GeckoProfiler.h"
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -807,146 +807,88 @@ class MOZ_NON_PARAM alignas(8) Value
 #endif
         data.asBits = bitsFromTagAndPayload(JSVAL_TAG_PRIVATE_GCTHING, PayloadType(cell));
     }
 
     bool isPrivateGCThing() const {
         return toTag() == JSVAL_TAG_PRIVATE_GCTHING;
     }
 
-    const size_t* payloadWord() const {
-#if defined(JS_NUNBOX32)
-        return &data.s.payload.word;
-#elif defined(JS_PUNBOX64)
-        return &data.asWord;
-#endif
-    }
-
     const uintptr_t* payloadUIntPtr() const {
 #if defined(JS_NUNBOX32)
         return &data.s.payload.uintptr;
 #elif defined(JS_PUNBOX64)
         return &data.asUIntPtr;
 #endif
     }
 
 #if !defined(_MSC_VER) && !defined(__sparc)
   // Value must be POD so that MSVC will pass it by value and not in memory
   // (bug 689101); the same is true for SPARC as well (bug 737344).  More
   // precisely, we don't want Value return values compiled as out params.
   private:
 #endif
 
-#if MOZ_LITTLE_ENDIAN
-# if defined(JS_NUNBOX32)
     union layout {
         uint64_t asBits;
+        double asDouble;
+        void* asPtr;
+#if defined(JS_PUNBOX64)
+        uintptr_t asUIntPtr;
+#endif // defined(JS_PUNBOX64)
+
+#if defined(JS_PUNBOX64) && !defined(_WIN64)
+        /* MSVC does not pack these correctly :-( */
         struct {
+#  if MOZ_LITTLE_ENDIAN
+            uint64_t   payload47 : 47;
+            JSValueTag tag : 17;
+#  else
+            JSValueTag tag : 17;
+            uint64_t   payload47 : 47;
+#  endif // MOZ_LITTLE_ENDIAN
+        } debugView;
+#endif // defined(JS_PUNBOX64) && !defined(_WIN64)
+
+        struct {
+#if defined(JS_PUNBOX64)
+#  if MOZ_BIG_ENDIAN
+            uint32_t padding;
+#  endif // MOZ_BIG_ENDIAN
+            union {
+                int32_t    i32;
+                uint32_t   u32;
+                JSWhyMagic why;
+            } payload;
+#else
+#  if MOZ_BIG_ENDIAN
+            JSValueTag tag;
+#  endif // MOZ_BIG_ENDIAN
             union {
                 int32_t        i32;
                 uint32_t       u32;
                 uint32_t       boo;     // Don't use |bool| -- it must be four bytes.
                 JSString*      str;
                 JS::Symbol*    sym;
                 JSObject*      obj;
                 js::gc::Cell*  cell;
                 void*          ptr;
                 JSWhyMagic     why;
-                size_t         word;
                 uintptr_t      uintptr;
             } payload;
+#  if MOZ_LITTLE_ENDIAN
             JSValueTag tag;
+#  endif // MOZ_LITTLE_ENDIAN
+#endif // defined(JS_PUNBOX64)
         } s;
-        double asDouble;
-        void* asPtr;
-
-        layout() : asBits(JSVAL_RAW64_UNDEFINED) {}
-        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
-        explicit constexpr layout(double d) : asDouble(d) {}
-    } data;
-# elif defined(JS_PUNBOX64)
-    union layout {
-        uint64_t asBits;
-#if !defined(_WIN64)
-        /* MSVC does not pack these correctly :-( */
-        struct {
-            uint64_t           payload47 : 47;
-            JSValueTag         tag : 17;
-        } debugView;
-#endif
-        struct {
-            union {
-                int32_t        i32;
-                uint32_t       u32;
-                JSWhyMagic     why;
-            } payload;
-        } s;
-        double asDouble;
-        void* asPtr;
-        size_t asWord;
-        uintptr_t asUIntPtr;
 
         layout() : asBits(JSVAL_RAW64_UNDEFINED) {}
         explicit constexpr layout(uint64_t bits) : asBits(bits) {}
         explicit constexpr layout(double d) : asDouble(d) {}
     } data;
-# endif  /* JS_PUNBOX64 */
-#else   /* MOZ_LITTLE_ENDIAN */
-# if defined(JS_NUNBOX32)
-    union layout {
-        uint64_t asBits;
-        struct {
-            JSValueTag tag;
-            union {
-                int32_t        i32;
-                uint32_t       u32;
-                uint32_t       boo;     // Don't use |bool| -- it must be four bytes.
-                JSString*      str;
-                JS::Symbol*    sym;
-                JSObject*      obj;
-                js::gc::Cell*  cell;
-                void*          ptr;
-                JSWhyMagic     why;
-                size_t         word;
-                uintptr_t      uintptr;
-            } payload;
-        } s;
-        double asDouble;
-        void* asPtr;
-
-        layout() : asBits(JSVAL_RAW64_UNDEFINED) {}
-        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
-        explicit constexpr layout(double d) : asDouble(d) {}
-    } data;
-# elif defined(JS_PUNBOX64)
-    union layout {
-        uint64_t asBits;
-        struct {
-            JSValueTag         tag : 17;
-            uint64_t           payload47 : 47;
-        } debugView;
-        struct {
-            uint32_t           padding;
-            union {
-                int32_t        i32;
-                uint32_t       u32;
-                JSWhyMagic     why;
-            } payload;
-        } s;
-        double asDouble;
-        void* asPtr;
-        size_t asWord;
-        uintptr_t asUIntPtr;
-
-        layout() : asBits(JSVAL_RAW64_UNDEFINED) {}
-        explicit constexpr layout(uint64_t bits) : asBits(bits) {}
-        explicit constexpr layout(double d) : asDouble(d) {}
-    } data;
-# endif /* JS_PUNBOX64 */
-#endif  /* MOZ_LITTLE_ENDIAN */
 
   private:
     explicit constexpr Value(uint64_t asBits) : data(asBits) {}
     explicit constexpr Value(double d) : data(d) {}
 
     void staticAssertions() {
         JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
         JS_STATIC_ASSERT(sizeof(JSValueTag) == 4);
@@ -1024,20 +966,24 @@ struct MOZ_NON_PARAM alignas(8) Uninitia
         return asValueRef();
     }
 
     inline void operator=(Value const& other) {
         asValueRef() = other;
     }
 };
 
-static_assert(sizeof(Value) == 8, "Value size must leave three tag bits, be a binary power, and is ubiquitously depended upon everywhere");
+static_assert(sizeof(Value) == 8,
+              "Value size must leave three tag bits, be a binary power, and "
+              "is ubiquitously depended upon everywhere");
 
-static_assert(sizeof(UninitializedValue) == sizeof(Value), "Value and UninitializedValue must be the same size");
-static_assert(alignof(UninitializedValue) == alignof(Value), "Value and UninitializedValue must have same alignment");
+static_assert(sizeof(UninitializedValue) == sizeof(Value),
+              "Value and UninitializedValue must be the same size");
+static_assert(alignof(UninitializedValue) == alignof(Value),
+              "Value and UninitializedValue must have same alignment");
 
 inline bool
 IsOptimizedPlaceholderMagicValue(const Value& v)
 {
     if (v.isMagic()) {
         MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_ARGUMENTS || v.whyMagic() == JS_OPTIMIZED_OUT);
         return true;
     }
--- a/js/src/build/moz.build
+++ b/js/src/build/moz.build
@@ -25,27 +25,19 @@ if CONFIG['JS_SHARED_LIBRARY']:
     SHARED_LIBRARY_NAME = CONFIG['JS_LIBRARY_NAME']
 else:
     Library('js')
 
 FORCE_STATIC_LIB = True
 STATIC_LIBRARY_NAME = 'js_static'
 
 if CONFIG['ENABLE_INTL_API']:
-    if not CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-        USE_LIBS += [
-            'icu',
-        ]
-    else:
-        # Linking 'icu' will pull in the stubdata library,
-        # which the shell doesn't want, so link the other bits.
-        USE_LIBS += [
-            'icui18n',
-            'icuuc',
-        ]
+    USE_LIBS += [
+        'icu',
+    ]
 
 USE_LIBS += [
     'nspr',
     'zlib',
 ]
 
 if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'):
     OS_LIBS += [
--- a/js/src/fuzz-tests/moz.build
+++ b/js/src/fuzz-tests/moz.build
@@ -18,21 +18,16 @@ if CONFIG['JS_BUILD_BINAST']:
 
 DEFINES['EXPORT_JS_API'] = True
 
 LOCAL_INCLUDES += [
     '!..',
     '..',
 ]
 
-if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-    # The ICU libraries linked into libmozjs will not include the ICU data,
-    # so link it directly.
-    USE_LIBS += ['icudata']
-
 if CONFIG['FUZZING']:
     USE_LIBS += [
         'static:fuzzer-registry',
     ]
 
 if CONFIG['LIBFUZZER']:
     USE_LIBS += [
         'static:fuzzer',
--- a/js/src/gdb/README
+++ b/js/src/gdb/README
@@ -1,37 +1,37 @@
 This directory holds Python code to support debugging SpiderMonkey with
-GDB. It includes pretty-printers for common SpiderMonkey types like jsval,
+GDB. It includes pretty-printers for common SpiderMonkey types like JS::Value,
 jsid, and JSObject, and makes GDB "see through" the SpiderMonkey rooting
 types like js::Rooted and JS::Handle. For example:
 
     (gdb) frame
     #0  js::baseops::SetPropertyHelper (cx=0xbf3460,
         obj=(JSObject * const) 0x7ffff150b060 [object global] delegate,
         receiver=(JSObject * const) 0x7ffff150b060 [object global] delegate,
-        id=$jsid("x"), defineHow=4, vp=$jsval(1), strict=0)
+        id=$jsid("x"), defineHow=4, vp=$JS::Int32Value(1), strict=0)
         at /home/jimb/moz/archer/js/src/jsobj.cpp:4495
     4495	    MOZ_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0);
     (gdb)
 
 Things to note here:
 
 - obj, a JS::HandleObject, prints as:
       obj=(JSObject * const) 0x7ffff150b060 [object global] delegate,
   This immediately shows the handle's referent, along with a JavaScript-like summary
   of the object.
 
 - id, a JS::HandleId, prints as:
       id=$jsid("x"),
   We show the handle's referent, and print the identifier as a string.
 
 - vp, a JS::MutableHandleValue, prints as:
-      vp=$jsval(1)
-  We show the handle's referent, using the jsval's tag to print it in its
-  JavaScript form.
+      vp=$JS::Int32Value(1)
+  We show the handle's referent, using the JS::Value's tag to print it noting
+  its particular internal type and value.
 
 You can still see the raw form of a value with 'print/r':
 
     (gdb) p/r obj
     $1 = {<js::HandleBase<JSObject*>> = {<No data fields>}, ptr = 0x7fffffffca60}
     (gdb)
 
 You can also use GDB's 'disable pretty-printer' command to turn off
@@ -51,23 +51,23 @@ In general, pretty-printers for pointer 
 pointer's referent:
 
     (gdb) b math_atan2
     Breakpoint 1 at 0x542e0a: file /home/jimb/moz/archer/js/src/jsmath.cpp, line 214.
     (gdb) run
     js> Math.atan2('Spleen', 42)
     Breakpoint 1, math_atan2 (cx=0xbf3440, argc=2, vp=0x7ffff172f0a0)
     (gdb) print vp[0]
-    $1 = $jsval((JSObject *) 0x7ffff151c0c0 [object Function "atan2"])
+    $1 = $JS::Value((JSObject *) 0x7ffff151c0c0 [object Function "atan2"])
     (gdb) print vp[1]
-    $2 = $jsval((JSObject *) 0x7ffff150d0a0 [object Math])
+    $2 = $JS::Value((JSObject *) 0x7ffff150d0a0 [object Math])
     (gdb) print vp[2]
-    $3 = $jsval("Spleen")
+    $3 = $JS::Value("Spleen")
     (gdb) print vp[3]
-    $4 = $jsval(42)
+    $4 = $JS::Int32Value(42)
     (gdb)
 
 We used to also have pretty-printers for the actual contents of a JSString
 struct, that knew which union branches were live and which were dead. These were
 more fragile than the summary pretty-printers, and harder to test, so I've
 removed them until we can see how to do better.
 
 There are unit tests; see 'Running the unit tests', below.
--- a/js/src/gdb/moz.build
+++ b/js/src/gdb/moz.build
@@ -34,21 +34,16 @@ LOCAL_INCLUDES += [
     '!..',
     '..',
 ]
 
 USE_LIBS += [
     'static:js',
 ]
 
-if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-    # The ICU libraries linked into libmozjs will not include the ICU data,
-    # so link it directly.
-    USE_LIBS += ['icudata']
-
 OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     CXXFLAGS += ['-Wno-shadow', '-fno-strict-aliasing']
 
 # This is intended as a temporary workaround to enable VS2015.
 if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
     CXXFLAGS += ['-wd4312']
--- a/js/src/gdb/mozilla/jsval.py
+++ b/js/src/gdb/mozilla/jsval.py
@@ -1,19 +1,19 @@
-# Pretty-printers for SpiderMonkey jsvals.
+# Pretty-printers for SpiderMonkey's JS::Value.
 
 import gdb
 import gdb.types
 import mozilla.prettyprinters
 from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer
 
 # Forget any printers from previous loads of this module.
 mozilla.prettyprinters.clear_module_printers(__name__)
 
-# Summary of the JS::Value (also known as jsval) type:
+# Summary of the JS::Value type:
 #
 # Viewed abstractly, JS::Value is a 64-bit discriminated union, with
 # JSString *, JSObject *, IEEE 64-bit floating-point, and 32-bit integer
 # branches (and a few others). (It is not actually a C++ union;
 # 'discriminated union' just describes the overall effect.) Note that
 # JS::Value is always 64 bits long, even on 32-bit architectures.
 #
 # The ECMAScript standard specifies that ECMAScript numbers are IEEE 64-bit
@@ -64,63 +64,38 @@ mozilla.prettyprinters.clear_module_prin
 #   JSString; and so on.
 #
 # On the only 64-bit platform we support, x86_64, only the lower 48 bits of
 # an address are significant, and only those values whose top bit is zero
 # are used for user-space addresses. This means that x86_64 addresses are
 # effectively 47 bits long, and thus fit nicely in the available portion of
 # the fraction field.
 #
-#
-# In detail:
-#
-# - jsval (Value.h) is a typedef for JS::Value.
-#
-# - JS::Value (Value.h) is a class with a lot of methods and a single data
-#   member, of type jsval_layout.
-#
-# - jsval_layout (Value.h) is a helper type for picking apart values. This
-#   is always 64 bits long, with a variant for each address size (32 bits
-#   or 64 bits) and endianness (little- or big-endian).
-#
-#   jsval_layout is a union with 'asBits', 'asDouble', and 'asPtr'
-#   branches, and an 's' branch, which is a struct that tries to break out
-#   the bitfields a little for the non-double types. On 64-bit machines,
-#   jsval_layout also has an 'asUIntPtr' branch.
-#
-#   On 32-bit platforms, the 's' structure has a 'tag' member at the
-#   exponent end of the 's' struct, and a 'payload' union at the mantissa
-#   end. The 'payload' union's branches are things like JSString *,
-#   JSObject *, and so on: the natural representations of the tags.
-#
-#   On 64-bit platforms, the payload is 47 bits long; since C++ doesn't let
-#   us declare bitfields that hold unions, we can't break it down so
-#   neatly. In this case, we apply bit-shifting tricks to the 'asBits'
-#   branch of the union to extract the tag.
+# See Value.h for full details.
 
 class Box(object):
     def __init__(self, asBits, jtc):
         self.asBits = asBits
         self.jtc = jtc
-        # jsval_layout::asBits is uint64, but somebody botches the sign bit, even
-        # though Python integers are arbitrary precision.
+        # Value::layout::asBits is uint64_t, but somehow the sign bit can be
+        # botched here, even though Python integers are arbitrary precision.
         if self.asBits < 0:
             self.asBits = self.asBits + (1 << 64)
 
     # Return this value's type tag.
     def tag(self): raise NotImplementedError
 
     # Return this value as a 32-bit integer, double, or address.
     def as_uint32(self): raise NotImplementedError
     def as_double(self): raise NotImplementedError
     def as_address(self): raise NotImplementedError
 
-# Packed non-number boxing --- the format used on x86_64. It would be nice to simply
-# call JSVAL_TO_INT, etc. here, but the debugger is likely to see many jsvals, and
-# doing several inferior calls for each one seems like a bad idea.
+# Packed non-number boxing --- the format used on x86_64. It would be nice to
+# simply call Value::toInt32, etc. here, but the debugger is likely to see many
+# Values, and doing several inferior calls for each one seems like a bad idea.
 class Punbox(Box):
 
     FULL_WIDTH     = 64
     TAG_SHIFT      = 47
     PAYLOAD_MASK   = (1 << TAG_SHIFT) - 1
     TAG_MASK       = (1 << (FULL_WIDTH - TAG_SHIFT)) - 1
     TAG_MAX_DOUBLE = 0x1fff0
     TAG_TYPE_MASK  = 0x0000f
@@ -145,78 +120,88 @@ class Nunbox(Box):
         tag = self.asBits >> Nunbox.TAG_SHIFT
         if tag < Nunbox.TAG_CLEAR:
             return self.jtc.DOUBLE
         return tag & Nunbox.TAG_TYPE_MASK
 
     def as_uint32(self): return int(self.asBits & Nunbox.PAYLOAD_MASK)
     def as_address(self): return gdb.Value(self.asBits & Nunbox.PAYLOAD_MASK)
 
-# Cache information about the jsval type for this objfile.
-class jsvalTypeCache(object):
+# Cache information about the Value type for this objfile.
+class JSValueTypeCache(object):
     def __init__(self, cache):
         # Capture the tag values.
         d = gdb.types.make_enum_dict(gdb.lookup_type('JSValueType'))
-        self.DOUBLE    = d['JSVAL_TYPE_DOUBLE']
-        self.INT32     = d['JSVAL_TYPE_INT32']
-        self.UNDEFINED = d['JSVAL_TYPE_UNDEFINED']
-        self.BOOLEAN   = d['JSVAL_TYPE_BOOLEAN']
-        self.MAGIC     = d['JSVAL_TYPE_MAGIC']
-        self.STRING    = d['JSVAL_TYPE_STRING']
-        self.SYMBOL    = d['JSVAL_TYPE_SYMBOL']
-        self.NULL      = d['JSVAL_TYPE_NULL']
-        self.OBJECT    = d['JSVAL_TYPE_OBJECT']
+
+        # The enum keys are prefixed when building with some compilers (clang at
+        # a minimum), so use a helper function to handle either key format.
+        def get(key):
+            val = d.get(key)
+            if val is not None:
+                return val
+            return d['JSValueType::' + key]
+
+        self.DOUBLE = get('JSVAL_TYPE_DOUBLE')
+        self.INT32 = get('JSVAL_TYPE_INT32')
+        self.UNDEFINED = get('JSVAL_TYPE_UNDEFINED')
+        self.BOOLEAN = get('JSVAL_TYPE_BOOLEAN')
+        self.MAGIC = get('JSVAL_TYPE_MAGIC')
+        self.STRING = get('JSVAL_TYPE_STRING')
+        self.SYMBOL = get('JSVAL_TYPE_SYMBOL')
+        self.NULL = get('JSVAL_TYPE_NULL')
+        self.OBJECT = get('JSVAL_TYPE_OBJECT')
 
         # Let self.magic_names be an array whose i'th element is the name of
         # the i'th magic value.
         d = gdb.types.make_enum_dict(gdb.lookup_type('JSWhyMagic'))
         self.magic_names = list(range(max(d.values()) + 1))
         for (k,v) in d.items(): self.magic_names[v] = k
 
         # Choose an unboxing scheme for this architecture.
         self.boxer = Punbox if cache.void_ptr_t.sizeof == 8 else Nunbox
 
-@pretty_printer('jsval_layout')
-class jsval_layout(object):
+@pretty_printer('JS::Value')
+class JSValue(object):
     def __init__(self, value, cache):
         # Save the generic typecache, and create our own, if we haven't already.
         self.cache = cache
-        if not cache.mod_jsval:
-            cache.mod_jsval = jsvalTypeCache(cache)
-        self.jtc = cache.mod_jsval
+        if not cache.mod_JS_Value:
+            cache.mod_JS_Value = JSValueTypeCache(cache)
+        self.jtc = cache.mod_JS_Value
 
-        self.value = value
-        self.box = self.jtc.boxer(value['asBits'], self.jtc)
+        data = value['data']
+        self.data = data
+        self.box = self.jtc.boxer(data['asBits'], self.jtc)
 
     def to_string(self):
         tag = self.box.tag()
+
+        if tag == self.jtc.UNDEFINED:
+            return '$JS::UndefinedValue()'
+        if tag == self.jtc.NULL:
+            return '$JS::NullValue()'
+        if tag == self.jtc.BOOLEAN:
+            return '$JS::BooleanValue(%s)' % str(self.box.as_uint32() != 0).lower()
+        if tag == self.jtc.MAGIC:
+            value = self.box.as_uint32()
+            if 0 <= value and value < len(self.jtc.magic_names):
+                return '$JS::MagicValue(%s)' % (self.jtc.magic_names[value],)
+            else:
+                return '$JS::MagicValue(%d)' % (value,)
+
         if tag == self.jtc.INT32:
             value = self.box.as_uint32()
             signbit = 1 << 31
             value = (value ^ signbit) - signbit
-        elif tag == self.jtc.UNDEFINED:
-            return 'JSVAL_VOID'
-        elif tag == self.jtc.BOOLEAN:
-            return 'JSVAL_TRUE' if self.box.as_uint32() else 'JSVAL_FALSE'
-        elif tag == self.jtc.MAGIC:
-            value = self.box.as_uint32()
-            if 0 <= value and value < len(self.jtc.magic_names):
-                return '$jsmagic(%s)' % (self.jtc.magic_names[value],)
-            else:
-                return '$jsmagic(%d)' % (value,)
-        elif tag == self.jtc.STRING:
+            return '$JS::Int32Value(%s)' % value
+
+        if tag == self.jtc.DOUBLE:
+            return '$JS::DoubleValue(%s)' % self.data['asDouble']
+
+        if tag == self.jtc.STRING:
             value = self.box.as_address().cast(self.cache.JSString_ptr_t)
+        elif tag == self.jtc.OBJECT:
+            value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
         elif tag == self.jtc.SYMBOL:
             value = self.box.as_address().cast(self.cache.JSSymbol_ptr_t)
-        elif tag == self.jtc.NULL:
-            return 'JSVAL_NULL'
-        elif tag == self.jtc.OBJECT:
-            value = self.box.as_address().cast(self.cache.JSObject_ptr_t)
-        elif tag == self.jtc.DOUBLE:
-            value = self.value['asDouble']
         else:
-            return '$jsval(unrecognized!)'
-        return '$jsval(%s)' % (value,)
-
-@pretty_printer('JS::Value')
-class JSValue(object):
-    def __new__(cls, value, cache):
-        return jsval_layout(value['data'], cache)
+            value = 'unrecognized!'
+        return '$JS::Value(%s)' % (value,)
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -178,17 +178,17 @@ class TypeCache(object):
             self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
         except gdb.error:
             raise NotSpiderMonkeyObjfileError
 
         self.mod_GCCellPtr = None
         self.mod_Interpreter = None
         self.mod_JSObject = None
         self.mod_JSString = None
-        self.mod_jsval = None
+        self.mod_JS_Value = None
         self.mod_ExecutableAllocator = None
         self.mod_IonGraph = None
 
 # Yield a series of all the types that |t| implements, by following typedefs
 # and iterating over base classes. Specifically:
 # - |t| itself is the first value yielded.
 # - If we yield a typedef, we later yield its definition.
 # - If we yield a type with base classes, we later yield those base classes.
--- a/js/src/gdb/run-tests.py
+++ b/js/src/gdb/run-tests.py
@@ -99,17 +99,17 @@ class Summary(object):
         self.update()
 
     def finish(self):
         if not OPTIONS.hide_progress:
             self.bar.finish()
 
         if self.failures:
 
-            print "tests failed:"
+            print("tests failed:")
             for test in self.failures:
                 test.show(sys.stdout)
 
             if OPTIONS.worklist:
                 try:
                     with open(OPTIONS.worklist) as out:
                         for test in self.failures:
                             out.write(test.name + '\n')
@@ -124,17 +124,17 @@ class Summary(object):
                         for test in self.failures:
                             test.show(out)
                 except IOError as err:
                     sys.stderr.write("Error writing worklist file '%s': %s"
                                      % (OPTIONS.write_failures, err))
                     sys.exit(1)
 
         if self.timeouts:
-            print "tests timed out:"
+            print("tests timed out:")
             for test in self.timeouts:
                 test.show(sys.stdout)
 
         if self.failures or self.timeouts:
             sys.exit(2)
 
 class Test(TaskPool.Task):
     def __init__(self, path, summary):
@@ -151,19 +151,19 @@ class Test(TaskPool.Task):
         self.stderr = ''
         self.returncode = None
 
     def cmd(self):
         testlibdir = os.path.normpath(os.path.join(OPTIONS.testdir, '..', 'lib-for-tests'))
         return [OPTIONS.gdb_executable,
                 '-nw',          # Don't create a window (unnecessary?)
                 '-nx',          # Don't read .gdbinit.
-                '--ex', 'add-auto-load-safe-path %s' % (OPTIONS.builddir,),
-                '--ex', 'set env LD_LIBRARY_PATH %s' % os.path.join(OPTIONS.objdir, 'js', 'src'),
-                '--ex', 'file %s' % (os.path.join(OPTIONS.builddir, 'gdb-tests'),),
+                '--ex', 'add-auto-load-safe-path %s' % (OPTIONS.bindir,),
+                '--ex', 'set env LD_LIBRARY_PATH %s' % (OPTIONS.bindir,),
+                '--ex', 'file %s' % (os.path.join(OPTIONS.bindir, 'gdb-tests'),),
                 '--eval-command', 'python testlibdir=%r' % (testlibdir,),
                 '--eval-command', 'python testscript=%r' % (self.test_path,),
                 '--eval-command', 'python exec(open(%r).read())' % os.path.join(testlibdir, 'catcher.py')]
 
     def start(self, pipe, deadline):
         super(Test, self).start(pipe, deadline)
         if OPTIONS.show_cmd:
             self.summary.interleave_output(lambda: self.show_cmd(sys.stdout))
@@ -182,48 +182,48 @@ class Test(TaskPool.Task):
             self.summary.failed(self)
         else:
             self.summary.passed(self)
 
     def onTimeout(self):
         self.summary.timeout(self)
 
     def show_cmd(self, out):
-        print "Command: ", make_shell_cmd(self.cmd())
+        out.write("Command: %s\n" % (make_shell_cmd(self.cmd()),))
 
     def show_output(self, out):
         if self.stdout:
             out.write('Standard output:')
             out.write('\n' + self.stdout + '\n')
         if self.stderr:
             out.write('Standard error:')
             out.write('\n' + self.stderr + '\n')
 
     def show(self, out):
         out.write(self.name + '\n')
         if OPTIONS.write_failure_output:
-            out.write('Command: %s\n' % (make_shell_cmd(self.cmd()),))
+            self.show_cmd(out)
             self.show_output(out)
             out.write('GDB exit code: %r\n' % (self.returncode,))
 
 def find_tests(dir, substring = None):
     ans = []
-    for dirpath, dirnames, filenames in os.walk(dir):
+    for dirpath, _, filenames in os.walk(dir):
         if dirpath == '.':
             continue
         for filename in filenames:
             if not filename.endswith('.py'):
                 continue
             test = os.path.join(dirpath, filename)
             if substring is None or substring in os.path.relpath(test, dir):
                 ans.append(test)
     return ans
 
 def build_test_exec(builddir):
-    p = subprocess.check_call(['make', 'gdb-tests'], cwd=builddir)
+    subprocess.check_call(['make'], cwd=builddir)
 
 def run_tests(tests, summary):
     pool = TaskPool(tests, job_limit=OPTIONS.workercount, timeout=OPTIONS.timeout)
     pool.run_all()
 
 OPTIONS = None
 def main(argv):
     global OPTIONS
@@ -261,67 +261,72 @@ def main(argv):
     op.add_option('--gdb', dest='gdb_executable', metavar='EXECUTABLE', default='gdb',
                   help='Run tests with [EXECUTABLE], rather than plain \'gdb\'.')
     op.add_option('--srcdir', dest='srcdir',
                   default=os.path.abspath(os.path.join(script_dir, '..')),
                   help='Use SpiderMonkey sources in [SRCDIR].')
     op.add_option('--testdir', dest='testdir', default=os.path.join(script_dir, 'tests'),
                   help='Find tests in [TESTDIR].')
     op.add_option('--builddir', dest='builddir',
-                  help='Build test executable in [BUILDDIR].')
+                  help='Build test executable from [BUILDDIR].')
+    op.add_option('--bindir', dest='bindir',
+                  help='Run test executable from [BINDIR].')
     (OPTIONS, args) = op.parse_args(argv)
     if len(args) < 1:
         op.error('missing OBJDIR argument')
     OPTIONS.objdir = os.path.abspath(args[0])
 
     test_args = args[1:]
 
     if not OPTIONS.workercount:
         OPTIONS.workercount = get_cpu_count()
 
-    # Compute default for OPTIONS.builddir now, since we've computed OPTIONS.objdir.
+    # Compute defaults for OPTIONS.builddir and OPTIONS.bindir now, since we've
+    # computed OPTIONS.objdir.
     if not OPTIONS.builddir:
         OPTIONS.builddir = os.path.join(OPTIONS.objdir, 'js', 'src', 'gdb')
+    if not OPTIONS.bindir:
+        OPTIONS.bindir = os.path.join(OPTIONS.objdir, 'dist', 'bin')
 
     test_set = set()
 
     # All the various sources of test names accumulate.
     if test_args:
         for arg in test_args:
             test_set.update(find_tests(OPTIONS.testdir, arg))
     if OPTIONS.worklist:
         try:
             with open(OPTIONS.worklist) as f:
                 for line in f:
-                    test_set.update(os.path.join(test_dir, line.strip('\n')))
+                    test_set.update(os.path.join(OPTIONS.testdir, line.strip('\n')))
         except IOError:
             # With worklist, a missing file means to start the process with
             # the complete list of tests.
             sys.stderr.write("Couldn't read worklist file '%s'; running all tests\n"
                              % (OPTIONS.worklist,))
             test_set = set(find_tests(OPTIONS.testdir))
     if OPTIONS.read_tests:
         try:
             with open(OPTIONS.read_tests) as f:
                 for line in f:
-                    test_set.update(os.path.join(test_dir, line.strip('\n')))
+                    test_set.update(os.path.join(OPTIONS.testdir, line.strip('\n')))
         except IOError as err:
             sys.stderr.write("Error trying to read test file '%s': %s\n"
                              % (OPTIONS.read_tests, err))
             sys.exit(1)
 
     # If none of the above options were passed, and no tests were listed
     # explicitly, use the complete set.
     if not test_args and not OPTIONS.worklist and not OPTIONS.read_tests:
         test_set = set(find_tests(OPTIONS.testdir))
 
     if OPTIONS.exclude:
         exclude_set = set()
         for exclude in OPTIONS.exclude:
-            exclude_set.update(find_tests(test_dir, exclude))
+            exclude_set.update(find_tests(OPTIONS.testdir, exclude))
         test_set -= exclude_set
 
     if not test_set:
         sys.stderr.write("No tests found matching command line arguments.\n")
         sys.exit(1)
 
     summary = Summary(len(test_set))
     test_list = [ Test(_, summary) for _ in sorted(test_set) ]
--- a/js/src/gdb/taskpool.py
+++ b/js/src/gdb/taskpool.py
@@ -58,24 +58,17 @@ class TaskPool(object):
     class TerminateTask(Exception):
         pass
 
     def __init__(self, tasks, cwd='.', job_limit=4, timeout=150):
         self.pending = iter(tasks)
         self.cwd = cwd
         self.job_limit = job_limit
         self.timeout = timeout
-        self.next_pending = self.get_next_pending()
-
-    # Set self.next_pending to the next task that has not yet been executed.
-    def get_next_pending(self):
-        try:
-            return self.pending.next()
-        except StopIteration:
-            return None
+        self.next_pending = next(self.pending, None)
 
     def run_all(self):
         # The currently running tasks: a set of Task instances.
         running = set()
         with open(os.devnull, 'r') as devnull:
             while True:
                 while len(running) < self.job_limit and self.next_pending:
                     t = self.next_pending
@@ -87,17 +80,17 @@ class TaskPool(object):
                     # the post-'select' code below for details.
                     flags = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
                     fcntl.fcntl(p.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
                     flags = fcntl.fcntl(p.stderr, fcntl.F_GETFL)
                     fcntl.fcntl(p.stderr, fcntl.F_SETFL, flags | os.O_NONBLOCK)
 
                     t.start(p, time.time() + self.timeout)
                     running.add(t)
-                    self.next_pending = self.get_next_pending()
+                    self.next_pending = next(self.pending, None)
 
                 # If we have no tasks running, and the above wasn't able to
                 # start any new ones, then we must be done!
                 if not running:
                     break
 
                 # How many seconds do we have until the earliest deadline?
                 now = time.time()
@@ -113,26 +106,26 @@ class TaskPool(object):
                     # Since we've placed the pipes in non-blocking mode, these
                     # 'read's will simply return as many bytes as are available,
                     # rather than blocking until they have accumulated the full
                     # amount requested (or reached EOF). The 'read's should
                     # never throw, since 'select' has told us there was
                     # something available.
                     if t.pipe.stdout in readable:
                         output = t.pipe.stdout.read(16384)
-                        if output != "":
+                        if len(output):
                             try:
-                                t.onStdout(output)
+                                t.onStdout(output.decode('utf-8'))
                             except TerminateTask:
                                 terminate.add(t)
                     if t.pipe.stderr in readable:
                         output = t.pipe.stderr.read(16384)
-                        if output != "":
+                        if len(output):
                             try:
-                                t.onStderr(output)
+                                t.onStderr(output.decode('utf-8'))
                             except TerminateTask:
                                 terminate.add(t)
                         else:
                             # We assume that, once a task has closed its stderr,
                             # it will soon terminate. If a task closes its
                             # stderr and then hangs, we'll hang too, here.
                             t.pipe.wait()
                             t.onFinished(t.pipe.returncode)
@@ -198,22 +191,22 @@ if __name__ == '__main__':
             def __init__(self, n):
                 super(SortableTask, self).__init__()
                 self.n = n
             def start(self, pipe, deadline):
                 super(SortableTask, self).start(pipe, deadline)
             def cmd(self):
                 return ['sh', '-c', 'echo out; sleep %d; echo err>&2' % (self.n,)]
             def onStdout(self, text):
-                print '%d stdout: %r' % (self.n, text)
+                print('%d stdout: %r' % (self.n, text))
             def onStderr(self, text):
-                print '%d stderr: %r' % (self.n, text)
+                print('%d stderr: %r' % (self.n, text))
             def onFinished(self, returncode):
-                print '%d (rc=%d)' % (self.n, returncode)
+                print('%d (rc=%d)' % (self.n, returncode))
                 sorted.append(self.n)
             def onTimeout(self):
-                print '%d timed out' % (self.n,)
+                print('%d timed out' % (self.n,))
 
         p = TaskPool([SortableTask(_) for _ in ns], job_limit=len(ns), timeout=timeout)
         p.run_all()
         return sorted
 
-    print repr(sleep_sort([1,1,2,3,5,8,13,21,34], 15))
+    print(repr(sleep_sort([1,1,2,3,5,8,13,21,34], 15)))
--- a/js/src/gdb/tests/test-Root.py
+++ b/js/src/gdb/tests/test-Root.py
@@ -9,19 +9,19 @@ run_fragment('Root.handle')
 
 assert_pretty('obj', '(JSObject * const)  [object global] delegate')
 assert_pretty('mutableObj', '(JSObject *)  [object global] delegate')
 
 run_fragment('Root.HeapSlot')
 
 # This depends on implementation details of arrays, but since HeapSlot is
 # not a public type, I'm not sure how to avoid doing *something* ugly.
-assert_pretty('((js::NativeObject *) array.ptr)->elements_[0]', '$jsval("plinth")')
+assert_pretty('((js::NativeObject *) array.ptr)->elements_[0]', '$JS::Value("plinth")')
 
 run_fragment('Root.barriers');
 
 assert_pretty('prebarriered', '(JSObject *)  [object Object]');
 assert_pretty('heapptr', '(JSObject *)  [object Object]');
 assert_pretty('relocatable', '(JSObject *)  [object Object]');
-assert_pretty('val', '$jsval((JSObject *)  [object Object])');
-assert_pretty('heapValue', '$jsval((JSObject *)  [object Object])');
-assert_pretty('prebarrieredValue', '$jsval((JSObject *)  [object Object])');
-assert_pretty('relocatableValue', '$jsval((JSObject *)  [object Object])');
+assert_pretty('val', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('heapValue', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('prebarrieredValue', '$JS::Value((JSObject *)  [object Object])');
+assert_pretty('relocatableValue', '$JS::Value((JSObject *)  [object Object])');
--- a/js/src/gdb/tests/test-asmjs.py
+++ b/js/src/gdb/tests/test-asmjs.py
@@ -7,9 +7,9 @@
 run_fragment('asmjs.segfault')
 
 # If SIGSEGV handling is broken, GDB would have stopped at the SIGSEGV signal.
 # The breakpoint would not have hit, and run_fragment would have thrown.
 #
 # So if we get here, and the asm.js code actually ran, we win.
 
 assert_pretty('ok', 'true')
-assert_pretty('rval', '$jsval("ok")')
+assert_pretty('rval', '$JS::Value("ok")')
--- a/js/src/gdb/tests/test-jsval.cpp
+++ b/js/src/gdb/tests/test-jsval.cpp
@@ -1,15 +1,16 @@
 #include "gdb-tests.h"
 #include "jsapi.h"
 
 FRAGMENT(jsval, simple) {
   using namespace JS;
 
   RootedValue fortytwo(cx, Int32Value(42));
+  RootedValue fortytwoD(cx, DoubleValue(42));
   RootedValue negone(cx, Int32Value(-1));
   RootedValue undefined(cx, UndefinedValue());
   RootedValue null(cx, NullValue());
   RootedValue js_true(cx, BooleanValue(true));
   RootedValue js_false(cx, BooleanValue(false));
   RootedValue elements_hole(cx, js::MagicValue(JS_ELEMENTS_HOLE));
 
   RootedValue empty_string(cx);
@@ -22,16 +23,17 @@ FRAGMENT(jsval, simple) {
   global.setObject(*CurrentGlobalOrNull(cx));
 
   // Some interesting value that floating-point won't munge.
   RootedValue onehundredthirtysevenonehundredtwentyeighths(cx, DoubleValue(137.0 / 128.0));
 
   breakpoint();
 
   use(fortytwo);
+  use(fortytwoD);
   use(negone);
   use(undefined);
   use(js_true);
   use(js_false);
   use(null);
   use(elements_hole);
   use(empty_string);
   use(friendly_string);
--- a/js/src/gdb/tests/test-jsval.py
+++ b/js/src/gdb/tests/test-jsval.py
@@ -1,18 +1,19 @@
 # Basic unit tests for jsval pretty-printer.
 
 assert_subprinter_registered('SpiderMonkey', 'JS::Value')
 
 run_fragment('jsval.simple')
 
-assert_pretty('fortytwo', '$jsval(42)')
-assert_pretty('negone', '$jsval(-1)')
-assert_pretty('undefined', 'JSVAL_VOID')
-assert_pretty('null', 'JSVAL_NULL')
-assert_pretty('js_true', 'JSVAL_TRUE')
-assert_pretty('js_false', 'JSVAL_FALSE')
-assert_pretty('elements_hole', '$jsmagic(JS_ELEMENTS_HOLE)')
-assert_pretty('empty_string', '$jsval("")')
-assert_pretty('friendly_string', '$jsval("Hello!")')
-assert_pretty('symbol', '$jsval(Symbol.for("Hello!"))')
-assert_pretty('global', '$jsval((JSObject *)  [object global] delegate)')
-assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$jsval(1.0703125)')
+assert_pretty('fortytwo', '$JS::Int32Value(42)')
+assert_pretty('fortytwoD', '$JS::DoubleValue(42)')
+assert_pretty('negone', '$JS::Int32Value(-1)')
+assert_pretty('undefined', '$JS::UndefinedValue()')
+assert_pretty('null', '$JS::NullValue()')
+assert_pretty('js_true', '$JS::BooleanValue(true)')
+assert_pretty('js_false', '$JS::BooleanValue(false)')
+assert_pretty('elements_hole', '$JS::MagicValue(JS_ELEMENTS_HOLE)')
+assert_pretty('empty_string', '$JS::Value("")')
+assert_pretty('friendly_string', '$JS::Value("Hello!")')
+assert_pretty('symbol', '$JS::Value(Symbol.for("Hello!"))')
+assert_pretty('global', '$JS::Value((JSObject *)  [object global] delegate)')
+assert_pretty('onehundredthirtysevenonehundredtwentyeighths', '$JS::DoubleValue(1.0703125)')
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1439180.js
@@ -0,0 +1,9 @@
+h = function f(x) {
+        x = +"NaN";
+        return /I/ (~x);
+    }
+for (var j = 0; j < 3; j++) {
+    try {
+        h();
+    } catch (e) {}
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/bug1448136.js
@@ -0,0 +1,23 @@
+print = function(s) { return s.toString(); }
+assertEq = function(a,b) {
+  try { print(a); print(b); } catch(exc) {}
+}
+g = newGlobal();
+g.parent = this;
+g.eval("(" + function() {
+  Debugger(parent).onExceptionUnwind = function(frame) {
+    frame.older
+  }
+} + ")()")
+function a() {};
+function b() {};
+for (let _ of Array(100))
+  assertEq(b instanceof a, true);
+function c(){};
+function d(){};
+function e(){};
+Object.defineProperty(a, Symbol.hasInstance, {value: assertEq });
+let funcs = [a, b, c, d];
+for (let f of funcs)
+  assertEq(e instanceof f, true);
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/unaryarith.js
@@ -0,0 +1,29 @@
+setJitCompilerOption('ion.forceinlineCaches', 1);
+
+function warmup(fun, input_array, output_array) {
+    assertEq(output_array.length, input_array.length);
+    for (var index = 0; index < input_array.length; index++) {
+        input = input_array[index];
+        output = output_array[index];
+        for (var i = 0; i < 30; i++) {
+            var y = fun(input);
+            assertEq(y, output)
+        }
+    }
+}
+
+var fun1 = (x) => { return -x; }
+var fun2 = (x) => { return -x; }
+
+var fun3 = (x) => { return ~x; }
+var fun4 = (x) => { return ~x; }
+
+warmup(fun1, [1, 2], [-1, -2]);
+warmup(fun2, [0], [-0]);
+
+warmup(fun2, [3, 4], [-3, -4]);
+warmup(fun1, [1.2, 1.4], [-1.2, -1.4]);
+
+warmup(fun3, [-1, 0], [0, -1]);
+warmup(fun4, [-1.0, 0.0, 1.2, 3], [0, -1, -2, -4]);
+
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2130,16 +2130,17 @@ BaselineCacheIRCompiler::init(CacheKind 
     switch (kind) {
       case CacheKind::GetIntrinsic:
         MOZ_ASSERT(numInputs == 0);
         break;
       case CacheKind::GetProp:
       case CacheKind::TypeOf:
       case CacheKind::GetIterator:
       case CacheKind::ToBool:
+      case CacheKind::UnaryArith:
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, R0);
         break;
       case CacheKind::Compare:
       case CacheKind::GetElem:
       case CacheKind::GetPropSuper:
       case CacheKind::SetProp:
       case CacheKind::In:
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1921,17 +1921,17 @@ BaselineCompiler::emitBinaryArith()
 
 bool
 BaselineCompiler::emitUnaryArith()
 {
     // Keep top stack value in R0.
     frame.popRegsAndSync(1);
 
     // Call IC
-    ICUnaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::Baseline);
+    ICUnaryArith_Fallback::Compiler stubCompiler(cx);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
         return false;
 
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4141,33 +4141,40 @@ TryAttachInstanceOfStub(JSContext* cx, B
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     return true;
 }
 
 static bool
-DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
+DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub_,
                      HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICInstanceOf_Fallback*> stub(ICStubEngine::Baseline, frame, stub_);
+
     FallbackICSpew(cx, stub, "InstanceOf");
 
     if (!rhs.isObject()) {
         ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, nullptr);
         return false;
     }
 
     RootedObject obj(cx, &rhs.toObject());
     bool cond = false;
     if (!HasInstance(cx, obj, lhs, &cond))
         return false;
 
     res.setBoolean(cond);
 
+    // Check if debug mode toggling made the stub invalid.
+    if (stub.invalid())
+        return true;
+
     if (!obj->is<JSFunction>()) {
         stub->noteUnoptimizableAccess();
         return true;
     }
 
     // For functions, keep track of the |prototype| property in type information,
     // for use during Ion compilation.
     EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
@@ -4527,10 +4534,94 @@ ICRest_Fallback::Compiler::generateStubC
     EmitRestoreTailCallReg(masm);
 
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoRestFallbackInfo, masm);
 }
 
+//
+// UnaryArith_Fallback
+//
+
+static bool
+DoUnaryArithFallback(JSContext* cx, BaselineFrame* frame, ICUnaryArith_Fallback* stub,
+                     HandleValue val, MutableHandleValue res)
+{
+    // This fallback stub may trigger debug mode toggling.
+    DebugModeOSRVolatileStub<ICUnaryArith_Fallback*> debug_stub(ICStubEngine::Baseline, frame, stub);
+
+    RootedScript script(cx, frame->script());
+    jsbytecode* pc = stub->icEntry()->pc(script);
+    JSOp op = JSOp(*pc);
+    FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
+
+    switch (op) {
+      case JSOP_BITNOT: {
+        int32_t result;
+        if (!BitNot(cx, val, &result))
+            return false;
+        res.setInt32(result);
+        break;
+      }
+      case JSOP_NEG:
+        if (!NegOperation(cx, val, res))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("Unexpected op");
+    }
+
+    // Check if debug mode toggling made the stub invalid.
+    if (debug_stub.invalid())
+        return true;
+
+    if (res.isDouble())
+        stub->setSawDoubleResult();
+
+    if (stub->state().maybeTransition())
+        stub->discardStubs(cx);
+
+    if (stub->state().canAttachStub()) {
+        UnaryArithIRGenerator gen(cx, script, pc, stub->state().mode(),
+                                    op, val, res);
+        if (gen.tryAttachStub()) {
+            bool attached = false;
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        BaselineCacheIRStubKind::Regular,
+                                                        ICStubEngine::Baseline, script, stub, &attached);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached (shared) CacheIR stub for %s", CodeName[op]);
+            }
+        }
+    }
+
+    return true;
+}
+
+typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
+                                       HandleValue, MutableHandleValue);
+static const VMFunction DoUnaryArithFallbackInfo =
+    FunctionInfo<DoUnaryArithFallbackFn>(DoUnaryArithFallback, "DoUnaryArithFallback", TailCall,
+                                         PopValues(1));
+
+bool
+ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    MOZ_ASSERT(R0 == JSReturnOperand);
+
+    // Restore the tail call register.
+    EmitRestoreTailCallReg(masm);
+
+    // Ensure stack is fully synced for the expression decompiler.
+    masm.pushValue(R0);
+
+    // Push arguments.
+    masm.pushValue(R0);
+    masm.push(ICStubReg);
+    pushStubPayload(masm, R0.scratchReg());
+
+    return tailCallVM(DoUnaryArithFallbackInfo, masm);
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1454,16 +1454,54 @@ class ICRetSub_Resume : public ICStub
         { }
 
         ICStub* getStub(ICStubSpace* space) override {
             return newStub<ICRetSub_Resume>(space, getStubCode(), pcOffset_, addr_);
         }
     };
 };
 
+// UnaryArith
+//     JSOP_BITNOT
+//     JSOP_NEG
+
+class ICUnaryArith_Fallback : public ICFallbackStub
+{
+    friend class ICStubSpace;
+
+    explicit ICUnaryArith_Fallback(JitCode* stubCode)
+      : ICFallbackStub(UnaryArith_Fallback, stubCode)
+    {
+        extra_ = 0;
+    }
+
+  public:
+    bool sawDoubleResult() {
+        return extra_;
+    }
+    void setSawDoubleResult() {
+        extra_ = 1;
+    }
+
+    // Compiler for this stub kind.
+    class Compiler : public ICStubCompiler {
+      protected:
+        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
+
+      public:
+        explicit Compiler(JSContext* cx)
+          : ICStubCompiler(cx, ICStub::UnaryArith_Fallback, Engine::Baseline)
+        {}
+
+        ICStub* getStub(ICStubSpace* space) override {
+            return newStub<ICUnaryArith_Fallback>(space, getStubCode());
+        }
+    };
+};
+
 inline bool
 IsCacheableDOMProxy(JSObject* obj)
 {
     if (!obj->is<ProxyObject>())
         return false;
 
     const BaseProxyHandler* handler = obj->as<ProxyObject>().handler();
     return handler->family() == GetDOMProxyHandlerFamily();
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -29,16 +29,18 @@ namespace jit {
     _(NewArray_Fallback)                         \
     _(NewObject_Fallback)                        \
     _(NewObject_WithTemplate)                    \
                                                  \
     _(ToBool_Fallback)                           \
                                                  \
     _(ToNumber_Fallback)                         \
                                                  \
+    _(UnaryArith_Fallback)                       \
+                                                 \
     _(Call_Fallback)                             \
     _(Call_Scripted)                             \
     _(Call_AnyScripted)                          \
     _(Call_Native)                               \
     _(Call_ClassHook)                            \
     _(Call_ScriptedApplyArray)                   \
     _(Call_ScriptedApplyArguments)               \
     _(Call_ScriptedFunCall)                      \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -329,21 +329,19 @@ BaselineInspector::expectedResultType(js
         return MIRType::None;
 
     switch (stub->kind()) {
       case ICStub::BinaryArith_Int32:
         if (stub->toBinaryArith_Int32()->allowDouble())
             return MIRType::Double;
         return MIRType::Int32;
       case ICStub::BinaryArith_BooleanWithInt32:
-      case ICStub::UnaryArith_Int32:
       case ICStub::BinaryArith_DoubleWithInt32:
         return MIRType::Int32;
       case ICStub::BinaryArith_Double:
-      case ICStub::UnaryArith_Double:
         return MIRType::Double;
       case ICStub::BinaryArith_StringConcat:
       case ICStub::BinaryArith_StringObjectConcat:
         return MIRType::String;
       default:
         return MIRType::None;
     }
 }
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4834,8 +4834,90 @@ GetIntrinsicIRGenerator::trackAttached(c
 bool
 GetIntrinsicIRGenerator::tryAttachStub()
 {
     writer.loadValueResult(val_);
     writer.returnFromIC();
     trackAttached("GetIntrinsic");
     return true;
 }
+UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
+                                             JSOp op, HandleValue val, HandleValue res)
+  : IRGenerator(cx, script, pc, CacheKind::UnaryArith, mode),
+    op_(op),
+    val_(val),
+    res_(res)
+{ }
+
+void
+UnaryArithIRGenerator::trackAttached(const char* name)
+{
+#ifdef JS_CACHEIR_SPEW
+    if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
+        sp.valueProperty("val", val_);
+    }
+#endif
+}
+
+bool
+UnaryArithIRGenerator::tryAttachStub()
+{
+    if (tryAttachInt32())
+        return true;
+    if (tryAttachNumber())
+        return true;
+
+    trackAttached(IRGenerator::NotAttached);
+    return false;
+}
+
+bool
+UnaryArithIRGenerator::tryAttachInt32()
+{
+    if (!val_.isInt32() || !res_.isInt32())
+        return false;
+
+    ValOperandId valId(writer.setInputOperandId(0));
+
+    Int32OperandId intId = writer.guardIsInt32(valId);
+    switch (op_) {
+      case JSOP_BITNOT:
+        writer.int32NotResult(intId);
+        trackAttached("UnaryArith.Int32Not");
+        break;
+      case JSOP_NEG:
+        writer.int32NegationResult(intId);
+        trackAttached("UnaryArith.Int32Neg");
+        break;
+      default:
+        MOZ_CRASH("Unexected OP");
+    }
+
+    writer.returnFromIC();
+    return true;
+}
+
+bool
+UnaryArithIRGenerator::tryAttachNumber()
+{
+    if (!val_.isNumber() || !res_.isNumber() || !cx_->runtime()->jitSupportsFloatingPoint)
+        return false;
+
+    ValOperandId valId(writer.setInputOperandId(0));
+    writer.guardType(valId, JSVAL_TYPE_DOUBLE);
+    Int32OperandId truncatedId;
+    switch (op_) {
+      case JSOP_BITNOT:
+        truncatedId = writer.truncateDoubleToUInt32(valId);
+        writer.int32NotResult(truncatedId);
+        trackAttached("UnaryArith.DoubleNot");
+        break;
+      case JSOP_NEG:
+        writer.doubleNegationResult(valId);
+        trackAttached("UnaryArith.DoubleNeg");
+        break;
+      default:
+        MOZ_CRASH("Unexpected OP");
+    }
+
+    writer.returnFromIC();
+    return true;
+}
\ No newline at end of file
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -158,17 +158,18 @@ class TypedOperandId : public OperandId
     _(BindName)             \
     _(In)                   \
     _(HasOwn)               \
     _(TypeOf)               \
     _(InstanceOf)           \
     _(GetIterator)          \
     _(Compare)              \
     _(ToBool)               \
-    _(Call)
+    _(Call)                 \
+    _(UnaryArith)
 
 enum class CacheKind : uint8_t
 {
 #define DEFINE_KIND(kind) kind,
     CACHE_IR_KINDS(DEFINE_KIND)
 #undef DEFINE_KIND
 };
 
@@ -176,16 +177,17 @@ extern const char* CacheKindNames[];
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsObjectOrNull)                \
     _(GuardIsNullOrUndefined)             \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsNumber)                      \
+    _(GuardIsInt32)                       \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         /* Guard an object class, per GuardClassKind */ \
     _(GuardAnyClass)                      /* Guard an arbitrary class for an object */ \
     _(GuardCompartment)                   \
@@ -214,16 +216,18 @@ extern const char* CacheKindNames[];
     _(GuardFunctionPrototype)             \
     _(LoadStackValue)                     \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
                                           \
+    _(TruncateDoubleToUInt32)             \
+                                          \
     _(MegamorphicLoadSlotResult)          \
     _(MegamorphicLoadSlotByValueResult)   \
     _(MegamorphicStoreSlot)               \
     _(MegamorphicSetElement)              \
     _(MegamorphicHasPropResult)           \
                                           \
     /* See CacheIR.cpp 'DOM proxies' comment. */ \
     _(LoadDOMExpandoValue)                \
@@ -279,16 +283,19 @@ extern const char* CacheKindNames[];
     _(CallProxyGetByValueResult)          \
     _(CallProxyHasPropResult)             \
     _(CallObjectHasSparseElementResult)   \
     _(LoadUndefinedResult)                \
     _(LoadBooleanResult)                  \
     _(LoadStringResult)                   \
     _(LoadInstanceOfObjectResult)         \
     _(LoadTypeOfObjectResult)             \
+    _(Int32NotResult)                     \
+    _(Int32NegationResult)                \
+    _(DoubleNegationResult)               \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
     _(LoadValueResult)                    \
                                           \
     _(CallStringSplitResult)              \
                                           \
@@ -530,16 +537,22 @@ class MOZ_RAII CacheIRWriter : public JS
     StringOperandId guardIsString(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsString, val);
         return StringOperandId(val.id());
     }
     SymbolOperandId guardIsSymbol(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsSymbol, val);
         return SymbolOperandId(val.id());
     }
+    Int32OperandId guardIsInt32(ValOperandId val) {
+        Int32OperandId res(nextOperandId_++);
+        writeOpWithOperandId(CacheOp::GuardIsInt32, val);
+        writeOperandId(res);
+        return res;
+    }
     Int32OperandId guardIsInt32Index(ValOperandId val) {
         Int32OperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::GuardIsInt32Index, val);
         writeOperandId(res);
         return res;
     }
     void guardIsNumber(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsNumber, val);
@@ -760,16 +773,23 @@ class MOZ_RAII CacheIRWriter : public JS
 
     ObjOperandId loadWrapperTarget(ObjOperandId obj) {
         ObjOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::LoadWrapperTarget, obj);
         writeOperandId(res);
         return res;
     }
 
+    Int32OperandId truncateDoubleToUInt32(ValOperandId val) {
+        Int32OperandId res(nextOperandId_++);
+        writeOpWithOperandId(CacheOp::TruncateDoubleToUInt32, val);
+        writeOperandId(res);
+        return res;
+    }
+
     ValueTagOperandId loadValueTag(ValOperandId val) {
         ValueTagOperandId res(nextOperandId_++);
         writeOpWithOperandId(CacheOp::LoadValueTag, val);
         writeOperandId(res);
         return res;
     }
 
     ValOperandId loadDOMExpandoValue(ObjOperandId obj) {
@@ -951,16 +971,25 @@ class MOZ_RAII CacheIRWriter : public JS
         buffer_.writeByte(uint32_t(strict));
     }
     void megamorphicHasPropResult(ObjOperandId obj, ValOperandId id, bool hasOwn) {
         writeOpWithOperandId(CacheOp::MegamorphicHasPropResult, obj);
         writeOperandId(id);
         buffer_.writeByte(uint32_t(hasOwn));
     }
 
+    void int32NotResult(Int32OperandId id) {
+        writeOpWithOperandId(CacheOp::Int32NotResult, id);
+    }
+    void int32NegationResult(Int32OperandId id) {
+        writeOpWithOperandId(CacheOp::Int32NegationResult, id);
+    }
+    void doubleNegationResult(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::DoubleNegationResult, val);
+    }
     void loadBooleanResult(bool val) {
         writeOp(CacheOp::LoadBooleanResult);
         buffer_.writeByte(uint32_t(val));
     }
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadStringResult(JSString* str) {
@@ -1707,12 +1736,30 @@ class MOZ_RAII GetIntrinsicIRGenerator :
 
   public:
     GetIntrinsicIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
                             HandleValue val);
 
     bool tryAttachStub();
 };
 
+class MOZ_RAII UnaryArithIRGenerator : public IRGenerator
+{
+    JSOp op_;
+    HandleValue val_;
+    HandleValue res_;
+
+    bool tryAttachInt32();
+    bool tryAttachNumber();
+
+    void trackAttached(const char* name);
+
+  public:
+    UnaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
+                          JSOp op, HandleValue val, HandleValue res);
+
+    bool tryAttachStub();
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -12,16 +12,18 @@
 #include "builtin/Boolean-inl.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "vm/JSCompartment-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
+using mozilla::BitwiseCast;
+
 ValueOperand
 CacheRegisterAllocator::useValueRegister(MacroAssembler& masm, ValOperandId op)
 {
     OperandLocation& loc = operandLocations_[op.id()];
 
     switch (loc.kind()) {
       case OperandLocation::ValueReg:
         currentOpRegs_.add(loc.valueReg());
@@ -1305,16 +1307,39 @@ CacheIRCompiler::emitGuardIsSymbol()
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardIsInt32()
+{
+    ValOperandId inputId = reader.valOperandId();
+    Register output = allocator.defineRegister(masm, reader.int32OperandId());
+
+    if (allocator.knownType(inputId) == JSVAL_TYPE_INT32) {
+        Register input = allocator.useRegister(masm, Int32OperandId(inputId.id()));
+        masm.move32(input, output);
+        return true;
+    }
+    ValueOperand input = allocator.useValueRegister(masm, inputId);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Label notInt32, done;
+    masm.branchTestInt32(Assembler::NotEqual, input, failure->label());
+    masm.unboxInt32(input, output);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitGuardIsInt32Index()
 {
     ValOperandId inputId = reader.valOperandId();
     Register output = allocator.defineRegister(masm, reader.int32OperandId());
 
     if (allocator.knownType(inputId) == JSVAL_TYPE_INT32) {
         Register input = allocator.useRegister(masm, Int32OperandId(inputId.id()));
         masm.move32(input, output);
@@ -1790,16 +1815,111 @@ CacheIRCompiler::emitLoadInt32ArrayLengt
 
     // Guard length fits in an int32.
     masm.branchTest32(Assembler::Signed, scratch, scratch, failure->label());
     EmitStoreResult(masm, scratch, JSVAL_TYPE_INT32, output);
     return true;
 }
 
 bool
+CacheIRCompiler::emitInt32NegationResult()
+{
+    AutoOutputRegister output(*this);
+    Register val = allocator.useRegister(masm, reader.int32OperandId());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Guard against 0 and MIN_INT by checking if low 31-bits are all zero.
+    // Both of these result in a double.
+    masm.branchTest32(Assembler::Zero, val, Imm32(0x7fffffff), failure->label());
+    masm.neg32(val);
+    masm.tagValue(JSVAL_TYPE_INT32, val, output.valueReg());
+    return true;
+}
+
+bool
+CacheIRCompiler::emitInt32NotResult()
+{
+    AutoOutputRegister output(*this);
+    Register val = allocator.useRegister(masm, reader.int32OperandId());
+    masm.not32(val);
+    masm.tagValue(JSVAL_TYPE_INT32, val, output.valueReg());
+    return true;
+}
+
+bool
+CacheIRCompiler::emitDoubleNegationResult()
+{
+    AutoOutputRegister output(*this);
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // If we're compiling a Baseline IC, FloatReg0 is always available.
+    Label failurePopReg, done;
+    if (mode_ != Mode::Baseline)
+        masm.push(FloatReg0);
+
+    masm.ensureDouble(val, FloatReg0, (mode_ != Mode::Baseline) ? &failurePopReg : failure->label());
+    masm.negateDouble(FloatReg0);
+    masm.boxDouble(FloatReg0, output.valueReg(), FloatReg0);
+
+    if (mode_ != Mode::Baseline) {
+        masm.pop(FloatReg0);
+        masm.jump(&done);
+
+        masm.bind(&failurePopReg);
+        masm.pop(FloatReg0);
+        masm.jump(failure->label());
+    }
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+CacheIRCompiler::emitTruncateDoubleToUInt32()
+{
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+    Register res = allocator.defineRegister(masm, reader.int32OperandId());
+
+    Label doneTruncate,  truncateABICall;
+    if (mode_ != Mode::Baseline)
+        masm.push(FloatReg0);
+
+    masm.unboxDouble(val, FloatReg0);
+    masm.branchTruncateDoubleMaybeModUint32(FloatReg0, res, &truncateABICall);
+    masm.jump(&doneTruncate);
+
+    masm.bind(&truncateABICall);
+    LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
+    save.takeUnchecked(FloatReg0);
+    masm.PushRegsInMask(save);
+
+    masm.setupUnalignedABICall(res);
+    masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
+    masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
+                     MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
+    masm.storeCallInt32Result(res);
+
+    LiveRegisterSet ignore;
+    ignore.add(res);
+    masm.PopRegsInMaskIgnore(save, ignore);
+
+    masm.bind(&doneTruncate);
+    if (mode_ != Mode::Baseline)
+        masm.pop(FloatReg0);
+    return true;
+}
+
+bool
 CacheIRCompiler::emitLoadArgumentsObjectLengthResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -18,16 +18,17 @@ namespace jit {
 // BaselineCacheIRCompiler and IonCacheIRCompiler.
 #define CACHE_IR_SHARED_OPS(_)            \
     _(GuardIsObject)                      \
     _(GuardIsNullOrUndefined)             \
     _(GuardIsObjectOrNull)                \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
     _(GuardIsNumber)                      \
+    _(GuardIsInt32)                       \
     _(GuardIsInt32Index)                  \
     _(GuardType)                          \
     _(GuardClass)                         \
     _(GuardIsNativeFunction)              \
     _(GuardIsNativeObject)                \
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificInt32Immediate)        \
@@ -43,16 +44,20 @@ namespace jit {
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(LoadUndefinedResult)                \
     _(LoadBooleanResult)                  \
     _(LoadInt32ArrayLengthResult)         \
+    _(Int32NegationResult)                \
+    _(Int32NotResult)                     \
+    _(DoubleNegationResult)               \
+    _(TruncateDoubleToUInt32)             \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadFunctionLengthResult)           \
     _(LoadStringLengthResult)             \
     _(LoadStringCharResult)               \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadInstanceOfObjectResult)         \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
--- a/js/src/jit/CacheIRSpewer.cpp
+++ b/js/src/jit/CacheIRSpewer.cpp
@@ -50,24 +50,28 @@ CacheIRSpewer::~CacheIRSpewer()
 # elif defined(__ANDROID__)
 #  define JIT_SPEW_DIR "/data/local/tmp"
 # else
 #  define JIT_SPEW_DIR "/tmp"
 # endif
 #endif
 
 bool
-CacheIRSpewer::init()
+CacheIRSpewer::init(const char* filename)
 {
     if (enabled())
         return true;
 
     char name[256];
     uint32_t pid = getpid();
-    SprintfLiteral(name, JIT_SPEW_DIR "/cacheir%" PRIu32 ".json", pid);
+    // Default to JIT_SPEW_DIR/cacheir${pid}.json
+    if (filename[0] == '1')
+        SprintfLiteral(name, JIT_SPEW_DIR "/cacheir%" PRIu32 ".json", pid);
+    else
+        SprintfLiteral(name, "%s%" PRIu32 ".json", filename, pid);
 
     if (!output.init(name))
         return false;
     output.put("[");
 
     json.emplace(output);
     return true;
 }
--- a/js/src/jit/CacheIRSpewer.h
+++ b/js/src/jit/CacheIRSpewer.h
@@ -37,17 +37,17 @@ class CacheIRSpewer
 
     void beginCache(const IRGenerator& generator);
     void valueProperty(const char* name, const Value& v);
     void attached(const char* name);
     void endCache();
 
   public:
     static CacheIRSpewer& singleton() { return cacheIRspewer; }
-    bool init();
+    bool init(const char* name);
 
     class MOZ_RAII Guard {
         CacheIRSpewer& sp_;
         const IRGenerator& gen_;
         const char* name_;
 
       public:
         Guard(const IRGenerator& gen, const char* name)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -170,22 +170,26 @@ static const VMFunction IonBindNameICInf
 typedef JSObject* (*IonGetIteratorICFn)(JSContext*, HandleScript, IonGetIteratorIC*, HandleValue);
 static const VMFunction IonGetIteratorICInfo =
     FunctionInfo<IonGetIteratorICFn>(IonGetIteratorIC::update, "IonGetIteratorIC::update");
 
 typedef bool (*IonInICFn)(JSContext*, HandleScript, IonInIC*, HandleValue, HandleObject, bool*);
 static const VMFunction IonInICInfo =
     FunctionInfo<IonInICFn>(IonInIC::update, "IonInIC::update");
 
-
 typedef bool (*IonInstanceOfICFn)(JSContext*, HandleScript, IonInstanceOfIC*,
                          HandleValue lhs, HandleObject rhs, bool* res);
 static const VMFunction IonInstanceOfInfo =
     FunctionInfo<IonInstanceOfICFn>(IonInstanceOfIC::update, "IonInstanceOfIC::update");
 
+typedef bool (*IonUnaryArithICFn)(JSContext* cx, HandleScript outerScript, IonUnaryArithIC* stub,
+                                    HandleValue val, MutableHandleValue res);
+static const VMFunction IonUnaryArithICInfo =
+    FunctionInfo<IonUnaryArithICFn>(IonUnaryArithIC::update, "IonUnaryArithIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -352,16 +356,32 @@ CodeGenerator::visitOutOfLineICFallback(
         callVM(IonInstanceOfInfo, lir);
 
         StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
+      case CacheKind::UnaryArith: {
+        IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
+
+        saveLive(lir);
+
+        pushArg(unaryArithIC->input());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+        callVM(IonUnaryArithICInfo, lir);
+
+        StoreValueTo(unaryArithIC->output()).generate(this);
+        restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
+
+        masm.jump(ool->rejoin());
+        return;
+      }
       case CacheKind::Call:
       case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
     MOZ_CRASH();
@@ -2755,32 +2775,24 @@ CodeGenerator::visitBinarySharedStub(LBi
         emitSharedStub(ICStub::Kind::Compare_Fallback, lir);
         break;
       default:
         MOZ_CRASH("Unsupported jsop in shared stubs.");
     }
 }
 
 void
-CodeGenerator::visitUnarySharedStub(LUnarySharedStub* lir)
-{
-    JSOp jsop = JSOp(*lir->mir()->resumePoint()->pc());
-    switch (jsop) {
-      case JSOP_BITNOT:
-      case JSOP_NEG:
-        emitSharedStub(ICStub::Kind::UnaryArith_Fallback, lir);
-        break;
-      case JSOP_CALLPROP:
-      case JSOP_GETPROP:
-      case JSOP_LENGTH:
-        emitSharedStub(ICStub::Kind::GetProp_Fallback, lir);
-        break;
-      default:
-        MOZ_CRASH("Unsupported jsop in shared stubs.");
-    }
+CodeGenerator::visitUnaryCache(LUnaryCache* lir)
+{
+    LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
+    TypedOrValueRegister input = TypedOrValueRegister(ToValue(lir, LUnaryCache::Input));
+    ValueOperand output = ToOutValue(lir);
+
+    IonUnaryArithIC ic(liveRegs, input, output);
+    addIC(lir, allocateIC(ic));
 }
 
 void
 CodeGenerator::visitNullarySharedStub(LNullarySharedStub* lir)
 {
     jsbytecode* pc = lir->mir()->resumePoint()->pc();
     JSOp jsop = JSOp(*pc);
     switch (jsop) {
@@ -10185,21 +10197,16 @@ CodeGenerator::linkSharedStubs(JSContext
         ICStub *stub = nullptr;
 
         switch (sharedStubs_[i].kind) {
           case ICStub::Kind::BinaryArith_Fallback: {
             ICBinaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonSharedIC);
             stub = stubCompiler.getStub(&stubSpace_);
             break;
           }
-          case ICStub::Kind::UnaryArith_Fallback: {
-            ICUnaryArith_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonSharedIC);
-            stub = stubCompiler.getStub(&stubSpace_);
-            break;
-          }
           case ICStub::Kind::Compare_Fallback: {
             ICCompare_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonSharedIC);
             stub = stubCompiler.getStub(&stubSpace_);
             break;
           }
           case ICStub::Kind::GetProp_Fallback: {
             ICGetProp_Fallback::Compiler stubCompiler(cx, ICStubCompiler::Engine::IonSharedIC);
             stub = stubCompiler.getStub(&stubSpace_);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -180,18 +180,18 @@ jit::InitializeIon()
 {
     if (!TlsJitContext.init())
         return false;
 
     CheckLogging();
 
 #ifdef JS_CACHEIR_SPEW
     const char* env = getenv("CACHEIR_LOGS");
-    if (env && env[0])
-        CacheIRSpewer::singleton().init();
+    if (env && env[0] && env[0] != '0')
+        CacheIRSpewer::singleton().init(env);
 #endif
 
 #if defined(JS_CODEGEN_ARM)
     InitARMFlags();
 #endif
     CheckPerf();
     return true;
 }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3532,31 +3532,25 @@ IonBuilder::arithTrySharedStub(bool* emi
 
     if (JitOptions.disableSharedStubs)
         return Ok();
 
     // The actual jsop 'jsop_pos' is not supported yet.
     if (actualOp == JSOP_POS)
         return Ok();
 
-    // FIXME: The JSOP_BITNOT path doesn't track optimizations yet.
-    if (actualOp != JSOP_BITNOT) {
-        trackOptimizationAttempt(TrackedStrategy::BinaryArith_SharedCache);
-        trackOptimizationSuccess();
-    }
 
     MInstruction* stub = nullptr;
     switch (actualOp) {
       case JSOP_NEG:
       case JSOP_BITNOT:
         MOZ_ASSERT_IF(op == JSOP_MUL,
                       left->maybeConstantValue() && left->maybeConstantValue()->toInt32() == -1);
         MOZ_ASSERT_IF(op != JSOP_MUL, !left);
-
-        stub = MUnarySharedStub::New(alloc(), right);
+        stub = MUnaryCache::New(alloc(), right);
         break;
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
       case JSOP_POW:
         stub = MBinarySharedStub::New(alloc(), left, right);
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -539,16 +539,29 @@ IonCacheIRCompiler::init()
         outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Boolean, AnyRegister(output)));
 
         MOZ_ASSERT(numInputs == 2);
         allocator.initInputLocation(0, ic->lhs());
         allocator.initInputLocation(1, TypedOrValueRegister(MIRType::Object,
                                                             AnyRegister(ic->rhs())));
         break;
       }
+      case CacheKind::UnaryArith: {
+        IonUnaryArithIC *ic = ic_->asUnaryArithIC();
+        ValueOperand output = ic->output();
+
+        available.add(output);
+
+        liveRegs_.emplace(ic->liveRegs());
+        outputUnchecked_.emplace(TypedOrValueRegister(output));
+
+        MOZ_ASSERT(numInputs == 1);
+        allocator.initInputLocation(0, ic->input());
+        break;
+      }
       case CacheKind::Call:
       case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
 
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -51,16 +51,18 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::In:
         return asInIC()->temp();
       case CacheKind::HasOwn:
         return asHasOwnIC()->output();
       case CacheKind::GetIterator:
         return asGetIteratorIC()->temp1();
       case CacheKind::InstanceOf:
         return asInstanceOfIC()->output();
+      case CacheKind::UnaryArith:
+        return asUnaryArithIC()->output().scratchReg();
       case CacheKind::Call:
       case CacheKind::Compare:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
         MOZ_CRASH("Unsupported IC");
     }
 
@@ -496,16 +498,58 @@ IonInstanceOfIC::update(JSContext* cx, H
 
         if (!attached)
             ic->state().trackNotAttached();
     }
 
     return HasInstance(cx, rhs, lhs, res);
 }
 
+/*  static */  bool
+IonUnaryArithIC::update(JSContext* cx, HandleScript outerScript, IonUnaryArithIC* ic,
+                        HandleValue val, MutableHandleValue res)
+{
+    IonScript* ionScript = outerScript->ionScript();
+    RootedScript script(cx, ic->script());
+    jsbytecode* pc = ic->pc();
+    JSOp op = JSOp(*pc);
+
+    switch (op) {
+      case JSOP_BITNOT: {
+        int32_t result;
+        if (!BitNot(cx, val, &result))
+            return false;
+        res.setInt32(result);
+        break;
+      }
+      case JSOP_NEG:
+        if (!NegOperation(cx, val, res))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("Unexpected op");
+    }
+
+    if (ic->state().maybeTransition())
+        ic->discardStubs(cx->zone());
+
+    if (ic->state().canAttachStub()) {
+        bool attached = false;
+        UnaryArithIRGenerator gen(cx, script, pc, ic->state().mode(), op, val, res);
+
+        if (gen.tryAttachStub())
+             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+        if (!attached)
+            ic->state().trackNotAttached();
+    }
+
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -60,16 +60,17 @@ class IonGetPropertyIC;
 class IonSetPropertyIC;
 class IonGetPropSuperIC;
 class IonGetNameIC;
 class IonBindNameIC;
 class IonGetIteratorIC;
 class IonHasOwnIC;
 class IonInIC;
 class IonInstanceOfIC;
+class IonUnaryArithIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
 
     // The first optimized stub, or nullptr.
@@ -167,16 +168,20 @@ class IonIC
     IonInIC* asInIC() {
         MOZ_ASSERT(kind_ == CacheKind::In);
         return (IonInIC*)this;
     }
     IonInstanceOfIC* asInstanceOfIC() {
         MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
         return (IonInstanceOfIC*)this;
     }
+    IonUnaryArithIC* asUnaryArithIC() {
+        MOZ_ASSERT(kind_ == CacheKind::UnaryArith);
+        return (IonUnaryArithIC*)this;
+    }
 
     void updateBaseAddress(JitCode* code);
 
     // Returns the Register to use as scratch when entering IC stubs. This
     // should either be an output register or a temp.
     Register scratchRegisterForEntryJump();
 
     void trace(JSTracer* trc);
@@ -470,12 +475,36 @@ class IonInstanceOfIC : public IonIC
     Register rhs() const { return rhs_; }
     Register output() const { return output_; }
 
     // This signature mimics that of TryAttachInstanceOfStub in baseline
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonInstanceOfIC* ic,
                                     HandleValue lhs, HandleObject rhs, bool* attached);
 };
 
+class IonUnaryArithIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    TypedOrValueRegister input_;
+    ValueOperand output_;
+
+    public:
+
+    IonUnaryArithIC(LiveRegisterSet liveRegs, TypedOrValueRegister input,  ValueOperand output)
+      : IonIC(CacheKind::UnaryArith),
+        liveRegs_(liveRegs),
+        input_(input),
+        output_(output)
+    { }
+
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+    TypedOrValueRegister input() const { return input_; }
+    ValueOperand output() const { return output_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonUnaryArithIC* stub,
+                                    HandleValue val, MutableHandleValue res);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/JSJitFrameIter.cpp
+++ b/js/src/jit/JSJitFrameIter.cpp
@@ -211,18 +211,17 @@ JSJitFrameIter::machineState() const
     char* floatSpill = reinterpret_cast<char*>(spillAlign);
     FloatRegisterSet fregs = reader.allFloatSpills().set();
     fregs = fregs.reduceSetForPush();
     for (FloatRegisterBackwardIterator iter(fregs); iter.more(); ++iter) {
         floatSpill -= (*iter).size();
         for (uint32_t a = 0; a < (*iter).numAlignedAliased(); a++) {
             // Only say that registers that actually start here start here.
             // e.g. d0 should not start at s1, only at s0.
-            FloatRegister ftmp;
-            (*iter).alignedAliased(a, &ftmp);
+            FloatRegister ftmp = (*iter).alignedAliased(a);
             machine.setRegisterLocation(ftmp, (double*)floatSpill);
         }
     }
 
     return machine;
 }
 
 JitFrameLayout*
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2528,23 +2528,23 @@ LIRGenerator::visitBinarySharedStub(MBin
 
     LBinarySharedStub* lir = new(alloc()) LBinarySharedStub(useBoxFixedAtStart(lhs, R0),
                                                             useBoxFixedAtStart(rhs, R1));
     defineSharedStubReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
-LIRGenerator::visitUnarySharedStub(MUnarySharedStub* ins)
+LIRGenerator::visitUnaryCache(MUnaryCache* ins)
 {
     MDefinition* input = ins->getOperand(0);
     MOZ_ASSERT(ins->type() == MIRType::Value);
 
-    LUnarySharedStub* lir = new(alloc()) LUnarySharedStub(useBoxFixedAtStart(input, R0));
-    defineSharedStubReturn(lir, ins);
+    LUnaryCache* lir = new(alloc()) LUnaryCache(useBox(input));
+    defineBox(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitNullarySharedStub(MNullarySharedStub* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType::Value);
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8282,28 +8282,28 @@ class MBinarySharedStub
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(BinarySharedStub)
     TRIVIAL_NEW_WRAPPERS
 };
 
-class MUnarySharedStub
+class MUnaryCache
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
 {
-    explicit MUnarySharedStub(MDefinition* input)
+    explicit MUnaryCache(MDefinition* input)
       : MUnaryInstruction(classOpcode, input)
     {
         setResultType(MIRType::Value);
     }
 
   public:
-    INSTRUCTION_HEADER(UnarySharedStub)
+    INSTRUCTION_HEADER(UnaryCache)
     TRIVIAL_NEW_WRAPPERS
 };
 
 class MNullarySharedStub
   : public MNullaryInstruction
 {
     explicit MNullarySharedStub()
       : MNullaryInstruction(classOpcode)
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -66,25 +66,20 @@ struct AnyRegister {
         MOZ_ASSERT(isValid());
         return code_;
     }
     bool volatile_() const {
         return isFloat() ? fpu().volatile_() : gpr().volatile_();
     }
     AnyRegister aliased(uint32_t aliasIdx) const {
         AnyRegister ret;
-        if (isFloat()) {
-            FloatRegister fret;
-            fpu().aliased(aliasIdx, &fret);
-            ret = AnyRegister(fret);
-        } else {
-            Register gret;
-            gpr().aliased(aliasIdx, &gret);
-            ret = AnyRegister(gret);
-        }
+        if (isFloat())
+            ret = AnyRegister(fpu().aliased(aliasIdx));
+        else
+            ret = AnyRegister(gpr().aliased(aliasIdx));
         MOZ_ASSERT_IF(aliasIdx == 0, ret == *this);
         return ret;
     }
     uint32_t numAliased() const {
         if (isFloat())
             return fpu().numAliased();
         return gpr().numAliased();
     }
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -76,22 +76,19 @@ struct Register {
     }
     bool aliases(const Register& other) const {
         return reg_ == other.reg_;
     }
     uint32_t numAliased() const {
         return 1;
     }
 
-    // N.B. FloatRegister is an explicit outparam here because msvc-2010
-    // miscompiled it on win64 when the value was simply returned.  This
-    // now has an explicit outparam for compatability.
-    void aliased(uint32_t aliasIdx, Register* ret) const {
+    Register aliased(uint32_t aliasIdx) const {
         MOZ_ASSERT(aliasIdx == 0);
-        *ret = *this;
+        return *this;
     }
 
     SetType alignedOrDominatedAliasedSet() const {
         return SetType(1) << code();
     }
 
     static constexpr RegTypeName DefaultType = RegTypeName::GPR;
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1228,154 +1228,16 @@ ICBinaryArith_DoubleWithInt32::Compiler:
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
-// UnaryArith_Fallback
-//
-
-static bool
-DoUnaryArithFallback(JSContext* cx, void* payload, ICUnaryArith_Fallback* stub_,
-                     HandleValue val, MutableHandleValue res)
-{
-    SharedStubInfo info(cx, payload, stub_->icEntry());
-    ICStubCompiler::Engine engine = info.engine();
-
-    // This fallback stub may trigger debug mode toggling.
-    DebugModeOSRVolatileStub<ICUnaryArith_Fallback*> stub(engine, info.maybeFrame(), stub_);
-
-    jsbytecode* pc = info.pc();
-    JSOp op = JSOp(*pc);
-    FallbackICSpew(cx, stub, "UnaryArith(%s)", CodeName[op]);
-
-    switch (op) {
-      case JSOP_BITNOT: {
-        int32_t result;
-        if (!BitNot(cx, val, &result))
-            return false;
-        res.setInt32(result);
-        break;
-      }
-      case JSOP_NEG:
-        if (!NegOperation(cx, val, res))
-            return false;
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    // Check if debug mode toggling made the stub invalid.
-    if (stub.invalid())
-        return true;
-
-    if (res.isDouble())
-        stub->setSawDoubleResult();
-
-    if (stub->numOptimizedStubs() >= ICUnaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
-        // TODO: Discard/replace stubs.
-        return true;
-    }
-
-    if (val.isInt32() && res.isInt32()) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32 => Int32) stub", CodeName[op]);
-        ICUnaryArith_Int32::Compiler compiler(cx, op, engine);
-        ICStub* int32Stub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!int32Stub)
-            return false;
-        stub->addNewStub(int32Stub);
-        return true;
-    }
-
-    if (val.isNumber() && res.isNumber() && cx->runtime()->jitSupportsFloatingPoint) {
-        JitSpew(JitSpew_BaselineIC, "  Generating %s(Number => Number) stub", CodeName[op]);
-
-        // Unlink int32 stubs, the double stub handles both cases and TI specializes for both.
-        stub->unlinkStubsWithKind(cx, ICStub::UnaryArith_Int32);
-
-        ICUnaryArith_Double::Compiler compiler(cx, op, engine);
-        ICStub* doubleStub = compiler.getStub(compiler.getStubSpace(info.outerScript(cx)));
-        if (!doubleStub)
-            return false;
-        stub->addNewStub(doubleStub);
-        return true;
-    }
-
-    return true;
-}
-
-typedef bool (*DoUnaryArithFallbackFn)(JSContext*, void*, ICUnaryArith_Fallback*,
-                                       HandleValue, MutableHandleValue);
-static const VMFunction DoUnaryArithFallbackInfo =
-    FunctionInfo<DoUnaryArithFallbackFn>(DoUnaryArithFallback, "DoUnaryArithFallback", TailCall,
-                                         PopValues(1));
-
-bool
-ICUnaryArith_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(R0 == JSReturnOperand);
-
-    // Restore the tail call register.
-    EmitRestoreTailCallReg(masm);
-
-    // Ensure stack is fully synced for the expression decompiler.
-    masm.pushValue(R0);
-
-    // Push arguments.
-    masm.pushValue(R0);
-    masm.push(ICStubReg);
-    pushStubPayload(masm, R0.scratchReg());
-
-    return tailCallVM(DoUnaryArithFallbackInfo, masm);
-}
-
-bool
-ICUnaryArith_Double::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.ensureDouble(R0, FloatReg0, &failure);
-
-    MOZ_ASSERT(op == JSOP_NEG || op == JSOP_BITNOT);
-
-    if (op == JSOP_NEG) {
-        masm.negateDouble(FloatReg0);
-        masm.boxDouble(FloatReg0, R0, FloatReg0);
-    } else {
-        // Truncate the double to an int32.
-        Register scratchReg = R1.scratchReg();
-
-        Label doneTruncate;
-        Label truncateABICall;
-        masm.branchTruncateDoubleMaybeModUint32(FloatReg0, scratchReg, &truncateABICall);
-        masm.jump(&doneTruncate);
-
-        masm.bind(&truncateABICall);
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(FloatReg0, MoveOp::DOUBLE);
-        masm.callWithABI(BitwiseCast<void*, int32_t(*)(double)>(JS::ToInt32),
-                         MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther);
-        masm.storeCallInt32Result(scratchReg);
-
-        masm.bind(&doneTruncate);
-        masm.not32(scratchReg);
-        masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
-    }
-
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
 // Compare_Fallback
 //
 
 static bool
 DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, HandleValue lhs,
                   HandleValue rhs, MutableHandleValue ret)
 {
     SharedStubInfo info(cx, payload, stub_->icEntry());
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1909,104 +1909,16 @@ class ICBinaryArith_DoubleWithInt32 : pu
 
         ICStub* getStub(ICStubSpace* space) override {
             return newStub<ICBinaryArith_DoubleWithInt32>(space, getStubCode(),
                                                               lhsIsDouble_);
         }
     };
 };
 
-// UnaryArith
-//     JSOP_BITNOT
-//     JSOP_NEG
-
-class ICUnaryArith_Fallback : public ICFallbackStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Fallback(JitCode* stubCode)
-      : ICFallbackStub(UnaryArith_Fallback, stubCode)
-    {
-        extra_ = 0;
-    }
-
-  public:
-    static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
-    bool sawDoubleResult() {
-        return extra_;
-    }
-    void setSawDoubleResult() {
-        extra_ = 1;
-    }
-
-    // Compiler for this stub kind.
-    class Compiler : public ICStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        explicit Compiler(JSContext* cx, Engine engine)
-          : ICStubCompiler(cx, ICStub::UnaryArith_Fallback, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICUnaryArith_Fallback>(space, getStubCode());
-        }
-    };
-};
-
-class ICUnaryArith_Int32 : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Int32(JitCode* stubCode)
-      : ICStub(UnaryArith_Int32, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Int32, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICUnaryArith_Int32>(space, getStubCode());
-        }
-    };
-};
-
-class ICUnaryArith_Double : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICUnaryArith_Double(JitCode* stubCode)
-      : ICStub(UnaryArith_Double, stubCode)
-    {}
-
-  public:
-    class Compiler : public ICMultiStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        Compiler(JSContext* cx, JSOp op, Engine engine)
-          : ICMultiStubCompiler(cx, ICStub::UnaryArith_Double, op, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICUnaryArith_Double>(space, getStubCode());
-        }
-    };
-};
-
 // Compare
 //      JSOP_LT
 //      JSOP_LE
 //      JSOP_GT
 //      JSOP_GE
 //      JSOP_EQ
 //      JSOP_NE
 //      JSOP_STRICTEQ
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -15,20 +15,16 @@ namespace jit {
     _(BinaryArith_Fallback)                      \
     _(BinaryArith_Int32)                         \
     _(BinaryArith_Double)                        \
     _(BinaryArith_StringConcat)                  \
     _(BinaryArith_StringObjectConcat)            \
     _(BinaryArith_BooleanWithInt32)              \
     _(BinaryArith_DoubleWithInt32)               \
                                                  \
-    _(UnaryArith_Fallback)                       \
-    _(UnaryArith_Int32)                          \
-    _(UnaryArith_Double)                         \
-                                                 \
     _(Compare_Fallback)                          \
     _(Compare_Int32)                             \
     _(Compare_Double)                            \
     _(Compare_NumberWithUndefined)               \
     _(Compare_String)                            \
     _(Compare_Symbol)                            \
     _(Compare_Boolean)                           \
     _(Compare_Object)                            \
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -493,61 +493,52 @@ class VFPRegister
         if (isDouble()) {
             if (code_ < NumAliasedDoubles)
                 return 3;
             return 1;
         }
         return 2;
     }
 
-    // N.B. FloatRegister is an explicit outparam here because msvc-2010
-    // miscompiled it on win64 when the value was simply returned
-    void aliased(uint32_t aliasIdx, VFPRegister* ret) {
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+    VFPRegister aliased(uint32_t aliasIdx) {
+        if (aliasIdx == 0)
+            return *this;
         if (isDouble()) {
             MOZ_ASSERT(code_ < NumAliasedDoubles);
             MOZ_ASSERT(aliasIdx <= 2);
-            *ret = singleOverlay(aliasIdx - 1);
-            return;
+            return singleOverlay(aliasIdx - 1);
         }
         MOZ_ASSERT(aliasIdx == 1);
-        *ret = doubleOverlay(aliasIdx - 1);
+        return doubleOverlay(aliasIdx - 1);
     }
     uint32_t numAlignedAliased() const {
         if (isDouble()) {
             if (code_ < NumAliasedDoubles)
                 return 2;
             return 1;
         }
         // s1 has 0 other aligned aliases, 1 total.
         // s0 has 1 other aligned aliase, 2 total.
         return 2 - (code_ & 1);
     }
     // |   d0    |
     // | s0 | s1 |
     // If we've stored s0 and s1 in memory, we also want to say that d0 is
     // stored there, but it is only stored at the location where it is aligned
     // e.g. at s0, not s1.
-    void alignedAliased(uint32_t aliasIdx, VFPRegister* ret) {
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+    VFPRegister alignedAliased(uint32_t aliasIdx) {
+        if (aliasIdx == 0)
+            return *this;
         MOZ_ASSERT(aliasIdx == 1);
         if (isDouble()) {
             MOZ_ASSERT(code_ < NumAliasedDoubles);
-            *ret = singleOverlay(aliasIdx - 1);
-            return;
+            return singleOverlay(aliasIdx - 1);
         }
         MOZ_ASSERT((code_ & 1) == 0);
-        *ret = doubleOverlay(aliasIdx - 1);
-        return;
+        return doubleOverlay(aliasIdx - 1);
     }
 
     typedef FloatRegisters::SetType SetType;
 
     // This function is used to ensure that Register set can take all Single
     // registers, even if we are taking a mix of either double or single
     // registers.
     //
--- a/js/src/jit/arm/SharedIC-arm.cpp
+++ b/js/src/jit/arm/SharedIC-arm.cpp
@@ -181,38 +181,10 @@ ICBinaryArith_Int32::Compiler::generateS
 
     // Failure case - jump to next stub.
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.ma_mvn(R0.payloadReg(), R0.payloadReg());
-        break;
-      case JSOP_NEG:
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
-
-        // Compile -x as 0 - x.
-        masm.as_rsb(R0.payloadReg(), R0.payloadReg(), Imm8(0));
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm64/Architecture-arm64.h
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -378,39 +378,38 @@ struct FloatRegister
     uint32_t numAliased() const {
         return 2;
     }
     static FloatRegisters::Kind otherkind(FloatRegisters::Kind k) {
         if (k == FloatRegisters::Double)
             return FloatRegisters::Single;
         return FloatRegisters::Double;
     }
-    void aliased(uint32_t aliasIdx, FloatRegister* ret) {
+    FloatRegister aliased(uint32_t aliasIdx) {
         if (aliasIdx == 0)
-            *ret = *this;
-        else
-            *ret = FloatRegister(code_, otherkind(k_));
+            return *this;
+        return FloatRegister(code_, otherkind(k_));
     }
     // This function mostly exists for the ARM backend.  It is to ensure that two
     // floating point registers' types are equivalent.  e.g. S0 is not equivalent
     // to D16, since S0 holds a float32, and D16 holds a Double.
     // Since all floating point registers on x86 and x64 are equivalent, it is
     // reasonable for this function to do the same.
     bool equiv(FloatRegister other) const {
         return k_ == other.k_;
     }
     constexpr uint32_t size() const {
         return k_ == FloatRegisters::Double ? sizeof(double) : sizeof(float);
     }
     uint32_t numAlignedAliased() {
         return numAliased();
     }
-    void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
+    FloatRegister alignedAliased(uint32_t aliasIdx) {
         MOZ_ASSERT(aliasIdx < numAliased());
-        aliased(aliasIdx, ret);
+        return aliased(aliasIdx);
     }
     SetType alignedOrDominatedAliasedSet() const {
         return Codes::SpreadCoefficient << code_;
     }
 
     bool isSingle() const {
         return k_ == FloatRegisters::Single;
     }
--- a/js/src/jit/arm64/SharedIC-arm64.cpp
+++ b/js/src/jit/arm64/SharedIC-arm64.cpp
@@ -180,41 +180,10 @@ ICBinaryArith_Int32::Compiler::generateS
 
     // Failure case - jump to next stub.
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.Mvn(ARMRegister(R1.valueReg(), 32), ARMRegister(R0.valueReg(), 32));
-        masm.movePayload(R1.valueReg(), R0.valueReg());
-        break;
-      case JSOP_NEG:
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
-
-        // Compile -x as 0 - x.
-        masm.Sub(ARMRegister(R1.valueReg(), 32), wzr, ARMRegister(R0.valueReg(), 32));
-        masm.movePayload(R1.valueReg(), R0.valueReg());
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-
-}
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/mips32/Architecture-mips32.h
+++ b/js/src/jit/mips32/Architecture-mips32.h
@@ -183,47 +183,39 @@ class FloatRegister : public FloatRegist
     bool aliases(const FloatRegister& other) {
         MOZ_ASSERT(isNotOdd());
         return code_ == other.code_;
     }
     uint32_t numAliased() const {
         MOZ_ASSERT(isNotOdd());
         return 2;
     }
-    void aliased(uint32_t aliasIdx, FloatRegister* ret) {
+    FloatRegister aliased(uint32_t aliasIdx) {
         MOZ_ASSERT(isNotOdd());
 
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+        if (aliasIdx == 0)
+            return *this;
         MOZ_ASSERT(aliasIdx == 1);
-        if (isDouble()) {
-            *ret = singleOverlay();
-        } else {
-            *ret = doubleOverlay();
-        }
+        if (isDouble())
+            return singleOverlay();
+        return doubleOverlay();
     }
     uint32_t numAlignedAliased() const {
         MOZ_ASSERT(isNotOdd());
         return 2;
     }
-    void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
+    FloatRegister alignedAliased(uint32_t aliasIdx) {
         MOZ_ASSERT(isNotOdd());
 
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+        if (aliasIdx == 0)
+            return *this;
         MOZ_ASSERT(aliasIdx == 1);
-        if (isDouble()) {
-            *ret = singleOverlay();
-        } else {
-            *ret = doubleOverlay();
-        }
+        if (isDouble())
+            return singleOverlay();
+        return doubleOverlay();
     }
 
     SetType alignedOrDominatedAliasedSet() const {
         MOZ_ASSERT(isNotOdd());
         return (SetType(1) << (code_ >> 1)) * ((1 << FloatRegisters::TotalSingle) + 1);
     }
 
     static constexpr RegTypeName DefaultType = RegTypeName::Float64;
--- a/js/src/jit/mips32/SharedIC-mips32.cpp
+++ b/js/src/jit/mips32/SharedIC-mips32.cpp
@@ -138,39 +138,10 @@ ICBinaryArith_Int32::Compiler::generateS
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.not32(R0.payloadReg());
-        break;
-      case JSOP_NEG:
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(INT32_MAX), &failure);
-
-        masm.neg32(R0.payloadReg());
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-        return false;
-    }
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/mips64/Architecture-mips64.h
+++ b/js/src/jit/mips64/Architecture-mips64.h
@@ -166,38 +166,33 @@ class FloatRegister : public FloatRegist
         return kind_ != other.kind_ || reg_ != other.reg_;
     }
     bool aliases(const FloatRegister& other) {
         return reg_ == other.reg_;
     }
     uint32_t numAliased() const {
         return 2;
     }
-    void aliased(uint32_t aliasIdx, FloatRegister* ret) {
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+    FloatRegister aliased(uint32_t aliasIdx) {
+        if (aliasIdx == 0)
+            return *this;
         MOZ_ASSERT(aliasIdx == 1);
         if (isDouble())
-          *ret = singleOverlay();
-        else
-          *ret = doubleOverlay();
+            return singleOverlay();
+        return doubleOverlay();
     }
     uint32_t numAlignedAliased() const {
         return 2;
     }
-    void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) {
+    FloatRegister alignedAliased(uint32_t aliasIdx) {
         MOZ_ASSERT(isDouble());
-        if (aliasIdx == 0) {
-            *ret = *this;
-            return;
-        }
+        if (aliasIdx == 0)
+            return *this;
         MOZ_ASSERT(aliasIdx == 1);
-        *ret = singleOverlay();
+        return singleOverlay();
     }
 
     SetType alignedOrDominatedAliasedSet() const {
         return Codes::Spread << reg_;
     }
 
     static constexpr RegTypeName DefaultType = RegTypeName::Float64;
 
--- a/js/src/jit/mips64/SharedIC-mips64.cpp
+++ b/js/src/jit/mips64/SharedIC-mips64.cpp
@@ -149,42 +149,10 @@ ICBinaryArith_Int32::Compiler::generateS
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.not32(R0.valueReg());
-        masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
-        break;
-      case JSOP_NEG:
-        masm.unboxInt32(R0, ExtractTemp0);
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, ExtractTemp0, Imm32(INT32_MAX), &failure);
-
-        masm.neg32(ExtractTemp0);
-        masm.tagValue(JSVAL_TYPE_INT32, ExtractTemp0, R0);
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-        return false;
-    }
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -122,21 +122,21 @@ struct FloatRegister
     Code code() const { MOZ_CRASH(); }
     Encoding encoding() const { MOZ_CRASH(); }
     const char* name() const { MOZ_CRASH(); }
     bool volatile_() const { MOZ_CRASH(); }
     bool operator != (FloatRegister) const { MOZ_CRASH(); }
     bool operator == (FloatRegister) const { MOZ_CRASH(); }
     bool aliases(FloatRegister) const { MOZ_CRASH(); }
     uint32_t numAliased() const { MOZ_CRASH(); }
-    void aliased(uint32_t, FloatRegister*) { MOZ_CRASH(); }
+    FloatRegister aliased(uint32_t) { MOZ_CRASH(); }
     bool equiv(FloatRegister) const { MOZ_CRASH(); }
     uint32_t size() const { MOZ_CRASH(); }
     uint32_t numAlignedAliased() const { MOZ_CRASH(); }
-    void alignedAliased(uint32_t, FloatRegister*) { MOZ_CRASH(); }
+    FloatRegister alignedAliased(uint32_t) { MOZ_CRASH(); }
     SetType alignedOrDominatedAliasedSet() const { MOZ_CRASH(); }
 
     static constexpr RegTypeName DefaultType = RegTypeName::Float64;
 
     template <RegTypeName = DefaultType>
     static SetType LiveAsIndexableSet(SetType s) {
         return SetType(0);
     }
--- a/js/src/jit/none/Trampoline-none.cpp
+++ b/js/src/jit/none/Trampoline-none.cpp
@@ -40,9 +40,8 @@ BailoutFrameInfo::BailoutFrameInfo(const
 BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& iter, InvalidationBailoutStack* bailout)
 {
     MOZ_CRASH();
 }
 
 bool ICCompare_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
 bool ICCompare_Double::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
 bool ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
-bool ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler&) { MOZ_CRASH(); }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -5536,29 +5536,33 @@ class LBinarySharedStub : public LCallIn
     const MBinarySharedStub* mir() const {
         return mir_->toBinarySharedStub();
     }
 
     static const size_t LhsInput = 0;
     static const size_t RhsInput = BOX_PIECES;
 };
 
-class LUnarySharedStub : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
-{
-  public:
-    LIR_HEADER(UnarySharedStub)
-
-    explicit LUnarySharedStub(const LBoxAllocation& input)
-      : LCallInstructionHelper(classOpcode)
+class LUnaryCache : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(UnaryCache)
+
+    explicit LUnaryCache(const LBoxAllocation& input)
+      : LInstructionHelper(classOpcode)
     {
         setBoxOperand(Input, input);
     }
 
-    const MUnarySharedStub* mir() const {
-        return mir_->toUnarySharedStub();
+    const MUnaryCache* mir() const {
+        return mir_->toUnaryCache();
+    }
+
+    const LAllocation* input() {
+        return getOperand(Input);
     }
 
     static const size_t Input = 0;
 };
 
 class LNullarySharedStub : public LCallInstructionHelper<BOX_PIECES, 0, 0>
 {
   public:
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -245,17 +245,17 @@ LIRGeneratorShared::defineInt64(LInstruc
     add(lir);
 }
 
 void
 LIRGeneratorShared::defineSharedStubReturn(LInstruction* lir, MDefinition* mir)
 {
     lir->setMir(mir);
 
-    MOZ_ASSERT(lir->isBinarySharedStub() || lir->isUnarySharedStub() || lir->isNullarySharedStub());
+    MOZ_ASSERT(lir->isBinarySharedStub() || lir->isNullarySharedStub());
     MOZ_ASSERT(mir->type() == MIRType::Value);
 
     uint32_t vreg = getVirtualRegister();
 
 #if defined(JS_NUNBOX32)
     lir->setDef(TYPE_INDEX, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE,
                                         LGeneralReg(JSReturnReg_Type)));
     lir->setDef(PAYLOAD_INDEX, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD,
--- a/js/src/jit/x64/SharedIC-x64.cpp
+++ b/js/src/jit/x64/SharedIC-x64.cpp
@@ -197,38 +197,10 @@ ICBinaryArith_Int32::Compiler::generateS
     }
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.notl(R0.valueReg());
-        break;
-      case JSOP_NEG:
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff), &failure);
-        masm.negl(R0.valueReg());
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/x86-shared/Architecture-x86-shared.h
+++ b/js/src/jit/x86-shared/Architecture-x86-shared.h
@@ -414,24 +414,22 @@ struct FloatRegister {
 
     uint32_t numAliased() const {
         return Codes::NumTypes;
     }
     uint32_t numAlignedAliased() const {
         return numAliased();
     }
 
-    // N.B. FloatRegister is an explicit outparam here because msvc-2010
-    // miscompiled it on win64 when the value was simply returned
-    void aliased(uint32_t aliasIdx, FloatRegister* ret) const {
+    FloatRegister aliased(uint32_t aliasIdx) const {
         MOZ_ASSERT(aliasIdx < Codes::NumTypes);
-        *ret = FloatRegister(reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
+        return FloatRegister(reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes));
     }
-    void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) const {
-        aliased(aliasIdx, ret);
+    FloatRegister alignedAliased(uint32_t aliasIdx) const {
+        return aliased(aliasIdx);
     }
 
     SetType alignedOrDominatedAliasedSet() const {
         return Codes::Spread << reg_;
     }
 
     static constexpr RegTypeName DefaultType = RegTypeName::Float64;
 
--- a/js/src/jit/x86/SharedIC-x86.cpp
+++ b/js/src/jit/x86/SharedIC-x86.cpp
@@ -207,36 +207,10 @@ ICBinaryArith_Int32::Compiler::generateS
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
 
     return true;
 }
 
-bool
-ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
-
-    switch (op) {
-      case JSOP_BITNOT:
-        masm.notl(R0.payloadReg());
-        break;
-      case JSOP_NEG:
-        // Guard against 0 and MIN_INT, both result in a double.
-        masm.branchTest32(Assembler::Zero, R0.payloadReg(), Imm32(0x7fffffff), &failure);
-        masm.negl(R0.payloadReg());
-        break;
-      default:
-        MOZ_CRASH("Unexpected op");
-    }
-
-    EmitReturnFromIC(masm);
-
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 } // namespace jit
 } // namespace js
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -158,21 +158,16 @@ if CONFIG['JS_BUILD_BINAST'] and CONFIG[
 DEFINES['EXPORT_JS_API'] = True
 DEFINES['ENABLE_WASM_GLOBAL'] = True
 
 LOCAL_INCLUDES += [
     '!..',
     '..',
 ]
 
-if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-    # The ICU libraries linked into libmozjs will not include the ICU data,
-    # so link it directly.
-    USE_LIBS += ['icudata']
-
 USE_LIBS += [
     'static:js',
 ]
 
 if CONFIG['MOZ_NEEDS_LIBATOMIC']:
     OS_LIBS += ['atomic']
 
 OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
--- a/js/src/jsapi-tests/testJitRegisterSet.cpp
+++ b/js/src/jsapi-tests/testJitRegisterSet.cpp
@@ -174,18 +174,17 @@ BEGIN_TEST(testJitRegisterSet_FPU_Aliase
 {
     BEGIN_All_WALK(FloatRegisters::Total);
     FOR_ALL_REGISTERS(FloatRegister, reg) {
         AllocatableFloatRegisterSet pool;
         pool.add(reg);
 
         uint32_t alias_bits = 0;
         for (uint32_t i = 0; i < reg.numAlignedAliased(); i++) {
-            FloatRegister alias;
-            reg.alignedAliased(i, &alias);
+            FloatRegister alias = reg.alignedAliased(i);
 
             if (alias.isSingle()) {
                 if (alias_bits <= 32)
                     alias_bits = 32;
             } else if (alias.isDouble()) {
                 if (alias_bits <= 64)
                     alias_bits = 64;
             } else if (alias.isSimd128()) {
--- a/js/src/shell/moz.build
+++ b/js/src/shell/moz.build
@@ -32,21 +32,16 @@ if CONFIG['CC_TYPE'] in ('msvc', 'clang-
 LOCAL_INCLUDES += [
     '!..',
     '..',
 ]
 
 OS_LIBS += CONFIG['EDITLINE_LIBS']
 OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
-if CONFIG['ENABLE_INTL_API'] and CONFIG['MOZ_ICU_DATA_ARCHIVE']:
-    # The ICU libraries linked into libmozjs will not include the ICU data,
-    # so link it directly.
-    USE_LIBS += ['icudata']
-
 # Prepare module loader JS code for embedding
 GENERATED_FILES += [('shellmoduleloader.out.h', 'shellmoduleloader.js')]
 shellmoduleloader = GENERATED_FILES[('shellmoduleloader.out.h', 'shellmoduleloader.js')]
 shellmoduleloader.script = '../builtin/embedjs.py:generate_shellmoduleloader'
 shellmoduleloader.inputs = [
     '../js.msg',
     'ModuleLoader.js',
 ]
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -22,16 +22,17 @@
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMemoryReporter.h"
 #include "nsIObserverService.h"
 #include "nsIDebug2.h"
 #include "nsIDocShell.h"
 #include "nsIRunnable.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
+#include "nsWindowSizes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "nsContentUtils.h"
 #include "nsCCUncollectableMarker.h"
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -25,16 +25,17 @@
 #include "nsError.h"
 #include "nsAutoPtr.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "ChildIterator.h"
 
 #include "GeckoProfiler.h"
 #include "nsIStatefulFrame.h"
 #include "nsContainerFrame.h"
+#include "nsWindowSizes.h"
 
 #include "mozilla/MemoryReporting.h"
 
 // #define DEBUG_UNDISPLAYED_MAP
 // #define DEBUG_DISPLAY_CONTENTS_MAP
 
 using namespace mozilla;
 using namespace mozilla::dom;
--- a/layout/base/nsPresArena.cpp
+++ b/layout/base/nsPresArena.cpp
@@ -198,31 +198,16 @@ nsPresArena::AddSizeOfExcludingThis(nsWi
         break;
 #define ABSTRACT_FRAME_ID(...)
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
       case eArenaObjectID_nsLineBox:
         aSizes.mArenaSizes.mLineBoxes += totalSize;
         break;
-      case eArenaObjectID_nsRuleNode:
-        aSizes.mArenaSizes.mRuleNodes += totalSize;
-        break;
-      case eArenaObjectID_GeckoComputedStyle:
-        aSizes.mArenaSizes.mComputedStyles += totalSize;
-        break;
-#define STYLE_STRUCT(name_) \
-      case eArenaObjectID_nsStyle##name_: \
-        aSizes.mArenaSizes.mGeckoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \
-          totalSize; \
-        break;
-#define STYLE_STRUCT_LIST_IGNORE_VARIABLES
-#include "nsStyleStructList.h"
-#undef STYLE_STRUCT
-#undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
       default:
         continue;
     }
 
     totalSizeInFreeLists += totalSize;
   }
 
   aSizes.mLayoutPresShellSize += mallocSize - totalSizeInFreeLists;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -89,16 +89,17 @@
 #include "gfxContext.h"
 #include "gfxPrefs.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "StickyScrollContainer.h"
 #include "nsFontInflationData.h"
 #include "nsRegion.h"
 #include "nsIFrameInlines.h"
 #include "nsStyleChangeList.h"
+#include "nsWindowSizes.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -44,16 +44,17 @@ static void
 MarkFramesWithItemsAndImagesModified(nsDisplayList* aList)
 {
   for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) {
     if (!i->HasDeletedFrame() && i->CanBeReused() && !i->Frame()->IsFrameModified()) {
       // If we have existing cached geometry for this item, then check that for
       // whether we need to invalidate for a sync decode. If we don't, then
       // use the item's flags.
       DisplayItemData* data = FrameLayerBuilder::GetOldDataFor(i);
+      //XXX: handle webrender case
       bool invalidate = false;
       if (data &&
           data->GetGeometry()) {
         invalidate = data->GetGeometry()->InvalidateForSyncDecodeImages();
       } else if (!(i->GetFlags() & TYPE_RENDERS_NO_IMAGES)) {
         invalidate = true;
       }
 
--- a/layout/painting/nsDisplayItemTypesList.h
+++ b/layout/painting/nsDisplayItemTypesList.h
@@ -57,17 +57,17 @@ DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR, T
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR_REGION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(MASK, 0)
 DECLARE_DISPLAY_ITEM_TYPE(FILTER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_OUTER_SVG, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_GEOMETRY, 0)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_TEXT, 0)
 DECLARE_DISPLAY_ITEM_TYPE(SVG_CHAR_CLIP, TYPE_RENDERS_NO_IMAGES)
-DECLARE_DISPLAY_ITEM_TYPE(SVG_WRAPPER, TYPE_RENDERS_NO_IMAGES)
+DECLARE_DISPLAY_ITEM_TYPE(SVG_WRAPPER, 0)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_BACKGROUND, 0)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_SELECTION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_BORDER_COLLAPSE, 0)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_BACKGROUND_COLOR, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_BACKGROUND_IMAGE, 0)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_THEMED_BACKGROUND_IMAGE, 0)
 DECLARE_DISPLAY_ITEM_TYPE(TABLE_FIXED_POSITION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(TEXT, TYPE_RENDERS_NO_IMAGES)
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6308,16 +6308,17 @@ nsDisplayWrapList::SetReferenceFrame(con
 bool
 nsDisplayWrapList::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                            mozilla::wr::IpcResourceUpdateQueue& aResources,
                                            const StackingContextHelper& aSc,
                                            mozilla::layers::WebRenderLayerManager* aManager,
                                            nsDisplayListBuilder* aDisplayListBuilder)
 {
   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(GetChildren(),
+                                                                    this,
                                                                     aDisplayListBuilder,
                                                                     aSc,
                                                                     aBuilder,
                                                                     aResources);
   return true;
 }
 
 static nsresult
@@ -6674,16 +6675,17 @@ nsDisplayOpacity::CreateWebRenderCommand
   }
 
   nsTArray<mozilla::wr::WrFilterOp> filters;
   StackingContextHelper sc(aSc, aBuilder, filters, LayoutDeviceRect(), nullptr,
                            animationsId ? &prop : nullptr,
                            opacityForSC);
 
   aManager->CommandBuilder().CreateWebRenderCommandsFromDisplayList(&mList,
+                                                                    this,
                                                                     aDisplayListBuilder,
                                                                     sc,
                                                                     aBuilder,
                                                                     aResources);
   return true;
 }
 
 nsDisplayBlendMode::nsDisplayBlendMode(nsDisplayListBuilder* aBuilder,
@@ -6751,16 +6753,22 @@ nsDisplayBlendMode::BuildLayer(nsDisplay
     return nullptr;
   }
 
   container->SetMixBlendMode(nsCSSRendering::GetGFXBlendMode(mBlendMode));
 
   return container.forget();
 }
 
+mozilla::gfx::CompositionOp
+nsDisplayBlendMode::BlendMode()
+{
+  return nsCSSRendering::GetGFXBlendMode(mBlendMode);
+}
+
 bool
 nsDisplayBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                               nsRegion* aVisibleRegion) {
   // Our children are need their backdrop so we should not allow them to subtract
   // area from aVisibleRegion. We do need to find out what is visible under
   // our children in the temporary compositing buffer, because if our children
   // paint our entire bounds opaquely then we don't need an alpha channel in
   // the temporary compositing buffer.
@@ -10011,17 +10019,25 @@ nsDisplaySVGWrapper::BuildLayer(nsDispla
 
 bool
 nsDisplaySVGWrapper::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                              mozilla::wr::IpcResourceUpdateQueue& aResources,
                                              const StackingContextHelper& aSc,
                                              mozilla::layers::WebRenderLayerManager* aManager,
                                              nsDisplayListBuilder* aDisplayListBuilder)
 {
-  return false;
+  if (gfxPrefs::WebRenderBlobInvalidation()) {
+    return nsDisplayWrapList::CreateWebRenderCommands(aBuilder,
+                                             aResources,
+                                             aSc,
+                                             aManager,
+                                             aDisplayListBuilder);
+  } else {
+    return false;
+  }
 }
 
 namespace mozilla {
 
 uint32_t PaintTelemetry::sPaintLevel = 0;
 uint32_t PaintTelemetry::sMetricLevel = 0;
 EnumeratedArray<PaintTelemetry::Metric,
                 PaintTelemetry::Metric::COUNT,
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -5166,16 +5166,18 @@ public:
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                        mozilla::wr::IpcResourceUpdateQueue& aResources,
                                        const StackingContextHelper& aSc,
                                        mozilla::layers::WebRenderLayerManager* aManager,
                                        nsDisplayListBuilder* aDisplayListBuilder) override;
 
+  float GetOpacity() { return mOpacity; }
+
 private:
   float mOpacity;
   bool mForEventsAndPluginsOnly;
 
   struct {
     float mOpacity;
   } mState;
 };
@@ -5229,16 +5231,17 @@ public:
                                  nsRegion* aVisibleRegion) override;
 
   virtual bool CanMerge(const nsDisplayItem* aItem) const override;
 
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override
   {
     return false;
   }
+  mozilla::gfx::CompositionOp BlendMode();
 
   NS_DISPLAY_DECL_NAME("BlendMode", TYPE_BLEND_MODE)
 
 protected:
   uint8_t mBlendMode;
   uint32_t mIndex;
 };
 
--- a/layout/style/CachedInheritingStyles.cpp
+++ b/layout/style/CachedInheritingStyles.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/CachedInheritingStyles.h"
 
 #include "mozilla/ComputedStyle.h"
 #include "nsCOMPtr.h"
 #include "nsIMemoryReporter.h"
+#include "nsWindowSizes.h"
 
 namespace mozilla {
 
 void
 CachedInheritingStyles::Insert(ComputedStyle* aStyle)
 {
   MOZ_ASSERT(aStyle);
   MOZ_ASSERT(aStyle->IsInheritingAnonBox() || aStyle->IsLazilyCascadedPseudoElement());
--- a/layout/style/ComputedStyle.cpp
+++ b/layout/style/ComputedStyle.cpp
@@ -13,16 +13,17 @@
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsFontMetrics.h"
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"
 #include "nsStyleStructInlines.h"
 #include "nsString.h"
 #include "nsPresContext.h"
+#include "nsWindowSizes.h"
 
 #include "nsCOMPtr.h"
 #include "nsIPresShell.h"
 
 #include "GeckoProfiler.h"
 #include "nsIDocument.h"
 #include "nsPrintfCString.h"
 #include "RubyUtils.h"
@@ -442,9 +443,24 @@ ComputedStyle::GetCachedLazyPseudoStyle(
 
   if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(aPseudo)) {
     return nullptr;
   }
 
   return mCachedInheritingStyles.Lookup(nsCSSPseudoElements::GetPseudoAtom(aPseudo));
 }
 
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)
+
+void
+ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes& aSizes,
+                                      size_t* aCVsSize) const
+{
+  // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
+  // refcount. So we need to measure it with a function that can handle an
+  // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
+  // clearly identify in DMD's output the memory measured here.
+  *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
+  mSource.AddSizeOfExcludingThis(aSizes);
+  mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
+}
+
 } // namespace mozilla
--- a/layout/style/ComputedStyle.h
+++ b/layout/style/ComputedStyle.h
@@ -5,42 +5,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* the interface (to internal code) for retrieving computed style data */
 
 #ifndef _ComputedStyle_h_
 #define _ComputedStyle_h_
 
 #include "nsIMemoryReporter.h"
-#include "nsWindowSizes.h"
 #include <algorithm>
 #include "mozilla/Assertions.h"
 #include "mozilla/RestyleLogging.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/StyleComplexColor.h"
 #include "mozilla/CachedInheritingStyles.h"
 #include "nsCSSAnonBoxes.h"
 
 class nsAtom;
 class nsPresContext;
+class nsWindowSizes;
 
 namespace mozilla {
 
 enum class CSSPseudoElementType : uint8_t;
 class ComputedStyle;
 
 extern "C" {
   void Servo_ComputedStyle_AddRef(const mozilla::ComputedStyle* aStyle);
   void Servo_ComputedStyle_Release(const mozilla::ComputedStyle* aStyle);
   void Gecko_ComputedStyle_Destroy(mozilla::ComputedStyle*);
 }
 
-MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoComputedValuesMallocEnclosingSizeOf)
-
 /**
  * A ComputedStyle represents the computed style data for an element.  The
  * computed style data are stored in a set of structs (see nsStyleStruct.h) that
  * are cached either on the ComputedStyle or in the rule tree (see nsRuleNode.h
  * for a description of this caching and how the cached structs are shared).
  *
  * Since the data in |nsIStyleRule|s and |nsRuleNode|s are immutable (with a few
  * exceptions, like system color changes), the data in an ComputedStyle are also
@@ -349,26 +347,17 @@ public:
    * Makes this context match |aOther| in terms of which style structs have
    * been resolved.
    */
   inline void ResolveSameStructsAs(const ComputedStyle* aOther);
 
   // The |aCVsSize| outparam on this function is where the actual CVs size
   // value is added. It's done that way because the callers know which value
   // the size should be added to.
-  void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const
-  {
-    // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
-    // refcount. So we need to measure it with a function that can handle an
-    // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
-    // clearly identify in DMD's output the memory measured here.
-    *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
-    mSource.AddSizeOfExcludingThis(aSizes);
-    mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
-  }
+  void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const;
 
 protected:
   // Needs to be friend so that it can call the destructor without making it
   // public.
   friend void ::Gecko_ComputedStyle_Destroy(ComputedStyle*);
 
   ~ComputedStyle() = default;
 
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -341,16 +341,17 @@ InvalidateImages(nsIFrame* aFrame)
   if (auto userDataTable =
        aFrame->GetProperty(layers::WebRenderUserDataProperty::Key())) {
     for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) {
       RefPtr<layers::WebRenderUserData> data = iter.UserData();
       if (data->GetType() == layers::WebRenderAnimationData::UserDataType::eFallback &&
           !IsRenderNoImages(data->GetDisplayItemKey())) {
         static_cast<layers::WebRenderFallbackData*>(data.get())->SetInvalid(true);
       }
+      //XXX: handle Blob data
       invalidateFrame = true;
     }
   }
 
   if (invalidateFrame) {
     aFrame->SchedulePaint();
   }
 }
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -42,16 +42,17 @@
 #include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
 #include "nsStyleUtil.h"
 #include "nsSVGElement.h"
 #include "nsTArray.h"
 #include "nsTransitionManager.h"
+#include "nsWindowSizes.h"
 
 #include "mozilla/CORSMode.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Keyframe.h"
 #include "mozilla/Mutex.h"
@@ -221,17 +222,17 @@ ServoComputedData::AddSizeOfExcludingThi
   // to measure it with a function that can handle an interior pointer. We use
   // ServoStyleStructsEnclosingMallocSizeOf to clearly identify in DMD's
   // output the memory measured here.
 #define STYLE_STRUCT(name_) \
   static_assert(alignof(nsStyle##name_) <= sizeof(size_t), \
                 "alignment will break AddSizeOfExcludingThis()"); \
   const void* p##name_ = GetStyle##name_(); \
   if (!aSizes.mState.HaveSeenPtr(p##name_)) { \
-    aSizes.mServoStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \
+    aSizes.mStyleSizes.NS_STYLE_SIZES_FIELD(name_) += \
       ServoStyleStructsMallocEnclosingSizeOf(p##name_); \
   }
   #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
   if (visited_style.mPtr && !aSizes.mState.HaveSeenPtr(visited_style.mPtr)) {
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -31,16 +31,17 @@
 #include "nsIAnonymousContentCreator.h"
 #include "nsIDocumentInlines.h"
 #include "nsMediaFeatures.h"
 #include "nsPrintfCString.h"
 #include "nsSMILAnimationController.h"
 #include "nsXBLPrototypeBinding.h"
 #include "gfxUserFontSet.h"
 #include "nsBindingManager.h"
+#include "nsWindowSizes.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #ifdef DEBUG
 bool
 ServoStyleSet::IsCurrentThreadInServoTraversal()
 {
@@ -335,44 +336,44 @@ ServoStyleSet::MediumFeaturesChanged(Med
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSetMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSetMallocEnclosingSizeOf)
 
 void
 ServoStyleSet::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
 {
   MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
 
-  aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(this);
+  aSizes.mLayoutStyleSetsOther += mallocSizeOf(this);
 
   if (mRawSet) {
-    aSizes.mLayoutServoStyleSetsOther += mallocSizeOf(mRawSet.get());
+    aSizes.mLayoutStyleSetsOther += mallocSizeOf(mRawSet.get());
     ServoStyleSetSizes sizes;
     // Measure mRawSet. We use ServoStyleSetMallocSizeOf rather than
     // aMallocSizeOf to distinguish in DMD's output the memory measured within
     // Servo code.
     Servo_StyleSet_AddSizeOfExcludingThis(ServoStyleSetMallocSizeOf,
                                           ServoStyleSetMallocEnclosingSizeOf,
                                           &sizes, mRawSet.get());
 
     // The StyleSet does not contain precomputed pseudos; they are in the UA
     // cache.
     MOZ_RELEASE_ASSERT(sizes.mPrecomputedPseudos == 0);
 
-    aSizes.mLayoutServoStyleSetsStylistRuleTree += sizes.mRuleTree;
-    aSizes.mLayoutServoStyleSetsStylistElementAndPseudosMaps +=
+    aSizes.mLayoutStyleSetsStylistRuleTree += sizes.mRuleTree;
+    aSizes.mLayoutStyleSetsStylistElementAndPseudosMaps +=
       sizes.mElementAndPseudosMaps;
-    aSizes.mLayoutServoStyleSetsStylistInvalidationMap +=
+    aSizes.mLayoutStyleSetsStylistInvalidationMap +=
       sizes.mInvalidationMap;
-    aSizes.mLayoutServoStyleSetsStylistRevalidationSelectors +=
+    aSizes.mLayoutStyleSetsStylistRevalidationSelectors +=
       sizes.mRevalidationSelectors;
-    aSizes.mLayoutServoStyleSetsStylistOther += sizes.mOther;
+    aSizes.mLayoutStyleSetsStylistOther += sizes.mOther;
   }
 
   if (mStyleRuleMap) {
-    aSizes.mLayoutServoStyleSetsOther +=
+    aSizes.mLayoutStyleSetsOther +=
       mStyleRuleMap->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
   }
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mSheets
   // - mNonInheritingComputedStyles
   //
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -156,16 +156,17 @@ nsSVGForeignObjectFrame::Reflow(nsPresCo
 
 void
 nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                           const nsDisplayListSet& aLists)
 {
   if (!static_cast<const nsSVGElement*>(GetContent())->HasValidDimensions()) {
     return;
   }
+  // TODO: wrap items into an nsDisplayForeignObject
   DisplayOutline(aBuilder, aLists);
   BuildDisplayListForNonBlockChildren(aBuilder, aLists);
 }
 
 bool
 nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform,
                                           Matrix *aFromParentTransform) const
 {
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 7401fc25a9d87ed57d49648883e3e5658305c1cb (2018-03-26 16:54:01 +0200)
+The git commit ID used was f34f392de5158feb46202e8ae58528bfb42f2759 (2018-03-27 17:45:10 -0400)
--- a/media/libcubeb/gtest/test_duplex.cpp
+++ b/media/libcubeb/gtest/test_duplex.cpp
@@ -154,20 +154,22 @@ TEST(cubeb, duplex_collection_change)
   std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
     cleanup_cubeb_at_exit(ctx, cubeb_destroy);
 
   /* typical user-case: mono input, stereo output, low latency. */
   input_params.format = STREAM_FORMAT;
   input_params.rate = 48000;
   input_params.channels = 1;
   input_params.layout = CUBEB_LAYOUT_MONO;
+  input_params.prefs = CUBEB_STREAM_PREF_NONE;
   output_params.format = STREAM_FORMAT;
   output_params.rate = 48000;
   output_params.channels = 2;
   output_params.layout = CUBEB_LAYOUT_STEREO;
+  output_params.prefs = CUBEB_STREAM_PREF_NONE;
 
   r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
   ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
 
   r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
                         NULL, &input_params, NULL, &output_params,
                         latency_frames, data_cb_duplex, state_cb_duplex, nullptr);
   ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
--- a/media/libcubeb/gtest/test_loopback.cpp
+++ b/media/libcubeb/gtest/test_loopback.cpp
@@ -343,16 +343,17 @@ void run_loopback_separate_streams_test(
   input_params.rate = SAMPLE_FREQUENCY;
   input_params.channels = 1;
   input_params.layout = CUBEB_LAYOUT_MONO;
   input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
   output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
   output_params.rate = SAMPLE_FREQUENCY;
   output_params.channels = 1;
   output_params.layout = CUBEB_LAYOUT_MONO;
+  output_params.prefs = CUBEB_STREAM_PREF_NONE;
 
   std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
   ASSERT_TRUE(!!user_data) << "Error allocating user data";
 
   r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
   ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
 
   /* setup an input stream with loopback */
@@ -510,16 +511,17 @@ void run_loopback_device_selection_test(
   input_params.rate = SAMPLE_FREQUENCY;
   input_params.channels = 1;
   input_params.layout = CUBEB_LAYOUT_MONO;
   input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
   output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
   output_params.rate = SAMPLE_FREQUENCY;
   output_params.channels = 1;
   output_params.layout = CUBEB_LAYOUT_MONO;
+  output_params.prefs = CUBEB_STREAM_PREF_NONE;
 
   std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
   ASSERT_TRUE(!!user_data) << "Error allocating user data";
 
   r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
   ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
 
   /* setup an input stream with loopback */
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -65,16 +65,17 @@ const uint32_t SAFE_MIN_LATENCY_FRAMES =
 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
 
 void audiounit_stream_stop_internal(cubeb_stream * stm);
 void audiounit_stream_start_internal(cubeb_stream * stm);
 static void audiounit_close_stream(cubeb_stream *stm);
 static int audiounit_setup_stream(cubeb_stream *stm);
 static vector<AudioObjectID>
 audiounit_get_devices_of_type(cubeb_device_type devtype);
+static UInt32 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope);
 
 extern cubeb_ops const audiounit_ops;
 
 struct cubeb {
   cubeb_ops const * ops = &audiounit_ops;
   owned_critical_section mutex;
   atomic<int> active_streams{ 0 };
   uint32_t global_latency_frames = 0;
@@ -173,18 +174,17 @@ struct cubeb_stream {
   /* Frame counters */
   atomic<uint64_t> frames_played{ 0 };
   uint64_t frames_queued = 0;
   atomic<int64_t> frames_read{ 0 };
   atomic<bool> shutdown{ true };
   atomic<bool> draining{ false };
   /* Latency requested by the user. */
   uint32_t latency_frames = 0;
-  atomic<uint64_t> current_latency_frames{ 0 };
-  uint64_t hw_latency_frames = UINT64_MAX;
+  atomic<uint32_t> current_latency_frames{ 0 };
   atomic<float> panning{ 0 };
   unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
   /* This is true if a device change callback is currently running.  */
   atomic<bool> switching_device{ false };
   atomic<bool> buffer_size_change_state{ false };
   AudioDeviceID aggregate_device_id = 0;    // the aggregate device id
   AudioObjectID plugin_id = 0;              // used to create aggregate device
   /* Mixer interface */
@@ -315,29 +315,16 @@ AudioConvertHostTimeToNanos(uint64_t hos
   if (timebase_info.numer != timebase_info.denom) {
     answer *= timebase_info.numer;
     answer /= timebase_info.denom;
   }
   return (uint64_t)answer;
 }
 #endif
 
-static int64_t
-audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream)
-{
-  if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
-    return 0;
-  }
-
-  uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
-  uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
-
-  return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL;
-}
-
 static void
 audiounit_set_global_latency(cubeb_stream * stm, uint32_t latency_frames)
 {
   stm->mutex.assert_current_thread_owns();
   assert(stm->context->active_streams == 1);
   stm->context->global_latency_frames = latency_frames;
 }
 
@@ -521,17 +508,16 @@ audiounit_output_callback(void * user_pt
   void * output_buffer = NULL, * input_buffer = NULL;
 
   if (stm->shutdown) {
     ALOG("(%p) output shutdown.", stm);
     audiounit_make_silent(&outBufferList->mBuffers[0]);
     return noErr;
   }
 
-  stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
   if (stm->draining) {
     OSStatus r = AudioOutputUnitStop(stm->output_unit);
     assert(r == 0);
     if (stm->input_unit) {
       r = AudioOutputUnitStop(stm->input_unit);
       assert(r == 0);
     }
     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
@@ -2536,16 +2522,24 @@ audiounit_setup_stream(cubeb_stream * st
   }
 
   if (stm->output_unit != NULL) {
     r = AudioUnitInitialize(stm->output_unit);
     if (r != noErr) {
       LOG("AudioUnitInitialize/output rv=%d", r);
       return CUBEB_ERROR;
     }
+
+    stm->current_latency_frames = audiounit_get_device_presentation_latency(stm->output_device.id, kAudioDevicePropertyScopeOutput);
+
+    Float64 unit_s;
+    UInt32 size = sizeof(unit_s);
+    if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &unit_s, &size) == noErr) {
+      stm->current_latency_frames += static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate);
+    }
   }
 
   if (stm->input_unit && stm->output_unit) {
     // According to the I/O hardware rate it is expected a specific pattern of callbacks
     // for example is input is 44100 and output is 48000 we expected no more than 2
     // out callback in a row.
     stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
   }
@@ -2759,95 +2753,32 @@ audiounit_stream_stop(cubeb_stream * stm
   LOG("Cubeb stream (%p) stopped successfully.", stm);
   return CUBEB_OK;
 }
 
 static int
 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   assert(stm);
-  *position = stm->frames_played;
+  if (stm->current_latency_frames > stm->frames_played) {
+    *position = 0;
+  } else {
+    *position = stm->frames_played - stm->current_latency_frames;
+  }
   return CUBEB_OK;
 }
 
 int
 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
 #if TARGET_OS_IPHONE
   //TODO
   return CUBEB_ERROR_NOT_SUPPORTED;
 #else
-  auto_lock lock(stm->mutex);
-  if (stm->hw_latency_frames == UINT64_MAX) {
-    UInt32 size;
-    uint32_t device_latency_frames, device_safety_offset;
-    double unit_latency_sec;
-    AudioDeviceID output_device_id;
-    OSStatus r;
-    AudioObjectPropertyAddress latency_address = {
-      kAudioDevicePropertyLatency,
-      kAudioDevicePropertyScopeOutput,
-      kAudioObjectPropertyElementMaster
-    };
-    AudioObjectPropertyAddress safety_offset_address = {
-      kAudioDevicePropertySafetyOffset,
-      kAudioDevicePropertyScopeOutput,
-      kAudioObjectPropertyElementMaster
-    };
-
-    output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
-    if (output_device_id == kAudioObjectUnknown) {
-      return CUBEB_ERROR;
-    }
-
-    size = sizeof(unit_latency_sec);
-    r = AudioUnitGetProperty(stm->output_unit,
-                             kAudioUnitProperty_Latency,
-                             kAudioUnitScope_Global,
-                             0,
-                             &unit_latency_sec,
-                             &size);
-    if (r != noErr) {
-      LOG("AudioUnitGetProperty/kAudioUnitProperty_Latency rv=%d", r);
-      return CUBEB_ERROR;
-    }
-
-    size = sizeof(device_latency_frames);
-    r = AudioObjectGetPropertyData(output_device_id,
-                                   &latency_address,
-                                   0,
-                                   NULL,
-                                   &size,
-                                   &device_latency_frames);
-    if (r != noErr) {
-      LOG("AudioUnitGetPropertyData/latency_frames rv=%d", r);
-      return CUBEB_ERROR;
-    }
-
-    size = sizeof(device_safety_offset);
-    r = AudioObjectGetPropertyData(output_device_id,
-                                   &safety_offset_address,
-                                   0,
-                                   NULL,
-                                   &size,
-                                   &device_safety_offset);
-    if (r != noErr) {
-      LOG("AudioUnitGetPropertyData/safety_offset rv=%d", r);
-      return CUBEB_ERROR;
-    }
-
-    /* This part is fixed and depend on the stream parameter and the hardware. */
-    stm->hw_latency_frames =
-      static_cast<uint32_t>(unit_latency_sec * stm->output_desc.mSampleRate)
-      + device_latency_frames
-      + device_safety_offset;
-  }
-
-  *latency = stm->hw_latency_frames + stm->current_latency_frames;
-
+  *latency = stm->current_latency_frames;
   return CUBEB_OK;
 #endif
 }
 
 static int
 audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
 {
   assert(stm->output_unit);
@@ -3084,40 +3015,34 @@ audiounit_get_available_samplerate(Audio
   }
 
 }
 
 static UInt32
 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
 {
   AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
-  UInt32 size, dev, stream = 0, offset;
+  UInt32 size, dev, stream = 0;
   AudioStreamID sid[1];
 
   adr.mSelector = kAudioDevicePropertyLatency;
   size = sizeof(UInt32);
   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) {
     dev = 0;
   }
 
   adr.mSelector = kAudioDevicePropertyStreams;
   size = sizeof(sid);
   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) {
     adr.mSelector = kAudioStreamPropertyLatency;
     size = sizeof(UInt32);
     AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
   }
 
-  adr.mSelector = kAudioDevicePropertySafetyOffset;
-  size = sizeof(UInt32);
-  if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) {
-    offset = 0;
-  }
-
-  return dev + stream + offset;
+  return dev + stream;
 }
 
 static int
 audiounit_create_device_from_hwdev(cubeb_device_info * ret, AudioObjectID devid, cubeb_device_type type)
 {
   AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster };
   UInt32 size, ch, latency;
   CFStringRef str = NULL;
deleted file mode 100644
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseGeckoViewTest.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.geckoview.test;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.mozilla.geckoview.GeckoSession;
-import org.mozilla.geckoview.GeckoView;
-
-import android.net.Uri;
-import android.os.SystemClock;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.IdlingResource;
-import android.support.test.espresso.assertion.ViewAssertions;
-import android.support.test.espresso.matcher.ViewMatchers;
-import android.support.test.rule.ActivityTestRule;
-import android.view.MotionEvent;
-
-import static org.junit.Assert.assertTrue;
-
-public class BaseGeckoViewTest {
-    protected GeckoSession mSession;
-    protected GeckoView mView;
-    private ExplicitIdlingResource mIdlingResource;
-
-    @Before
-    public void setup() {
-        mView = mActivityRule.getActivity().getGeckoView();
-        mSession = mActivityRule.getActivity().getGeckoSession();
-
-        mIdlingResource = new ExplicitIdlingResource();
-        Espresso.registerIdlingResources(mIdlingResource);
-    }
-
-    @After
-    public void tearDown() {
-        Espresso.unregisterIdlingResources(mIdlingResource);
-    }
-
-    @Rule
-    public ActivityTestRule<TestRunnerActivity> mActivityRule = new ActivityTestRule<>(
-            TestRunnerActivity.class);
-
-    protected void waitUntilDone() {
-        Espresso.onView(ViewMatchers.isRoot()).check(ViewAssertions.matches(ViewMatchers.isRoot()));
-    }
-
-    protected void done() {
-        mIdlingResource.done();
-    }
-
-    protected String buildAssetUrl(String path) {
-        return "resource://android/assets/www/" + path;
-    }
-
-    protected void loadTestPath(final String path, Runnable finished) {
-        loadTestPage(buildAssetUrl(path), finished);
-    }
-
-    protected void loadTestPage(final String url, final Runnable finished) {
-        final String path = Uri.parse(url).getPath();
-        mSession.setProgressDelegate(new GeckoSession.ProgressDelegate() {
-            @Override
-            public void onPageStart(GeckoSession session, String loadingUrl) {
-                assertTrue("Loaded url should end with " + path, loadingUrl.endsWith(path));
-            }
-
-            @Override
-            public void onPageStop(GeckoSession session, boolean success) {
-                assertTrue("Load should succeed", success);
-                mSession.setProgressDelegate(null);
-                finished.run();
-            }
-
-            @Override
-            public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
-            }
-        });
-
-        mSession.loadUri(url);
-    }
-
-    protected void sendClick(int x, int y) {
-        mSession.getPanZoomController().onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 5, 5, 0));
-        mSession.getPanZoomController().onTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 5, 5, 0));
-    }
-
-    private class ExplicitIdlingResource implements IdlingResource {
-        private boolean mIsIdle;
-        private ResourceCallback mCallback;
-
-        @Override
-        public String getName() {
-            return "Explicit Idling Resource";
-        }
-
-        @Override
-        public boolean isIdleNow() {
-            return mIsIdle;
-        }
-
-        public void done() {
-            mIsIdle = true;
-            if (mCallback != null) {
-                mCallback.onTransitionToIdle();
-            }
-        }
-
-        @Override
-        public void registerIdleTransitionCallback(ResourceCallback callback) {
-            mCallback = callback;
-        }
-    }
-}
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -1,10 +1,12 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
 
 package org.mozilla.geckoview.test
 
 import android.os.Parcel
 import android.support.test.InstrumentationRegistry
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/GeckoSessionTestRuleTest.kt
@@ -1,10 +1,12 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
 
 package org.mozilla.geckoview.test
 
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.GeckoSessionSettings
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/NavigationDelegateTest.kt
@@ -1,10 +1,12 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
 
 package org.mozilla.geckoview.test
 
 import android.support.test.InstrumentationRegistry
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
 import org.mozilla.geckoview.test.util.Callbacks
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/ProgressDelegateTest.kt
@@ -1,10 +1,12 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
 
 package org.mozilla.geckoview.test
 
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
 import org.mozilla.geckoview.test.util.Callbacks
 
 import android.support.test.filters.MediumTest
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/SessionLifecycleTest.kt
@@ -1,10 +1,12 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
 
 package org.mozilla.geckoview.test
 
 import org.mozilla.geckoview.GeckoSession
 import org.mozilla.geckoview.GeckoSessionSettings
 
 import android.support.test.filters.MediumTest
 import android.support.test.runner.AndroidJUnit4
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
@@ -1,8 +1,13 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
 package org.mozilla.geckoview.test;
 
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.geckoview.GeckoSession;
 import org.mozilla.geckoview.GeckoSessionSettings;
 import org.mozilla.geckoview.GeckoView;
 
--- a/mobile/android/modules/geckoview/GeckoViewModule.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewModule.jsm
@@ -124,49 +124,49 @@ class EventProxy {
     this.listener = aListener;
     this.eventDispatcher = aEventDispatcher;
     this._eventQueue = [];
     this._registeredEvents = [];
     this._enableQueuing = false;
   }
 
   registerListener(aEventList) {
-    debug("rabbit register " + aEventList);
+    debug("register " + aEventList);
     this.eventDispatcher.registerListener(this, aEventList);
     this._registeredEvents = this._registeredEvents.concat(aEventList);
   }
 
   unregisterListener() {
-    debug("rabbit unregister");
+    debug("unregister");
     if (this._registeredEvents.length === 0) {
       return;
     }
     this.eventDispatcher.unregisterListener(this, this._registeredEvents);
     this._registeredEvents = [];
   }
 
   onEvent(aEvent, aData, aCallback) {
     if (this._enableQueuing) {
-      debug("rabbit queue " + aEvent + ", aData=" + JSON.stringify(aData));
+      debug("queue " + aEvent + ", aData=" + JSON.stringify(aData));
       this._eventQueue.unshift(arguments);
     } else {
       this._dispatch.apply(this, arguments);
     }
   }
 
   enableQueuing(aEnable) {
     debug("enableQueuing " + aEnable);
     this._enableQueuing = aEnable;
   }
 
   _dispatch(aEvent, aData, aCallback) {
-    debug("rabbit dispatch " + aEvent + ", aData=" + JSON.stringify(aData));
+    debug("dispatch " + aEvent + ", aData=" + JSON.stringify(aData));
     this.listener.onEvent.apply(this.listener, arguments);
   }
 
   dispatchQueuedEvents() {
-    debug("rabbit dispatchQueued");
+    debug("dispatchQueued");
     while (this._eventQueue.length) {
       const e = this._eventQueue.pop();
       this._dispatch.apply(this, e);
     }
   }
 }
--- a/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewNavigation.jsm
@@ -147,53 +147,79 @@ class GeckoViewNavigation extends GeckoV
   }
 
   // nsIBrowserDOMWindow.
   createContentWindow(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
     debug("createContentWindow: aUri=" + (aUri && aUri.spec) +
           " aWhere=" + aWhere +
           " aFlags=" + aFlags);
 
+    if (LoadURIDelegate.load(this.eventDispatcher, aUri, aWhere, aFlags,
+                             aTriggeringPrincipal)) {
+      // The app has handled the load, abort open-window handling.
+      Components.returnCode = Cr.NS_ERROR_ABORT;
+      return null;
+    }
+
     const browser = this.handleNewSession(aUri, aOpener, aWhere, aFlags,
                                           aTriggeringPrincipal);
-    return browser && browser.contentWindow;
+    if (!browser) {
+      Components.returnCode = Cr.NS_ERROR_ABORT;
+      return null;
+    }
+
+    return browser.contentWindow;
   }
 
   // nsIBrowserDOMWindow.
   createContentWindowInFrame(aUri, aParams, aWhere, aFlags, aNextTabParentId,
                              aName) {
     debug("createContentWindowInFrame: aUri=" + (aUri && aUri.spec) +
           " aParams=" + aParams +
           " aWhere=" + aWhere +
           " aFlags=" + aFlags +
           " aNextTabParentId=" + aNextTabParentId +
           " aName=" + aName);
 
-    const browser = this.handleNewSession(aUri, null, aWhere, aFlags, null);
-    if (browser) {
-      browser.setAttribute("nextTabParentId", aNextTabParentId);
+    if (LoadURIDelegate.load(this.eventDispatcher, aUri, aWhere, aFlags, null)) {
+      // The app has handled the load, abort open-window handling.
+      Components.returnCode = Cr.NS_ERROR_ABORT;
+      return null;
     }
 
+    const browser = this.handleNewSession(aUri, null, aWhere, aFlags, null);
+    if (!browser) {
+      Components.returnCode = Cr.NS_ERROR_ABORT;
+      return null;
+    }
+
+    browser.setAttribute("nextTabParentId", aNextTabParentId);
     return browser;
   }
 
   handleOpenUri(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal,
                 aNextTabParentId) {
-    let browser = this.browser;
+    debug("handleOpenUri: aUri=" + (aUri && aUri.spec) +
+          " aWhere=" + aWhere +
+          " aFlags=" + aFlags);
+
     if (LoadURIDelegate.load(this.eventDispatcher, aUri, aWhere, aFlags,
                              aTriggeringPrincipal)) {
-      return browser;
+      return null;
     }
 
+    let browser = this.browser;
+
     if (aWhere === Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW ||
         aWhere === Ci.nsIBrowserDOMWindow.OPEN_NEWTAB ||
         aWhere === Ci.nsIBrowserDOMWindow.OPEN_SWITCHTAB) {
       browser = this.handleNewSession(aUri, aOpener, aWhere, aFlags,
                                       aTriggeringPrincipal);
     }
+
     if (!browser) {
       // Should we throw?
       return null;
     }
     browser.loadURI(aUri.spec, null, null, null, null, aTriggeringPrincipal);
     return browser;
   }
 
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -4105,16 +4105,20 @@ pref_ReadPrefFromJar(nsZipArchive* aJarR
   return NS_OK;
 }
 
 // Initialize default preference JavaScript buffers from appropriate TEXT
 // resources.
 /* static */ Result<Ok, const char*>
 Preferences::InitInitialObjects(bool aIsStartup)
 {
+  // Initialize static prefs before prefs from data files so that the latter
+  // will override the former.
+  StaticPrefs::InitAll(aIsStartup);
+
   // In the omni.jar case, we load the following prefs:
   // - jar:$gre/omni.jar!/greprefs.js
   // - jar:$gre/omni.jar!/defaults/pref/*.js
   //
   // In the non-omni.jar case, we load:
   // - $gre/greprefs.js
   //
   // In both cases, we also load:
@@ -4267,21 +4271,16 @@ Preferences::InitInitialObjects(bool aIs
         continue;
       }
 
       // Do we care if a file provided by this process fails to load?
       pref_LoadPrefsInDir(path, nullptr, 0);
     }
   }
 
-  // We initialize static prefs after prefs from data files so that we can
-  // detect and complain if there are any static prefs that are also defined in
-  // data files.
-  StaticPrefs::InitAll(aIsStartup);
-
   if (XRE_IsParentProcess()) {
     SetupTelemetryPref();
   }
 
   NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
                                 nullptr,
                                 NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
 
@@ -4998,82 +4997,66 @@ Preferences::AddFloatVarCache(float* aCa
 //
 #define PREF(name, cpp_type, value)
 #define VARCACHE_PREF(name, id, cpp_type, value)                               \
   cpp_type StaticPrefs::sVarCache_##id(value);
 #include "mozilla/StaticPrefList.h"
 #undef PREF
 #undef VARCACHE_PREF
 
-static void
-CheckForDoubleDefinition(const char* aName, bool aIsStartup)
-{
-#ifdef DEBUG
-  // If this warning is hit, it's probably because a pref is present in both a
-  // data file (such as all.js) and StaticPrefsList.h, which is not desirable.
-  if (aIsStartup && Preferences::GetType(aName) != Preferences::PREF_INVALID) {
-    NS_ERROR(nsPrintfCString("'%s' already registered\n", aName).get());
-  }
-#endif
-}
-
 // The SetPref_*() functions below end in a `_<type>` suffix because they are
 // used by the PREF macro definition in InitAll() below.
 
 static void
-SetPref_bool(const char* aName, bool aDefaultValue, bool aIsStartup)
+SetPref_bool(const char* aName, bool aDefaultValue)
 {
-  CheckForDoubleDefinition(aName, aIsStartup);
   PrefValue value;
   value.mBoolVal = aDefaultValue;
   pref_SetPref(aName,
                PrefType::Bool,
                PrefValueKind::Default,
                value,
                /* isSticky */ false,
                /* isLocked */ false,
                /* fromInit */ true);
 }
 
 static void
-SetPref_int32_t(const char* aName, int32_t aDefaultValue, bool aIsStartup)
+SetPref_int32_t(const char* aName, int32_t aDefaultValue)
 {
-  CheckForDoubleDefinition(aName, aIsStartup);
   PrefValue value;
   value.mIntVal = aDefaultValue;
   pref_SetPref(aName,
                PrefType::Int,
                PrefValueKind::Default,
                value,
                /* isSticky */ false,
                /* isLocked */ false,
                /* fromInit */ true);
 }
 
 static void
-SetPref_float(const char* aName, float aDefaultValue, bool aIsStartup)
+SetPref_float(const char* aName, float aDefaultValue)
 {
-  CheckForDoubleDefinition(aName, aIsStartup);
   PrefValue value;
   nsPrintfCString defaultValue("%f", aDefaultValue);
   value.mStringVal = defaultValue.get();
   pref_SetPref(aName,
                PrefType::String,
                PrefValueKind::Default,
                value,
                /* isSticky */ false,
                /* isLocked */ false,
                /* fromInit */ true);
 }
 
 // XXX: this will eventually become used
 MOZ_MAYBE_UNUSED static void
-SetPref_String(const char* aName, const char* aDefaultValue, bool aIsStartup)
+SetPref_String(const char* aName, const char* aDefaultValue)
 {
-  CheckForDoubleDefinition(aName, aIsStartup);
   PrefValue value;
   value.mStringVal = aDefaultValue;
   pref_SetPref(aName,
                PrefType::String,
                PrefValueKind::Default,
                value,
                /* isSticky */ false,
                /* isLocked */ false,
@@ -5081,125 +5064,125 @@ SetPref_String(const char* aName, const 
 }
 
 static void
 InitVarCachePref(const char* aName,
                  bool* aCache,
                  bool aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_bool(aName, aDefaultValue, aIsStartup);
+  SetPref_bool(aName, aDefaultValue);
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddBoolVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 template<MemoryOrdering Order>
 static void
 InitVarCachePref(const char* aName,
                  Atomic<bool, Order>* aCache,
                  bool aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_bool(aName, aDefaultValue, aIsStartup);
+  SetPref_bool(aName, aDefaultValue);
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddAtomicBoolVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 // XXX: this will eventually become used
 MOZ_MAYBE_UNUSED static void
 InitVarCachePref(const char* aName,
                  int32_t* aCache,
                  int32_t aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_int32_t(aName, aDefaultValue, aIsStartup);
+  SetPref_int32_t(aName, aDefaultValue);
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddIntVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 template<MemoryOrdering Order>
 static void
 InitVarCachePref(const char* aName,
                  Atomic<int32_t, Order>* aCache,
                  int32_t aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_int32_t(aName, aDefaultValue, aIsStartup);
+  SetPref_int32_t(aName, aDefaultValue);
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddAtomicIntVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 static void
 InitVarCachePref(const char* aName,
                  uint32_t* aCache,
                  uint32_t aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue), aIsStartup);
+  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddUintVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 template<MemoryOrdering Order>
 static void
 InitVarCachePref(const char* aName,
                  Atomic<uint32_t, Order>* aCache,
                  uint32_t aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue), aIsStartup);
+  SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddAtomicUintVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 // XXX: this will eventually become used
 MOZ_MAYBE_UNUSED static void
 InitVarCachePref(const char* aName,
                  float* aCache,
                  float aDefaultValue,
                  bool aIsStartup)
 {
-  SetPref_float(aName, aDefaultValue, aIsStartup);
+  SetPref_float(aName, aDefaultValue);
   *aCache = aDefaultValue;
   if (aIsStartup) {
     Preferences::AddFloatVarCache(aCache, aName, aDefaultValue, true);
   }
 }
 
 /* static */ void
 StaticPrefs::InitAll(bool aIsStartup)
 {
 // For prefs like these:
 //
 //   PREF("foo.bar.baz", bool, true)
 //   VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
 //
 // we generate registration calls:
 //
-//   SetPref_bool("foo.bar.baz", true, aIsStartup);
+//   SetPref_bool("foo.bar.baz", true);
 //   InitVarCachePref("my.varcache", &StaticPrefs::sVarCache_my_varcache, 99,
 //                    aIsStartup);
 //
 // The SetPref_*() functions have a type suffix to avoid ambiguity between
 // prefs having int32_t and float default values. That suffix is not needed for
 // the InitVarCachePref() functions because they take a pointer parameter,
 // which prevents automatic int-to-float coercion.
-#define PREF(name, cpp_type, value) SetPref_##cpp_type(name, value, aIsStartup);
+#define PREF(name, cpp_type, value) SetPref_##cpp_type(name, value);
 #define VARCACHE_PREF(name, id, cpp_type, value)                               \
   InitVarCachePref(name, &StaticPrefs::sVarCache_##id, value, aIsStartup);
 #include "mozilla/StaticPrefList.h"
 #undef PREF
 #undef VARCACHE_PREF
 }
 
 } // namespace mozilla
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -2,18 +2,20 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This file defines static prefs, i.e. those that are defined at startup and
 // used entirely or mostly from C++ code.
 //
-// Prefs defined in this file should *not* be listed in a prefs data file such
-// as all.js. If they are, CheckForDoubleDefinition() will issue an NS_ERROR.
+// If a pref is listed here and also in a prefs data file such as all.js, the
+// value from the latter will override the value given here. For vanilla
+// browser builds such overrides are discouraged, but they are necessary for
+// some configurations (e.g. Thunderbird).
 //
 // The file is separated into sections, where the sections are determined by
 // the first segment of the prefnames within (e.g. "network.predictor.enabled"
 // is within the "Network" section). Sections should be kept in alphabetical
 // order, but prefs within sections need not be.
 //
 // Normal prefs
 // ------------
@@ -106,16 +108,138 @@ VARCACHE_PREF(
 // - true: They are allow to present http auth. dialog
 // - false: They are not allow to present http auth. dialog.
 VARCACHE_PREF(
   "network.auth.non-web-content-triggered-resources-http-auth-allow",
    network_auth_non_web_content_triggered_resources_http_auth_allow,
   bool, false
 )
 
+// Enables the predictive service.
+VARCACHE_PREF(
+  "network.predictor.enabled",
+   network_predictor_enabled,
+  bool, true
+)
+
+VARCACHE_PREF(
+  "network.predictor.enable-hover-on-ssl",
+   network_predictor_enable_hover_on_ssl,
+  bool, false
+)
+
+VARCACHE_PREF(
+  "network.predictor.enable-prefetch",
+   network_predictor_enable_prefetch,
+  bool, false
+)
+
+VARCACHE_PREF(
+  "network.predictor.page-degradation.day",
+   network_predictor_page_degradation_day,
+  int32_t, 0
+)
+VARCACHE_PREF(
+  "network.predictor.page-degradation.week",
+   network_predictor_page_degradation_week,
+  int32_t, 5
+)
+VARCACHE_PREF(
+  "network.predictor.page-degradation.month",
+   network_predictor_page_degradation_month,
+  int32_t, 10
+)
+VARCACHE_PREF(
+  "network.predictor.page-degradation.year",
+   network_predictor_page_degradation_year,
+  int32_t, 25
+)
+VARCACHE_PREF(
+  "network.predictor.page-degradation.max",
+   network_predictor_page_degradation_max,
+  int32_t, 50
+)
+
+VARCACHE_PREF(
+  "network.predictor.subresource-degradation.day",
+   network_predictor_subresource_degradation_day,
+  int32_t, 1
+)
+VARCACHE_PREF(
+  "network.predictor.subresource-degradation.week",
+   network_predictor_subresource_degradation_week,
+  int32_t, 10
+)
+VARCACHE_PREF(
+  "network.predictor.subresource-degradation.month",
+   network_predictor_subresource_degradation_month,
+  int32_t, 25
+)
+VARCACHE_PREF(
+  "network.predictor.subresource-degradation.year",
+   network_predictor_subresource_degradation_year,
+  int32_t, 50
+)
+VARCACHE_PREF(
+  "network.predictor.subresource-degradation.max",
+   network_predictor_subresource_degradation_max,
+  int32_t, 100
+)
+
+VARCACHE_PREF(
+  "network.predictor.prefetch-rolling-load-count",
+   network_predictor_prefetch_rolling_load_count,
+  int32_t, 10
+)
+
+VARCACHE_PREF(
+  "network.predictor.prefetch-min-confidence",
+   network_predictor_prefetch_min_confidence,
+  int32_t, 100
+)
+VARCACHE_PREF(
+  "network.predictor.preconnect-min-confidence",
+   network_predictor_preconnect_min_confidence,
+  int32_t, 90
+)
+VARCACHE_PREF(
+  "network.predictor.preresolve-min-confidence",
+   network_predictor_preresolve_min_confidence,
+  int32_t, 60
+)
+
+VARCACHE_PREF(
+  "network.predictor.prefetch-force-valid-for",
+   network_predictor_prefetch_force_valid_for,
+  int32_t, 10
+)
+
+VARCACHE_PREF(
+  "network.predictor.max-resources-per-entry",
+   network_predictor_max_resources_per_entry,
+  int32_t, 100
+)
+
+// This is selected in concert with max-resources-per-entry to keep memory
+// usage low-ish. The default of the combo of the two is ~50k.
+VARCACHE_PREF(
+  "network.predictor.max-uri-length",
+   network_predictor_max_uri_length,
+  uint32_t, 500
+)
+
+PREF("network.predictor.cleaned-up", bool, false)
+
+// A testing flag.
+VARCACHE_PREF(
+  "network.predictor.doing-tests",
+   network_predictor_doing_tests,
+  bool, false
+)
+
 //---------------------------------------------------------------------------
 // Preferences prefs
 //---------------------------------------------------------------------------
 
 PREF("preferences.allow.omt-write", bool, true)
 
 //---------------------------------------------------------------------------
 // End of prefs
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -912,16 +912,17 @@ pref("gfx.webrender.enabled", false);
 #endif
 #ifdef XP_WIN
 pref("gfx.webrender.force-angle", true);
 pref("gfx.webrender.program-binary", true);
 #endif
 
 pref("gfx.webrender.highlight-painted-layers", false);
 pref("gfx.webrender.blob-images", 1);
+pref("gfx.webrender.blob.invalidation", false);
 pref("gfx.webrender.hit-test", true);
 
 // WebRender debugging utilities.
 pref("gfx.webrender.debug.texture-cache", false);
 pref("gfx.webrender.debug.render-targets", false);
 pref("gfx.webrender.debug.alpha-primitives", false);
 pref("gfx.webrender.debug.profiler", false);
 pref("gfx.webrender.debug.gpu-time-queries", false);
@@ -2167,39 +2168,16 @@ pref("network.ftp.idleConnectionTimeout"
 pref("network.dir.format", 2);
 
 // enables the prefetch service (i.e., prefetching of <link rel="next"> and
 // <link rel="prefetch"> URLs).
 pref("network.prefetch-next", true);
 // enables the preloading (i.e., preloading of <link rel="preload"> URLs).
 pref("network.preload", false);
 
-// enables the predictive service
-pref("network.predictor.enabled", true);
-pref("network.predictor.enable-hover-on-ssl", false);
-pref("network.predictor.enable-prefetch", false);
-pref("network.predictor.page-degradation.day", 0);
-pref("network.predictor.page-degradation.week", 5);
-pref("network.predictor.page-degradation.month", 10);
-pref("network.predictor.page-degradation.year", 25);
-pref("network.predictor.page-degradation.max", 50);
-pref("network.predictor.subresource-degradation.day", 1);
-pref("network.predictor.subresource-degradation.week", 10);
-pref("network.predictor.subresource-degradation.month", 25);
-pref("network.predictor.subresource-degradation.year", 50);
-pref("network.predictor.subresource-degradation.max", 100);
-pref("network.predictor.prefetch-rolling-load-count", 10);
-pref("network.predictor.prefetch-min-confidence", 100);
-pref("network.predictor.preconnect-min-confidence", 90);
-pref("network.predictor.preresolve-min-confidence", 60);
-pref("network.predictor.prefetch-force-valid-for", 10);
-pref("network.predictor.max-resources-per-entry", 100);
-pref("network.predictor.max-uri-length", 500);
-pref("network.predictor.cleaned-up", false);
-
 // The following prefs pertain to the negotiate-auth extension (see bug 17578),
 // which provides transparent Kerberos or NTLM authentication using the SPNEGO
 // protocol.  Each pref is a comma-separated list of keys, where each key has
 // the format:
 //   [scheme "://"] [host [":" port]]
 // For example, "foo.com" would match "http://www.foo.com/bar", etc.
 
 // Force less-secure NTLMv1 when needed (NTLMv2 is the default).
--- a/netwerk/base/Predictor.cpp
+++ b/netwerk/base/Predictor.cpp
@@ -33,16 +33,17 @@
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/NeckoParent.h"
 
 #include "LoadContextInfo.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "SerializedLoadContext.h"
@@ -68,81 +69,16 @@ static LazyLogModule gPredictorLog("Netw
   do { \
     if (NS_FAILED(_rv)) { \
       return; \
     } \
   } while (0)
 
 #define NOW_IN_SECONDS() static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC)
 
-
-static const char PREDICTOR_ENABLED_PREF[] = "network.predictor.enabled";
-static const char PREDICTOR_SSL_HOVER_PREF[] = "network.predictor.enable-hover-on-ssl";
-static const char PREDICTOR_PREFETCH_PREF[] = "network.predictor.enable-prefetch";
-
-static const char PREDICTOR_PAGE_DELTA_DAY_PREF[] =
-  "network.predictor.page-degradation.day";
-static const int32_t PREDICTOR_PAGE_DELTA_DAY_DEFAULT = 0;
-static const char PREDICTOR_PAGE_DELTA_WEEK_PREF[] =
-  "network.predictor.page-degradation.week";
-static const int32_t PREDICTOR_PAGE_DELTA_WEEK_DEFAULT = 5;
-static const char PREDICTOR_PAGE_DELTA_MONTH_PREF[] =
-  "network.predictor.page-degradation.month";
-static const int32_t PREDICTOR_PAGE_DELTA_MONTH_DEFAULT = 10;
-static const char PREDICTOR_PAGE_DELTA_YEAR_PREF[] =
-  "network.predictor.page-degradation.year";
-static const int32_t PREDICTOR_PAGE_DELTA_YEAR_DEFAULT = 25;
-static const char PREDICTOR_PAGE_DELTA_MAX_PREF[] =
-  "network.predictor.page-degradation.max";
-static const int32_t PREDICTOR_PAGE_DELTA_MAX_DEFAULT = 50;
-static const char PREDICTOR_SUB_DELTA_DAY_PREF[] =
-  "network.predictor.subresource-degradation.day";
-static const int32_t PREDICTOR_SUB_DELTA_DAY_DEFAULT = 1;
-static const char PREDICTOR_SUB_DELTA_WEEK_PREF[] =
-  "network.predictor.subresource-degradation.week";
-static const int32_t PREDICTOR_SUB_DELTA_WEEK_DEFAULT = 10;
-static const char PREDICTOR_SUB_DELTA_MONTH_PREF[] =
-  "network.predictor.subresource-degradation.month";
-static const int32_t PREDICTOR_SUB_DELTA_MONTH_DEFAULT = 25;
-static const char PREDICTOR_SUB_DELTA_YEAR_PREF[] =
-  "network.predictor.subresource-degradation.year";
-static const int32_t PREDICTOR_SUB_DELTA_YEAR_DEFAULT = 50;
-static const char PREDICTOR_SUB_DELTA_MAX_PREF[] =
-  "network.predictor.subresource-degradation.max";
-static const int32_t PREDICTOR_SUB_DELTA_MAX_DEFAULT = 100;
-
-static const char PREDICTOR_PREFETCH_ROLLING_LOAD_PREF[] =
-  "network.predictor.prefetch-rolling-load-count";
-static const int32_t PREFETCH_ROLLING_LOAD_DEFAULT = 10;
-static const char PREDICTOR_PREFETCH_MIN_PREF[] =
-  "network.predictor.prefetch-min-confidence";
-static const int32_t PREFETCH_MIN_DEFAULT = 100;
-static const char PREDICTOR_PRECONNECT_MIN_PREF[] =
-  "network.predictor.preconnect-min-confidence";
-static const int32_t PRECONNECT_MIN_DEFAULT = 90;
-static const char PREDICTOR_PRERESOLVE_MIN_PREF[] =
-  "network.predictor.preresolve-min-confidence";
-static const int32_t PRERESOLVE_MIN_DEFAULT = 60;
-
-static const char PREDICTOR_PREFETCH_FORCE_VALID_PREF[] =
-  "network.predictor.prefetch-force-valid-for";
-static const int32_t PREFETCH_FORCE_VALID_DEFAULT = 10;
-
-static const char PREDICTOR_MAX_RESOURCES_PREF[] =
-  "network.predictor.max-resources-per-entry";
-static const uint32_t PREDICTOR_MAX_RESOURCES_DEFAULT = 100;
-
-// This is selected in concert with max-resources-per-entry to keep memory usage
-// low-ish. The default of the combo of the two is ~50k
-static const char PREDICTOR_MAX_URI_LENGTH_PREF[] =
-  "network.predictor.max-uri-length";
-static const uint32_t PREDICTOR_MAX_URI_LENGTH_DEFAULT = 500;
-
-static const char PREDICTOR_DOING_TESTS_PREF[] = "network.predictor.doing-tests";
-
 static const char PREDICTOR_CLEANED_UP_PREF[] = "network.predictor.cleaned-up";
 
 // All these time values are in sec
 static const uint32_t ONE_DAY = 86400U;
 static const uint32_t ONE_WEEK = 7U * ONE_DAY;
 static const uint32_t ONE_MONTH = 30U * ONE_DAY;
 static const uint32_t ONE_YEAR = 365U * ONE_DAY;
 
@@ -324,38 +260,17 @@ NS_IMPL_ISUPPORTS(Predictor,
                   nsIObserver,
                   nsISpeculativeConnectionOverrider,
                   nsIInterfaceRequestor,
                   nsICacheEntryMetaDataVisitor,
                   nsINetworkPredictorVerifier)
 
 Predictor::Predictor()
   :mInitialized(false)
-  ,mEnabled(true)
-  ,mEnableHoverOnSSL(false)
-  ,mEnablePrefetch(true)
-  ,mPageDegradationDay(PREDICTOR_PAGE_DELTA_DAY_DEFAULT)
-  ,mPageDegradationWeek(PREDICTOR_PAGE_DELTA_WEEK_DEFAULT)
-  ,mPageDegradationMonth(PREDICTOR_PAGE_DELTA_MONTH_DEFAULT)
-  ,mPageDegradationYear(PREDICTOR_PAGE_DELTA_YEAR_DEFAULT)
-  ,mPageDegradationMax(PREDICTOR_PAGE_DELTA_MAX_DEFAULT)
-  ,mSubresourceDegradationDay(PREDICTOR_SUB_DELTA_DAY_DEFAULT)
-  ,mSubresourceDegradationWeek(PREDICTOR_SUB_DELTA_WEEK_DEFAULT)
-  ,mSubresourceDegradationMonth(PREDICTOR_SUB_DELTA_MONTH_DEFAULT)
-  ,mSubresourceDegradationYear(PREDICTOR_SUB_DELTA_YEAR_DEFAULT)
-  ,mSubresourceDegradationMax(PREDICTOR_SUB_DELTA_MAX_DEFAULT)
-  ,mPrefetchRollingLoadCount(PREFETCH_ROLLING_LOAD_DEFAULT)
-  ,mPrefetchMinConfidence(PREFETCH_MIN_DEFAULT)
-  ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT)
-  ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT)
-  ,mPrefetchForceValidFor(PREFETCH_FORCE_VALID_DEFAULT)
-  ,mMaxResourcesPerEntry(PREDICTOR_MAX_RESOURCES_DEFAULT)
   ,mStartupCount(1)
-  ,mMaxURILength(PREDICTOR_MAX_URI_LENGTH_DEFAULT)
-  ,mDoingTests(false)
 {
   MOZ_ASSERT(!sSelf, "multiple Predictor instances!");
   sSelf = this;
 }
 
 Predictor::~Predictor()
 {
   if (mInitialized)
@@ -376,80 +291,18 @@ Predictor::InstallObserver()
     mozilla::services::GetObserverService();
   if (!obs) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  Preferences::AddBoolVarCache(&mEnabled, PREDICTOR_ENABLED_PREF, true);
-  Preferences::AddBoolVarCache(&mEnableHoverOnSSL,
-                               PREDICTOR_SSL_HOVER_PREF, false);
-  Preferences::AddBoolVarCache(&mEnablePrefetch, PREDICTOR_PREFETCH_PREF, true);
-  Preferences::AddIntVarCache(&mPageDegradationDay,
-                              PREDICTOR_PAGE_DELTA_DAY_PREF,
-                              PREDICTOR_PAGE_DELTA_DAY_DEFAULT);
-  Preferences::AddIntVarCache(&mPageDegradationWeek,
-                              PREDICTOR_PAGE_DELTA_WEEK_PREF,
-                              PREDICTOR_PAGE_DELTA_WEEK_DEFAULT);
-  Preferences::AddIntVarCache(&mPageDegradationMonth,
-                              PREDICTOR_PAGE_DELTA_MONTH_PREF,
-                              PREDICTOR_PAGE_DELTA_MONTH_DEFAULT);
-  Preferences::AddIntVarCache(&mPageDegradationYear,
-                              PREDICTOR_PAGE_DELTA_YEAR_PREF,
-                              PREDICTOR_PAGE_DELTA_YEAR_DEFAULT);
-  Preferences::AddIntVarCache(&mPageDegradationMax,
-                              PREDICTOR_PAGE_DELTA_MAX_PREF,
-                              PREDICTOR_PAGE_DELTA_MAX_DEFAULT);
-
-  Preferences::AddIntVarCache(&mSubresourceDegradationDay,
-                              PREDICTOR_SUB_DELTA_DAY_PREF,
-                              PREDICTOR_SUB_DELTA_DAY_DEFAULT);
-  Preferences::AddIntVarCache(&mSubresourceDegradationWeek,
-                              PREDICTOR_SUB_DELTA_WEEK_PREF,
-                              PREDICTOR_SUB_DELTA_WEEK_DEFAULT);
-  Preferences::AddIntVarCache(&mSubresourceDegradationMonth,
-                              PREDICTOR_SUB_DELTA_MONTH_PREF,
-                              PREDICTOR_SUB_DELTA_MONTH_DEFAULT);
-  Preferences::AddIntVarCache(&mSubresourceDegradationYear,
-                              PREDICTOR_SUB_DELTA_YEAR_PREF,
-                              PREDICTOR_SUB_DELTA_YEAR_DEFAULT);
-  Preferences::AddIntVarCache(&mSubresourceDegradationMax,
-                              PREDICTOR_SUB_DELTA_MAX_PREF,
-                              PREDICTOR_SUB_DELTA_MAX_DEFAULT);
-
-  Preferences::AddIntVarCache(&mPrefetchRollingLoadCount,
-                              PREDICTOR_PREFETCH_ROLLING_LOAD_PREF,
-                              PREFETCH_ROLLING_LOAD_DEFAULT);
-  Preferences::AddIntVarCache(&mPrefetchMinConfidence,
-                              PREDICTOR_PREFETCH_MIN_PREF,
-                              PREFETCH_MIN_DEFAULT);
-  Preferences::AddIntVarCache(&mPreconnectMinConfidence,
-                              PREDICTOR_PRECONNECT_MIN_PREF,