Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 28 Jul 2017 17:40:43 -0700
changeset 420449 ec666e910442ae55686160c1bd0c93b00a08dead
parent 420403 ec329722b2f8bad3b1b9d0829e8d89764a879fd1 (current diff)
parent 420448 9bf3949a7ae8577007f7692cdb9beb3bc85fae8b (diff)
child 420450 2fba314d7de77ad8ab693a2ea0112c0cda5dd564
child 420483 d394c7b2df1d26f7348f6092675a4d0b94926fb3
child 420593 a09d4e4b31bc001d2a050fa311d32fca37b28db4
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge MozReview-Commit-ID: JNxfwQh0cac
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderCompositableHolder.cpp
gfx/layers/wr/WebRenderCompositableHolder.h
gfx/layers/wr/WebRenderLayer.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
modules/libpref/init/all.js
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
testing/web-platform/meta/streams/readable-byte-streams/general.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-byte-streams/general.html.ini
testing/web-platform/meta/streams/readable-byte-streams/general.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-byte-streams/general.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/bad-underlying-sources.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/floating-point-total-queue-size.sharedworker.html.ini
testing/web-platform/meta/streams/readable-streams/general.dedicatedworker.html.ini
testing/web-platform/meta/streams/readable-streams/general.html.ini
testing/web-platform/meta/streams/readable-streams/general.serviceworker.https.html.ini
testing/web-platform/meta/streams/readable-streams/general.sharedworker.html.ini
toolkit/components/urlformatter/api_keys.in
widget/nsBaseWidget.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1617,16 +1617,19 @@ var gBrowserInit = {
       document.getElementById("textfieldDirection-swap").hidden = false;
     }
 
     // Setup click-and-hold gestures access to the session history
     // menus if global click-and-hold isn't turned on
     if (!getBoolPref("ui.click_hold_context_menus", false))
       SetClickAndHoldHandlers();
 
+    Cu.import("resource:///modules/UpdateTopLevelContentWindowIDHelper.jsm", {})
+      .trackBrowserWindow(window);
+
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
     // active downloads) and speeds up the first-load of the download manager UI.
new file mode 100644
--- /dev/null
+++ b/browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
@@ -0,0 +1,141 @@
+/* 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 module tracks each browser window and informs network module
+ * the current selected tab's content outer window ID.
+ */
+
+this.EXPORTED_SYMBOLS = ["trackBrowserWindow"];
+
+const Ci = Components.interfaces;
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Lazy getters
+XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
+                                   "@mozilla.org/focus-manager;1",
+                                   "nsIFocusManager");
+
+// Constants
+const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
+const WINDOW_EVENTS = ["activate", "unload"];
+const DEBUG = false;
+
+// Variables
+var _lastFocusedWindow = null;
+var _lastTopLevelWindowID = 0;
+
+// Exported symbol
+this.trackBrowserWindow = function trackBrowserWindow(aWindow) {
+  WindowHelper.addWindow(aWindow);
+}
+
+// Global methods
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- UpdateTopLevelContentWindowIDHelper: " + s + "\n");
+  }
+}
+
+function _updateCurrentContentOuterWindowID(aBrowser) {
+  if (!aBrowser.outerWindowID ||
+      aBrowser.outerWindowID === _lastTopLevelWindowID) {
+    return;
+  }
+
+  debug("Current window uri=" + aBrowser.currentURI.spec +
+        " id=" + aBrowser.outerWindowID);
+
+  _lastTopLevelWindowID = aBrowser.outerWindowID;
+  let windowIDWrapper = Components.classes["@mozilla.org/supports-PRUint64;1"]
+                          .createInstance(Ci.nsISupportsPRUint64);
+  windowIDWrapper.data = _lastTopLevelWindowID;
+  Services.obs.notifyObservers(windowIDWrapper,
+                               "net:current-toplevel-outer-content-windowid");
+}
+
+function _handleEvent(aEvent) {
+  switch (aEvent.type) {
+    case "TabBrowserInserted":
+      if (aEvent.target.ownerGlobal.gBrowser.selectedBrowser === aEvent.target.linkedBrowser) {
+        _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
+      }
+      break;
+    case "TabSelect":
+      _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
+      break;
+    case "activate":
+      WindowHelper.onActivate(aEvent.target);
+      break;
+    case "unload":
+      WindowHelper.removeWindow(aEvent.currentTarget);
+      break;
+  }
+}
+
+function _handleMessage(aMessage) {
+  let browser = aMessage.target;
+  if (aMessage.name === "Browser:Init") {
+    if (browser === browser.ownerGlobal.gBrowser.selectedBrowser) {
+      _updateCurrentContentOuterWindowID(browser);
+    }
+  }
+}
+
+// Methods that impact a window. Put into single object for organization.
+var WindowHelper = {
+  addWindow: function NP_WH_addWindow(aWindow) {
+    // Add event listeners
+    TAB_EVENTS.forEach(function(event) {
+      aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent);
+    });
+    WINDOW_EVENTS.forEach(function(event) {
+      aWindow.addEventListener(event, _handleEvent);
+    });
+
+    let messageManager = aWindow.getGroupMessageManager("browsers");
+    messageManager.addMessageListener("Browser:Init", _handleMessage);
+
+    // This gets called AFTER activate event, so if this is the focused window
+    // we want to activate it.
+    if (aWindow == _focusManager.activeWindow)
+      this.handleFocusedWindow(aWindow);
+
+    // Update the selected tab's content outer window ID.
+    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
+  },
+
+  removeWindow: function NP_WH_removeWindow(aWindow) {
+    if (aWindow == _lastFocusedWindow)
+      _lastFocusedWindow = null;
+
+    // Remove the event listeners
+    TAB_EVENTS.forEach(function(event) {
+      aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent);
+    });
+    WINDOW_EVENTS.forEach(function(event) {
+      aWindow.removeEventListener(event, _handleEvent);
+    });
+
+    let messageManager = aWindow.getGroupMessageManager("browsers");
+    messageManager.removeMessageListener("Browser:Init", _handleMessage);
+  },
+
+  onActivate: function NP_WH_onActivate(aWindow, aHasFocus) {
+    // If this window was the last focused window, we don't need to do anything
+    if (aWindow == _lastFocusedWindow)
+      return;
+
+    this.handleFocusedWindow(aWindow);
+
+    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
+  },
+
+  handleFocusedWindow: function NP_WH_handleFocusedWindow(aWindow) {
+    // aWindow is now focused
+    _lastFocusedWindow = aWindow;
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -107,16 +107,19 @@ with Files("WindowsJumpLists.jsm"):
     BUG_COMPONENT = ("Firefox", "Shell Integration")
 
 with Files("WindowsPreviewPerTab.jsm"):
     BUG_COMPONENT = ("Core", "Widget: Win32")
 
 with Files("offlineAppCache.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
 
+with Files("UpdateTopLevelContentWindowIDHelper.jsm"):
+    BUG_COMPONENT = ("Core", "Networking")
+
 with Files("webrtcUI.jsm"):
     BUG_COMPONENT = ("Firefox", "Device Permissions")
 
 with Files("ZoomUI.jsm"):
     BUG_COMPONENT = ("Firefox", "Toolbars and Customization")
 
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
@@ -152,16 +155,17 @@ EXTRA_JS_MODULES += [
     'ReaderParent.jsm',
     'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SitePermissions.jsm',
     'Social.jsm',
     'SocialService.jsm',
     'TransientPrefs.jsm',
+    'UpdateTopLevelContentWindowIDHelper.jsm',
     'webrtcUI.jsm',
     'ZoomUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
--- a/devtools/client/netmonitor/src/components/toolbar.js
+++ b/devtools/client/netmonitor/src/components/toolbar.js
@@ -12,20 +12,21 @@ const {
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { FILTER_SEARCH_DELAY } = require("../constants");
 const {
   getDisplayedRequestsSummary,
   getRequestFilterTypes,
+  getTypeFilteredRequests,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
 
-const { autocompleteProvider } = require("../utils/filter-text-utils");
+const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
 const { L10N } = require("../utils/l10n");
 
 // Components
 const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
 
 const { button, div, input, label, span } = DOM;
 
 const COLLPASE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
@@ -49,16 +50,17 @@ const Toolbar = createClass({
     setRequestFilterText: PropTypes.func.isRequired,
     networkDetailsToggleDisabled: PropTypes.bool.isRequired,
     networkDetailsOpen: PropTypes.bool.isRequired,
     toggleNetworkDetails: PropTypes.func.isRequired,
     disableBrowserCache: PropTypes.func.isRequired,
     toggleBrowserCache: PropTypes.func.isRequired,
     browserCacheDisabled: PropTypes.bool.isRequired,
     toggleRequestFilterType: PropTypes.func.isRequired,
+    filteredRequests: PropTypes.object.isRequired,
   },
 
   toggleRequestFilterType(evt) {
     if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
       return;
     }
     this.props.toggleRequestFilterType(evt.target.dataset.key);
   },
@@ -68,16 +70,17 @@ const Toolbar = createClass({
       clearRequests,
       requestFilterTypes,
       setRequestFilterText,
       networkDetailsToggleDisabled,
       networkDetailsOpen,
       toggleNetworkDetails,
       toggleBrowserCache,
       browserCacheDisabled,
+      filteredRequests,
     } = this.props;
 
     let toggleButtonClassName = [
       "network-details-panel-toggle",
       "devtools-button",
     ];
     if (!networkDetailsOpen) {
       toggleButtonClassName.push("pane-collapsed");
@@ -127,17 +130,18 @@ const Toolbar = createClass({
         ),
         span({ className: "devtools-toolbar-group" },
           SearchBox({
             delay: FILTER_SEARCH_DELAY,
             keyShortcut: SEARCH_KEY_SHORTCUT,
             placeholder: SEARCH_PLACE_HOLDER,
             type: "filter",
             onChange: setRequestFilterText,
-            autocompleteProvider,
+            autocompleteProvider: filter =>
+              autocompleteProvider(filter, filteredRequests),
           }),
           button({
             className: toggleButtonClassName.join(" "),
             title: networkDetailsOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
             disabled: networkDetailsToggleDisabled,
             tabIndex: "0",
             onClick: toggleNetworkDetails,
           }),
@@ -163,16 +167,17 @@ const Toolbar = createClass({
 });
 
 module.exports = connect(
   (state) => ({
     networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
     networkDetailsOpen: state.ui.networkDetailsOpen,
     browserCacheDisabled: state.ui.browserCacheDisabled,
     requestFilterTypes: getRequestFilterTypes(state),
+    filteredRequests: getTypeFilteredRequests(state),
     summary: getDisplayedRequestsSummary(state),
   }),
   (dispatch) => ({
     clearRequests: () => dispatch(Actions.clearRequests()),
     setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
     toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
     toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
     disableBrowserCache: (disabled) => dispatch(Actions.disableBrowserCache(disabled)),
--- a/devtools/client/netmonitor/src/selectors/requests.js
+++ b/devtools/client/netmonitor/src/selectors/requests.js
@@ -40,16 +40,26 @@ const getFilterFn = createSelector(
   filters => r => {
     const matchesType = filters.requestFilterTypes.some((enabled, filter) => {
       return enabled && Filters[filter] && Filters[filter](r);
     });
     return matchesType && isFreetextMatch(r, filters.requestFilterText);
   }
 );
 
+const getTypeFilterFn = createSelector(
+  state => state.filters,
+  filters => r => {
+    const matchesType = filters.requestFilterTypes.some((enabled, filter) => {
+      return enabled && Filters[filter] && Filters[filter](r);
+    });
+    return matchesType;
+  }
+);
+
 const getSortFn = createSelector(
   state => state.requests.requests,
   state => state.sort,
   (requests, sort) => {
     const sorter = Sorters[sort.type || "waterfall"];
     const ascending = sort.ascending ? +1 : -1;
     return (a, b) => ascending * sortWithClones(requests, sorter, a, b);
   }
@@ -64,16 +74,22 @@ const getSortedRequests = createSelector
 const getDisplayedRequests = createSelector(
   state => state.requests.requests,
   getFilterFn,
   getSortFn,
   (requests, filterFn, sortFn) => requests.valueSeq()
     .filter(filterFn).sort(sortFn).toList()
 );
 
+const getTypeFilteredRequests = createSelector(
+  state => state.requests.requests,
+  getTypeFilterFn,
+  (requests, filterFn) => requests.valueSeq().filter(filterFn).toList()
+);
+
 const getDisplayedRequestsSummary = createSelector(
   getDisplayedRequests,
   state => state.requests.lastEndedMillis - state.requests.firstStartedMillis,
   (requests, totalMillis) => {
     if (requests.size == 0) {
       return { count: 0, bytes: 0, millis: 0 };
     }
 
@@ -113,9 +129,10 @@ function getDisplayedRequestById(state, 
 
 module.exports = {
   getDisplayedRequestById,
   getDisplayedRequests,
   getDisplayedRequestsSummary,
   getRequestById,
   getSelectedRequest,
   getSortedRequests,
+  getTypeFilteredRequests,
 };
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
@@ -0,0 +1,177 @@
+/* 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 { FILTER_FLAGS } = require("../constants");
+
+/*
+ * Generates a value for the given filter
+ * ie. if flag = status-code, will generate "200" from the given request item.
+ * For flags related to cookies, it might generate an array based on the request
+ * ie. ["cookie-name-1", "cookie-name-2", ...]
+ *
+ * @param {string} flag - flag specified in filter, ie. "status-code"
+ * @param {object} request - Network request item
+ * @return {string|Array} - The output is a string or an array based on the request
+ */
+function getAutocompleteValuesForFlag(flag, request) {
+  let values = [];
+  let { responseCookies = { cookies: [] } } = request;
+  responseCookies = responseCookies.cookies || responseCookies;
+
+  switch (flag) {
+    case "status-code":
+      // Sometimes status comes as Number
+      values.push(String(request.status));
+      break;
+    case "scheme":
+      values.push(request.urlDetails.scheme);
+      break;
+    case "domain":
+      values.push(request.urlDetails.host);
+      break;
+    case "remote-ip":
+      values.push(request.remoteAddress);
+      break;
+    case "cause":
+      values.push(request.cause.type);
+      break;
+    case "mime-type":
+      values.push(request.mimeType);
+      break;
+    case "set-cookie-name":
+      values = responseCookies.map(c => c.name);
+      break;
+    case "set-cookie-value":
+      values = responseCookies.map(c => c.value);
+      break;
+    case "set-cookie-domain":
+      values = responseCookies.map(c => c.hasOwnProperty("domain") ?
+          c.domain : request.urlDetails.host);
+      break;
+    case "is":
+      values = ["cached", "from-cache", "running"];
+      break;
+    case "has-response-header":
+      // Some requests not having responseHeaders..?
+      values = request.responseHeaders &&
+        request.responseHeaders.headers.map(h => h.name);
+      break;
+    case "protocol":
+      values.push(request.httpVersion);
+      break;
+    case "method":
+    default:
+      values.push(request[flag]);
+  }
+
+  return values;
+}
+
+/*
+ * For a given lastToken passed ie. "is:", returns an array of populated flag
+ * values for consumption in autocompleteProvider
+ * ie. ["is:cached", "is:running", "is:from-cache"]
+ *
+ * @param {string} lastToken - lastToken parsed from filter input, ie "is:"
+ * @param {object} requests - List of requests from which values are generated
+ * @return {Array} - array of autocomplete values
+ */
+function getLastTokenFlagValues(lastToken, requests) {
+  // The last token must be a string like "method:GET" or "method:", Any token
+  // without a ":" cant be used to parse out flag values
+  if (!lastToken.includes(":")) {
+    return [];
+  }
+
+  // Parse out possible flag from lastToken
+  let [flag, typedFlagValue] = lastToken.split(":");
+  let isNegativeFlag = false;
+
+  // Check if flag is used with negative match
+  if (flag.startsWith("-")) {
+    flag = flag.slice(1);
+    isNegativeFlag = true;
+  }
+
+  // Flag is some random string, return
+  if (!FILTER_FLAGS.includes(flag)) {
+    return [];
+  }
+
+  let values = [];
+  for (let request of requests) {
+    values.push(...getAutocompleteValuesForFlag(flag, request));
+  }
+  values = [...new Set(values)];
+
+  return values
+    .filter(value => {
+      if (typedFlagValue) {
+        let lowerTyped = typedFlagValue.toLowerCase(),
+          lowerValue = value.toLowerCase();
+        return lowerValue.includes(lowerTyped) && lowerValue !== lowerTyped;
+      }
+      return typeof value !== "undefined" && value !== "" && value !== "undefined";
+    })
+    .sort()
+    .map(value => isNegativeFlag ? `-${flag}:${value}` : `${flag}:${value}`);
+}
+
+/**
+ * Generates an autocomplete list for the search-box for network monitor
+ *
+ * It expects an entire string of the searchbox ie "is:cached pr".
+ * The string is then tokenized into "is:cached" and "pr"
+ *
+ * @param {string} filter - The entire search string of the search box
+ * @param {object} requests - Iteratable object of requests displayed
+ * @return {Array} - The output is an array of objects as below
+ * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
+ * `value` is used to update the search-box input box for given item
+ * `displayValue` is used to render the autocomplete list
+ */
+function autocompleteProvider(filter, requests) {
+  if (!filter) {
+    return [];
+  }
+
+  let negativeAutocompleteList = FILTER_FLAGS.map((item) => `-${item}`);
+  let baseList = [...FILTER_FLAGS, ...negativeAutocompleteList]
+    .map((item) => `${item}:`);
+
+  // The last token is used to filter the base autocomplete list
+  let tokens = filter.split(/\s+/g);
+  let lastToken = tokens[tokens.length - 1];
+  let previousTokens = tokens.slice(0, tokens.length - 1);
+
+  // Autocomplete list is not generated for empty lastToken
+  if (!lastToken) {
+    return [];
+  }
+
+  let autocompleteList;
+  let availableValues = getLastTokenFlagValues(lastToken, requests);
+  if (availableValues.length > 0) {
+    autocompleteList = availableValues;
+  } else {
+    autocompleteList = baseList
+      .filter((item) => {
+        return item.toLowerCase().startsWith(lastToken.toLowerCase())
+          && item.toLowerCase() !== lastToken.toLowerCase();
+      });
+  }
+
+  return autocompleteList
+    .sort()
+    .map(item => ({
+      value: [...previousTokens, item].join(" "),
+      displayValue: item,
+    }));
+}
+
+module.exports = {
+  autocompleteProvider,
+};
--- a/devtools/client/netmonitor/src/utils/filter-text-utils.js
+++ b/devtools/client/netmonitor/src/utils/filter-text-utils.js
@@ -98,16 +98,21 @@ function processFlagFilter(type, value) 
       }
       return quantity * multiplier;
     default:
       return value.toLowerCase();
   }
 }
 
 function isFlagFilterMatch(item, { type, value, negative }) {
+  // Ensures when filter token is exactly a flag ie. "remote-ip:", all values are shown
+  if (value.length < 1) {
+    return true;
+  }
+
   let match = true;
   let { responseCookies = { cookies: [] } } = item;
   responseCookies = responseCookies.cookies || responseCookies;
   switch (type) {
     case "status-code":
       match = item.status === value;
       break;
     case "method":
@@ -180,28 +185,30 @@ function isFlagFilterMatch(item, { type,
         match = false;
       }
       break;
     case "set-cookie-domain":
       if (responseCookies.length > 0) {
         let host = item.urlDetails.host;
         let i = responseCookies.findIndex(c => {
           let domain = c.hasOwnProperty("domain") ? c.domain : host;
-          return domain === value;
+          return domain.includes(value);
         });
         match = i > -1;
       } else {
         match = false;
       }
       break;
     case "set-cookie-name":
-      match = responseCookies.findIndex(c => c.name.toLowerCase() === value) > -1;
+      match = responseCookies.findIndex(c =>
+        c.name.toLowerCase().includes(value)) > -1;
       break;
     case "set-cookie-value":
-      match = responseCookies.findIndex(c => c.value.toLowerCase() === value) > -1;
+      match = responseCookies.findIndex(c =>
+        c.value.toLowerCase().includes(value)) > -1;
       break;
   }
   if (negative) {
     return !match;
   }
   return match;
 }
 
@@ -237,55 +244,11 @@ function isFreetextMatch(item, text) {
 
   for (let flagFilter of filters.flags) {
     match = match && isFlagFilterMatch(item, flagFilter);
   }
 
   return match;
 }
 
-/**
- * Generates an autocomplete list for the search-box for network monitor
- *
- * It expects an entire string of the searchbox ie "is:cached pr".
- * The string is then tokenized into "is:cached" and "pr"
- *
- * @param {string} filter - The entire search string of the search box
- * @return {Array} - The output is an array of objects as below
- * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
- * `value` is used to update the search-box input box for given item
- * `displayValue` is used to render the autocomplete list
- */
-function autocompleteProvider(filter) {
-  if (!filter) {
-    return [];
-  }
-
-  let negativeAutocompleteList = FILTER_FLAGS.map((item) => `-${item}`);
-  let baseList = [...FILTER_FLAGS, ...negativeAutocompleteList]
-    .map((item) => `${item}:`);
-
-  // The last token is used to filter the base autocomplete list
-  let tokens = filter.split(/\s+/g);
-  let lastToken = tokens[tokens.length - 1];
-  let previousTokens = tokens.slice(0, tokens.length - 1);
-
-  // Autocomplete list is not generated for empty lastToken
-  if (!lastToken) {
-    return [];
-  }
-
-  return baseList
-    .filter((item) => {
-      return item.toLowerCase().startsWith(lastToken.toLowerCase())
-        && item.toLowerCase() !== lastToken.toLowerCase();
-    })
-    .sort()
-    .map(item => ({
-      value: [...previousTokens, item].join(" "),
-      displayValue: item,
-    }));
-}
-
 module.exports = {
   isFreetextMatch,
-  autocompleteProvider,
 };
--- a/devtools/client/netmonitor/src/utils/moz.build
+++ b/devtools/client/netmonitor/src/utils/moz.build
@@ -1,15 +1,16 @@
 # 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/.
 
 DevToolsModules(
     'create-store.js',
+    'filter-autocomplete-provider.js',
     'filter-predicates.js',
     'filter-text-utils.js',
     'format-utils.js',
     'l10n.js',
     'mdn-utils.js',
     'menu.js',
     'prefs.js',
     'request-utils.js',
--- a/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-autocomplete.js
@@ -1,39 +1,67 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+/**
+ * Test autocomplete based on filtering flags and requests
+ */
+const REQUESTS = [
+  { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined&text=Sample" },
+  { url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined&text=Sample" +
+         "&cookies=1" },
+  { url: "sjs_content-type-test-server.sjs?fmt=css&text=sample" },
+  { url: "sjs_content-type-test-server.sjs?fmt=js&text=sample" },
+  { url: "sjs_content-type-test-server.sjs?fmt=font" },
+  { url: "sjs_content-type-test-server.sjs?fmt=image" },
+  { url: "sjs_content-type-test-server.sjs?fmt=audio" },
+  { url: "sjs_content-type-test-server.sjs?fmt=video" },
+  { url: "sjs_content-type-test-server.sjs?fmt=gzip" },
+  { url: "sjs_status-codes-test-server.sjs?sts=304" },
+];
+
 function testAutocompleteContents(expected, document) {
   expected.forEach(function (item, i) {
     is(
       document
         .querySelector(
           `.devtools-autocomplete-listbox .autocomplete-item:nth-child(${i + 1})`
         )
         .textContent,
       item,
       `${expected[i]} found`
     );
   });
 }
 
 add_task(async function () {
   let { monitor } = await initNetMonitor(FILTERING_URL);
-  let { document, window } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
+  let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
+  store.dispatch(Actions.batchEnable(false));
 
   info("Starting test... ");
 
+  // Let the requests load completely before the autocomplete tests begin
+  // as autocomplete values also rely on the network requests.
+  let waitNetwork = waitForNetworkEvents(monitor, REQUESTS.length);
+  loadCommonFrameScript();
+  await performRequestsInContent(REQUESTS);
+  await waitNetwork;
+
   EventUtils.synthesizeMouseAtCenter(
     document.querySelector(".devtools-filterinput"), {}, window);
   // Empty Mouse click should keep autocomplete hidden
   ok(!document.querySelector(".devtools-autocomplete-popup"),
-    "Autocomplete Popup Created");
+    "Autocomplete Popup still hidden");
 
+  document.querySelector(".devtools-filterinput").focus();
   // Typing a char should invoke a autocomplete
   EventUtils.synthesizeKey("s", {});
   ok(document.querySelector(".devtools-autocomplete-popup"),
     "Autocomplete Popup Created");
   testAutocompleteContents([
     "scheme:",
     "set-cookie-domain:",
     "set-cookie-name:",
@@ -41,35 +69,52 @@ add_task(async function () {
     "size:",
     "status-code:",
   ], document);
 
   EventUtils.synthesizeKey("c", {});
   testAutocompleteContents(["scheme:"], document);
   EventUtils.synthesizeKey("VK_TAB", {});
   // Tab selection should hide autocomplete
+  ok(document.querySelector(".devtools-autocomplete-popup"),
+    "Autocomplete Popup alive with content values");
+  testAutocompleteContents(["scheme:http"], document);
+
+  EventUtils.synthesizeKey("VK_RETURN", {});
+  is(document.querySelector(".devtools-filterinput").value,
+    "scheme:http", "Value correctly set after Enter");
   ok(!document.querySelector(".devtools-autocomplete-popup"),
-    "Autocomplete Popup Hidden");
-  is(document.querySelector(".devtools-filterinput").value,
-    "scheme:", "Value correctly set after TAB");
+    "Autocomplete Popup hidden after keyboard Enter key");
 
   // Space separated tokens
-  EventUtils.synthesizeKey("https ", {});
-  // Adding just a space should keep popup hidden
-  ok(!document.querySelector(".devtools-autocomplete-popup"),
-    "Autocomplete Popup still hidden");
-
   // The last token where autocomplete is availabe shall generate the popup
-  EventUtils.synthesizeKey("p", {});
+  EventUtils.synthesizeKey(" p", {});
   testAutocompleteContents(["protocol:"], document);
 
   // The new value of the text box should be previousTokens + latest value selected
+  // First return selects "protocol:"
+  EventUtils.synthesizeKey("VK_RETURN", {});
+  // Second return selects "protocol:HTTP/1.1"
   EventUtils.synthesizeKey("VK_RETURN", {});
   is(document.querySelector(".devtools-filterinput").value,
-    "scheme:https protocol:", "Tokenized click generates correct value in input box");
+    "scheme:http protocol:HTTP/1.1",
+    "Tokenized click generates correct value in input box");
+
+  // Explicitly type in `flag:` renders autocomplete with values
+  EventUtils.synthesizeKey(" status-code:", {});
+  testAutocompleteContents(["status-code:200", "status-code:304"], document);
+
+  // Typing the exact value closes autocomplete
+  EventUtils.synthesizeKey("304", {});
+  ok(!document.querySelector(".devtools-autocomplete-popup"),
+    "Typing the exact value closes autocomplete");
+
+  // Check if mime-type has been correctly parsed out and values also get autocomplete
+  EventUtils.synthesizeKey(" mime-type:au", {});
+  testAutocompleteContents(["mime-type:audio/ogg"], document);
 
   // The negative filter flags
   EventUtils.synthesizeKey(" -", {});
   testAutocompleteContents([
     "-cause:",
     "-domain:",
     "-has-response-header:",
     "-is:",
@@ -84,10 +129,14 @@ add_task(async function () {
     "-set-cookie-name:",
     "-set-cookie-value:",
     "-size:",
     "-status-code:",
     "-transferred-larger-than:",
     "-transferred:",
   ], document);
 
+  // Autocomplete for negative filtering
+  EventUtils.synthesizeKey("is:", {});
+  testAutocompleteContents(["-is:cached", "-is:from-cache", "-is:running"], document);
+
   await teardown(monitor);
 });
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -78,16 +78,19 @@ html|button, html|select {
 }
 
 .devtools-autocomplete-listbox .autocomplete-item {
   width: 100%;
   background-color: transparent;
   border-radius: 4px;
   padding: 1px 0;
   cursor: default;
+  text-overflow: ellipsis;
+  white-space: pre;
+  overflow: hidden;
 }
 
 .devtools-autocomplete-listbox .autocomplete-item > .initial-value,
 .devtools-autocomplete-listbox .autocomplete-item > .autocomplete-value {
   margin: 0;
   padding: 0;
 }
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -62,16 +62,17 @@
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/SizeOfState.h"
 #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"
@@ -4132,8 +4133,19 @@ Element::ClearServoData() {
 
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
   nsExtendedDOMSlots *slots = ExtendedDOMSlots();
   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
   slots->mCustomElementData = aData;
 }
+
+size_t
+Element::SizeOfExcludingThis(SizeOfState& aState) const
+{
+  size_t n = FragmentOrElement::SizeOfExcludingThis(aState);
+
+  // XXX: measure mServoData.
+
+  return n;
+}
+
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -200,16 +200,18 @@ public:
   {
     NS_ASSERTION(!HasServoData(), "expected ServoData to be cleared earlier");
   }
 
 #endif // MOZILLA_INTERNAL_API
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)
 
+  NS_DECL_SIZEOF_EXCLUDING_THIS
+
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
 
   /**
    * Method to get the full state of this element.  See mozilla/EventStates.h
    * for the possible bits that could be set here.
    */
   EventStates State() const
   {
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -2492,25 +2492,25 @@ FragmentOrElement::FireNodeRemovedForChi
   for (child = GetFirstChild();
        child && child->GetParentNode() == this;
        child = child->GetNextSibling()) {
     nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
   }
 }
 
 size_t
-FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+FragmentOrElement::SizeOfExcludingThis(SizeOfState& aState) const
 {
   size_t n = 0;
-  n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
-  n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
+  n += nsIContent::SizeOfExcludingThis(aState);
+  n += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
 
   nsDOMSlots* slots = GetExistingDOMSlots();
   if (slots) {
-    n += slots->SizeOfIncludingThis(aMallocSizeOf);
+    n += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
   }
 
   return n;
 }
 
 void
 FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)
 {
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -825,24 +825,24 @@ Link::SetHrefAttribute(nsIURI *aURI)
   // "nature" of the nsIURL/nsIURI implementation.
   nsAutoCString href;
   (void)aURI->GetSpec(href);
   (void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
                           NS_ConvertUTF8toUTF16(href), true);
 }
 
 size_t
-Link::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+Link::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
 {
   size_t n = 0;
 
   if (mCachedURI) {
     nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
     if (iface) {
-      n += iface->SizeOfIncludingThis(aMallocSizeOf);
+      n += iface->SizeOfIncludingThis(aState.mMallocSizeOf);
     }
   }
 
   // The following members don't need to be measured:
   // - mElement, because it is a pointer-to-self used to avoid QIs
   // - mHistory, because it is non-owning
 
   return n;
--- a/dom/base/Link.h
+++ b/dom/base/Link.h
@@ -109,17 +109,17 @@ public:
    * Checks if DNS Prefetching is ok
    *
    * @returns boolean
    *          Defaults to true; should be overridden for specialised cases
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
   virtual size_t
-    SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+    SizeOfExcludingThis(mozilla::SizeOfState& aState) const;
 
   virtual bool ElementHasHref() const;
 
   // This is called by HTMLAnchorElement.
   void TryDNSPrefetch();
   void CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag,
                          nsWrapperCache::FlagsType aRequestedFlag);
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -200,17 +200,18 @@ TimeoutManager::MinSchedulingDelay() con
   // mExecutionBudget is -15ms
   // factor is 0.01, which is 1 ms/100ms
   // delay is 1000ms
   // then we will compute the minimum delay:
   // max(1000, - (- 15) * 1/0.01) = max(1000, 1500) = 1500
   TimeDuration unthrottled =
     isBackground ? TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)
                  : TimeDuration();
-  if (BudgetThrottlingEnabled() && mExecutionBudget < TimeDuration()) {
+  if (BudgetThrottlingEnabled(isBackground) &&
+      mExecutionBudget < TimeDuration()) {
     // Only throttle if execution budget is less than 0
     double factor = 1.0 / GetRegenerationFactor(mWindow.IsBackgroundInternal());
     return TimeDuration::Min(
       TimeDuration::FromMilliseconds(gBudgetThrottlingMaxDelay),
       TimeDuration::Max(unthrottled, -mExecutionBudget.MultDouble(factor)));
   }
   //
   return unthrottled;
@@ -327,18 +328,18 @@ TimeoutManager::UpdateBudget(const TimeS
 
   // The budget is adjusted by increasing it with the time since the
   // last budget update factored with the regeneration rate. If a
   // runnable has executed, subtract that duration from the
   // budget. The budget updated without consideration of wether the
   // window is active or not. If throttling is enabled and the window
   // is active and then becomes inactive, an overdrawn budget will
   // still be counted against the minimum delay.
-  if (BudgetThrottlingEnabled()) {
-    bool isBackground = mWindow.IsBackgroundInternal();
+  bool isBackground = mWindow.IsBackgroundInternal();
+  if (BudgetThrottlingEnabled(isBackground)) {
     double factor = GetRegenerationFactor(isBackground);
     TimeDuration regenerated = (aNow - mLastBudgetUpdate).MultDouble(factor);
     // Clamp the budget to the maximum allowed budget.
     mExecutionBudget = TimeDuration::Min(
       GetMaxBudget(isBackground), mExecutionBudget - aDuration + regenerated);
   }
   mLastBudgetUpdate = aNow;
 }
@@ -1138,16 +1139,18 @@ TimeoutManager::Thaw()
     aTimeout->SetWhenOrTimeRemaining(now, aTimeout->TimeRemaining());
     MOZ_DIAGNOSTIC_ASSERT(!aTimeout->When().IsNull());
   });
 }
 
 void
 TimeoutManager::UpdateBackgroundState()
 {
+  mExecutionBudget = GetMaxBudget(mWindow.IsBackgroundInternal());
+
   // When the window moves to the background or foreground we should
   // reschedule the TimeoutExecutor in case the MinSchedulingDelay()
   // changed.  Only do this if the window is not suspended and we
   // actually have a timeout.
   if (!mWindow.IsSuspended()) {
     OrderedTimeoutIterator iter(mNormalTimeouts, mTrackingTimeouts);
     Timeout* nextTimeout = iter.Next();
     if (nextTimeout) {
@@ -1203,28 +1206,32 @@ ThrottleTimeoutsCallback::Notify(nsITime
   mWindow->AsInner()->TimeoutManager().StartThrottlingTimeouts();
   mWindow = nullptr;
   return NS_OK;
 }
 
 }
 
 bool
-TimeoutManager::BudgetThrottlingEnabled() const
+TimeoutManager::BudgetThrottlingEnabled(bool aIsBackground) const
 {
   // A window can be throttled using budget if
   // * It isn't active
   // * If it isn't using user media
   // * If it isn't using WebRTC
   // * If it hasn't got open WebSockets
   // * If it hasn't got active IndexedDB databases
-  //
+
   // Note that we allow both foreground and background to be
   // considered for budget throttling. What determines if they are if
-  // budget throttling is enabled is the regeneration factor.
+  // budget throttling is enabled is the max budget.
+  if ((aIsBackground ? gBackgroundThrottlingMaxBudget
+       : gForegroundThrottlingMaxBudget) < 0) {
+    return false;
+  }
 
   if (!mBudgetThrottleTimeouts || IsActive()) {
     return false;
   }
 
   // Check if there are any active IndexedDB databases
   if (mWindow.AsInner()->HasActiveIndexedDBDatabases()) {
     return false;
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -143,17 +143,17 @@ private:
                          const TimeStamp& aNow = TimeStamp::Now());
 
   void RecordExecution(Timeout* aRunningTimeout,
                        Timeout* aTimeout);
 
   void UpdateBudget(const TimeStamp& aNow,
                     const TimeDuration& aDuration = TimeDuration());
 
-  bool BudgetThrottlingEnabled() const;
+  bool BudgetThrottlingEnabled(bool aIsBackground) const;
 
 private:
   struct Timeouts {
     explicit Timeouts(const TimeoutManager& aManager)
       : mManager(aManager)
     {
     }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12326,49 +12326,50 @@ nsDocument::GetVisibilityState(nsAString
   aState.AssignASCII(entry.value, entry.length);
   return NS_OK;
 }
 
 /* virtual */ void
 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
 {
   aWindowSizes->mDOMOtherSize +=
-    nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
+    nsINode::SizeOfExcludingThis(aWindowSizes->mState);
 
   if (mPresShell) {
-    mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
+    mPresShell->AddSizeOfIncludingThis(aWindowSizes->mState.mMallocSizeOf,
                                        &aWindowSizes->mArenaStats,
                                        &aWindowSizes->mLayoutPresShellSize,
                                        &aWindowSizes->mLayoutStyleSetsSize,
                                        &aWindowSizes->mLayoutTextRunsSize,
                                        &aWindowSizes->mLayoutPresContextSize,
                                        &aWindowSizes->mLayoutFramePropertiesSize);
   }
 
   aWindowSizes->mPropertyTablesSize +=
-    mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
+    mPropertyTable.SizeOfExcludingThis(aWindowSizes->mState.mMallocSizeOf);
   for (uint32_t i = 0, count = mExtraPropertyTables.Length();
        i < count; ++i) {
     aWindowSizes->mPropertyTablesSize +=
-      mExtraPropertyTables[i]->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+      mExtraPropertyTables[i]->SizeOfIncludingThis(
+        aWindowSizes->mState.mMallocSizeOf);
   }
 
   if (EventListenerManager* elm = GetExistingListenerManager()) {
     aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
   }
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
 
 void
 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 {
-  aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
+  aWindowSizes->mDOMOtherSize += aWindowSizes->mState.mMallocSizeOf(this);
   DocAddSizeOfExcludingThis(aWindowSizes);
 }
 
 static size_t
 SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
                                    MallocSizeOf aMallocSizeOf)
 {
   size_t n = 0;
@@ -12379,17 +12380,17 @@ SizeOfOwnedSheetArrayExcludingThis(const
       continue;
     }
     n += sheet->SizeOfIncludingThis(aMallocSizeOf);
   }
   return n;
 }
 
 size_t
-nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
 {
   // This SizeOfExcludingThis() overrides the one from nsINode.  But
   // nsDocuments can only appear at the top of the DOM tree, and we use the
   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
   // be called.
   MOZ_CRASH();
 }
 
@@ -12397,17 +12398,17 @@ void
 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
 {
   nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
 
   for (nsIContent* node = nsINode::GetFirstChild();
        node;
        node = node->GetNextNode(this))
   {
-    size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+    size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mState);
     size_t* p;
 
     switch (node->NodeType()) {
     case nsIDOMNode::ELEMENT_NODE:
       p = &aWindowSizes->mDOMElementNodesSize;
       break;
     case nsIDOMNode::TEXT_NODE:
       p = &aWindowSizes->mDOMTextNodesSize;
@@ -12427,43 +12428,44 @@ nsDocument::DocAddSizeOfExcludingThis(ns
 
     if (EventListenerManager* elm = node->GetExistingListenerManager()) {
       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
     }
   }
 
   aWindowSizes->mStyleSheetsSize +=
     SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
-                                       aWindowSizes->mMallocSizeOf);
+                                       aWindowSizes->mState.mMallocSizeOf);
   // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
   // (the nsLayoutStyleSheetCache singleton does).
   aWindowSizes->mStyleSheetsSize +=
     mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
-        aWindowSizes->mMallocSizeOf);
+      aWindowSizes->mState.mMallocSizeOf);
   for (auto& sheetArray : mAdditionalSheets) {
     aWindowSizes->mStyleSheetsSize +=
       SizeOfOwnedSheetArrayExcludingThis(sheetArray,
-                                         aWindowSizes->mMallocSizeOf);
+                                         aWindowSizes->mState.mMallocSizeOf);
   }
   // Lumping in the loader with the style-sheets size is not ideal,
   // but most of the things in there are in fact stylesheets, so it
   // doesn't seem worthwhile to separate it out.
   aWindowSizes->mStyleSheetsSize +=
-    CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+    CSSLoader()->SizeOfIncludingThis(aWindowSizes->mState.mMallocSizeOf);
+
+  aWindowSizes->mDOMOtherSize += mAttrStyleSheet
+                               ? mAttrStyleSheet->DOMSizeOfIncludingThis(
+                                   aWindowSizes->mState.mMallocSizeOf)
+                               : 0;
 
   aWindowSizes->mDOMOtherSize +=
-    mAttrStyleSheet ?
-    mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
-    0;
+    mStyledLinks.ShallowSizeOfExcludingThis(
+      aWindowSizes->mState.mMallocSizeOf);
 
   aWindowSizes->mDOMOtherSize +=
-    mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
-
-  aWindowSizes->mDOMOtherSize +=
-    mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
+    mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mState.mMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
   // - many!
 }
 
 already_AddRefed<nsIDocument>
 nsIDocument::Constructor(const GlobalObject& aGlobal,
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1871,18 +1871,16 @@ nsFocusManager::Focus(nsPIDOMWindowOuter
   }
 
   // indicate that the window has taken focus.
   if (aWindow->TakeFocus(true, focusMethod))
     aIsNewDocument = true;
 
   SetFocusedWindowInternal(aWindow);
 
-  NotifyCurrentTopLevelContentWindowChange(aWindow);
-
   // Update the system focus by focusing the root widget.  But avoid this
   // if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
   // own widget and is either already focused or is about to be focused.
   nsCOMPtr<nsIWidget> objectFrameWidget;
   if (aContent) {
     nsIFrame* contentFrame = aContent->GetPrimaryFrame();
     nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
     if (objectFrame)
@@ -3684,30 +3682,16 @@ nsFocusManager::SetFocusedWindowInternal
       doc->SetLastFocusTime(now);
     }
   }
 
   mFocusedWindow = aWindow;
 }
 
 void
-nsFocusManager::NotifyCurrentTopLevelContentWindowChange(
-                                                   nsPIDOMWindowOuter* aWindow)
-{
-  MOZ_ASSERT(aWindow);
-
-  nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetTop();
-  if (nsGlobalWindow::Cast(topWindow)->IsChromeWindow()) {
-    return;
-  }
-
-  NS_NotifyCurrentTopLevelOuterContentWindowId(topWindow->WindowID());
-}
-
-void
 nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
 {
   if (!sInstance) {
     return;
   }
 
   if (sInstance->mActiveWindow) {
     sInstance->mActiveWindow->
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -537,20 +537,16 @@ private:
   // wrong..
   static void NotifyFocusStateChange(nsIContent* aContent,
                                      nsIContent* aContentToFocus,
                                      bool aWindowShouldShowFocusRing,
                                      bool aGettingFocus);
 
   void SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow);
 
-  // Notify the change of content window ID
-  // belonging to the top level outer window.
-  void NotifyCurrentTopLevelContentWindowChange(nsPIDOMWindowOuter* aWindow);
-
   // the currently active and front-most top-most window
   nsCOMPtr<nsPIDOMWindowOuter> mActiveWindow;
 
   // the child or top-level window that is currently focused. This window will
   // either be the same window as mActiveWindow or a descendant of it.
   // Except during shutdown use SetFocusedWindowInternal to set mFocusedWindow!
   nsCOMPtr<nsPIDOMWindowOuter> mFocusedWindow;
 
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -1105,15 +1105,15 @@ nsChangeHint
 nsGenericDOMDataNode::GetAttributeChangeHint(const nsIAtom* aAttribute,
                                              int32_t aModType) const
 {
   NS_NOTREACHED("Shouldn't be calling this!");
   return nsChangeHint(0);
 }
 
 size_t
-nsGenericDOMDataNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+nsGenericDOMDataNode::SizeOfExcludingThis(SizeOfState& aState) const
 {
-  size_t n = nsIContent::SizeOfExcludingThis(aMallocSizeOf);
-  n += mText.SizeOfExcludingThis(aMallocSizeOf);
+  size_t n = nsIContent::SizeOfExcludingThis(aState);
+  n += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
   return n;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13773,62 +13773,64 @@ void
 nsGlobalWindow::DisableTimeChangeNotifications()
 {
   mozilla::time::RemoveWindowListener(AsInner());
 }
 
 void
 nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 {
-  aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
+  aWindowSizes->mDOMOtherSize += aWindowSizes->mState.mMallocSizeOf(this);
 
   if (IsInnerWindow()) {
     EventListenerManager* elm = GetExistingListenerManager();
     if (elm) {
       aWindowSizes->mDOMOtherSize +=
-        elm->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+        elm->SizeOfIncludingThis(aWindowSizes->mState.mMallocSizeOf);
       aWindowSizes->mDOMEventListenersCount +=
         elm->ListenerCount();
     }
     if (mDoc) {
       // Multiple global windows can share a document. So only measure the
       // document if it (a) doesn't have a global window, or (b) it's the
       // primary document for the window.
       if (!mDoc->GetInnerWindow() ||
           mDoc->GetInnerWindow() == AsInner()) {
         mDoc->DocAddSizeOfIncludingThis(aWindowSizes);
       }
     }
   }
 
   if (mNavigator) {
     aWindowSizes->mDOMOtherSize +=
-      mNavigator->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
+      mNavigator->SizeOfIncludingThis(aWindowSizes->mState.mMallocSizeOf);
   }
 
   aWindowSizes->mDOMEventTargetsSize +=
-    mEventTargetObjects.ShallowSizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
+    mEventTargetObjects.ShallowSizeOfExcludingThis(
+      aWindowSizes->mState.mMallocSizeOf);
 
   for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
     DOMEventTargetHelper* et = iter.Get()->GetKey();
     if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
       aWindowSizes->mDOMEventTargetsSize +=
-        iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf);
+        iSizeOf->SizeOfEventTargetIncludingThis(
+          aWindowSizes->mState.mMallocSizeOf);
     }
     if (EventListenerManager* elm = et->GetExistingListenerManager()) {
       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
     }
     ++aWindowSizes->mDOMEventTargetsCount;
   }
 
   if (IsInnerWindow() && mPerformance) {
     aWindowSizes->mDOMPerformanceUserEntries =
-      mPerformance->SizeOfUserEntries(aWindowSizes->mMallocSizeOf);
+      mPerformance->SizeOfUserEntries(aWindowSizes->mState.mMallocSizeOf);
     aWindowSizes->mDOMPerformanceResourceEntries =
-      mPerformance->SizeOfResourceEntries(aWindowSizes->mMallocSizeOf);
+      mPerformance->SizeOfResourceEntries(aWindowSizes->mState.mMallocSizeOf);
   }
 }
 
 void
 nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
 {
   MOZ_ASSERT(IsInnerWindow());
   // Create the index we will present to content based on which indices are
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2574,22 +2574,22 @@ nsINode::GetAccessibleNode()
   RefPtr<AccessibleNode> anode = new AccessibleNode(this);
   return anode.forget();
 #endif
 
   return nullptr;
 }
 
 size_t
-nsINode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+nsINode::SizeOfExcludingThis(SizeOfState& aState) const
 {
   size_t n = 0;
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
-    n += elm->SizeOfIncludingThis(aMallocSizeOf);
+    n += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
   }
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mNodeInfo
   // - mSlots
   //
   // The following members are not measured:
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -14,16 +14,17 @@
 #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 "mozilla/ErrorResult.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/SizeOfState.h"    // for SizeOfState
 #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 "nsTHashtable.h"
 #include <iosfwd>
 
 // Including 'windows.h' will #define GetClassInfo to something else.
@@ -263,17 +264,18 @@ private:
 };
 
 // This should be used for any nsINode sub-class that has fields of its own
 // that it needs to measure;  any sub-class that doesn't use it will inherit
 // SizeOfExcludingThis from its super-class.  SizeOfIncludingThis() need not be
 // defined, it is inherited from nsINode.
 // This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
 #define NS_DECL_SIZEOF_EXCLUDING_THIS \
-  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
+  virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) \
+    const override;
 
 // Categories of node properties
 // 0 is global.
 #define DOM_USER_DATA         1
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
 { 0x70ba4547, 0x7699, 0x44fc, \
@@ -321,25 +323,25 @@ public:
   // - nsHTMLSelectElement:   mOptions, mRestoreState
   // - nsHTMLTableElement:    mTBodies, mRows, mTableInheritedAttributes
   // - nsHTMLTableSectionElement: mRows
   // - nsHTMLTextAreaElement: mControllers, mState
   //
   // The following members don't need to be measured:
   // - nsIContent: mPrimaryFrame, because it's non-owning and measured elsewhere
   //
-  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+  virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) 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, and so |aMallocSizeOf(this)| is always
+  // methods of nsINode's sub-classes, so aState.mMallocSizeOf(this) is always
   // safe to call no matter which object it was invoked on.
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
-    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  virtual size_t SizeOfIncludingThis(mozilla::SizeOfState& aState) const {
+    return aState.mMallocSizeOf(this) + SizeOfExcludingThis(aState);
   }
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
   friend class nsAttrAndChildArray;
 
 #ifdef MOZILLA_INTERNAL_API
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -47,29 +47,29 @@ nsWindowMemoryReporter::~nsWindowMemoryR
 NS_IMPL_ISUPPORTS(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
                   nsISupportsWeakReference)
 
 static nsresult
 AddNonJSSizeOfWindowAndItsDescendents(nsGlobalWindow* aWindow,
                                       nsTabSizes* aSizes)
 {
   // Measure the window.
-  nsWindowSizes windowSizes(moz_malloc_size_of);
+  SizeOfState state(moz_malloc_size_of);
+  nsWindowSizes windowSizes(state);
   aWindow->AddSizeOfIncludingThis(&windowSizes);
-  windowSizes.addToTabSizes(aSizes);
 
   // Measure the inner window, if there is one.
-  nsWindowSizes innerWindowSizes(moz_malloc_size_of);
   nsGlobalWindow* inner = aWindow->IsOuterWindow() ? aWindow->GetCurrentInnerWindowInternal()
                                                    : nullptr;
   if (inner) {
-    inner->AddSizeOfIncludingThis(&innerWindowSizes);
-    innerWindowSizes.addToTabSizes(aSizes);
+    inner->AddSizeOfIncludingThis(&windowSizes);
   }
 
+  windowSizes.addToTabSizes(aSizes);
+
   nsCOMPtr<nsIDOMWindowCollection> frames = aWindow->GetFrames();
 
   uint32_t length;
   nsresult rv = frames->GetLength(&length);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Measure this window's descendents.
   for (uint32_t i = 0; i < length; i++) {
@@ -304,17 +304,20 @@ CollectWindowReports(nsGlobalWindow *aWi
 #define REPORT_SIZE(_pathTail, _amount, _desc) \
   ReportSize(windowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
              aHandleReport, aData);
 
 #define REPORT_COUNT(_pathTail, _amount, _desc) \
   ReportCount(censusWindowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
               aHandleReport, aData);
 
-  nsWindowSizes windowSizes(WindowsMallocSizeOf);
+  // This SizeOfState contains the SeenPtrs used for all memory reporting of
+  // this window.
+  SizeOfState state(WindowsMallocSizeOf);
+  nsWindowSizes windowSizes(state);
   aWindow->AddSizeOfIncludingThis(&windowSizes);
 
   REPORT_SIZE("/dom/element-nodes", windowSizes.mDOMElementNodesSize,
               "Memory used by the element nodes in a window's DOM.");
   aWindowTotalSizes->mDOMElementNodesSize += windowSizes.mDOMElementNodesSize;
 
   REPORT_SIZE("/dom/text-nodes", windowSizes.mDOMTextNodesSize,
               "Memory used by the text nodes in a window's DOM.");
@@ -513,17 +516,18 @@ nsWindowMemoryReporter::CollectReports(n
 "about:memory's minimize memory usage button.\n\n"
 "Ghost windows can happen legitimately, but they are often indicative of "
 "leaks in the browser or add-ons.");
 
   WindowPaths windowPaths;
   WindowPaths topWindowPaths;
 
   // Collect window memory usage.
-  nsWindowSizes windowTotalSizes(nullptr);
+  SizeOfState fakeState(nullptr);   // this won't be used
+  nsWindowSizes windowTotalSizes(fakeState);
   nsCOMPtr<amIAddonManager> addonManager;
   if (XRE_IsParentProcess()) {
     // Only try to access the service from the main process.
     addonManager = do_GetService("@mozilla.org/addons/integration;1");
   }
   for (uint32_t i = 0; i < windows.Length(); i++) {
     CollectWindowReports(windows[i], addonManager,
                          &windowTotalSizes, &ghostWindows,
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -34,25 +34,25 @@ class nsWindowSizes {
   macro(Other, mLayoutPresShellSize) \
   macro(Style, mLayoutStyleSetsSize) \
   macro(Other, mLayoutTextRunsSize) \
   macro(Other, mLayoutPresContextSize) \
   macro(Other, mLayoutFramePropertiesSize) \
   macro(Other, mPropertyTablesSize) \
 
 public:
-  explicit nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf)
+  explicit nsWindowSizes(mozilla::SizeOfState& aState)
     :
       #define ZERO_SIZE(kind, mSize)  mSize(0),
       FOR_EACH_SIZE(ZERO_SIZE)
       #undef ZERO_SIZE
       mDOMEventTargetsCount(0),
       mDOMEventListenersCount(0),
       mArenaStats(),
-      mMallocSizeOf(aMallocSizeOf)
+      mState(aState)
   {}
 
   void addToTabSizes(nsTabSizes *sizes) const {
     #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize);
     FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
     #undef ADD_TO_TAB_SIZES
     mArenaStats.addToTabSizes(sizes);
   }
@@ -70,17 +70,17 @@ public:
   #define DECL_SIZE(kind, mSize) size_t mSize;
   FOR_EACH_SIZE(DECL_SIZE);
   #undef DECL_SIZE
 
   uint32_t mDOMEventTargetsCount;
   uint32_t mDOMEventListenersCount;
 
   nsArenaMemoryStats mArenaStats;
-  mozilla::MallocSizeOf mMallocSizeOf;
+  mozilla::SizeOfState& mState;
 
 #undef FOR_EACH_SIZE
 };
 
 /**
  * nsWindowMemoryReporter is responsible for the 'explicit/window-objects'
  * memory reporter.
  *
--- a/dom/html/HTMLAnchorElement.cpp
+++ b/dom/html/HTMLAnchorElement.cpp
@@ -401,16 +401,16 @@ HTMLAnchorElement::AfterSetAttr(int32_t 
 
 EventStates
 HTMLAnchorElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
 size_t
-HTMLAnchorElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+HTMLAnchorElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
-         Link::SizeOfExcludingThis(aMallocSizeOf);
+  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
+         Link::SizeOfExcludingThis(aState);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLAreaElement.cpp
+++ b/dom/html/HTMLAreaElement.cpp
@@ -215,20 +215,20 @@ HTMLAreaElement::GetHrefURI() const
 
 EventStates
 HTMLAreaElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
 size_t
-HTMLAreaElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+HTMLAreaElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
-         Link::SizeOfExcludingThis(aMallocSizeOf);
+  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
+         Link::SizeOfExcludingThis(aState);
 }
 
 JSObject*
 HTMLAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLAreaElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -504,20 +504,20 @@ HTMLLinkElement::GetCORSMode() const
 
 EventStates
 HTMLLinkElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
 size_t
-HTMLLinkElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+HTMLLinkElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
 {
-  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
-         Link::SizeOfExcludingThis(aMallocSizeOf);
+  return nsGenericHTMLElement::SizeOfExcludingThis(aState) +
+         Link::SizeOfExcludingThis(aState);
 }
 
 JSObject*
 HTMLLinkElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLLinkElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3530,16 +3530,21 @@ HTMLMediaElement::CaptureStreamInternal(
 }
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureAudio(ErrorResult& aRv,
                                MediaStreamGraph* aGraph)
 {
   MOZ_RELEASE_ASSERT(aGraph);
 
+  if (!CanBeCaptured(true)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
   RefPtr<DOMMediaStream> stream =
     CaptureStreamInternal(false, true, aGraph);
   if (!stream) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return stream.forget();
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -698,19 +698,17 @@ parent:
 
     async AccumulateMixedContentHSTS(URIParams aURI, bool aActive, bool aHasHSTSPriming,
                                      OriginAttributes aOriginAttributes);
 
     nested(inside_cpow) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
-    // Giving high priority to prevent other messages sending before this one.
-    // See bug 1360549 for details.
-    prio(high) async PNecko();
+    async PNecko();
 
     async PPrinting();
 
     async PChildToParentStream();
 
     async PSpeechSynthesis();
 
     nested(inside_cpow) async PStorage();
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/ipc/TaskFactory.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
+#include "mozilla/WeakPtr.h"
 
 #include "nsIFrameLoader.h"
 #include "nsIHangReport.h"
 #include "nsITabParent.h"
 #include "nsPluginHost.h"
 #include "nsThreadUtils.h"
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
@@ -203,21 +204,24 @@ private:
   HangMonitorParent* mActor;
   ContentParent* mContentParent;
   HangData mHangData;
   nsAutoString mDumpId;
 };
 
 class HangMonitorParent
   : public PProcessHangMonitorParent
+  , public SupportsWeakPtr<HangMonitorParent>
 {
 public:
   explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
   ~HangMonitorParent() override;
 
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(HangMonitorParent)
+
   void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
 
   mozilla::ipc::IPCResult RecvHangEvidence(const HangData& aHangData) override;
   mozilla::ipc::IPCResult RecvClearHang() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
@@ -688,21 +692,29 @@ HangMonitorParent::SendHangNotification(
   // chrome process, main thread
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if ((aHangData.type() == HangData::TPluginHangData) && aTakeMinidump) {
     // We've been handed a partial minidump; complete it with plugin and
     // content process dumps.
     const PluginHangData& phd = aHangData.get_PluginHangData();
 
+    WeakPtr<HangMonitorParent> self = this;
     std::function<void(nsString)> callback =
-      [this, aHangData](nsString aResult) {
-        this->UpdateMinidump(aHangData.get_PluginHangData().pluginId(),
+      [self, aHangData](nsString aResult) {
+        MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+        if (!self) {
+          // Don't report hang since the process has already shut down.
+          return;
+        }
+
+        self->UpdateMinidump(aHangData.get_PluginHangData().pluginId(),
                        aResult);
-        this->OnTakeFullMinidumpComplete(aHangData, aResult);
+        self->OnTakeFullMinidumpComplete(aHangData, aResult);
       };
 
     plugins::TakeFullMinidump(phd.pluginId(),
                               phd.contentProcessId(),
                               aBrowserDumpId,
                               Move(callback),
                               true);
   } else {
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1384248.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <script>
+      try { o1 = document.createElement('audio') } catch(e) { }
+      try { o2 = document.implementation.createDocument('', '', null).adoptNode(o1); } catch(e) { };
+      try { o3 = new AudioContext('alarm') } catch(e) { }
+      try { o3.createMediaElementSource(o1) } catch(e) { }
+    </script>
+  </head>
+</html>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -79,16 +79,17 @@ load 1122218.html
 load 1127188.html
 load 1157994.html
 load 1158427.html
 load 1185176.html
 load 1185192.html
 load 1304948.html
 load 1319486.html
 load 1291702.html
+load 1384248.html
 load disconnect-wrong-destination.html
 load analyser-channels-1.html
 load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load buffer-source-slow-resampling-1.html
 load doppler-1.html
--- a/dom/plugins/test/mochitest/test_pluginstream_err.html
+++ b/dom/plugins/test/mochitest/test_pluginstream_err.html
@@ -83,21 +83,24 @@ var tests = [
     "streammode": "normal",
     "functiontofail": "npp_destroystream",
     "failurecode": "1",
     "frame": "testframe"
   },
 ];
 
 function iframeonload(evt) {
-  var contentLength = evt.target.contentDocument.body.innerHTML.length;
+  // We have to use SpecialPowers because nptest.cpp prepends
+  // data: whichs makes the frame cross origin with the including page.
+  var wrappedDoc = SpecialPowers.wrap(evt.target).contentDocument;
+  var contentLength = wrappedDoc.body.innerHTML.length;
   var plugin = gTestWindow.document.getElementById("embedtest");
   var functionToFail = plugin.getAttribute("functiontofail");
   if (contentLength > 0) {
-    is(evt.target.contentDocument.body.innerHTML, "pass",
+    is(wrappedDoc.body.innerHTML, "pass",
       "test frame has unexpected content");
     setTimeout(function() {
       // This verifies that the plugin hasn't been unloaded, and that
       // no calls to NPP_ functions have been made unexpectedly.
       is(plugin.getError(), "pass", "plugin reported an error");
       gTestWindow.close();
       setTimeout(runNextTest, 10);
     }, functionToFail == "npp_newstream" ? 500 : 10);
--- a/dom/plugins/test/mochitest/test_pluginstream_src_referer.html
+++ b/dom/plugins/test/mochitest/test_pluginstream_src_referer.html
@@ -10,17 +10,20 @@
   <p id="display"></p>
 
   <script class="testbody" type="application/javascript">
   SimpleTest.waitForExplicitFinish();
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   function frameLoaded() {
     var testframe = document.getElementById('pluginframe');
-    var content = testframe.contentDocument.body.innerHTML;
+    // We have to use SpecialPowers because nptest.cpp prepends
+    // data: whichs makes the frame cross origin with the including page.
+    var wrappedDoc = SpecialPowers.wrap(testframe).contentDocument;
+    var content = wrappedDoc.body.innerHTML;
     if (!content.length)
       return;
 
     is(content, "Referer found: " + window.location);
     SimpleTest.finish();
   }
   </script>
 
--- a/dom/plugins/test/mochitest/test_twostreams.html
+++ b/dom/plugins/test/mochitest/test_twostreams.html
@@ -14,23 +14,31 @@
   SimpleTest.expectAssertions(0, 2);
 
   SimpleTest.waitForExplicitFinish();
   setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
   var framesToLoad = 2;
   function frameLoaded(id) {
     var frame = document.getElementById('testframe' + id);
-    if (!frame.contentDocument.body.innerHTML.length)
+    // We have to use SpecialPowers because nptest.cpp prepends
+    // data: whichs makes the frame cross origin with the including page.
+    var wrappedDoc = SpecialPowers.wrap(frame).contentDocument;
+
+    if (!wrappedDoc.body.innerHTML.length)
       return;
 
     --framesToLoad;
     if (0 == framesToLoad) {
-      is(document.getElementById('testframe1').contentDocument.body.innerHTML,
-         document.getElementById('testframe2').contentDocument.body.innerHTML,
+      var frame1 = document.getElementById('testframe1');
+      var frame2 = document.getElementById('testframe2');
+      var wrappedDocFrame1 = SpecialPowers.wrap(frame1).contentDocument;
+      var wrappedDocFrame2 = SpecialPowers.wrap(frame2).contentDocument;
+
+      is(wrappedDocFrame1.body.innerHTML, wrappedDocFrame2.body.innerHTML,
          "Frame contents should match");
       SimpleTest.finish();
     }
   }
   </script>
 
   <iframe id="testframe1" name="testframe1" onload="frameLoaded(1)"></iframe>
   <iframe id="testframe2" name="testframe2" onload="frameLoaded(2)"></iframe>
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1923,94 +1923,40 @@ ScriptLoader::ShouldCacheBytecode(Script
 
   // Look at the preference to know which strategy (parameters) should be used
   // when the bytecode cache is enabled.
   int32_t strategy = nsContentUtils::BytecodeCacheStrategy();
 
   // List of parameters used by the strategies.
   bool hasSourceLengthMin = false;
   bool hasFetchCountMin = false;
-  bool hasTimeSinceLastFetched = false;
   size_t sourceLengthMin = 100;
-  int32_t fetchCountMin = 5;
-  TimeDuration timeSinceLastFetched;
+  int32_t fetchCountMin = 4;
 
   LOG(("ScriptLoadRequest (%p): Bytecode-cache: strategy = %d.", aRequest, strategy));
   switch (strategy) {
     case -2: {
       // Reader mode, keep requesting alternate data but no longer save it.
       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Encoding disabled.", aRequest));
       return false;
     }
     case -1: {
       // Eager mode, skip heuristics!
       hasSourceLengthMin = false;
       hasFetchCountMin = false;
-      hasTimeSinceLastFetched = false;
       break;
     }
     default:
     case 0: {
       hasSourceLengthMin = true;
       hasFetchCountMin = true;
-      hasTimeSinceLastFetched = true;
       sourceLengthMin = 1024;
-      fetchCountMin = 5;
-      timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
-      break;
-    }
-
-    // The following strategies are made-up to study what impact each parameter
-    // has when compared to the default case.
-    case 1: {
-      hasSourceLengthMin = true;
-      hasFetchCountMin = true;
-      hasTimeSinceLastFetched = false;
-      sourceLengthMin = 1024;
-      fetchCountMin = 5;
-      break;
-    }
-    case 2: {
-      hasSourceLengthMin = true;
-      hasFetchCountMin = false;
-      hasTimeSinceLastFetched = true;
-      sourceLengthMin = 1024;
-      timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
-      break;
-    }
-    case 3: {
-      hasSourceLengthMin = false;
-      hasFetchCountMin = true;
-      hasTimeSinceLastFetched = true;
-      fetchCountMin = 5;
-      timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
-      break;
-    }
-
-    // The following strategies are made-up to study what impact each parameter
-    // has individually.
-    case 4: {
-      hasSourceLengthMin = false;
-      hasFetchCountMin = false;
-      hasTimeSinceLastFetched = true;
-      timeSinceLastFetched = TimeDuration::FromSeconds(72 * 3600);
-      break;
-    }
-    case 5: {
-      hasSourceLengthMin = false;
-      hasFetchCountMin = true;
-      hasTimeSinceLastFetched = false;
-      fetchCountMin = 5;
-      break;
-    }
-    case 6: {
-      hasSourceLengthMin = true;
-      hasFetchCountMin = false;
-      hasTimeSinceLastFetched = false;
-      sourceLengthMin = 1024;
+      // If we were to optimize only for speed, without considering the impact
+      // on memory, we should set this threshold to 2. (Bug 900784 comment 120)
+      fetchCountMin = 4;
       break;
     }
   }
 
   // If the script is too small/large, do not attempt at creating a bytecode
   // cache for this script, as the overhead of parsing it might not be worth the
   // effort.
   if (hasSourceLengthMin && aRequest->mScriptText.length() < sourceLengthMin) {
@@ -2028,36 +1974,16 @@ ScriptLoader::ShouldCacheBytecode(Script
       return false;
     }
     LOG(("ScriptLoadRequest (%p): Bytecode-cache: fetchCount = %d.", aRequest, fetchCount));
     if (fetchCount < fetchCountMin) {
       return false;
     }
   }
 
-  // Check that the cache entry got accessed recently, before caching it.
-  if (hasTimeSinceLastFetched) {
-    uint32_t lastFetched = 0;
-    if (NS_FAILED(aRequest->mCacheInfo->GetCacheTokenLastFetched(&lastFetched))) {
-      LOG(("ScriptLoadRequest (%p): Bytecode-cache: Cannot get lastFetched.", aRequest));
-      return false;
-    }
-    uint32_t now = PR_Now() / PR_USEC_PER_SEC;
-    if (now < lastFetched) {
-      LOG(("ScriptLoadRequest (%p): Bytecode-cache: (What?) lastFetched set in the future.", aRequest));
-      return false;
-    }
-    TimeDuration since = TimeDuration::FromSeconds(now - lastFetched);
-    LOG(("ScriptLoadRequest (%p): Bytecode-cache: lastFetched = %f sec. ago.",
-         aRequest, since.ToSeconds()));
-    if (since >= timeSinceLastFetched) {
-      return false;
-    }
-  }
-
   LOG(("ScriptLoadRequest (%p): Bytecode-cache: Trigger encoding.", aRequest));
   return true;
 }
 
 nsresult
 ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
 {
   using namespace mozilla::Telemetry;
--- a/dom/svg/SVGPathElement.cpp
+++ b/dom/svg/SVGPathElement.cpp
@@ -41,20 +41,20 @@ SVGPathElement::SVGPathElement(already_A
   : SVGPathElementBase(aNodeInfo)
 {
 }
 
 //----------------------------------------------------------------------
 // memory reporting methods
 
 size_t
-SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+SVGPathElement::SizeOfExcludingThis(mozilla::SizeOfState& aState) const
 {
-  return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf) +
-         mD.SizeOfExcludingThis(aMallocSizeOf);
+  return SVGPathElementBase::SizeOfExcludingThis(aState) +
+         mD.SizeOfExcludingThis(aState.mMallocSizeOf);
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
 
 uint32_t
--- a/dom/webauthn/U2FTokenManager.cpp
+++ b/dom/webauthn/U2FTokenManager.cpp
@@ -153,27 +153,16 @@ U2FTokenManager::Get()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   // We should only be accessing this on the background thread
   MOZ_ASSERT(!NS_IsMainThread());
   return gU2FTokenManager;
 }
 
 void
-U2FTokenManager::MaybeAbortTransaction(uint64_t aTransactionId,
-                                       const nsresult& aError)
-{
-  if (mTransactionId != aTransactionId) {
-    return;
-  }
-
-  AbortTransaction(aError);
-}
-
-void
 U2FTokenManager::AbortTransaction(const nsresult& aError)
 {
   Unused << mTransactionParent->SendCancel(aError);
   ClearTransaction();
 }
 
 void
 U2FTokenManager::MaybeClearTransaction(WebAuthnTransactionParent* aParent)
@@ -186,26 +175,28 @@ U2FTokenManager::MaybeClearTransaction(W
 }
 
 void
 U2FTokenManager::ClearTransaction()
 {
   mTransactionParent = nullptr;
   // Drop managers at the end of all transactions
   mTokenManagerImpl = nullptr;
-  // Drop promises.
-  mRegisterPromise = nullptr;
-  mSignPromise = nullptr;
-  // Increase in case we're called by the WebAuthnTransactionParent.
+  // Forget promises, if necessary.
+  mRegisterPromise.DisconnectIfExists();
+  mSignPromise.DisconnectIfExists();
+  // Bump transaction id.
   mTransactionId++;
 }
 
 RefPtr<U2FTokenTransport>
 U2FTokenManager::GetTokenManagerImpl()
 {
+  MOZ_ASSERT(U2FPrefManager::Get());
+
   if (mTokenManagerImpl) {
     return mTokenManagerImpl;
   }
 
   auto pm = U2FPrefManager::Get();
   bool useSoftToken = pm->GetSoftTokenEnabled();
   bool useUsbToken = pm->GetUsbTokenEnabled();
 
@@ -226,19 +217,18 @@ U2FTokenManager::GetTokenManagerImpl()
   return new U2FHIDTokenManager();
 }
 
 void
 U2FTokenManager::Register(WebAuthnTransactionParent* aTransactionParent,
                           const WebAuthnTransactionInfo& aTransactionInfo)
 {
   MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));
-  MOZ_ASSERT(U2FPrefManager::Get());
 
-  uint64_t tid = ++mTransactionId;
+  ClearTransaction();
   mTransactionParent = aTransactionParent;
   mTokenManagerImpl = GetTokenManagerImpl();
 
   if (!mTokenManagerImpl) {
     AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     return;
   }
 
@@ -247,107 +237,137 @@ U2FTokenManager::Register(WebAuthnTransa
   // UnknownError and terminate the operation.
 
   if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
       (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
     AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
     return;
   }
 
-  mRegisterPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
-                                                 aTransactionInfo.RpIdHash(),
-                                                 aTransactionInfo.ClientDataHash(),
-                                                 aTransactionInfo.TimeoutMS());
-
-  mRegisterPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
+  uint64_t tid = mTransactionId;
+  mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
+                              aTransactionInfo.RpIdHash(),
+                              aTransactionInfo.ClientDataHash(),
+                              aTransactionInfo.TimeoutMS())
+                   ->Then(GetCurrentThreadSerialEventTarget(), __func__,
                          [tid](U2FRegisterResult&& aResult) {
                            U2FTokenManager* mgr = U2FTokenManager::Get();
                            mgr->MaybeConfirmRegister(tid, aResult);
                          },
                          [tid](nsresult rv) {
                            MOZ_ASSERT(NS_FAILED(rv));
                            U2FTokenManager* mgr = U2FTokenManager::Get();
-                           mgr->MaybeAbortTransaction(tid, rv);
-                         });
+                           mgr->MaybeAbortRegister(tid, rv);
+                         })
+                   ->Track(mRegisterPromise);
 }
 
 void
 U2FTokenManager::MaybeConfirmRegister(uint64_t aTransactionId,
                                       U2FRegisterResult& aResult)
 {
   if (mTransactionId != aTransactionId) {
     return;
   }
 
+  mRegisterPromise.Complete();
+
   nsTArray<uint8_t> registration;
   aResult.ConsumeRegistration(registration);
 
   Unused << mTransactionParent->SendConfirmRegister(registration);
   ClearTransaction();
 }
 
 void
+U2FTokenManager::MaybeAbortRegister(uint64_t aTransactionId,
+                                    const nsresult& aError)
+{
+  if (mTransactionId != aTransactionId) {
+    return;
+  }
+
+  mRegisterPromise.Complete();
+  AbortTransaction(aError);
+}
+
+void
 U2FTokenManager::Sign(WebAuthnTransactionParent* aTransactionParent,
                       const WebAuthnTransactionInfo& aTransactionInfo)
 {
   MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign"));
-  MOZ_ASSERT(U2FPrefManager::Get());
 
-  uint64_t tid = ++mTransactionId;
+  ClearTransaction();
   mTransactionParent = aTransactionParent;
   mTokenManagerImpl = GetTokenManagerImpl();
 
   if (!mTokenManagerImpl) {
     AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     return;
   }
 
   if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
       (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
     AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
     return;
   }
 
-  mSignPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
-                                         aTransactionInfo.RpIdHash(),
-                                         aTransactionInfo.ClientDataHash(),
-                                         aTransactionInfo.TimeoutMS());
-
-  mSignPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
+  uint64_t tid = mTransactionId;
+  mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
+                          aTransactionInfo.RpIdHash(),
+                          aTransactionInfo.ClientDataHash(),
+                          aTransactionInfo.TimeoutMS())
+                   ->Then(GetCurrentThreadSerialEventTarget(), __func__,
                      [tid](U2FSignResult&& aResult) {
                        U2FTokenManager* mgr = U2FTokenManager::Get();
                        mgr->MaybeConfirmSign(tid, aResult);
                      },
                      [tid](nsresult rv) {
                        MOZ_ASSERT(NS_FAILED(rv));
                        U2FTokenManager* mgr = U2FTokenManager::Get();
-                       mgr->MaybeAbortTransaction(tid, rv);
-                     });
+                       mgr->MaybeAbortSign(tid, rv);
+                     })
+                   ->Track(mSignPromise);
 }
 
 void
 U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId,
                                   U2FSignResult& aResult)
 {
   if (mTransactionId != aTransactionId) {
     return;
   }
 
+  mSignPromise.Complete();
+
   nsTArray<uint8_t> keyHandle;
   aResult.ConsumeKeyHandle(keyHandle);
   nsTArray<uint8_t> signature;
   aResult.ConsumeSignature(signature);
 
   Unused << mTransactionParent->SendConfirmSign(keyHandle, signature);
   ClearTransaction();
 }
 
 void
+U2FTokenManager::MaybeAbortSign(uint64_t aTransactionId, const nsresult& aError)
+{
+  if (mTransactionId != aTransactionId) {
+    return;
+  }
+
+  mSignPromise.Complete();
+  AbortTransaction(aError);
+}
+
+void
 U2FTokenManager::Cancel(WebAuthnTransactionParent* aParent)
 {
-  if (mTransactionParent == aParent) {
-    mTokenManagerImpl->Cancel();
-    ClearTransaction();
+  if (mTransactionParent != aParent) {
+    return;
   }
+
+  mTokenManagerImpl->Cancel();
+  ClearTransaction();
 }
 
 }
 }
--- a/dom/webauthn/U2FTokenManager.h
+++ b/dom/webauthn/U2FTokenManager.h
@@ -44,28 +44,28 @@ public:
   void MaybeClearTransaction(WebAuthnTransactionParent* aParent);
   static void Initialize();
 private:
   U2FTokenManager();
   ~U2FTokenManager();
   RefPtr<U2FTokenTransport> GetTokenManagerImpl();
   void AbortTransaction(const nsresult& aError);
   void ClearTransaction();
-  void MaybeAbortTransaction(uint64_t aTransactionId,
-                             const nsresult& aError);
   void MaybeConfirmRegister(uint64_t aTransactionId,
                             U2FRegisterResult& aResult);
+  void MaybeAbortRegister(uint64_t aTransactionId, const nsresult& aError);
   void MaybeConfirmSign(uint64_t aTransactionId, U2FSignResult& aResult);
+  void MaybeAbortSign(uint64_t aTransactionId, const nsresult& aError);
   // Using a raw pointer here, as the lifetime of the IPC object is managed by
   // the PBackground protocol code. This means we cannot be left holding an
   // invalid IPC protocol object after the transaction is finished.
   WebAuthnTransactionParent* mTransactionParent;
   RefPtr<U2FTokenTransport> mTokenManagerImpl;
-  RefPtr<U2FRegisterPromise> mRegisterPromise;
-  RefPtr<U2FSignPromise> mSignPromise;
+  MozPromiseRequestHolder<U2FRegisterPromise> mRegisterPromise;
+  MozPromiseRequestHolder<U2FSignPromise> mSignPromise;
   // Guards the asynchronous promise resolution of token manager impls.
   // We don't need to protect this with a lock as it will only be modified
   // and checked on the PBackground thread in the parent process.
   uint64_t mTransactionId;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -234,24 +234,35 @@ WebAuthnManager::MaybeClearTransaction()
   }
 }
 
 WebAuthnManager::~WebAuthnManager()
 {
   MaybeClearTransaction();
 }
 
-already_AddRefed<MozPromise<nsresult, nsresult, false>>
+RefPtr<WebAuthnManager::BackgroundActorPromise>
 WebAuthnManager::GetOrCreateBackgroundActor()
 {
-  bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
-  if (NS_WARN_IF(!ok)) {
-    ActorFailed();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  PBackgroundChild *actor = BackgroundChild::GetForCurrentThread();
+  RefPtr<WebAuthnManager::BackgroundActorPromise> promise =
+    mPBackgroundCreationPromise.Ensure(__func__);
+
+  if (actor) {
+    ActorCreated(actor);
+  } else {
+    bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
+    if (NS_WARN_IF(!ok)) {
+      ActorFailed();
+    }
   }
-  return mPBackgroundCreationPromise.Ensure(__func__);
+
+  return promise;
 }
 
 //static
 WebAuthnManager*
 WebAuthnManager::GetOrCreate()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (gWebAuthnManager) {
@@ -870,18 +881,23 @@ WebAuthnManager::Cancel(const nsresult& 
     mTransactionPromise->MaybeReject(aError);
   }
   MaybeClearTransaction();
 }
 
 void
 WebAuthnManager::ActorCreated(PBackgroundChild* aActor)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aActor);
 
+  if (mChild) {
+    return;
+  }
+
   RefPtr<WebAuthnTransactionChild> mgr(new WebAuthnTransactionChild());
   PWebAuthnTransactionChild* constructedMgr =
     aActor->SendPWebAuthnTransactionConstructor(mgr);
 
   if (NS_WARN_IF(!constructedMgr)) {
     ActorFailed();
     return;
   }
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -94,18 +94,19 @@ public:
   void ActorFailed() override;
   void ActorDestroyed();
 private:
   WebAuthnManager();
   virtual ~WebAuthnManager();
 
   void MaybeClearTransaction();
 
-  already_AddRefed<MozPromise<nsresult, nsresult, false>>
-  GetOrCreateBackgroundActor();
+  typedef MozPromise<nsresult, nsresult, false> BackgroundActorPromise;
+
+  RefPtr<BackgroundActorPromise> GetOrCreateBackgroundActor();
 
   // JS Promise representing transaction status.
   RefPtr<Promise> mTransactionPromise;
 
   // IPC Channel for the current transaction.
   RefPtr<WebAuthnTransactionChild> mChild;
 
   // Parent of the context we're currently running the transaction in.
@@ -115,15 +116,15 @@ private:
   // used to assemble reply objects.
   Maybe<nsCString> mClientData;
 
   // Holds the parameters of the current transaction, as we need them both
   // before the transaction request is sent, and on successful return.
   Maybe<WebAuthnTransactionInfo> mInfo;
 
   // Promise for dealing with PBackground Actor creation.
-  MozPromiseHolder<MozPromise<nsresult, nsresult, false>> mPBackgroundCreationPromise;
+  MozPromiseHolder<BackgroundActorPromise> mPBackgroundCreationPromise;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WebAuthnManager_h
--- a/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
+++ b/dom/workers/test/serviceworkers/test_devtools_serviceworker_interception.html
@@ -86,17 +86,23 @@ function fetchAndCheckTimedChannel(aWind
   var resolveFunction;
   var promise = new Promise(aResolve => resolveFunction = aResolve);
 
   var topic = aFetch ? "http-on-examine-response"
                      : "service-worker-synthesized-response";
 
   function observer(aSubject) {
     var channel = aSubject.QueryInterface(Ci.nsIChannel);
-    ok(channel.URI.spec.endsWith(aURL));
+
+    // Since we cannot make sure that the network event triggered by the fetch()
+    // in this testcase is the very next event processed by ObserverService, we
+    // have to wait until we catch the one we want.
+    if (!channel.URI.spec.endsWith(aURL)) {
+      return;
+    }
 
     var tc = aSubject.QueryInterface(Ci.nsITimedChannel);
 
     // Check service worker related timings.
     var serviceWorkerTimings = [{start: tc.launchServiceWorkerStartTime,
                                  end:   tc.launchServiceWorkerEndTime},
                                 {start: tc.dispatchFetchEventStartTime,
                                  end:   tc.dispatchFetchEventEndTime},
@@ -109,36 +115,37 @@ function fetchAndCheckTimedChannel(aWind
         ok(aPreviousTimings.end <= aCurrentTimings.end,
            "End time order check.");
         ok(aCurrentTimings.start <= aCurrentTimings.end,
            "Start time should be smaller than end time.");
         return aCurrentTimings;
       });
     } else {
       serviceWorkerTimings.forEach(aTimings => {
-        is(aTimings.start, 0);
-        is(aTimings.end, 0);
+        is(aTimings.start, 0, "SW timings should be 0.");
+        is(aTimings.end, 0, "SW timings should be 0.");
       });
     }
 
     // Check network related timings.
     var networkTimings = [tc.domainLookupStartTime,
                           tc.domainLookupEndTime,
                           tc.connectStartTime,
                           tc.connectEndTime,
                           tc.requestStartTime,
                           tc.responseStartTime,
                           tc.responseEndTime];
     if (aFetch) {
       networkTimings.reduce((aPreviousTiming, aCurrentTiming) => {
-        ok(aPreviousTiming <= aCurrentTiming);
+        ok(aPreviousTiming <= aCurrentTiming, "Checking network timings");
         return aCurrentTiming;
       });
     } else {
-      networkTimings.forEach(aTiming => is(aTiming, 0));
+      networkTimings.forEach(aTiming => is(aTiming, 0,
+                                           "Network timings should be 0."));
     }
 
     SpecialPowers.removeObserver(observer, topic);
     resolveFunction();
   }
 
   SpecialPowers.addObserver(observer, topic);
 
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/file_bug635636.xhtml
@@ -0,0 +1,3 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<div>1</div>
+</html>
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -4,16 +4,17 @@ support-files =
   data/cfhtml-firefox.txt
   data/cfhtml-ie.txt
   data/cfhtml-ooo.txt
   data/cfhtml-nocontext.txt
   file_bug549262.html
   file_bug586662.html
   file_bug611182.html
   file_bug611182.sjs
+  file_bug635636.xhtml
   file_bug674770-1.html
   file_bug795418-2.sjs
   file_bug915962.html
   file_select_all_without_body.html
   green.png
   spellcheck.js
 
 [test_bug46555.html]
--- a/editor/libeditor/tests/test_bug635636.html
+++ b/editor/libeditor/tests/test_bug635636.html
@@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 635636 **/
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var w, d;
 
   function b1()
   {
-    w = window.open('data:application/xhtml+xml,<html xmlns="http://www.w3.org/1999/xhtml"><div>1</div></html>');
+    w = window.open('file_bug635636.xhtml');
     SimpleTest.waitForFocus(b2, w);
   }
 
   function b2()
   {
     w.document.designMode = 'on';
     w.location = "data:text/plain,2";
     d = w.document.getElementsByTagName("div")[0];
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -52,17 +52,17 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerManagerMLGPU.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/WebRenderBridgeParent.h"
-#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
 #include "basic/X11BasicCompositor.h" // for X11BasicCompositor
 #endif
@@ -1651,17 +1651,17 @@ CompositorBridgeParent::RecvAdoptChild(c
       sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager, GetAnimationStorage());
       // Trigger composition to handle a case that mLayerTree was not composited yet
       // by previous CompositorBridgeParent, since nsRefreshDriver might wait composition complete.
       ScheduleComposition();
     }
     if (mWrBridge && sIndirectLayerTrees[child].mWrBridge) {
       sIndirectLayerTrees[child].mWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(),
                                                             mWrBridge->GetWebRenderAPI(),
-                                                            mWrBridge->CompositableHolder(),
+                                                            mWrBridge->AsyncImageManager(),
                                                             GetAnimationStorage());
       // Pretend we composited, since parent CompositorBridgeParent was replaced.
       CrossProcessCompositorBridgeParent* cpcp = sIndirectLayerTrees[child].mCrossProcessParent;
       if (cpcp) {
         TimeStamp now = TimeStamp::Now();
         cpcp->DidCompositeLocked(child, now, now);
       }
     }
@@ -1690,22 +1690,28 @@ CompositorBridgeParent::AllocPWebRenderB
   MOZ_ASSERT(!mCompositor);
   MOZ_ASSERT(!mCompositorScheduler);
 
 
   MOZ_ASSERT(mWidget);
   RefPtr<widget::CompositorWidget> widget = mWidget;
   RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(
     gfxPrefs::WebRenderProfilerEnabled(), this, Move(widget), aSize);
-  RefPtr<WebRenderCompositableHolder> holder =
-    new WebRenderCompositableHolder(WebRenderBridgeParent::AllocIdNameSpace());
+  RefPtr<AsyncImagePipelineManager> asyncMgr =
+    new AsyncImagePipelineManager(WebRenderBridgeParent::AllocIdNameSpace());
+  if (!api) {
+    mWrBridge = WebRenderBridgeParent::CreateDestroyed();
+    *aIdNamespace = mWrBridge->GetIdNameSpace();
+    *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
+    return mWrBridge;
+  }
   MOZ_ASSERT(api); // TODO have a fallback
   api->SetRootPipeline(aPipelineId);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
-  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(holder), Move(animStorage));
+  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, Move(api), Move(asyncMgr), Move(animStorage));
   *aIdNamespace = mWrBridge->GetIdNameSpace();
 
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   mWrBridge.get()->AddRef(); // IPDL reference
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
   sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
@@ -1945,17 +1951,17 @@ CompositorBridgeParent::DidComposite(Tim
 }
 
 void
 CompositorBridgeParent::NotifyDidCompositeToPipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd)
 {
   if (!mWrBridge) {
     return;
   }
-  mWrBridge->CompositableHolder()->Update(aPipelineId, aEpoch);
+  mWrBridge->AsyncImageManager()->Update(aPipelineId, aEpoch);
 
   if (mPaused) {
     return;
   }
 
   if (mWrBridge->PipelineId() == aPipelineId) {
     uint64_t transactionId = mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd);
     Unused << SendDidComposite(0, transactionId, aCompositeStart, aCompositeEnd);
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -21,17 +21,17 @@
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/WebRenderBridgeParent.h"
-#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsTArray.h"                   // for nsTArray
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPtr.h"
 
 using namespace std;
@@ -212,25 +212,25 @@ CrossProcessCompositorBridgeParent::Allo
   MOZ_ASSERT(sIndirectLayerTrees.find(layersId) != sIndirectLayerTrees.end());
   MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
   WebRenderBridgeParent* parent = nullptr;
   CompositorBridgeParent* cbp = sIndirectLayerTrees[layersId].mParent;
   if (!cbp) {
     // This could happen when this function is called after CompositorBridgeParent destruction.
     // This was observed during Tab move between different windows.
     NS_WARNING("Created child without a matching parent?");
-    parent = WebRenderBridgeParent::CeateDestroyed();
+    parent = WebRenderBridgeParent::CreateDestroyed();
     *aIdNamespace = parent->GetIdNameSpace();
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return parent;
   }
   WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get();
 
   RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI();
-  RefPtr<WebRenderCompositableHolder> holder = root->CompositableHolder();
+  RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder), Move(animStorage));
 
   parent->AddRef(); // IPDL reference
   sIndirectLayerTrees[layersId].mCrossProcessParent = this;
   sIndirectLayerTrees[layersId].mWrBridge = parent;
   *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
   *aIdNamespace = parent->GetIdNameSpace();
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -220,21 +220,21 @@ EXPORTS.mozilla.layers += [
     'PersistentBufferProvider.h',
     'RenderTrace.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'UpdateImageHelper.h',
+    'wr/AsyncImagePipelineManager.h',
     'wr/ScrollingLayersHelper.h',
     'wr/StackingContextHelper.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
-    'wr/WebRenderCompositableHolder.h',
     'wr/WebRenderDisplayItemLayer.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayer.h',
     'wr/WebRenderLayerManager.h',
     'wr/WebRenderLayersLogging.h',
     'wr/WebRenderMessageUtils.h',
     'wr/WebRenderScrollData.h',
     'wr/WebRenderScrollDataWrapper.h',
@@ -446,23 +446,23 @@ UNIFIED_SOURCES += [
     'ReadbackProcessor.cpp',
     'RenderTrace.cpp',
     'RotatedBuffer.cpp',
     'ShareableCanvasLayer.cpp',
     'SourceSurfaceSharedData.cpp',
     'SourceSurfaceVolatileData.cpp',
     'TextureSourceProvider.cpp',
     'TextureWrapperImage.cpp',
+    'wr/AsyncImagePipelineManager.cpp',
     'wr/ScrollingLayersHelper.cpp',
     'wr/StackingContextHelper.cpp',
     'wr/WebRenderBridgeChild.cpp',
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasLayer.cpp',
     'wr/WebRenderColorLayer.cpp',
-    'wr/WebRenderCompositableHolder.cpp',
     'wr/WebRenderContainerLayer.cpp',
     'wr/WebRenderDisplayItemLayer.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderImageLayer.cpp',
     'wr/WebRenderLayer.cpp',
     'wr/WebRenderLayerManager.cpp',
     'wr/WebRenderLayersLogging.cpp',
     'wr/WebRenderPaintedLayer.cpp',
rename from gfx/layers/wr/WebRenderCompositableHolder.cpp
rename to gfx/layers/wr/AsyncImagePipelineManager.cpp
--- a/gfx/layers/wr/WebRenderCompositableHolder.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -1,71 +1,71 @@
 /* -*- Mode: C++; tab-width: 20; 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 "WebRenderCompositableHolder.h"
+#include "AsyncImagePipelineManager.h"
 
 #include "CompositableHost.h"
 #include "gfxEnv.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 namespace layers {
 
-WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
+AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
  : mInitialised(false)
  , mIsChanged(false)
  , mUseExternalImage(false)
  , mFilter(wr::ImageRendering::Auto)
  , mMixBlendMode(wr::MixBlendMode::Normal)
 {}
 
-WebRenderCompositableHolder::WebRenderCompositableHolder(uint32_t aIdNamespace)
+AsyncImagePipelineManager::AsyncImagePipelineManager(uint32_t aIdNamespace)
  : mIdNamespace(aIdNamespace)
  , mResourceId(0)
  , mAsyncImageEpoch(0)
  , mDestroyed(false)
 {
-  MOZ_COUNT_CTOR(WebRenderCompositableHolder);
+  MOZ_COUNT_CTOR(AsyncImagePipelineManager);
 }
 
-WebRenderCompositableHolder::~WebRenderCompositableHolder()
+AsyncImagePipelineManager::~AsyncImagePipelineManager()
 {
-  MOZ_COUNT_DTOR(WebRenderCompositableHolder);
+  MOZ_COUNT_DTOR(AsyncImagePipelineManager);
 }
 
 void
-WebRenderCompositableHolder::Destroy(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::Destroy(wr::WebRenderAPI* aApi)
 {
   DeleteOldAsyncImages(aApi);
   mDestroyed = true;
 }
 
 bool
-WebRenderCompositableHolder::HasKeysToDelete()
+AsyncImagePipelineManager::HasKeysToDelete()
 {
   return !mKeysToDelete.IsEmpty();
 }
 
 void
-WebRenderCompositableHolder::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
 {
   for (wr::ImageKey key : mKeysToDelete) {
     aApi->DeleteImage(key);
   }
   mKeysToDelete.Clear();
 }
 
 void
-WebRenderCompositableHolder::AddPipeline(const wr::PipelineId& aPipelineId)
+AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
     return;
   }
   uint64_t id = wr::AsUint64(aPipelineId);
 
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   if(holder) {
@@ -75,100 +75,100 @@ WebRenderCompositableHolder::AddPipeline
     holder->mDestroyedEpoch = Nothing(); // Revive holder
     return;
   }
   holder = new PipelineTexturesHolder();
   mPipelineTexturesHolders.Put(id, holder);
 }
 
 void
-WebRenderCompositableHolder::RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
+AsyncImagePipelineManager::RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
 {
   if (mDestroyed) {
     return;
   }
 
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   MOZ_ASSERT(holder);
   if (!holder) {
     return;
   }
   holder->mDestroyedEpoch = Some(aEpoch);
 }
 
 void
-WebRenderCompositableHolder::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
+AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
 {
   if (mDestroyed) {
     return;
   }
   MOZ_ASSERT(aImageHost);
   uint64_t id = wr::AsUint64(aPipelineId);
 
-  MOZ_ASSERT(!mAsyncImagePipelineHolders.Get(id));
-  AsyncImagePipelineHolder* holder = new AsyncImagePipelineHolder();
+  MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
+  AsyncImagePipeline* holder = new AsyncImagePipeline();
   holder->mImageHost = aImageHost;
-  mAsyncImagePipelineHolders.Put(id, holder);
+  mAsyncImagePipelines.Put(id, holder);
   AddPipeline(aPipelineId);
 }
 
 void
-WebRenderCompositableHolder::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
+AsyncImagePipelineManager::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
     return;
   }
 
   uint64_t id = wr::AsUint64(aPipelineId);
-  if (auto entry = mAsyncImagePipelineHolders.Lookup(id)) {
-    AsyncImagePipelineHolder* holder = entry.Data();
+  if (auto entry = mAsyncImagePipelines.Lookup(id)) {
+    AsyncImagePipeline* holder = entry.Data();
     ++mAsyncImageEpoch; // Update webrender epoch
     aApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
     for (wr::ImageKey key : holder->mKeys) {
       aApi->DeleteImage(key);
     }
     entry.Remove();
     RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
   }
 }
 
 void
-WebRenderCompositableHolder::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
-                                                      const LayerRect& aScBounds,
-                                                      const gfx::Matrix4x4& aScTransform,
-                                                      const gfx::MaybeIntSize& aScaleToSize,
-                                                      const wr::ImageRendering& aFilter,
-                                                      const wr::MixBlendMode& aMixBlendMode)
+AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
+                                                    const LayerRect& aScBounds,
+                                                    const gfx::Matrix4x4& aScTransform,
+                                                    const gfx::MaybeIntSize& aScaleToSize,
+                                                    const wr::ImageRendering& aFilter,
+                                                    const wr::MixBlendMode& aMixBlendMode)
 {
   if (mDestroyed) {
     return;
   }
-  AsyncImagePipelineHolder* holder = mAsyncImagePipelineHolders.Get(wr::AsUint64(aPipelineId));
-  if (!holder) {
+  AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
+  if (!pipeline) {
     return;
   }
-  holder->mInitialised = true;
-  holder->mIsChanged = true;
-  holder->mScBounds = aScBounds;
-  holder->mScTransform = aScTransform;
-  holder->mScaleToSize = aScaleToSize;
-  holder->mFilter = aFilter;
-  holder->mMixBlendMode = aMixBlendMode;
+  pipeline->mInitialised = true;
+  pipeline->mIsChanged = true;
+  pipeline->mScBounds = aScBounds;
+  pipeline->mScTransform = aScTransform;
+  pipeline->mScaleToSize = aScaleToSize;
+  pipeline->mFilter = aFilter;
+  pipeline->mMixBlendMode = aMixBlendMode;
 }
 
 bool
-WebRenderCompositableHolder::GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
+AsyncImagePipelineManager::GenerateImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
 {
   MOZ_ASSERT(aKeys.IsEmpty());
   MOZ_ASSERT(aTexture);
 
   WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
 
   if (!gfxEnv::EnableWebRenderRecording() && wrTexture) {
-    wrTexture->GetWRImageKeys(aKeys, std::bind(&WebRenderCompositableHolder::GetImageKey, this));
+    wrTexture->GetWRImageKeys(aKeys, std::bind(&AsyncImagePipelineManager::GenerateImageKey, this));
     MOZ_ASSERT(!aKeys.IsEmpty());
     Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
     wrTexture->AddWRImage(aApi, keys, wrTexture->GetExternalImageKey());
     return true;
   } else {
     RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
     if (!dSurf) {
       NS_ERROR("TextureHost does not return DataSourceSurface");
@@ -178,166 +178,166 @@ WebRenderCompositableHolder::GetImageKey
     if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
       NS_ERROR("DataSourceSurface failed to map");
       return false;
     }
     gfx::IntSize size = dSurf->GetSize();
     wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
     auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
 
-    wr::ImageKey key = GetImageKey();
+    wr::ImageKey key = GenerateImageKey();
     aKeys.AppendElement(key);
     aApi->AddImage(key, descriptor, slice);
     dSurf->Unmap();
   }
   return false;
 }
 
 bool
-WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
-                                             bool& aUseExternalImage,
-                                             AsyncImagePipelineHolder* aHolder,
-                                             nsTArray<wr::ImageKey>& aKeys,
-                                             nsTArray<wr::ImageKey>& aKeysToDelete)
+AsyncImagePipelineManager::UpdateImageKeys(wr::WebRenderAPI* aApi,
+                                           bool& aUseExternalImage,
+                                           AsyncImagePipeline* aImageMgr,
+                                           nsTArray<wr::ImageKey>& aKeys,
+                                           nsTArray<wr::ImageKey>& aKeysToDelete)
 {
   MOZ_ASSERT(aKeys.IsEmpty());
-  MOZ_ASSERT(aHolder);
-  TextureHost* texture = aHolder->mImageHost->GetAsTextureHostForComposite();
+  MOZ_ASSERT(aImageMgr);
+  TextureHost* texture = aImageMgr->mImageHost->GetAsTextureHostForComposite();
 
-  if (!aHolder->mInitialised) {
+  if (!aImageMgr->mInitialised) {
     return false;
   }
 
   // No change
-  if (!aHolder->mIsChanged && texture == aHolder->mCurrentTexture) {
+  if (!aImageMgr->mIsChanged && texture == aImageMgr->mCurrentTexture) {
     // No need to update DisplayList.
     return false;
   }
 
-  aHolder->mIsChanged = false;
+  aImageMgr->mIsChanged = false;
 
-  if (texture == aHolder->mCurrentTexture) {
+  if (texture == aImageMgr->mCurrentTexture) {
     // Reuse previous ImageKeys.
-    aKeys.AppendElements(aHolder->mKeys);
-    aUseExternalImage = aHolder->mUseExternalImage;
+    aKeys.AppendElements(aImageMgr->mKeys);
+    aUseExternalImage = aImageMgr->mUseExternalImage;
     return true;
   }
 
   // Delete old ImageKeys
-  aKeysToDelete.AppendElements(aHolder->mKeys);
-  aHolder->mKeys.Clear();
-  aHolder->mCurrentTexture = nullptr;
+  aKeysToDelete.AppendElements(aImageMgr->mKeys);
+  aImageMgr->mKeys.Clear();
+  aImageMgr->mCurrentTexture = nullptr;
 
   // No txture to render
   if (!texture) {
     return true;
   }
 
-  aUseExternalImage = aHolder->mUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
+  aUseExternalImage = aImageMgr->mUseExternalImage = GenerateImageKeyForTextureHost(aApi, texture, aKeys);
   MOZ_ASSERT(!aKeys.IsEmpty());
-  aHolder->mKeys.AppendElements(aKeys);
-  aHolder->mCurrentTexture = texture;
+  aImageMgr->mKeys.AppendElements(aKeys);
+  aImageMgr->mCurrentTexture = texture;
   return true;
 }
 
 void
-WebRenderCompositableHolder::ApplyAsyncImages(wr::WebRenderAPI* aApi)
+AsyncImagePipelineManager::ApplyAsyncImages(wr::WebRenderAPI* aApi)
 {
-  if (mDestroyed || mAsyncImagePipelineHolders.Count() == 0) {
+  if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   ++mAsyncImageEpoch; // Update webrender epoch
   wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
   nsTArray<wr::ImageKey> keysToDelete;
 
-  for (auto iter = mAsyncImagePipelineHolders.Iter(); !iter.Done(); iter.Next()) {
+  for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
-    AsyncImagePipelineHolder* holder = iter.Data();
+    AsyncImagePipeline* pipeline = iter.Data();
 
     nsTArray<wr::ImageKey> keys;
     bool useExternalImage = false;
     bool updateDisplayList = UpdateImageKeys(aApi,
                                              useExternalImage,
-                                             holder,
+                                             pipeline,
                                              keys,
                                              keysToDelete);
     if (!updateDisplayList) {
       continue;
     }
 
-    wr::LayoutSize contentSize { holder->mScBounds.width, holder->mScBounds.height };
+    wr::LayoutSize contentSize { pipeline->mScBounds.width, pipeline->mScBounds.height };
     wr::DisplayListBuilder builder(pipelineId, contentSize);
 
     if (!keys.IsEmpty()) {
-      MOZ_ASSERT(holder->mCurrentTexture.get());
+      MOZ_ASSERT(pipeline->mCurrentTexture.get());
 
       float opacity = 1.0f;
-      builder.PushStackingContext(wr::ToLayoutRect(holder->mScBounds),
+      builder.PushStackingContext(wr::ToLayoutRect(pipeline->mScBounds),
                                   0,
                                   &opacity,
-                                  holder->mScTransform.IsIdentity() ? nullptr : &holder->mScTransform,
+                                  pipeline->mScTransform.IsIdentity() ? nullptr : &pipeline->mScTransform,
                                   wr::TransformStyle::Flat,
-                                  holder->mMixBlendMode,
+                                  pipeline->mMixBlendMode,
                                   nsTArray<wr::WrFilterOp>());
 
-      LayerRect rect(0, 0, holder->mCurrentTexture->GetSize().width, holder->mCurrentTexture->GetSize().height);
-      if (holder->mScaleToSize.isSome()) {
-        rect = LayerRect(0, 0, holder->mScaleToSize.value().width, holder->mScaleToSize.value().height);
+      LayerRect rect(0, 0, pipeline->mCurrentTexture->GetSize().width, pipeline->mCurrentTexture->GetSize().height);
+      if (pipeline->mScaleToSize.isSome()) {
+        rect = LayerRect(0, 0, pipeline->mScaleToSize.value().width, pipeline->mScaleToSize.value().height);
       }
 
       if (useExternalImage) {
-        MOZ_ASSERT(holder->mCurrentTexture->AsWebRenderTextureHost());
+        MOZ_ASSERT(pipeline->mCurrentTexture->AsWebRenderTextureHost());
         Range<const wr::ImageKey> range_keys(&keys[0], keys.Length());
-        holder->mCurrentTexture->PushExternalImage(builder,
-                                                   wr::ToLayoutRect(rect),
-                                                   wr::ToLayoutRect(rect),
-                                                   holder->mFilter,
-                                                   range_keys);
-        HoldExternalImage(pipelineId, epoch, holder->mCurrentTexture->AsWebRenderTextureHost());
+        pipeline->mCurrentTexture->PushExternalImage(builder,
+                                                     wr::ToLayoutRect(rect),
+                                                     wr::ToLayoutRect(rect),
+                                                     pipeline->mFilter,
+                                                     range_keys);
+        HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
       } else {
         MOZ_ASSERT(keys.Length() == 1);
         builder.PushImage(wr::ToLayoutRect(rect),
                           wr::ToLayoutRect(rect),
-                          holder->mFilter,
+                          pipeline->mFilter,
                           keys[0]);
       }
       builder.PopStackingContext();
     }
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
     builder.Finalize(builderContentSize, dl);
-    aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(holder->mScBounds.width, holder->mScBounds.height),
+    aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.width, pipeline->mScBounds.height),
                              pipelineId, builderContentSize,
                              dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
   }
   DeleteOldAsyncImages(aApi);
   mKeysToDelete.SwapElements(keysToDelete);
 }
 
 void
-WebRenderCompositableHolder::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
+AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
 {
   if (mDestroyed) {
     return;
   }
   MOZ_ASSERT(aTexture);
 
   PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
   MOZ_ASSERT(holder);
   if (!holder) {
     return;
   }
   // Hold WebRenderTextureHost until end of its usage on RenderThread
   holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
 }
 
 void
-WebRenderCompositableHolder::Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
+AsyncImagePipelineManager::Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
 {
   if (mDestroyed) {
     return;
   }
   if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
     PipelineTexturesHolder* holder = entry.Data();
     // Remove Pipeline
     if (holder->mDestroyedEpoch.isSome() && holder->mDestroyedEpoch.ref() <= aEpoch) {
rename from gfx/layers/wr/WebRenderCompositableHolder.h
rename to gfx/layers/wr/AsyncImagePipelineManager.h
--- a/gfx/layers/wr/WebRenderCompositableHolder.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -23,25 +23,25 @@ class WebRenderAPI;
 
 namespace layers {
 
 class CompositableHost;
 class CompositorVsyncScheduler;
 class WebRenderImageHost;
 class WebRenderTextureHost;
 
-class WebRenderCompositableHolder final
+class AsyncImagePipelineManager final
 {
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderCompositableHolder)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
 
-  explicit WebRenderCompositableHolder(uint32_t aIdNamespace);
+  explicit AsyncImagePipelineManager(uint32_t aIdNamespace);
 
 protected:
-  ~WebRenderCompositableHolder();
+  ~AsyncImagePipelineManager();
 
 public:
   void Destroy(wr::WebRenderAPI* aApi);
   bool HasKeysToDelete();
 
   void AddPipeline(const wr::PipelineId& aPipelineId);
   void RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
@@ -79,67 +79,67 @@ public:
                                 const wr::MixBlendMode& aMixBlendMode);
   void ApplyAsyncImages(wr::WebRenderAPI* aApi);
 
 private:
   void DeleteOldAsyncImages(wr::WebRenderAPI* aApi);
 
   uint32_t GetNextResourceId() { return ++mResourceId; }
   uint32_t GetNamespace() { return mIdNamespace; }
-  wr::ImageKey GetImageKey()
+  wr::ImageKey GenerateImageKey()
   {
     wr::ImageKey key;
     key.mNamespace.mHandle = GetNamespace();
     key.mHandle = GetNextResourceId();
     return key;
   }
-  bool GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
+  bool GenerateImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
 
   struct ForwardingTextureHost {
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
     CompositableTextureHostRef mTexture;
   };
 
   struct PipelineTexturesHolder {
     // Holds forwarding WebRenderTextureHosts.
     std::queue<ForwardingTextureHost> mTextureHosts;
     Maybe<wr::Epoch> mDestroyedEpoch;
   };
 
-  struct AsyncImagePipelineHolder {
-    AsyncImagePipelineHolder();
+  struct AsyncImagePipeline {
+    AsyncImagePipeline();
 
     bool mInitialised;
     bool mIsChanged;
     bool mUseExternalImage;
     LayerRect mScBounds;
     gfx::Matrix4x4 mScTransform;
     gfx::MaybeIntSize mScaleToSize;
     wr::ImageRendering mFilter;
     wr::MixBlendMode mMixBlendMode;
     RefPtr<WebRenderImageHost> mImageHost;
     CompositableTextureHostRef mCurrentTexture;
     nsTArray<wr::ImageKey> mKeys;
   };
 
   bool UpdateImageKeys(wr::WebRenderAPI* aApi,
                        bool& aUseExternalImage,
-                       AsyncImagePipelineHolder* aHolder,
+                       AsyncImagePipeline* aImageMgr,
                        nsTArray<wr::ImageKey>& aKeys,
                        nsTArray<wr::ImageKey>& aKeysToDelete);
 
   uint32_t mIdNamespace;
   uint32_t mResourceId;
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
-  nsClassHashtable<nsUint64HashKey, AsyncImagePipelineHolder> mAsyncImagePipelineHolders;
+  nsClassHashtable<nsUint64HashKey, AsyncImagePipeline> mAsyncImagePipelines;
   uint32_t mAsyncImageEpoch;
   nsTArray<wr::ImageKey> mKeysToDelete;
   bool mDestroyed;
 
   // Render time for the current composition.
   TimeStamp mCompositionTime;
 
   // When nonnull, during rendering, some compositable indicated that it will
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -4,30 +4,31 @@
  * 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/layers/WebRenderBridgeParent.h"
 
 #include "apz/src/AsyncPanZoomController.h"
 #include "CompositableHost.h"
 #include "gfxPrefs.h"
+#include "gfxEnv.h"
 #include "GeckoProfiler.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/Range.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorVsyncScheduler.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/TextureHost.h"
-#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 bool is_in_main_thread()
@@ -112,36 +113,36 @@ private:
 
 /* static */ uint32_t WebRenderBridgeParent::sIdNameSpace = 0;
 
 WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                                              const wr::PipelineId& aPipelineId,
                                              widget::CompositorWidget* aWidget,
                                              CompositorVsyncScheduler* aScheduler,
                                              RefPtr<wr::WebRenderAPI>&& aApi,
-                                             RefPtr<WebRenderCompositableHolder>&& aHolder,
+                                             RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                                              RefPtr<CompositorAnimationStorage>&& aAnimStorage)
   : mCompositorBridge(aCompositorBridge)
   , mPipelineId(aPipelineId)
   , mWidget(aWidget)
   , mApi(aApi)
-  , mCompositableHolder(aHolder)
+  , mAsyncImageManager(aImageMgr)
   , mCompositorScheduler(aScheduler)
   , mAnimStorage(aAnimStorage)
   , mChildLayerObserverEpoch(0)
   , mParentLayerObserverEpoch(0)
   , mWrEpoch(0)
   , mIdNameSpace(AllocIdNameSpace())
   , mPaused(false)
   , mDestroyed(false)
   , mForceRendering(false)
 {
-  MOZ_ASSERT(mCompositableHolder);
+  MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
-  mCompositableHolder->AddPipeline(mPipelineId);
+  mAsyncImageManager->AddPipeline(mPipelineId);
   if (mWidget) {
     MOZ_ASSERT(!mCompositorScheduler);
     mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
   }
 }
 
 WebRenderBridgeParent::WebRenderBridgeParent()
   : mCompositorBridge(nullptr)
@@ -151,17 +152,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   , mIdNameSpace(AllocIdNameSpace())
   , mPaused(false)
   , mDestroyed(true)
   , mForceRendering(false)
 {
 }
 
 /* static */ WebRenderBridgeParent*
-WebRenderBridgeParent::CeateDestroyed()
+WebRenderBridgeParent::CreateDestroyed()
 {
   return new WebRenderBridgeParent();
 }
 
 WebRenderBridgeParent::~WebRenderBridgeParent()
 {
 }
 
@@ -611,17 +612,17 @@ WebRenderBridgeParent::ProcessWebRenderP
         auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
         mApi->AddImage(keys[0], descriptor, slice);
 
         dSurf->Unmap();
         break;
       }
       case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
         const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
-        mCompositableHolder->UpdateAsyncImagePipeline(op.pipelineId(),
+        mAsyncImageManager->UpdateAsyncImagePipeline(op.pipelineId(),
                                                       op.scBounds(),
                                                       op.scTransform(),
                                                       op.scaleToSize(),
                                                       op.filter(),
                                                       op.mixBlendMode());
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
@@ -658,17 +659,17 @@ WebRenderBridgeParent::ProcessWebRenderP
 
 void
 WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
                                                 InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
                                                 const wr::LayoutSize& aContentSize, const wr::ByteBuffer& dl,
                                                 const wr::BuiltDisplayListDescriptor& dlDesc,
                                                 const uint32_t& aIdNameSpace)
 {
-  mCompositableHolder->SetCompositionTime(TimeStamp::Now());
+  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
   ProcessWebRenderParentCommands(aCommands);
 
   // The command is obsoleted.
   // Do not set the command to webrender since it causes crash in webrender.
   if (mIdNameSpace != aIdNameSpace) {
     return;
   }
 
@@ -763,17 +764,17 @@ WebRenderBridgeParent::RecvAddPipelineId
   MOZ_ASSERT(host->AsWebRenderImageHost());
   WebRenderImageHost* wrHost = host->AsWebRenderImageHost();
   if (!wrHost) {
     return IPC_OK();
   }
 
   wrHost->SetWrBridge(this);
   mAsyncCompositables.Put(wr::AsUint64(aPipelineId), wrHost);
-  mCompositableHolder->AddAsyncImagePipeline(aPipelineId, wrHost);
+  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvRemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
@@ -781,17 +782,17 @@ WebRenderBridgeParent::RecvRemovePipelin
   }
 
   WebRenderImageHost* wrHost = mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get();
   if (!wrHost) {
     return IPC_OK();
   }
 
   wrHost->ClearWrBridge();
-  mCompositableHolder->RemoveAsyncImagePipeline(mApi, aPipelineId);
+  mAsyncImageManager->RemoveAsyncImagePipeline(mApi, aPipelineId);
   mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
                                                              const CompositableHandle& aHandle)
 {
@@ -859,23 +860,23 @@ WebRenderBridgeParent::RecvClearCachedRe
   }
   mActiveAnimations.clear();
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                                        wr::WebRenderAPI* aApi,
-                                       WebRenderCompositableHolder* aHolder,
+                                       AsyncImagePipelineManager* aImageMgr,
                                        CompositorAnimationStorage* aAnimStorage)
 {
   MOZ_ASSERT(!mWidget);
   MOZ_ASSERT(aScheduler);
   MOZ_ASSERT(aApi);
-  MOZ_ASSERT(aHolder);
+  MOZ_ASSERT(aImageMgr);
   MOZ_ASSERT(aAnimStorage);
 
   if (mDestroyed) {
     return;
   }
 
   // Update id name space to identify obsoleted keys.
   // Since usage of invalid keys could cause crash in webrender.
@@ -890,22 +891,22 @@ WebRenderBridgeParent::UpdateWebRender(C
   // before new layer/webrender keys allocation. In future, we could address the problem.
   Unused << SendWrUpdated(mIdNameSpace);
   CompositorBridgeParentBase* cBridge = mCompositorBridge;
   // XXX Stop to clear resources if webreder supports resources sharing between different webrender instances.
   ClearResources();
   mCompositorBridge = cBridge;
   mCompositorScheduler = aScheduler;
   mApi = aApi;
-  mCompositableHolder = aHolder;
+  mAsyncImageManager = aImageMgr;
   mAnimStorage = aAnimStorage;
 
   Unused << GetNextWrEpoch(); // Update webrender epoch
-  // Register pipeline to updated CompositableHolder.
-  mCompositableHolder->AddPipeline(mPipelineId);
+  // Register pipeline to updated AsyncImageManager.
+  mAsyncImageManager->AddPipeline(mPipelineId);
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvForceComposite()
 {
   if (mDestroyed) {
     return IPC_OK();
   }
@@ -1105,18 +1106,18 @@ WebRenderBridgeParent::CompositeToTarget
     ScheduleComposition();
     return;
   }
 
   bool scheduleComposite = false;
   nsTArray<wr::WrOpacityProperty> opacityArray;
   nsTArray<wr::WrTransformProperty> transformArray;
 
-  mCompositableHolder->SetCompositionTime(TimeStamp::Now());
-  mCompositableHolder->ApplyAsyncImages(mApi);
+  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+  mAsyncImageManager->ApplyAsyncImages(mApi);
 
   SampleAnimations(opacityArray, transformArray);
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
     scheduleComposite = true;
   }
 
   if (PushAPZStateToWR(transformArray)) {
     scheduleComposite = true;
@@ -1130,17 +1131,17 @@ WebRenderBridgeParent::CompositeToTarget
 #endif
 
   if (!transformArray.IsEmpty() || !opacityArray.IsEmpty()) {
     mApi->GenerateFrame(opacityArray, transformArray);
   } else {
     mApi->GenerateFrame();
   }
 
-  if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
+  if (!mAsyncImageManager->GetCompositeUntilTime().IsNull()) {
     scheduleComposite = true;
   }
 
   if (scheduleComposite) {
     ScheduleComposition();
   }
 }
 
@@ -1304,21 +1305,21 @@ WebRenderBridgeParent::ClearResources()
     iter.Data()->ClearWrBridge();
   }
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     RefPtr<WebRenderImageHost> host = iter.Data();
     MOZ_ASSERT(host->GetAsyncRef());
     host->ClearWrBridge();
-    mCompositableHolder->RemoveAsyncImagePipeline(mApi, pipelineId);
+    mAsyncImageManager->RemoveAsyncImagePipeline(mApi, pipelineId);
   }
   mAsyncCompositables.Clear();
 
-  mCompositableHolder->RemovePipeline(mPipelineId, wr::NewEpoch(wrEpoch));
+  mAsyncImageManager->RemovePipeline(mPipelineId, wr::NewEpoch(wrEpoch));
 
   for (std::unordered_set<uint64_t>::iterator iter = mActiveAnimations.begin(); iter != mActiveAnimations.end(); iter++) {
     mAnimStorage->ClearById(*iter);
   }
   mActiveAnimations.clear();
 
   if (mWidget) {
     mCompositorScheduler->Destroy();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -34,38 +34,38 @@ class WebRenderAPI;
 }
 
 namespace layers {
 
 class Compositor;
 class CompositorAnimationStorage;
 class CompositorBridgeParentBase;
 class CompositorVsyncScheduler;
-class WebRenderCompositableHolder;
+class AsyncImagePipelineManager;
 class WebRenderImageHost;
 
 class WebRenderBridgeParent final : public PWebRenderBridgeParent
                                   , public CompositorVsyncSchedulerOwner
                                   , public CompositableParentManager
 {
 public:
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
                         widget::CompositorWidget* aWidget,
                         CompositorVsyncScheduler* aScheduler,
                         RefPtr<wr::WebRenderAPI>&& aApi,
-                        RefPtr<WebRenderCompositableHolder>&& aHolder,
+                        RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                         RefPtr<CompositorAnimationStorage>&& aAnimStorage);
 
-  static WebRenderBridgeParent* CeateDestroyed();
+  static WebRenderBridgeParent* CreateDestroyed();
 
   wr::PipelineId PipelineId() { return mPipelineId; }
   wr::WebRenderAPI* GetWebRenderAPI() { return mApi; }
   wr::Epoch WrEpoch() { return wr::NewEpoch(mWrEpoch); }
-  WebRenderCompositableHolder* CompositableHolder() { return mCompositableHolder; }
+  AsyncImagePipelineManager* AsyncImageManager() { return mAsyncImageManager; }
   CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
 
   mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                               const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
 
@@ -201,17 +201,17 @@ public:
   }
 
   void FlushRendering(bool aIsSync);
 
   void ScheduleComposition();
 
   void UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                        wr::WebRenderAPI* aApi,
-                       WebRenderCompositableHolder* aHolder,
+                       AsyncImagePipelineManager* aImageMgr,
                        CompositorAnimationStorage* aAnimStorage);
 
 private:
   WebRenderBridgeParent();
   virtual ~WebRenderBridgeParent();
 
   uint64_t GetLayersId() const;
   void DeleteOldImages();
@@ -268,17 +268,17 @@ private:
     uint64_t mId;
     TimeStamp mFwdTime;
   };
 
   CompositorBridgeParentBase* MOZ_NON_OWNING_REF mCompositorBridge;
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<wr::WebRenderAPI> mApi;
-  RefPtr<WebRenderCompositableHolder> mCompositableHolder;
+  RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   RefPtr<CompositorAnimationStorage> mAnimStorage;
   std::vector<wr::ImageKey> mKeysToDelete;
   // mActiveImageKeys and mFontKeys are used to avoid leaking animations when
   // WebRenderBridgeParent is destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveImageKeys;
   std::unordered_set<uint64_t> mFontKeys;
   // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -78,17 +78,17 @@ WebRenderCanvasLayer::RenderLayer(wr::Di
   wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("CanvasLayer %p texture-filter=%s\n",
                   this->GetLayer(),
                   Stringify(filter).c_str());
   }
 
-  wr::WrImageKey key = GetImageKey();
+  wr::WrImageKey key = GenerateImageKey();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushImage(r, r, filter, key);
 }
 
 void
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -5,17 +5,17 @@
 
 #include "WebRenderImageHost.h"
 
 #include "LayersLogging.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/WebRenderBridgeParent.h"
-#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
 namespace mozilla {
 
 using namespace gfx;
@@ -72,22 +72,22 @@ WebRenderImageHost::UseTextureHost(const
     mWrBridge->ScheduleComposition();
   }
 
   // Video producers generally send replacement images with the same frameID but
   // slightly different timestamps in order to sync with the audio clock. This
   // means that any CompositeUntil() call we made in Composite() may no longer
   // guarantee that we'll composite until the next frame is ready. Fix that here.
   if (mWrBridge && mLastFrameID >= 0) {
-    MOZ_ASSERT(mWrBridge->CompositableHolder());
+    MOZ_ASSERT(mWrBridge->AsyncImageManager());
     for (size_t i = 0; i < mImages.Length(); ++i) {
       bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
                              mImages[i].mProducerID != mLastProducerID;
       if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
-        mWrBridge->CompositableHolder()->CompositeUntil(mImages[i].mTimeStamp +
+        mWrBridge->AsyncImageManager()->CompositeUntil(mImages[i].mTimeStamp +
                            TimeDuration::FromMilliseconds(BIAS_TIME_MS));
         break;
       }
     }
   }
 }
 
 void
@@ -119,18 +119,18 @@ WebRenderImageHost::RemoveTextureHost(Te
   }
 }
 
 TimeStamp
 WebRenderImageHost::GetCompositionTime() const
 {
   TimeStamp time;
   if (mWrBridge) {
-    MOZ_ASSERT(mWrBridge->CompositableHolder());
-    time = mWrBridge->CompositableHolder()->GetCompositionTime();
+    MOZ_ASSERT(mWrBridge->AsyncImageManager());
+    time = mWrBridge->AsyncImageManager()->GetCompositionTime();
   }
   return time;
 }
 
 TextureHost*
 WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
   TimedImage* img = ChooseImage();
@@ -145,18 +145,18 @@ WebRenderImageHost::GetAsTextureHostForC
 {
   int imageIndex = ChooseImageIndex();
   if (imageIndex < 0) {
     SetCurrentTextureHost(nullptr);
     return nullptr;
   }
 
   if (mWrBridge && uint32_t(imageIndex) + 1 < mImages.Length()) {
-    MOZ_ASSERT(mWrBridge->CompositableHolder());
-    mWrBridge->CompositableHolder()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+    MOZ_ASSERT(mWrBridge->AsyncImageManager());
+    mWrBridge->AsyncImageManager()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
   }
 
   TimedImage* img = &mImages[imageIndex];
 
   if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
     mLastFrameID = img->mFrameID;
     mLastProducerID = img->mProducerID;
   }
@@ -170,20 +170,20 @@ WebRenderImageHost::SetCurrentTextureHos
   if (aTexture == mCurrentTextureHost.get()) {
     return;
   }
 
   if (mWrBridge &&
       !!mCurrentTextureHost &&
       mCurrentTextureHost != aTexture &&
       mCurrentTextureHost->AsWebRenderTextureHost()) {
-    MOZ_ASSERT(mWrBridge->CompositableHolder());
+    MOZ_ASSERT(mWrBridge->AsyncImageManager());
     wr::PipelineId piplineId = mWrBridge->PipelineId();
     wr::Epoch epoch = mWrBridge->WrEpoch();
-    mWrBridge->CompositableHolder()->HoldExternalImage(
+    mWrBridge->AsyncImageManager()->HoldExternalImage(
       piplineId,
       epoch,
       mCurrentTextureHost->AsWebRenderTextureHost());
   }
 
   mCurrentTextureHost = aTexture;
 }
 
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -156,17 +156,17 @@ WebRenderImageLayer::RenderLayer(wr::Dis
 
     ParentLayerRect bounds = GetLocalTransformTyped().TransformBounds(Bounds());
 
     // We don't push a stacking context for this async image pipeline here.
     // Instead, we do it inside the iframe that hosts the image. As a result,
     // a bunch of the calculations normally done as part of that stacking
     // context need to be done manually and pushed over to the parent side,
     // where it will be done when we build the display list for the iframe.
-    // That happens in WebRenderCompositableHolder.
+    // That happens in AsyncImagePipelineManager.
 
     LayerRect rect = ViewAs<LayerPixel>(bounds,
         PixelCastJustification::MovingDownToChildren);
     DumpLayerInfo("Image Layer async", rect);
 
     wr::LayoutRect r = aSc.ToRelativeLayoutRect(rect);
     aBuilder.PushIFrame(r, mPipelineId.ref());
 
--- a/gfx/layers/wr/WebRenderLayer.cpp
+++ b/gfx/layers/wr/WebRenderLayer.cpp
@@ -27,17 +27,17 @@ WebRenderLayer::WrManager()
 
 WebRenderBridgeChild*
 WebRenderLayer::WrBridge()
 {
   return WrManager()->WrBridge();
 }
 
 wr::WrImageKey
-WebRenderLayer::GetImageKey()
+WebRenderLayer::GenerateImageKey()
 {
   wr::WrImageKey key;
   key.mNamespace.mHandle = WrBridge()->GetNamespace();
   key.mHandle = WrBridge()->GetNextResourceId();
   return key;
 }
 
 Maybe<wr::WrImageMask>
@@ -106,17 +106,17 @@ WebRenderLayer::UpdateImageKey(ImageClie
     return aOldKey;
   }
 
   // Delete old key, we are generating a new key.
   if (aOldKey.isSome()) {
     WrManager()->AddImageKeyForDiscard(aOldKey.value());
   }
 
-  wr::WrImageKey key = GetImageKey();
+  wr::WrImageKey key = GenerateImageKey();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(aExternalImageId, key));
   return Some(key);
 }
 
 void
 WebRenderLayer::DumpLayerInfo(const char* aLayerType, const LayerRect& aRect)
 {
   if (!gfxPrefs::LayersDump()) {
--- a/gfx/layers/wr/WebRenderLayer.h
+++ b/gfx/layers/wr/WebRenderLayer.h
@@ -42,17 +42,17 @@ public:
 
   Maybe<wr::ImageKey> UpdateImageKey(ImageClientSingle* aImageClient,
                                      ImageContainer* aContainer,
                                      Maybe<wr::ImageKey>& aOldKey,
                                      wr::ExternalImageId& aExternalImageId);
 
   WebRenderLayerManager* WrManager();
   WebRenderBridgeChild* WrBridge();
-  wr::WrImageKey GetImageKey();
+  wr::WrImageKey GenerateImageKey();
 
   LayerRect Bounds();
   LayerRect BoundsForStackingContext();
 
   // Builds a WrImageMask from the mask layer on this layer, if there is one.
   // The |aRelativeTo| parameter should be a reference to the stacking context
   // that we want this mask to be relative to. This is usually the stacking
   // context of the *parent* layer of |this|, because that is what the mask
--- a/gfx/layers/wr/WebRenderPaintedLayer.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayer.cpp
@@ -94,17 +94,17 @@ WebRenderPaintedLayer::CreateWebRenderDi
                                                   const StackingContextHelper& aSc)
 {
   ScrollingLayersHelper scroller(this, aBuilder, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
 
-  wr::WrImageKey key = GetImageKey();
+  wr::WrImageKey key = GenerateImageKey();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushImage(r, r, wr::ImageRendering::Auto, key);
 }
 
 void
--- a/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
@@ -73,17 +73,17 @@ WebRenderPaintedLayerBlob::RenderLayer(w
 
     wr::ByteBuffer bytes(recorder->mOutputStream.mLength, (uint8_t*)recorder->mOutputStream.mData);
 
     //XXX: We should switch to updating the blob image instead of adding a new one
     //     That will get rid of this discard bit
     if (mImageKey.isSome()) {
       WrManager()->AddImageKeyForDiscard(mImageKey.value());
     }
-    mImageKey = Some(GetImageKey());
+    mImageKey = Some(GenerateImageKey());
     WrBridge()->SendAddBlobImage(mImageKey.value(), imageSize, size.width * 4, dt->GetFormat(), bytes);
     mImageBounds = visibleRegion.GetBounds();
   }
 
   ScrollingLayersHelper scroller(this, aBuilder, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -108,17 +108,17 @@ WebRenderImageData::CreateAsyncImageWebR
 
   // Push IFrame for async image pipeline.
   //
   // We don't push a stacking context for this async image pipeline here.
   // Instead, we do it inside the iframe that hosts the image. As a result,
   // a bunch of the calculations normally done as part of that stacking
   // context need to be done manually and pushed over to the parent side,
   // where it will be done when we build the display list for the iframe.
-  // That happens in WebRenderCompositableHolder.
+  // That happens in AsyncImagePipelineManager.
   wr::LayoutRect r = aSc.ToRelativeLayoutRect(aBounds);
   aBuilder.PushIFrame(r, mPipelineId.ref());
 
   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
                                                                    aSCBounds,
                                                                    aSCTransform,
                                                                    aScaleToSize,
                                                                    aFilter,
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -433,38 +433,64 @@ WebRenderAPI::AddExternalImage(ImageKey 
                             key,
                             &aDescriptor,
                             aExtID,
                             aBufferType,
                             aChannelIndex);
 }
 
 void
-WebRenderAPI::AddExternalImageBuffer(ImageKey key,
+WebRenderAPI::AddExternalImageBuffer(ImageKey aKey,
                                      const ImageDescriptor& aDescriptor,
                                      ExternalImageId aHandle)
 {
-  wr_api_add_external_image_buffer(mRenderApi,
-                                   key,
-                                   &aDescriptor,
-                                   aHandle);
+  auto channelIndex = 0;
+  AddExternalImage(aKey, aDescriptor, aHandle,
+                   wr::WrExternalImageBufferType::ExternalBuffer,
+                   channelIndex);
 }
 
 void
 WebRenderAPI::UpdateImageBuffer(ImageKey aKey,
                                 const ImageDescriptor& aDescriptor,
                                 Range<uint8_t> aBytes)
 {
   wr_api_update_image(mRenderApi,
                       aKey,
                       &aDescriptor,
                       RangeToByteSlice(aBytes));
 }
 
 void
+WebRenderAPI::UpdateBlobImage(ImageKey aKey,
+                              const ImageDescriptor& aDescriptor,
+                              Range<uint8_t> aBytes)
+{
+  wr_api_update_blob_image(mRenderApi,
+                           aKey,
+                           &aDescriptor,
+                           RangeToByteSlice(aBytes));
+}
+
+void
+WebRenderAPI::UpdateExternalImage(ImageKey aKey,
+                                  const ImageDescriptor& aDescriptor,
+                                  ExternalImageId aExtID,
+                                  wr::WrExternalImageBufferType aBufferType,
+                                  uint8_t aChannelIndex)
+{
+  wr_api_update_external_image(mRenderApi,
+                               aKey,
+                               &aDescriptor,
+                               aExtID,
+                               aBufferType,
+                               aChannelIndex);
+}
+
+void
 WebRenderAPI::DeleteImage(ImageKey aKey)
 {
   wr_api_delete_image(mRenderApi, aKey);
 }
 
 void
 WebRenderAPI::AddRawFont(wr::FontKey aKey, Range<uint8_t> aBytes, uint32_t aIndex)
 {
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -82,22 +82,32 @@ public:
   void AddExternalImageBuffer(ImageKey key,
                               const ImageDescriptor& aDescriptor,
                               ExternalImageId aHandle);
 
   void AddExternalImage(ImageKey key,
                         const ImageDescriptor& aDescriptor,
                         ExternalImageId aExtID,
                         WrExternalImageBufferType aBufferType,
-                        uint8_t aChannelIndex);
+                        uint8_t aChannelIndex = 0);
 
   void UpdateImageBuffer(wr::ImageKey aKey,
                          const ImageDescriptor& aDescriptor,
                          Range<uint8_t> aBytes);
 
+  void UpdateBlobImage(wr::ImageKey aKey,
+                       const ImageDescriptor& aDescriptor,
+                       Range<uint8_t> aBytes);
+
+  void UpdateExternalImage(ImageKey aKey,
+                           const ImageDescriptor& aDescriptor,
+                           ExternalImageId aExtID,
+                           wr::WrExternalImageBufferType aBufferType,
+                           uint8_t aChannelIndex = 0);
+
   void DeleteImage(wr::ImageKey aKey);
 
   void AddRawFont(wr::FontKey aKey, Range<uint8_t> aBytes, uint32_t aIndex);
 
   void DeleteFont(wr::FontKey aKey);
 
   void SetProfilerEnabled(bool aEnabled);
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -639,43 +639,64 @@ pub extern "C" fn wr_api_add_external_im
                                           id: external_image_id.into(),
                                           channel_index: channel_index,
                                           image_type: buffer_type,
                                       }),
                   None);
 }
 
 #[no_mangle]
-pub extern "C" fn wr_api_add_external_image_buffer(api: &mut RenderApi,
-                                                   image_key: WrImageKey,
-                                                   descriptor: &WrImageDescriptor,
-                                                   external_image_id: WrExternalImageId) {
-    assert!(unsafe { is_in_compositor_thread() });
-    api.add_image(image_key,
-                  descriptor.into(),
-                  ImageData::External(ExternalImageData {
-                                          id: external_image_id.into(),
-                                          channel_index: 0,
-                                          image_type: ExternalImageType::ExternalBuffer,
-                                      }),
-                  None);
-}
-
-#[no_mangle]
 pub extern "C" fn wr_api_update_image(api: &mut RenderApi,
                                       key: WrImageKey,
                                       descriptor: &WrImageDescriptor,
                                       bytes: ByteSlice) {
     assert!(unsafe { is_in_compositor_thread() });
     let copied_bytes = bytes.as_slice().to_owned();
 
     api.update_image(key, descriptor.into(), ImageData::new(copied_bytes), None);
 }
 
 #[no_mangle]
+pub extern "C" fn wr_api_update_external_image(
+    api: &mut RenderApi,
+    key: WrImageKey,
+    descriptor: &WrImageDescriptor,
+    external_image_id: WrExternalImageId,
+    image_type: WrExternalImageBufferType,
+    channel_index: u8
+) {
+    assert!(unsafe { is_in_compositor_thread() });
+
+    let data = ImageData::External(
+        ExternalImageData {
+            id: external_image_id.into(),
+            channel_index,
+            image_type,
+        }
+    );
+
+    api.update_image(key, descriptor.into(), data, None);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_update_blob_image(api: &mut RenderApi,
+                                           image_key: WrImageKey,
+                                           descriptor: &WrImageDescriptor,
+                                           bytes: ByteSlice) {
+    assert!(unsafe { is_in_compositor_thread() });
+    let copied_bytes = bytes.as_slice().to_owned();
+    api.update_image(
+        image_key,
+        descriptor.into(),
+        ImageData::new_blob_image(copied_bytes),
+        None
+    );
+}
+
+#[no_mangle]
 pub extern "C" fn wr_api_delete_image(api: &mut RenderApi,
                                       key: WrImageKey) {
     assert!(unsafe { is_in_compositor_thread() });
     api.delete_image(key)
 }
 
 #[no_mangle]
 pub extern "C" fn wr_api_set_root_pipeline(api: &mut RenderApi,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -634,23 +634,16 @@ void wr_api_add_external_image(RenderApi
                                WrImageKey aImageKey,
                                const WrImageDescriptor *aDescriptor,
                                WrExternalImageId aExternalImageId,
                                WrExternalImageBufferType aBufferType,
                                uint8_t aChannelIndex)
 WR_FUNC;
 
 WR_INLINE
-void wr_api_add_external_image_buffer(RenderApi *aApi,
-                                      WrImageKey aImageKey,
-                                      const WrImageDescriptor *aDescriptor,
-                                      WrExternalImageId aExternalImageId)
-WR_FUNC;
-
-WR_INLINE
 void wr_api_add_image(RenderApi *aApi,
                       WrImageKey aImageKey,
                       const WrImageDescriptor *aDescriptor,
                       ByteSlice aBytes)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_add_raw_font(RenderApi *aApi,
@@ -728,16 +721,32 @@ WR_FUNC;
 
 WR_INLINE
 void wr_api_set_window_parameters(RenderApi *aApi,
                                   int32_t aWidth,
                                   int32_t aHeight)
 WR_FUNC;
 
 WR_INLINE
+void wr_api_update_blob_image(RenderApi *aApi,
+                              WrImageKey aImageKey,
+                              const WrImageDescriptor *aDescriptor,
+                              ByteSlice aBytes)
+WR_FUNC;
+
+WR_INLINE
+void wr_api_update_external_image(RenderApi *aApi,
+                                  WrImageKey aKey,
+                                  const WrImageDescriptor *aDescriptor,
+                                  WrExternalImageId aExternalImageId,
+                                  WrExternalImageBufferType aImageType,
+                                  uint8_t aChannelIndex)
+WR_FUNC;
+
+WR_INLINE
 void wr_api_update_image(RenderApi *aApi,
                          WrImageKey aKey,
                          const WrImageDescriptor *aDescriptor,
                          ByteSlice aBytes)
 WR_FUNC;
 
 WR_INLINE
 void wr_dec_ref_arc(const VecU8 *aArc)
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -27,17 +27,17 @@ namespace image {
 
 already_AddRefed<ProgressTracker>
 DynamicImage::GetProgressTracker()
 {
   return nullptr;
 }
 
 size_t
-DynamicImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
+DynamicImage::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
   return 0;
 }
 
 void
 DynamicImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                     MallocSizeOf aMallocSizeOf) const
 {
--- a/image/DynamicImage.h
+++ b/image/DynamicImage.h
@@ -29,17 +29,17 @@ public:
   {
     MOZ_ASSERT(aDrawable, "Must have a gfxDrawable to wrap");
   }
 
   // Inherited methods from Image.
   nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
   virtual size_t SizeOfSourceWithComputedFallback(
-                                 MallocSizeOf aMallocSizeOf) const override;
+                                 SizeOfState& aState) const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual void IncrementAnimationConsumers() override;
   virtual void DecrementAnimationConsumers() override;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() override;
 #endif
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -10,17 +10,17 @@
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // Memory Reporting
 ///////////////////////////////////////////////////////////////////////////////
 
 ImageMemoryCounter::ImageMemoryCounter(Image* aImage,
-                                       MallocSizeOf aMallocSizeOf,
+                                       SizeOfState& aState,
                                        bool aIsUsed)
   : mIsUsed(aIsUsed)
 {
   MOZ_ASSERT(aImage);
 
   // Extract metadata about the image.
   RefPtr<ImageURL> imageURL(aImage->GetURI());
   if (imageURL) {
@@ -31,18 +31,18 @@ ImageMemoryCounter::ImageMemoryCounter(I
   int32_t height = 0;
   aImage->GetWidth(&width);
   aImage->GetHeight(&height);
   mIntrinsicSize.SizeTo(width, height);
 
   mType = aImage->GetType();
 
   // Populate memory counters for source and decoded data.
-  mValues.SetSource(aImage->SizeOfSourceWithComputedFallback(aMallocSizeOf));
-  aImage->CollectSizeOfSurfaces(mSurfaces, aMallocSizeOf);
+  mValues.SetSource(aImage->SizeOfSourceWithComputedFallback(aState));
+  aImage->CollectSizeOfSurfaces(mSurfaces, aState.mMallocSizeOf);
 
   // Compute totals.
   for (const SurfaceMemoryCounter& surfaceCounter : mSurfaces) {
     mValues += surfaceCounter.Values();
   }
 }
 
 
--- a/image/Image.h
+++ b/image/Image.h
@@ -89,19 +89,17 @@ private:
   const SurfaceKey mKey;
   MemoryCounter mValues;
   const SurfaceMemoryCounterType mType;
   const bool mIsLocked;
 };
 
 struct ImageMemoryCounter
 {
-  ImageMemoryCounter(Image* aImage,
-                     MallocSizeOf aMallocSizeOf,
-                     bool aIsUsed);
+  ImageMemoryCounter(Image* aImage, SizeOfState& aState, bool aIsUsed);
 
   nsCString& URI() { return mURI; }
   const nsCString& URI() const { return mURI; }
   const nsTArray<SurfaceMemoryCounter>& Surfaces() const { return mSurfaces; }
   const gfx::IntSize IntrinsicSize() const { return mIntrinsicSize; }
   const MemoryCounter& Values() const { return mValues; }
   uint16_t Type() const { return mType; }
   bool IsUsed() const { return mIsUsed; }
@@ -162,17 +160,17 @@ public:
   virtual void SetProgressTracker(ProgressTracker* aProgressTracker) {}
 
   /**
    * The size, in bytes, occupied by the compressed source data of the image.
    * If MallocSizeOf does not work on this platform, uses a fallback approach to
    * ensure that something reasonable is always returned.
    */
   virtual size_t
-    SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const = 0;
+    SizeOfSourceWithComputedFallback(SizeOfState& aState) const = 0;
 
   /**
    * Collect an accounting of the memory occupied by the image's surfaces (which
    * together make up its decoded data). Each surface is recorded as a separate
    * SurfaceMemoryCounter, stored in @aCounters.
    */
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const = 0;
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -25,19 +25,19 @@ namespace image {
 
 already_AddRefed<ProgressTracker>
 ImageWrapper::GetProgressTracker()
 {
   return mInnerImage->GetProgressTracker();
 }
 
 size_t
-ImageWrapper::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
+ImageWrapper::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
-  return mInnerImage->SizeOfSourceWithComputedFallback(aMallocSizeOf);
+  return mInnerImage->SizeOfSourceWithComputedFallback(aState);
 }
 
 void
 ImageWrapper::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                     MallocSizeOf aMallocSizeOf) const
 {
   mInnerImage->CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
 }
--- a/image/ImageWrapper.h
+++ b/image/ImageWrapper.h
@@ -21,17 +21,17 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGICONTAINER
 
   // Inherited methods from Image.
   nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
   virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
 
   virtual size_t
-    SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const override;
+    SizeOfSourceWithComputedFallback(SizeOfState& aState) const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual void IncrementAnimationConsumers() override;
   virtual void DecrementAnimationConsumers() override;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() override;
 #endif
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -700,19 +700,20 @@ RasterImage::UpdateImageContainer()
   AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
   imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
                                                          mLastFrameID++,
                                                          mImageProducerID));
   container->SetCurrentImages(imageList);
 }
 
 size_t
-RasterImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
+RasterImage::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
-  return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(aMallocSizeOf);
+  return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
+    aState.mMallocSizeOf);
 }
 
 void
 RasterImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                    MallocSizeOf aMallocSizeOf) const
 {
   SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
   if (mFrameAnimator) {
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -162,17 +162,17 @@ public:
 
   nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
 
   // Methods inherited from Image
   virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
-  virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf)
+  virtual size_t SizeOfSourceWithComputedFallback(SizeOfState& aState)
     const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   /* Triggers discarding. */
   void Discard();
 
 
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -375,28 +375,28 @@ VectorImage::Init(const char* aMimeType,
     SurfaceCache::LockImage(ImageKey(this));
   }
 
   mIsInitialized = true;
   return NS_OK;
 }
 
 size_t
-VectorImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
+VectorImage::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
   if (!mSVGDocumentWrapper) {
     return 0; // No document, so no memory used for the document.
   }
 
   nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
   if (!doc) {
     return 0; // No document, so no memory used for the document.
   }
 
-  nsWindowSizes windowSizes(aMallocSizeOf);
+  nsWindowSizes windowSizes(aState);
   doc->DocAddSizeOfIncludingThis(&windowSizes);
 
   if (windowSizes.getTotalSize() == 0) {
     // MallocSizeOf fails on this platform. Because we also use this method for
     // determining the size of cache entries, we need to return something
     // reasonable here. Unfortunately, there's no way to estimate the document's
     // size accurately, so we just use a constant value of 100KB, which will
     // generally underestimate the true size.
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -30,17 +30,17 @@ public:
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_IMGICONTAINER
 
   // (no public constructor - use ImageFactory)
 
   // Methods inherited from Image
   nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
-  virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf)
+  virtual size_t SizeOfSourceWithComputedFallback(SizeOfState& aState)
     const override;
   virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                                      MallocSizeOf aMallocSizeOf) const override;
 
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -135,17 +135,18 @@ public:
         if (!image) {
           continue;
         }
 
         // Both this and EntryImageSizes measure images/content/raster/used/decoded
         // memory.  This function's measurement is secondary -- the result doesn't
         // go in the "explicit" tree -- so we use moz_malloc_size_of instead of
         // ImagesMallocSizeOf to prevent DMD from seeing it reported twice.
-        ImageMemoryCounter counter(image, moz_malloc_size_of, /* aIsUsed = */ true);
+        SizeOfState state(moz_malloc_size_of);
+        ImageMemoryCounter counter(image, state, /* aIsUsed = */ true);
 
         n += counter.Values().DecodedHeap();
         n += counter.Values().DecodedNonHeap();
       }
     }
     return n;
   }
 
@@ -404,17 +405,18 @@ private:
                                       nsTArray<ImageMemoryCounter>* aArray,
                                       bool aIsUsed)
   {
     RefPtr<image::Image> image = aRequest->GetImage();
     if (!image) {
       return;
     }
 
-    ImageMemoryCounter counter(image, ImagesMallocSizeOf, aIsUsed);
+    SizeOfState state(ImagesMallocSizeOf);
+    ImageMemoryCounter counter(image, state, aIsUsed);
 
     aArray->AppendElement(Move(counter));
   }
 };
 
 NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
 
 NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -612,17 +612,18 @@ imgRequest::SetIsInCache(bool aInCache)
 void
 imgRequest::UpdateCacheEntrySize()
 {
   if (!mCacheEntry) {
     return;
   }
 
   RefPtr<Image> image = GetImage();
-  size_t size = image->SizeOfSourceWithComputedFallback(moz_malloc_size_of);
+  SizeOfState state(moz_malloc_size_of);
+  size_t size = image->SizeOfSourceWithComputedFallback(state);
   mCacheEntry->SetDataSize(size);
 }
 
 void
 imgRequest::SetCacheValidation(imgCacheEntry* aCacheEntry, nsIRequest* aRequest)
 {
   /* get the expires info */
   if (aCacheEntry) {
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -218,17 +218,18 @@ class JS_PUBLIC_API(CallbackTracer) : pu
     };
 
 #ifdef DEBUG
     enum class TracerKind {
         DoNotCare,
         Moving,
         GrayBuffering,
         VerifyTraceProtoAndIface,
-        ClearEdges
+        ClearEdges,
+        UnmarkGray
     };
     virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; }
 #endif
 
     // In C++, overriding a method hides all methods in the base class with
     // that name, not just methods with that signature. Thus, the typed edge
     // methods have to have distinct names to allow us to override them
     // individually, which is freqently useful if, for example, we only want to
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -845,21 +845,16 @@ class GCRuntime
     JS::GCNurseryCollectionCallback setNurseryCollectionCallback(
         JS::GCNurseryCollectionCallback callback);
     JS::DoCycleCollectionCallback setDoCycleCollectionCallback(JS::DoCycleCollectionCallback callback);
     void callDoCycleCollectionCallback(JSContext* cx);
 
     void setFullCompartmentChecks(bool enable);
 
     JS::Zone* getCurrentSweepGroup() { return currentSweepGroup; }
-    void setFoundBlackGrayEdges(TenuredCell& target) {
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!foundBlackGrayEdges.ref().append(&target))
-            oomUnsafe.crash("OOM|small: failed to insert into foundBlackGrayEdges");
-    }
 
     uint64_t gcNumber() const { return number; }
 
     uint64_t minorGCCount() const { return minorGCNumber; }
     void incMinorGcNumber() { ++minorGCNumber; ++number; }
 
     uint64_t majorGCCount() const { return majorGCNumber; }
     void incMajorGcNumber() { ++majorGCNumber; ++number; }
@@ -1247,19 +1242,16 @@ class GCRuntime
     ActiveThreadData<bool> lastMarkSlice;
 
     /* Whether any sweeping will take place in the separate GC helper thread. */
     ActiveThreadData<bool> sweepOnBackgroundThread;
 
     /* Whether observed type information is being released in the current GC. */
     ActiveThreadData<bool> releaseObservedTypes;
 
-    /* Whether any black->gray edges were found during marking. */
-    ActiveThreadData<BlackGrayEdgeVector> foundBlackGrayEdges;
-
     /* Singly linked list of zones to be swept in the background. */
     ActiveThreadOrGCTaskData<ZoneList> backgroundSweepZones;
 
     /*
      * Free LIFO blocks are transferred to this allocator before being freed on
      * the background GC thread after sweeping.
      */
     ActiveThreadOrGCTaskData<LifoAlloc> blocksToFreeAfterSweeping;
--- a/js/src/gc/GenerateStatsPhases.py
+++ b/js/src/gc/GenerateStatsPhases.py
@@ -66,16 +66,18 @@ MarkRootsPhaseKind = PhaseKind("MARK_ROO
     PhaseKind("MARK_STACK", "Mark C and JS stacks", 51),
     PhaseKind("MARK_RUNTIME_DATA", "Mark Runtime-wide Data", 52),
     PhaseKind("MARK_EMBEDDING", "Mark Embedding", 53),
     PhaseKind("MARK_COMPARTMENTS", "Mark Compartments", 54)
 ])
 
 JoinParallelTasksPhaseKind = PhaseKind("JOIN_PARALLEL_TASKS", "Join Parallel Tasks", 67)
 
+UnmarkGrayPhaseKind = PhaseKind("UNMARK_GRAY", "Unmark gray", 56)
+
 PhaseKindGraphRoots = [
     PhaseKind("MUTATOR", "Mutator Running", 0),
     PhaseKind("GC_BEGIN", "Begin Callback", 1),
     PhaseKind("EVICT_NURSERY_FOR_MAJOR_GC", "Evict Nursery For Major GC", 70, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("WAIT_BACKGROUND_THREAD", "Wait Background Thread", 2),
     PhaseKind("PREPARE", "Prepare For Collection", 69, [
@@ -84,23 +86,28 @@ PhaseKindGraphRoots = [
         PhaseKind("MARK_DISCARD_CODE", "Mark Discard Code", 3),
         PhaseKind("RELAZIFY_FUNCTIONS", "Relazify Functions", 4),
         PhaseKind("PURGE", "Purge", 5),
         PhaseKind("PURGE_SHAPE_TABLES", "Purge ShapeTables", 60),
         JoinParallelTasksPhaseKind
         ]),
     PhaseKind("MARK", "Mark", 6, [
         MarkRootsPhaseKind,
+        UnmarkGrayPhaseKind,
         PhaseKind("MARK_DELAYED", "Mark Delayed", 8)
         ]),
     PhaseKind("SWEEP", "Sweep", 9, [
         PhaseKind("SWEEP_MARK", "Mark During Sweeping", 10, [
-            PhaseKind("SWEEP_MARK_TYPES", "Mark Types During Sweeping", 11),
-            PhaseKind("SWEEP_MARK_INCOMING_BLACK", "Mark Incoming Black Pointers", 12),
-            PhaseKind("SWEEP_MARK_WEAK", "Mark Weak", 13),
+            UnmarkGrayPhaseKind,
+            PhaseKind("SWEEP_MARK_INCOMING_BLACK", "Mark Incoming Black Pointers", 12, [
+                UnmarkGrayPhaseKind,
+            ]),
+            PhaseKind("SWEEP_MARK_WEAK", "Mark Weak", 13, [
+                UnmarkGrayPhaseKind,
+            ]),
             PhaseKind("SWEEP_MARK_INCOMING_GRAY", "Mark Incoming Gray Pointers", 14),
             PhaseKind("SWEEP_MARK_GRAY", "Mark Gray", 15),
             PhaseKind("SWEEP_MARK_GRAY_WEAK", "Mark Gray and Weak", 16)
         ]),
         PhaseKind("FINALIZE_START", "Finalize Start Callbacks", 17, [
             PhaseKind("WEAK_ZONES_CALLBACK", "Per-Slice Weak Callback", 57),
             PhaseKind("WEAK_COMPARTMENT_CALLBACK", "Per-Compartment Weak Callback", 58)
         ]),
@@ -152,17 +159,17 @@ PhaseKindGraphRoots = [
     ]),
     PhaseKind("EVICT_NURSERY", "Minor GCs to Evict Nursery", 46, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("TRACE_HEAP", "Trace Heap", 47, [
         MarkRootsPhaseKind,
     ]),
     PhaseKind("BARRIER", "Barriers", 55, [
-        PhaseKind("UNMARK_GRAY", "Unmark gray", 56),
+        UnmarkGrayPhaseKind
     ])
 ]
 
 # Make a linear list of all unique phases by performing a depth first
 # search on the phase graph starting at the roots.  This will be used to
 # generate the PhaseKind enum.
 
 def findAllPhaseKinds():
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -43,20 +43,16 @@ RuntimeFromActiveCooperatingThreadIsHeap
 #ifdef DEBUG
 
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 extern bool
 CurrentThreadIsIonCompiling();
 #endif
 
-// The return value indicates if anything was unmarked.
-extern bool
-UnmarkGrayCellRecursively(gc::Cell* cell, JS::TraceKind kind);
-
 extern void
 TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name);
 
 namespace gc {
 
 class Arena;
 class ArenaCellSet;
 class ArenaList;
@@ -1381,17 +1377,17 @@ TenuredCell::readBarrier(TenuredCell* th
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
         MOZ_ASSERT(tmp == thing);
     }
 
     if (thing->isMarkedGray()) {
         // There shouldn't be anything marked grey unless we're on the active thread.
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
         if (!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone))
-            UnmarkGrayCellRecursively(thing, thing->getTraceKind());
+            JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(thing, thing->getTraceKind()));
     }
 }
 
 void
 AssertSafeToSkipBarrier(TenuredCell* thing);
 
 /* static */ MOZ_ALWAYS_INLINE void
 TenuredCell::writeBarrierPre(TenuredCell* thing)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -232,17 +232,18 @@ js::CheckTracedThing(JSTracer* trc, T* t
                thing->getTraceKind());
 
     /*
      * Do not check IsMarkingTracer directly -- it should only be used in paths
      * where we cannot be the gray buffering tracer.
      */
     bool isGcMarkingTracer = trc->isMarkingTracer();
 
-    MOZ_ASSERT_IF(zone->requireGCTracer(), isGcMarkingTracer || IsBufferGrayRootsTracer(trc));
+    MOZ_ASSERT_IF(zone->requireGCTracer(),
+                  isGcMarkingTracer || IsBufferGrayRootsTracer(trc) || IsUnmarkGrayTracer(trc));
 
     if (isGcMarkingTracer) {
         GCMarker* gcMarker = GCMarker::fromTracer(trc);
         MOZ_ASSERT_IF(gcMarker->shouldCheckCompartments(),
                       zone->isCollecting() || zone->isAtomsZone());
 
         MOZ_ASSERT_IF(gcMarker->markColor() == MarkColor::Gray,
                       !zone->isGCMarkingBlack() || zone->isAtomsZone());
@@ -284,42 +285,47 @@ js::CheckTracedThing(JSTracer* trc, T th
 
 namespace js {
 #define IMPL_CHECK_TRACED_THING(_, type, __) \
     template void CheckTracedThing<type>(JSTracer*, type*);
 JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_THING);
 #undef IMPL_CHECK_TRACED_THING
 } // namespace js
 
+static bool UnmarkGrayGCThing(JSRuntime* rt, JS::GCCellPtr thing);
+
 static bool
 ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
 
     MarkColor color = GCMarker::fromTracer(trc)->markColor();
 
     if (!cell->isTenured()) {
         MOZ_ASSERT(color == MarkColor::Black);
         return false;
     }
     TenuredCell& tenured = cell->asTenured();
 
     JS::Zone* zone = tenured.zone();
+    if (!src->zone()->isGCMarking() && !zone->isGCMarking())
+        return false;
+
     if (color == MarkColor::Black) {
         /*
          * Having black->gray edges violates our promise to the cycle
          * collector. This can happen if we're collecting a compartment and it
          * has an edge to an uncollected compartment: it's possible that the
          * source and destination of the cross-compartment edge should be gray,
          * but the source was marked black by the write barrier.
          */
         if (tenured.isMarkedGray()) {
             MOZ_ASSERT(!zone->isCollecting());
-            trc->runtime()->gc.setFoundBlackGrayEdges(tenured);
+            UnmarkGrayGCThing(trc->runtime(), JS::GCCellPtr(cell, cell->getTraceKind()));
         }
         return zone->isGCMarking();
     } else {
         if (zone->isGCMarkingBlack()) {
             /*
              * The destination compartment is being not being marked gray now,
              * but it will be later, so record the cell so it can be marked gray
              * at the appropriate time.
@@ -3343,16 +3349,20 @@ class UnmarkGrayTracer : public JS::Call
     // Whether we ran out of memory.
     bool oom;
 
   private:
     // Stack of cells to traverse.
     Vector<JS::GCCellPtr, 0, SystemAllocPolicy>& stack;
 
     void onChild(const JS::GCCellPtr& thing) override;
+
+#ifdef DEBUG
+    TracerKind getTracerKind() const override { return TracerKind::UnmarkGray; }
+#endif
 };
 
 void
 UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing)
 {
     Cell* cell = thing.asCell();
 
     // Cells in the nursery cannot be gray, and therefore must necessarily point
@@ -3390,53 +3400,51 @@ UnmarkGrayTracer::unmark(JS::GCCellPtr c
          // If we run out of memory, we take a drastic measure: require that we
          // GC again before the next CC.
         stack.clear();
         runtime()->gc.setGrayBitsInvalid();
         return;
     }
 }
 
-template <typename T>
-static bool
-TypedUnmarkGrayCellRecursively(T* t)
+#ifdef DEBUG
+bool
+js::IsUnmarkGrayTracer(JSTracer* trc)
 {
-    MOZ_ASSERT(t);
-
-    JSRuntime* rt = t->runtimeFromActiveCooperatingThread();
-    MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
-    MOZ_ASSERT(!JS::CurrentThreadIsHeapCycleCollecting());
+    return trc->isCallbackTracer() &&
+           trc->asCallbackTracer()->getTracerKind() == JS::CallbackTracer::TracerKind::UnmarkGray;
+}
+#endif
+
+static bool
+UnmarkGrayGCThing(JSRuntime* rt, JS::GCCellPtr thing)
+{
+    MOZ_ASSERT(thing);
 
     UnmarkGrayTracer unmarker(rt);
-    gcstats::AutoPhase outerPhase(rt->gc.stats(), gcstats::PhaseKind::BARRIER);
     gcstats::AutoPhase innerPhase(rt->gc.stats(), gcstats::PhaseKind::UNMARK_GRAY);
-    unmarker.unmark(JS::GCCellPtr(t, MapTypeToTraceKind<T>::kind));
+    unmarker.unmark(thing);
     return unmarker.unmarkedAny;
 }
 
-struct UnmarkGrayCellRecursivelyFunctor {
-    template <typename T> bool operator()(T* t) { return TypedUnmarkGrayCellRecursively(t); }
-};
-
-bool
-js::UnmarkGrayCellRecursively(Cell* cell, JS::TraceKind kind)
+JS_FRIEND_API(bool)
+JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr thing)
 {
-    return DispatchTraceKindTyped(UnmarkGrayCellRecursivelyFunctor(), cell, kind);
+    MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
+    MOZ_ASSERT(!JS::CurrentThreadIsHeapCycleCollecting());
+
+    JSRuntime* rt = thing.asCell()->runtimeFromActiveCooperatingThread();
+    gcstats::AutoPhase outerPhase(rt->gc.stats(), gcstats::PhaseKind::BARRIER);
+    return UnmarkGrayGCThing(rt, thing);
 }
 
 bool
 js::UnmarkGrayShapeRecursively(Shape* shape)
 {
-    return TypedUnmarkGrayCellRecursively(shape);
-}
-
-JS_FRIEND_API(bool)
-JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr thing)
-{
-    return js::UnmarkGrayCellRecursively(thing.asCell(), thing.kind());
+    return JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(shape));
 }
 
 namespace js {
 namespace debug {
 
 MarkInfo
 GetMarkInfo(Cell* rawCell)
 {
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -392,16 +392,19 @@ class GCMarker : public JSTracer
 #endif // DEBUG
 };
 
 #ifdef DEBUG
 // Return true if this trace is happening on behalf of gray buffering during
 // the marking phase of incremental GC.
 bool
 IsBufferGrayRootsTracer(JSTracer* trc);
+
+bool
+IsUnmarkGrayTracer(JSTracer* trc);
 #endif
 
 namespace gc {
 
 /*** Special Cases ***/
 
 void
 PushArena(GCMarker* gcmarker, Arena* arena);
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -158,31 +158,29 @@ Statistics::lookupChildPhase(PhaseKind p
 {
     if (phaseKind == PhaseKind::IMPLICIT_SUSPENSION)
         return Phase::IMPLICIT_SUSPENSION;
     if (phaseKind == PhaseKind::EXPLICIT_SUSPENSION)
         return Phase::EXPLICIT_SUSPENSION;
 
     MOZ_ASSERT(phaseKind < PhaseKind::LIMIT);
 
-    // Most phases only correspond to a single expanded phase so check for that
-    // first.
-    Phase phase = phaseKinds[phaseKind].firstPhase;
-    if (phases[phase].nextInPhase == Phase::NONE) {
-        MOZ_ASSERT(phases[phase].parent == currentPhase());
-        return phase;
+    // Search all expanded phases that correspond to the required
+    // phase to find the one whose parent is the current expanded phase.
+    Phase phase;
+    for (phase = phaseKinds[phaseKind].firstPhase;
+         phase != Phase::NONE;
+         phase = phases[phase].nextInPhase)
+    {
+        if (phases[phase].parent == currentPhase())
+            break;
     }
 
-    // Otherwise search all expanded phases that correspond to the required
-    // phase to find the one whose parent is the current expanded phase.
-    Phase parent = currentPhase();
-    while (phases[phase].parent != parent) {
-        phase = phases[phase].nextInPhase;
-        MOZ_ASSERT(phase != Phase::NONE);
-    }
+    MOZ_RELEASE_ASSERT(phase != Phase::NONE,
+                       "Requested child phase not found under current phase");
 
     return phase;
 }
 
 inline decltype(mozilla::MakeEnumeratedRange(Phase::FIRST, Phase::LIMIT))
 AllPhases()
 {
     return mozilla::MakeEnumeratedRange(Phase::FIRST, Phase::LIMIT);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/indexof-equal.js
@@ -0,0 +1,10 @@
+var x = "abc";
+assertEq(x.indexOf(x), 0);
+assertEq(x.indexOf(x, -1), 0);
+assertEq(x.indexOf(x, 1), -1);
+assertEq(x.indexOf(x, 100), -1);
+
+assertEq(x.lastIndexOf(x), 0);
+assertEq(x.lastIndexOf(x, -1), 0);
+assertEq(x.lastIndexOf(x, 1), 0);
+assertEq(x.lastIndexOf(x, 100), 0);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6853,48 +6853,16 @@ class AutoScheduleZonesForGC
     }
 
     ~AutoScheduleZonesForGC() {
         for (ZonesIter zone(rt_, WithAtoms); !zone.done(); zone.next())
             zone->unscheduleGC();
     }
 };
 
-/*
- * An invariant of our GC/CC interaction is that there must not ever be any
- * black to gray edges in the system. It is possible to violate this with
- * simple compartmental GC. For example, in GC[n], we collect in both
- * compartmentA and compartmentB, and mark both sides of the cross-compartment
- * edge gray. Later in GC[n+1], we only collect compartmentA, but this time
- * mark it black. Now we are violating the invariants and must fix it somehow.
- *
- * To prevent this situation, we explicitly detect the black->gray state when
- * marking cross-compartment edges -- see ShouldMarkCrossCompartment -- adding
- * each violating edges to foundBlackGrayEdges. After we leave the trace
- * session for each GC slice, we "ExposeToActiveJS" on each of these edges
- * (which we cannot do safely from the guts of the GC).
- */
-class AutoExposeLiveCrossZoneEdges
-{
-    BlackGrayEdgeVector* edges;
-
-  public:
-    explicit AutoExposeLiveCrossZoneEdges(BlackGrayEdgeVector* edgesPtr) : edges(edgesPtr) {
-        MOZ_ASSERT(edges->empty());
-    }
-    ~AutoExposeLiveCrossZoneEdges() {
-        for (auto& target : *edges) {
-            MOZ_ASSERT(target);
-            MOZ_ASSERT(!target->zone()->isCollecting());
-            UnmarkGrayCellRecursively(target, target->getTraceKind());
-        }
-        edges->clear();
-    }
-};
-
 } /* anonymous namespace */
 
 /*
  * Run one GC "cycle" (either a slice of incremental GC or an entire
  * non-incremental GC. We disable inlining to ensure that the bottom of the
  * stack with possible GC roots recorded in MarkRuntime excludes any pointers we
  * use during the marking implementation.
  *
@@ -6904,18 +6872,16 @@ class AutoExposeLiveCrossZoneEdges
 MOZ_NEVER_INLINE GCRuntime::IncrementalResult
 GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::Reason reason)
 {
     // Note that the following is allowed to re-enter GC in the finalizer.
     AutoNotifyGCActivity notify(*this);
 
     gcstats::AutoGCSlice agc(stats(), scanZonesBeforeGC(), invocationKind, budget, reason);
 
-    AutoExposeLiveCrossZoneEdges aelcze(&foundBlackGrayEdges.ref());
-
     minorGC(reason, gcstats::PhaseKind::EVICT_NURSERY_FOR_MAJOR_GC);
 
     AutoTraceSession session(rt, JS::HeapState::MajorCollecting);
 
     majorGCTriggerReason = JS::gcreason::NO_REASON;
     interFrameGC = true;
 
     number++;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2159,16 +2159,23 @@ js::str_indexOf(JSContext* cx, unsigned 
     }
 
    // Step 8
     uint32_t textLen = str->length();
 
     // Step 9
     uint32_t start = Min(Max(pos, 0U), textLen);
 
+    if (str == searchStr) {
+        // AngularJS often invokes "false".indexOf("false"). This check should
+        // be cheap enough to not hurt anything else.
+        args.rval().setInt32(start == 0 ? 0 : -1);
+        return true;
+    }
+
     // Steps 10 and 11
     JSLinearString* text = str->ensureLinear(cx);
     if (!text)
         return false;
 
     args.rval().setInt32(StringMatch(text, searchStr, start));
     return true;
 }
@@ -2243,16 +2250,21 @@ js::str_lastIndexOf(JSContext* cx, unsig
                 if (d <= 0)
                     start = 0;
                 else if (d < start)
                     start = int(d);
             }
         }
     }
 
+    if (str == searchStr) {
+        args.rval().setInt32(0);
+        return true;
+    }
+
     if (searchLen > len) {
         args.rval().setInt32(-1);
         return true;
     }
 
     if (searchLen == 0) {
         args.rval().setInt32(start);
         return true;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1749,24 +1749,16 @@ XPCConvert::JSStringWithSize2Native(void
 
             if (s.isUndefined() || s.isNull()) {
                 if (0 != count) {
                     if (pErr)
                         *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
                     return false;
                 }
 
-                if (0 != count) {
-                    len = (count + 1) * sizeof(char16_t);
-                    if (!(*((void**)d) = moz_xmalloc(len)))
-                        return false;
-                    return true;
-                }
-
-                // else ...
                 *((const char16_t**)d) = nullptr;
                 return true;
             }
 
             if (!(str = ToString(cx, s))) {
                 return false;
             }
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2114,61 +2114,56 @@ class JSMainRuntimeCompartmentsReporter 
 
 NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
 
 MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
 
 namespace xpc {
 
 static size_t
-SizeOfTreeIncludingThis(nsINode* tree)
+SizeOfTreeIncludingThis(nsINode* tree, SizeOfState& aState)
 {
-    size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
+    size_t n = tree->SizeOfIncludingThis(aState);
     for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
-        n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
+        n += child->SizeOfIncludingThis(aState);
 
     return n;
 }
 
 class OrphanReporter : public JS::ObjectPrivateVisitor
 {
   public:
     explicit OrphanReporter(GetISupportsFun aGetISupports)
       : JS::ObjectPrivateVisitor(aGetISupports)
+      , mState(OrphanMallocSizeOf)
+    {}
+
+    virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override
     {
-    }
-
-    virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override {
         size_t n = 0;
         nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
         // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
         // that we have to skip XBL elements because they violate certain
         // assumptions.  Yuk.
         if (node && !node->IsInUncomposedDoc() &&
             !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
         {
             // This is an orphan node.  If we haven't already handled the
             // sub-tree that this node belongs to, measure the sub-tree's size
             // and then record its root so we don't measure it again.
             nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
-            if (orphanTree &&
-                !mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
-                // If PutEntry() fails we don't measure this tree, which could
-                // lead to under-measurement. But that's better than the
-                // alternatives, which are over-measurement or an OOM abort.
-                if (mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree, fallible)) {
-                    n += SizeOfTreeIncludingThis(orphanTree);
-                }
+            if (orphanTree && !mState.HaveSeenPtr(orphanTree.get())) {
+                n += SizeOfTreeIncludingThis(orphanTree, mState);
             }
         }
         return n;
     }
 
   private:
-    nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
+    SizeOfState mState;
 };
 
 #ifdef DEBUG
 static bool
 StartsWithExplicit(nsACString& s)
 {
     return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -213,17 +213,17 @@ pref("dom.keyboardevent.dispatch_during_
 // causes a separate compartment for each (addon, global) combination, which may
 // significantly increase the number of compartments in the system.
 pref("dom.compartment_per_addon", true);
 
 // Whether to enable the JavaScript start-up cache. This causes one of the first
 // execution to record the bytecode of the JavaScript function used, and save it
 // in the existing cache entry. On the following loads of the same script, the
 // bytecode would be loaded from the cache instead of being generated once more.
-pref("dom.script_loader.bytecode_cache.enabled", false); // Not tuned yet.
+pref("dom.script_loader.bytecode_cache.enabled", false);
 
 // Ignore the heuristics of the bytecode cache, and always record on the first
 // visit. (used for testing purposes).
 
 // Choose one strategy to use to decide when the bytecode should be encoded and
 // saved. The following strategies are available right now:
 //   * -2 : (reader mode) The bytecode cache would be read, but it would never
 //          be saved.
--- a/netwerk/base/nsICacheInfoChannel.idl
+++ b/netwerk/base/nsICacheInfoChannel.idl
@@ -14,25 +14,16 @@ interface nsICacheInfoChannel : nsISuppo
    * equivalent to nsICachingChannel.cacheToken.fetchCount.
    *
    * @throws NS_ERROR_NOT_AVAILABLE if the cache entry or the alternate data
    *         cache entry cannot be read.
    */
   readonly attribute int32_t cacheTokenFetchCount;
 
   /**
-   * Get the last time the cache entry was opened from cache token. This
-   * attribute is equivalent to nsICachingChannel.cacheToken.lastFetched.
-   *
-   * @throws NS_ERROR_NOT_AVAILABLE if the cache entry or the alternate data
-   *         cache entry cannot be read.
-   */
-  readonly attribute uint32_t cacheTokenLastFetched;
-
-  /**
    * Get expiration time from cache token. This attribute is equivalent to
    * nsICachingChannel.cacheToken.expirationTime.
    */
   readonly attribute uint32_t cacheTokenExpirationTime;
 
   /**
    * Set/get charset of cache entry. Accessing this attribute is equivalent to
    * calling nsICachingChannel.cacheToken.getMetaDataElement("charset") and
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2831,37 +2831,16 @@ NS_IsOffline()
     nsCOMPtr<nsIIOService> ios = do_GetIOService();
     if (ios) {
         ios->GetOffline(&offline);
         ios->GetConnectivity(&connectivity);
     }
     return offline || !connectivity;
 }
 
-nsresult
-NS_NotifyCurrentTopLevelOuterContentWindowId(uint64_t aWindowId)
-{
-  nsCOMPtr<nsIObserverService> obs =
-    do_GetService("@mozilla.org/observer-service;1");
-  if (!obs) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsISupportsPRUint64> wrapper =
-    do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
-  if (!wrapper) {
-    return NS_ERROR_FAILURE;
-  }
-
-  wrapper->SetData(aWindowId);
-  return obs->NotifyObservers(wrapper,
-                              "net:current-toplevel-outer-content-windowid",
-                              nullptr);
-}
-
 namespace mozilla {
 namespace net {
 
 bool
 InScriptableRange(int64_t val)
 {
     return (val <= kJS_MAX_SAFE_INTEGER) && (val >= kJS_MIN_SAFE_INTEGER);
 }
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -963,21 +963,16 @@ nsresult NS_GetSecureUpgradedURI(nsIURI*
 nsresult NS_CompareLoadInfoAndLoadContext(nsIChannel *aChannel);
 
 /**
  * Return default referrer policy which is controlled by user
  * pref network.http.referer.userControlPolicy
  */
 uint32_t NS_GetDefaultReferrerPolicy();
 
-/**
- * Update the window id of the current focused top level content window.
- */
-nsresult NS_NotifyCurrentTopLevelOuterContentWindowId(uint64_t aWindowId);
-
 namespace mozilla {
 namespace net {
 
 const static uint64_t kJS_MAX_SAFE_UINTEGER = +9007199254740991ULL;
 const static  int64_t kJS_MIN_SAFE_INTEGER  = -9007199254740991LL;
 const static  int64_t kJS_MAX_SAFE_INTEGER  = +9007199254740991LL;
 
 // Make sure a 64bit value can be captured by JS MAX_SAFE_INTEGER
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -970,26 +970,16 @@ NeckoParent::RecvRemoveRequestContext(co
   }
 
   rcsvc->RemoveRequestContext(rcid);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-NeckoParent::RecvNotifyCurrentTopLevelOuterContentWindowId(const uint64_t& aWindowId)
-{
-  if (NS_FAILED(NS_NotifyCurrentTopLevelOuterContentWindowId(aWindowId))) {
-    NS_WARNING("NS_NotifyCurrentTopLevelOuterContentWindowId failed!");
-  }
-
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult
 NeckoParent::RecvGetExtensionStream(const URIParams& aURI,
                                     GetExtensionStreamResolver&& aResolve)
 {
   nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(aURI);
   if (!deserializedURI) {
     return IPC_FAIL_NO_REASON(this);
   }
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -237,18 +237,16 @@ protected:
   virtual mozilla::ipc::IPCResult RecvPredLearn(const ipc::URIParams& aTargetURI,
                                                 const ipc::OptionalURIParams& aSourceURI,
                                                 const PredictorPredictReason& aReason,
                                                 const OriginAttributes& aOriginAttributes) override;
   virtual mozilla::ipc::IPCResult RecvPredReset() override;
 
   virtual mozilla::ipc::IPCResult RecvRemoveRequestContext(const uint64_t& rcid) override;
 
-  virtual mozilla::ipc::IPCResult RecvNotifyCurrentTopLevelOuterContentWindowId(const uint64_t& aWindowId) override;
-
   /* WebExtensions */
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionStream(const URIParams& aURI,
                            GetExtensionStreamResolver&& aResolve) override;
 
   virtual mozilla::ipc::IPCResult
     RecvGetExtensionFD(const URIParams& aURI,
                        GetExtensionFDResolver&& aResolve) override;
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -123,18 +123,16 @@ parent:
   async OnAuthCancelled(uint64_t callbackId, bool userCancel);
 
   async RemoveRequestContext(uint64_t rcid);
 
   async PAltDataOutputStream(nsCString type, PHttpChannel channel);
 
   async PStunAddrsRequest();
 
-  prio(high) async NotifyCurrentTopLevelOuterContentWindowId(uint64_t windowId);
-
   /**
    * WebExtension-specific remote resource loading
    */
   async GetExtensionStream(URIParams uri) returns (OptionalIPCStream stream);
   async GetExtensionFD(URIParams uri) returns (FileDescriptor fd);
 
 child:
   /*
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -157,17 +157,16 @@ InterceptStreamListener::Cleanup()
 HttpChannelChild::HttpChannelChild()
   : HttpAsyncAborter<HttpChannelChild>(this)
   , NeckoTargetHolder(nullptr)
   , mSynthesizedStreamLength(0)
   , mIsFromCache(false)
   , mCacheEntryAvailable(false)
   , mAltDataCacheEntryAvailable(false)
   , mCacheFetchCount(0)
-  , mCacheLastFetched(0)
   , mCacheExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
   , mSendResumeAt(false)
   , mDeletingChannelSent(false)
   , mIPCOpen(false)
   , mKeptAlive(false)
   , mUnknownDecoderInvolved(false)
   , mDivertingToParent(false)
   , mFlushedForDiversion(false)
@@ -391,64 +390,60 @@ class StartRequestEvent : public NeckoTa
   StartRequestEvent(HttpChannelChild* aChild,
                     const nsresult& aChannelStatus,
                     const nsHttpResponseHead& aResponseHead,
                     const bool& aUseResponseHead,
                     const nsHttpHeaderArray& aRequestHeaders,
                     const bool& aIsFromCache,
                     const bool& aCacheEntryAvailable,
                     const int32_t& aCacheFetchCount,
-                    const uint32_t& aCacheLastFetched,
                     const uint32_t& aCacheExpirationTime,
                     const nsCString& aCachedCharset,
                     const nsCString& aSecurityInfoSerialization,
                     const NetAddr& aSelfAddr,
                     const NetAddr& aPeerAddr,
                     const uint32_t& aCacheKey,
                     const nsCString& altDataType,
                     const int64_t& altDataLen)
   : NeckoTargetChannelEvent<HttpChannelChild>(aChild)
   , mChannelStatus(aChannelStatus)
   , mResponseHead(aResponseHead)
   , mRequestHeaders(aRequestHeaders)
   , mUseResponseHead(aUseResponseHead)
   , mIsFromCache(aIsFromCache)
   , mCacheEntryAvailable(aCacheEntryAvailable)
   , mCacheFetchCount(aCacheFetchCount)
-  , mCacheLastFetched(aCacheLastFetched)
   , mCacheExpirationTime(aCacheExpirationTime)
   , mCachedCharset(aCachedCharset)
   , mSecurityInfoSerialization(aSecurityInfoSerialization)
   , mSelfAddr(aSelfAddr)
   , mPeerAddr(aPeerAddr)
   , mCacheKey(aCacheKey)
   , mAltDataType(altDataType)
   , mAltDataLen(altDataLen)
   {}
 
   void Run()
   {
     LOG(("StartRequestEvent [this=%p]\n", mChild));
     mChild->OnStartRequest(mChannelStatus, mResponseHead, mUseResponseHead,
                            mRequestHeaders, mIsFromCache, mCacheEntryAvailable,
-                           mCacheFetchCount, mCacheLastFetched,
-                           mCacheExpirationTime, mCachedCharset,
+                           mCacheFetchCount, mCacheExpirationTime, mCachedCharset,
                            mSecurityInfoSerialization, mSelfAddr, mPeerAddr,
                            mCacheKey, mAltDataType, mAltDataLen);
   }
 
  private:
   nsresult mChannelStatus;
   nsHttpResponseHead mResponseHead;
   nsHttpHeaderArray mRequestHeaders;
   bool mUseResponseHead;
   bool mIsFromCache;
   bool mCacheEntryAvailable;
   int32_t mCacheFetchCount;
-  uint32_t mCacheLastFetched;
   uint32_t mCacheExpirationTime;
   nsCString mCachedCharset;
   nsCString mSecurityInfoSerialization;
   NetAddr mSelfAddr;
   NetAddr mPeerAddr;
   uint32_t mCacheKey;
   nsCString mAltDataType;
   int64_t mAltDataLen;
@@ -457,17 +452,16 @@ class StartRequestEvent : public NeckoTa
 mozilla::ipc::IPCResult
 HttpChannelChild::RecvOnStartRequest(const nsresult& channelStatus,
                                      const nsHttpResponseHead& responseHead,
                                      const bool& useResponseHead,
                                      const nsHttpHeaderArray& requestHeaders,
                                      const bool& isFromCache,
                                      const bool& cacheEntryAvailable,
                                      const int32_t& cacheFetchCount,
-                                     const uint32_t& cacheLastFetched,
                                      const uint32_t& cacheExpirationTime,
                                      const nsCString& cachedCharset,
                                      const nsCString& securityInfoSerialization,
                                      const NetAddr& selfAddr,
                                      const NetAddr& peerAddr,
                                      const int16_t& redirectCount,
                                      const uint32_t& cacheKey,
                                      const nsCString& altDataType,
@@ -482,17 +476,17 @@ HttpChannelChild::RecvOnStartRequest(con
     "mDivertingToParent should be unset before OnStartRequest!");
 
 
   mRedirectCount = redirectCount;
 
   mEventQ->RunOrEnqueue(new StartRequestEvent(this, channelStatus, responseHead,
                                               useResponseHead, requestHeaders,
                                               isFromCache, cacheEntryAvailable,
-                                              cacheFetchCount, cacheLastFetched,
+                                              cacheFetchCount,
                                               cacheExpirationTime, cachedCharset,
                                               securityInfoSerialization,
                                               selfAddr, peerAddr, cacheKey,
                                               altDataType, altDataLen));
 
   {
     // Child's mEventQ is to control the execution order of the IPC messages
     // from both main thread IPDL and PBackground IPDL.
@@ -518,17 +512,16 @@ HttpChannelChild::RecvOnStartRequest(con
 void
 HttpChannelChild::OnStartRequest(const nsresult& channelStatus,
                                  const nsHttpResponseHead& responseHead,
                                  const bool& useResponseHead,
                                  const nsHttpHeaderArray& requestHeaders,
                                  const bool& isFromCache,
                                  const bool& cacheEntryAvailable,
                                  const int32_t& cacheFetchCount,
-                                 const uint32_t& cacheLastFetched,
                                  const uint32_t& cacheExpirationTime,
                                  const nsCString& cachedCharset,
                                  const nsCString& securityInfoSerialization,
                                  const NetAddr& selfAddr,
                                  const NetAddr& peerAddr,
                                  const uint32_t& cacheKey,
                                  const nsCString& altDataType,
                                  const int64_t& altDataLen)
@@ -552,17 +545,16 @@ HttpChannelChild::OnStartRequest(const n
   if (!securityInfoSerialization.IsEmpty()) {
     NS_DeserializeObject(securityInfoSerialization,
                          getter_AddRefs(mSecurityInfo));
   }
 
   mIsFromCache = isFromCache;
   mCacheEntryAvailable = cacheEntryAvailable;
   mCacheFetchCount = cacheFetchCount;
-  mCacheLastFetched = cacheLastFetched;
   mCacheExpirationTime = cacheExpirationTime;
   mCachedCharset = cachedCharset;
   mSelfAddr = selfAddr;
   mPeerAddr = peerAddr;
 
   mAvailableCachedAltDataType = altDataType;
   mAltDataLength = altDataLen;
 
@@ -2766,28 +2758,16 @@ HttpChannelChild::GetCacheTokenFetchCoun
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *_retval = mCacheFetchCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HttpChannelChild::GetCacheTokenLastFetched(uint32_t *_retval)
-{
-  NS_ENSURE_ARG_POINTER(_retval);
-  if (!mCacheEntryAvailable && !mAltDataCacheEntryAvailable) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  *_retval = mCacheLastFetched;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 HttpChannelChild::GetCacheTokenExpirationTime(uint32_t *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   if (!mCacheEntryAvailable)
     return NS_ERROR_NOT_AVAILABLE;
 
   *_retval = mCacheExpirationTime;
   return NS_OK;
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -128,17 +128,16 @@ public:
 protected:
   mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
                                              const nsHttpResponseHead& responseHead,
                                              const bool& useResponseHead,
                                              const nsHttpHeaderArray& requestHeaders,
                                              const bool& isFromCache,
                                              const bool& cacheEntryAvailable,
                                              const int32_t& cacheFetchCount,
-                                             const uint32_t& cacheLastFetched,
                                              const uint32_t& cacheExpirationTime,
                                              const nsCString& cachedCharset,
                                              const nsCString& securityInfoSerialization,
                                              const NetAddr& selfAddr,
                                              const NetAddr& peerAddr,
                                              const int16_t& redirectCount,
                                              const uint32_t& cacheKey,
                                              const nsCString& altDataType,
@@ -274,17 +273,16 @@ private:
   RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
   nsCOMPtr<nsIInputStream> mSynthesizedInput;
   int64_t mSynthesizedStreamLength;
 
   bool mIsFromCache;
   bool mCacheEntryAvailable;
   bool mAltDataCacheEntryAvailable;
   int32_t      mCacheFetchCount;
-  uint32_t     mCacheLastFetched;
   uint32_t     mCacheExpirationTime;
   nsCString    mCachedCharset;
 
   nsCString mProtocolVersion;
 
   // If ResumeAt is called before AsyncOpen, we need to send extra data upstream
   bool mSendResumeAt;
 
@@ -373,17 +371,16 @@ private:
                                  const nsCString &clientID);
   void OnStartRequest(const nsresult& channelStatus,
                       const nsHttpResponseHead& responseHead,
                       const bool& useResponseHead,
                       const nsHttpHeaderArray& requestHeaders,
                       const bool& isFromCache,
                       const bool& cacheEntryAvailable,
                       const int32_t& cacheFetchCount,
-                      const uint32_t& cacheLastFetched,
                       const uint32_t& cacheExpirationTime,
                       const nsCString& cachedCharset,
                       const nsCString& securityInfoSerialization,
                       const NetAddr& selfAddr,
                       const NetAddr& peerAddr,
                       const uint32_t& cacheKey,
                       const nsCString& altDataType,
                       const int64_t& altDataLen);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1412,18 +1412,16 @@ HttpChannelParent::OnStartRequest(nsIReq
   }
 
   nsHttpResponseHead *responseHead = chan->GetResponseHead();
   nsHttpRequestHead  *requestHead = chan->GetRequestHead();
   bool isFromCache = false;
   chan->IsFromCache(&isFromCache);
   int32_t fetchCount = 0;
   chan->GetCacheTokenFetchCount(&fetchCount);
-  uint32_t lastFetchedTime = 0;
-  chan->GetCacheTokenLastFetched(&lastFetchedTime);
   uint32_t expirationTime = nsICacheEntry::NO_EXPIRATION_TIME;
   chan->GetCacheTokenExpirationTime(&expirationTime);
   nsCString cachedCharset;
   chan->GetCacheTokenCachedCharset(cachedCharset);
 
   bool loadedFromApplicationCache;
   chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache);
   if (loadedFromApplicationCache) {
@@ -1484,17 +1482,17 @@ HttpChannelParent::OnStartRequest(nsIReq
   nsresult rv = NS_OK;
   if (mIPCClosed ||
       !SendOnStartRequest(channelStatus,
                           responseHead ? *responseHead : nsHttpResponseHead(),
                           !!responseHead,
                           requestHead->Headers(),
                           isFromCache,
                           mCacheEntry ? true : false,
-                          fetchCount, lastFetchedTime, expirationTime,
+                          fetchCount, expirationTime,
                           cachedCharset, secInfoSerialization,
                           chan->GetSelfAddr(), chan->GetPeerAddr(),
                           redirectCount,
                           cacheKeyValue,
                           altDataType,
                           altDataLen))
   {
     rv = NS_ERROR_UNEXPECTED;
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -94,17 +94,16 @@ parent:
 child:
   async OnStartRequest(nsresult            channelStatus,
                        nsHttpResponseHead  responseHead,
                        bool                useResponseHead,
                        nsHttpHeaderArray   requestHeaders,
                        bool                isFromCache,
                        bool                cacheEntryAvailable,
                        int32_t             cacheFetchCount,
-                       uint32_t            cacheLastFetched,
                        uint32_t            cacheExpirationTime,
                        nsCString           cachedCharset,
                        nsCString           securityInfoSerialization,
                        NetAddr             selfAddr,
                        NetAddr             peerAddr,
                        int16_t             redirectCount,
                        uint32_t            cacheKey,
                        nsCString           altDataType,
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7936,28 +7936,16 @@ nsHttpChannel::GetCacheTokenFetchCount(i
     if (!cacheEntry) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     return cacheEntry->GetFetchCount(_retval);
 }
 
 NS_IMETHODIMP
-nsHttpChannel::GetCacheTokenLastFetched(uint32_t *_retval)
-{
-    NS_ENSURE_ARG_POINTER(_retval);
-    nsCOMPtr<nsICacheEntry> cacheEntry = mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
-    if (!cacheEntry) {
-        return NS_ERROR_NOT_AVAILABLE;
-    }
-
-    return cacheEntry->GetLastFetched(_retval);
-}
-
-NS_IMETHODIMP
 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval)
 {
     NS_ENSURE_ARG_POINTER(_retval);
     if (!mCacheEntry)
         return NS_ERROR_NOT_AVAILABLE;
 
     return mCacheEntry->GetExpirationTime(_retval);
 }
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -518,19 +518,22 @@ nsHttpHandler::Init()
         obsService->AddObserver(this, "net:clear-active-logins", true);
         obsService->AddObserver(this, "net:prune-dead-connections", true);
         // Sent by the TorButton add-on in the Tor Browser
         obsService->AddObserver(this, "net:prune-all-connections", true);
         obsService->AddObserver(this, "last-pb-context-exited", true);
         obsService->AddObserver(this, "browser:purge-session-history", true);
         obsService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
         obsService->AddObserver(this, "application-background", true);
-        obsService->AddObserver(this,
-                                "net:current-toplevel-outer-content-windowid",
-                                true);
+
+        if (!IsNeckoChild()) {
+            obsService->AddObserver(this,
+                                    "net:current-toplevel-outer-content-windowid",
+                                    true);
+        }
 
         if (mFastOpenSupported) {
             obsService->AddObserver(this, "captive-portal-login", true);
             obsService->AddObserver(this, "captive-portal-login-success", true);
         }
 
         // disabled as its a nop right now
         // obsService->AddObserver(this, "net:failed-to-process-uri-content", true);
@@ -2372,29 +2375,22 @@ nsHttpHandler::Observe(nsISupports *subj
     } else if (!strcmp(topic, "net:current-toplevel-outer-content-windowid")) {
         nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(subject);
         MOZ_RELEASE_ASSERT(wrapper);
 
         uint64_t windowId = 0;
         wrapper->GetData(&windowId);
         MOZ_ASSERT(windowId);
 
-        if (IsNeckoChild()) {
-            if (gNeckoChild) {
-                gNeckoChild->SendNotifyCurrentTopLevelOuterContentWindowId(
-                    windowId);
-            }
-        } else {
-            static uint64_t sCurrentTopLevelOuterContentWindowId = 0;
-            if (sCurrentTopLevelOuterContentWindowId != windowId) {
-                sCurrentTopLevelOuterContentWindowId = windowId;
-                if (mConnMgr) {
-                    mConnMgr->UpdateCurrentTopLevelOuterContentWindowId(
-                        sCurrentTopLevelOuterContentWindowId);
-                }
+        static uint64_t sCurrentTopLevelOuterContentWindowId = 0;
+        if (sCurrentTopLevelOuterContentWindowId != windowId) {
+            sCurrentTopLevelOuterContentWindowId = windowId;
+            if (mConnMgr) {
+                mConnMgr->UpdateCurrentTopLevelOuterContentWindowId(
+                    sCurrentTopLevelOuterContentWindowId);
             }
         }
     } else if (!strcmp(topic, "captive-portal-login") ||
                !strcmp(topic, "captive-portal-login-success")) {
          // We have detected a captive portal and we will reset the Fast Open
          // failure counter.
          ResetFastOpenConsecutiveFailureCounter();
     }
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -168,23 +168,29 @@ void nsHttpTransaction::ResumeReading()
     if (mConnection) {
         nsresult rv = mConnection->ResumeRecv();
         if (NS_FAILED(rv)) {
             LOG(("  resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
         }
     }
 }
 
+bool nsHttpTransaction::EligibleForThrottling()
+{
+  return (mClassOfService & (nsIClassOfService::Throttleable |
+                             nsIClassOfService::Leader |
+                             nsIClassOfService::Unblocked)) ==
+    nsIClassOfService::Throttleable;
+}
+
 void nsHttpTransaction::SetClassOfService(uint32_t cos)
 {
-    bool wasThrottling = mClassOfService & nsIClassOfService::Throttleable;
-
+    bool wasThrottling = EligibleForThrottling();
     mClassOfService = cos;
-
-    bool isThrottling = mClassOfService & nsIClassOfService::Throttleable;
+    bool isThrottling = EligibleForThrottling();
 
     if (mConnection && wasThrottling != isThrottling) {
         // Do nothing until we are actually activated.  For now
         // only remember the throttle flag.  Call to MoveActiveTransaction
         // would add this transaction to the list too early.
         gHttpHandler->ConnMgr()->MoveActiveTransaction(this, isThrottling);
 
         if (mReadingStopped && !isThrottling) {
@@ -833,18 +839,17 @@ bool nsHttpTransaction::ShouldStopReadin
 {
     if (mActivatedAsH2) {
         // Throttling feature is now disabled for http/2 transactions
         // because of bug 1367861.  The logic around mActivatedAsH2
         // will be removed when that is fixed
         return false;
     }
 
-    if (!gHttpHandler->ConnMgr()->ShouldStopReading(
-            this, mClassOfService & nsIClassOfService::Throttleable)) {
+    if (!gHttpHandler->ConnMgr()->ShouldStopReading(this, EligibleForThrottling())) {
         // We are not obligated to throttle
         return false;
     }
 
     if (mContentRead < 16000) {
         // Let the first bytes go, it may also well be all the content we get
         LOG(("nsHttpTransaction::ShouldStopReading too few content (%" PRIi64 ") this=%p", mContentRead, this));
         return false;
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -388,16 +388,20 @@ public:
     // transaction is believed to be HTTP/1 (and thus subject to rate pacing)
     // but later can be dispatched via spdy (not subject to rate pacing).
     void CancelPacing(nsresult reason);
 
     // Called by the connetion manager on the socket thread when reading for this
     // previously throttled transaction has to be resumed.
     void ResumeReading();
 
+    // This examins classification of this transaction whether the Throttleable class
+    // has been set while Leader or Unblocked are not.
+    bool EligibleForThrottling();
+
 private:
     bool mSubmittedRatePacing;
     bool mPassedRatePacing;
     bool mSynchronousRatePaceRequest;
     nsCOMPtr<nsICancelable> mTokenBucketCancel;
 public:
     void     SetClassOfService(uint32_t cos);
     uint32_t ClassOfService() { return mClassOfService; }
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -35,58 +35,52 @@ linux32-devedition/opt:
         -  linux32-tests
         -  linux32-opt-tests
 
 linux64/debug:
     build-platform: linux64/debug
     test-sets:
         - common-tests
         - web-platform-tests
-        - headless
 linux64/opt:
     build-platform: linux64/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
         - desktop-screenshot-capture
         - talos
         - awsy
-        - headless
 linux64-nightly/opt:
     build-platform: linux64-nightly/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
         - desktop-screenshot-capture
-        - headless
 linux64-devedition/opt:
     build-platform: linux64-devedition-nightly/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - opt-only-tests
         - desktop-screenshot-capture
-        - headless
 
 # TODO: use 'pgo' and 'asan' labels here, instead of -pgo/opt
 linux64-pgo/opt:
     build-platform: linux64-pgo/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - talos
-        - headless
 
 linux64-asan/opt:
     build-platform: linux64-asan/opt
     test-sets:
         - common-tests
-        - headless
 
 # Stylo builds only run a subset of tests for the moment. So give them
 # their own test set.
 # Stylo doesn't work on 32-bit Linux yet (bug 1385025)
 # linux32-stylo/debug:
 #     build-platform: linux/debug
 #     test-sets:
 #         - stylo-tests
@@ -152,104 +146,93 @@ linux64-qr/debug:
 
 linux64-ccov/opt:
     build-platform: linux64-ccov/opt
     test-sets:
         - common-tests
         - web-platform-tests
         - awsy
         - talos
-        - headless
 linux64-jsdcov/opt:
     build-platform: linux64-jsdcov/opt
     test-sets:
         - jsdcov-code-coverage-tests
 
 ##
 # Windows platforms (matching /windows.*/)
 
 # win32
 windows7-32/debug:
     build-platform: win32/debug
     test-sets:
-        - headless
         - windows-tests
 windows7-32/opt:
     build-platform: win32/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-talos
         - windows-tests
 
 windows7-32-pgo/opt:
     build-platform: win32-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-tests
         - windows-talos
 
 windows7-32-nightly/opt:
     build-platform: win32-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-tests
 
 windows7-32-devedition/opt:
     build-platform: win32-devedition/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-tests
 
 # win64
 windows10-64/debug:
     build-platform: win64/debug
     test-sets:
-        - headless
         - windows-tests
 
 windows10-64/opt:
     build-platform: win64/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-talos
         - windows-tests
 
 windows10-64-pgo/opt:
     build-platform: win64-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-talos
         - windows-tests
 
 windows10-64-nightly/opt:
     build-platform: win64-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-tests
 
 windows10-64-devedition/opt:
     build-platform: win64-devedition/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
-        - headless
         - windows-tests
 
 windows10-64-asan/opt:
     build-platform: win64-asan/opt
     test-sets:
         - common-tests
 
 # Windows8 tests; all via BBB
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -19,16 +19,17 @@ common-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
     - marionette
+    - marionette-headless
     - mochitest
     - mochitest-a11y
     - mochitest-browser-chrome
     - mochitest-chrome
     - mochitest-clipboard
     - mochitest-devtools-chrome
     - mochitest-gpu
     - mochitest-jetpack
@@ -59,19 +60,16 @@ talos:
     - talos-svgr
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
 
 awsy:
     - awsy
 
-headless:
-    - marionette-headless
-
 ##
 # Limited test sets for specific platforms
 
 stylo-tests:
     - cppunit
     - crashtest
     - reftest
     - reftest-stylo
@@ -109,16 +107,17 @@ windows-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
     - marionette
+    - marionette-headless
     - mochitest
     - mochitest-a11y
     - mochitest-browser-chrome
     - mochitest-chrome
     - mochitest-clipboard
     - mochitest-devtools-chrome
     - mochitest-gpu
     - mochitest-jetpack
@@ -151,16 +150,17 @@ macosx64-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
     - marionette
+    - marionette-headless
     - mochitest
     - mochitest-a11y
     - mochitest-browser-chrome
     - mochitest-chrome
     - mochitest-clipboard
     - mochitest-devtools-chrome
     - mochitest-gpu
     - mochitest-jetpack
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
@@ -169,17 +169,18 @@ class TestScreenCaptureChrome(WindowMana
 
         # Ensure we do not capture the full window
         screenshot_dialog = self.marionette.screenshot()
         self.assertNotEqual(screenshot_dialog, screenshot_element)
 
         self.marionette.close_chrome_window()
         self.marionette.switch_to_window(self.start_window)
 
-    @skip_if_mobile("Fennec doesn't support other chrome windows")
+    # @skip_if_mobile("Fennec doesn't support other chrome windows")
+    @skip("Bug 1329424 - AssertionError: u'iVBORw0KGgoA... (images unexpectedly equal)")
     def test_capture_flags(self):
         dialog = self.open_dialog()
         self.marionette.switch_to_window(dialog)
 
         textbox = self.marionette.find_element(By.ID, "text-box")
         textbox.send_keys("")
         screenshot_focus = self.marionette.screenshot()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_rect.py
@@ -77,20 +77,26 @@ class TestPosition(MarionetteTestCase):
         # not throw.
         #
         # Because we have to cater to an unknown set of environments,
         # the following assertions are the most common denominator that
         # make this test pass, irregardless of system characteristics.
 
         os = self.marionette.session_capabilities["platformName"]
 
+        # Regardless of platform, headless always supports being positioned
+        # off-screen.
+        if self.marionette.session_capabilities["moz:headless"]:
+            self.assertEqual(-8, position["x"])
+            self.assertEqual(-8, position["y"])
+
         # Certain WMs prohibit windows from being moved off-screen,
         # but we don't have this information.  It should be safe to
         # assume a window can be moved to (0,0) or less.
-        if os == "linux":
+        elif os == "linux":
             # certain WMs prohibit windows from being moved off-screen
             self.assertLessEqual(position["x"], 0)
             self.assertLessEqual(position["y"], 0)
 
         # On macOS, windows can only be moved off the screen on the
         # horizontal axis.  The system menu bar also blocks windows from
         # being moved to (0,0).
         elif os == "darwin":
--- a/testing/marionette/session.js
+++ b/testing/marionette/session.js
@@ -1,15 +1,15 @@
 /* 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 {interfaces: Ci, utils: Cu} = Components;
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 Cu.import("chrome://marionette/content/assert.js");
 const {
   error,
@@ -262,16 +262,17 @@ session.Capabilities = class extends Map
       // features
       ["rotatable", appinfo.name == "B2G"],
 
       // proprietary
       ["specificationLevel", 0],
       ["moz:processID", Services.appinfo.processID],
       ["moz:profile", maybeProfile()],
       ["moz:accessibilityChecks", false],
+      ["moz:headless", Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless],
     ]);
   }
 
   /**
    * @param {string} key
    *     Capability name.
    * @param {(string|number|boolean)} value
    *     JSON-safe capability value.
--- a/testing/mozharness/configs/single_locale/macosx64.py
+++ b/testing/mozharness/configs/single_locale/macosx64.py
@@ -13,16 +13,17 @@ config = {
         "MOZ_UPDATE_CHANNEL": "%(update_channel)s",
         "MOZ_PKG_PLATFORM": "mac",
         # "IS_NIGHTLY": "yes",
         "DIST": "%(abs_objdir)s",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
+        'EN_US_PACKAGE_NAME': 'target.dmg',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "/builds/hg-shared",
 
     "upload_env_extra": {
--- a/testing/mozharness/configs/single_locale/macosx64_devedition.py
+++ b/testing/mozharness/configs/single_locale/macosx64_devedition.py
@@ -13,16 +13,17 @@ config = {
         "MOZ_UPDATE_CHANNEL": "%(update_channel)s",
         "MOZ_PKG_PLATFORM": "mac",
         # "IS_NIGHTLY": "yes",
         "DIST": "%(abs_objdir)s",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
+        'EN_US_PACKAGE_NAME': 'target.dmg',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "/builds/hg-shared",
 
     "upload_env_extra": {
--- a/testing/mozharness/configs/single_locale/win32.py
+++ b/testing/mozharness/configs/single_locale/win32.py
@@ -13,16 +13,18 @@ config = {
         "MOZ_UPDATE_CHANNEL": "%(update_channel)s",
         "DIST": "%(abs_objdir)s",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "PATH": 'C:\\mozilla-build\\nsis-3.01;'
                 '%s' % (os.environ.get('path')),
         'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/c/builds',
+        'EN_US_PACKAGE_NAME': 'target.zip',
+        'EN_US_PKG_INST_BASENAME': 'target.installer',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "c:/builds/hg-shared",
 
     # tooltool
--- a/testing/mozharness/configs/single_locale/win32_devedition.py
+++ b/testing/mozharness/configs/single_locale/win32_devedition.py
@@ -13,16 +13,18 @@ config = {
         "MOZ_UPDATE_CHANNEL": "%(update_channel)s",
         "DIST": "%(abs_objdir)s",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "PATH": 'C:\\mozilla-build\\nsis-3.01;'
                 '%s' % (os.environ.get('path')),
         'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/c/builds',
+        'EN_US_PACKAGE_NAME': 'target.zip',
+        'EN_US_PKG_INST_BASENAME': 'target.installer',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "c:/builds/hg-shared",
 
     # tooltool
--- a/testing/mozharness/configs/single_locale/win64.py
+++ b/testing/mozharness/configs/single_locale/win64.py
@@ -13,16 +13,18 @@ config = {
         "DIST": "%(abs_objdir)s",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "PATH": 'C:\\mozilla-build\\nsis-3.01;'
                 '%s' % (os.environ.get('path')),
         'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/c/builds',
+        'EN_US_PACKAGE_NAME': 'target.zip',
+        'EN_US_PKG_INST_BASENAME': 'target.installer',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "c:/builds/hg-shared",
 
     # tooltool
--- a/testing/mozharness/configs/single_locale/win64_devedition.py
+++ b/testing/mozharness/configs/single_locale/win64_devedition.py
@@ -13,16 +13,18 @@ config = {
         "DIST": "%(abs_objdir)s",
         "LOCALE_MERGEDIR": "%(abs_merge_dir)s",
         "L10NBASEDIR": "../../l10n",
         "MOZ_MAKE_COMPLETE_MAR": "1",
         "PATH": 'C:\\mozilla-build\\nsis-3.01;'
                 '%s' % (os.environ.get('path')),
         'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
         'TOOLTOOL_HOME': '/c/builds',
+        'EN_US_PACKAGE_NAME': 'target.zip',
+        'EN_US_PKG_INST_BASENAME': 'target.installer',
     },
     "ssh_key_dir": "~/.ssh",
     "log_name": "single_locale",
     "objdir": "obj-l10n",
     "js_src_dir": "js/src",
     "vcs_share_base": "c:/builds/hg-shared",
 
     # tooltool
--- a/toolkit/components/extensions/schemas/extension_protocol_handlers.json
+++ b/toolkit/components/extensions/schemas/extension_protocol_handlers.json
@@ -11,19 +11,19 @@
             "description": "A user-readable title string for the protocol handler. This will be displayed to the user in interface objects as needed.",
             "type": "string"
           },
           "protocol": {
             "description": "The protocol the site wishes to handle, specified as a string. For example, you can register to handle SMS text message links by registering to handle the \"sms\" scheme.",
             "choices": [{
               "type": "string",
               "enum": [
-                "bitcoin", "geo", "im", "irc", "ircs", "magnet", "mailto",
-                "mms", "news", "nntp", "sip", "sms", "smsto", "ssh", "tel",
-                "urn", "webcal", "wtai", "xmpp"
+                "bitcoin", "geo", "gopher", "im", "irc", "ircs", "magnet",
+                "mailto", "mms", "news", "nntp", "sip", "sms", "smsto", "ssh",
+                "tel", "urn", "webcal", "wtai", "xmpp"
               ]
             }, {
               "type": "string",
               "pattern": "^(ext|web)\\+[a-z0-9.+-]+$"
             }]
           },
           "uriTemplate": {
             "description": "The URL of the handler, as a string. This string should include \"%s\" as a placeholder which will be replaced with the escaped URL of the document to be handled. This URL might be a true URL, or it could be a phone number, email address, or so forth.",
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_filter.html
@@ -54,17 +54,18 @@ add_task(async function setup() {
 
 add_task(async function test_webRequest_filter_window() {
   if (AppConstants.MOZ_BUILD_APP !== "browser") {
     // Android does not support multiple windows.
     return;
   }
 
   await SpecialPowers.pushPrefEnv({
-    set: [["dom.serviceWorkers.testing.enabled", true]],
+    set: [["dom.serviceWorkers.testing.enabled", true],
+          ["network.http.rcwn.enabled", false]],
   });
 
   let events = {
     "onBeforeRequest":     [{urls: ["<all_urls>"], windowId: windowData.windowId}],
     "onBeforeSendHeaders": [{urls: ["<all_urls>"], windowId: windowData.windowId}, ["requestHeaders"]],
     "onSendHeaders":       [{urls: ["<all_urls>"], windowId: windowData.windowId}, ["requestHeaders"]],
     "onBeforeRedirect":    [{urls: ["<all_urls>"], windowId: windowData.windowId}],
     "onHeadersReceived":   [{urls: ["<all_urls>"], windowId: windowData.windowId}, ["responseHeaders"]],
--- a/toolkit/components/places/tests/gtest/mock_Link.h
+++ b/toolkit/components/places/tests/gtest/mock_Link.h
@@ -37,17 +37,18 @@ public:
   {
     // Notify our callback function.
     mHandler(aState);
 
     // Break the cycle so the object can be destroyed.
     mDeathGrip = nullptr;
   }
 
-  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override
+  virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState)
+    const override
   {
     return 0;   // the value shouldn't matter
   }
 
   virtual void NodeInfoChanged(nsIDocument* aOldDoc) final override {}
 
 protected:
   ~mock_Link() {
deleted file mode 100644
--- a/toolkit/components/urlformatter/api_keys.in
+++ /dev/null
@@ -1,4 +0,0 @@
-#define MOZ_MOZILLA_API_KEY @MOZ_MOZILLA_API_KEY@
-#define MOZ_GOOGLE_API_KEY @MOZ_GOOGLE_API_KEY@
-#define MOZ_BING_API_KEY @MOZ_BING_API_KEY@
-#define MOZ_BING_API_CLIENTID @MOZ_BING_API_CLIENTID@
--- a/toolkit/components/urlformatter/moz.build
+++ b/toolkit/components/urlformatter/moz.build
@@ -11,20 +11,13 @@ XPCSHELL_TESTS_MANIFESTS += ['tests/unit
 
 XPIDL_SOURCES += [
     'nsIURLFormatter.idl',
 ]
 
 XPIDL_MODULE = 'urlformatter'
 
 EXTRA_COMPONENTS += [
+    'nsURLFormatter.js',
     'nsURLFormatter.manifest',
 ]
 
-EXTRA_PP_COMPONENTS += [
-    'nsURLFormatter.js',
-]
-
-CONFIGURE_SUBST_FILES += [
-    'api_keys',
-]
-
 DEFINES['OBJDIR'] = OBJDIR
--- a/toolkit/components/urlformatter/nsURLFormatter.js
+++ b/toolkit/components/urlformatter/nsURLFormatter.js
@@ -1,11 +1,8 @@
-#filter substitution
-#include @OBJDIR@/api_keys
-
 /* 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/. */
 
  /**
  * @class nsURLFormatterService
  *
  * nsURLFormatterService exposes methods to substitute variables in URL formats.
@@ -17,16 +14,17 @@
  */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
 
 const PREF_APP_DISTRIBUTION           = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
 
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 
 function nsURLFormatterService() {
@@ -107,20 +105,20 @@ nsURLFormatterService.prototype = {
     PLATFORMVERSION:  function() { return this.appInfo.platformVersion; },
     PLATFORMBUILDID:  function() { return this.appInfo.platformBuildID; },
     APP:              function() { return this.appInfo.name.toLowerCase().replace(/ /, ""); },
     OS:               function() { return this.appInfo.OS; },
     XPCOMABI:         function() { return this.ABI; },
     BUILD_TARGET:     function() { return this.appInfo.OS + "_" + this.ABI; },
     OS_VERSION:       function() { return this.OSVersion; },
     CHANNEL:          () => UpdateUtils.UpdateChannel,
-    MOZILLA_API_KEY:  () => "@MOZ_MOZILLA_API_KEY@",
-    GOOGLE_API_KEY:   () => "@MOZ_GOOGLE_API_KEY@",
-    BING_API_CLIENTID:() => "@MOZ_BING_API_CLIENTID@",
-    BING_API_KEY:     () => "@MOZ_BING_API_KEY@",
+    MOZILLA_API_KEY:  () => AppConstants.MOZ_MOZILLA_API_KEY,
+    GOOGLE_API_KEY:   () => AppConstants.MOZ_GOOGLE_API_KEY,
+    BING_API_CLIENTID:() => AppConstants.MOZ_BING_API_CLIENTID,
+    BING_API_KEY:     () => AppConstants.MOZ_BING_API_KEY,
     DISTRIBUTION:     function() { return this.distribution.id; },
     DISTRIBUTION_VERSION: function() { return this.distribution.version; }
   },
 
   formatURL: function uf_formatURL(aFormat) {
     var _this = this;
     var replacementCallback = function(aMatch, aKey) {
       if (aKey in _this._defaults) {
@@ -152,15 +150,15 @@ nsURLFormatterService.prototype = {
       } catch(ex) {}
     }
 
     return this.formatURL(format);
   },
 
   trimSensitiveURLs: function uf_trimSensitiveURLs(aMsg) {
     // Only the google API key is sensitive for now.
-    return "@MOZ_GOOGLE_API_KEY@" ? aMsg.replace(/@MOZ_GOOGLE_API_KEY@/g,
+    return AppConstants.MOZ_GOOGLE_API_KEY ? aMsg.replace(RegExp(AppConstants.MOZ_GOOGLE_API_KEY, 'g'),
                                                  "[trimmed-google-api-key]")
                                   : aMsg;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsURLFormatterService]);
--- a/toolkit/components/urlformatter/tests/unit/test_urlformatter.js
+++ b/toolkit/components/urlformatter/tests/unit/test_urlformatter.js
@@ -52,9 +52,17 @@ function run_test() {
   do_check_eq(formatter.formatURLPref(pref), ulUrlRef);
   // Keys must be uppercase
   do_check_neq(formatter.formatURL(lowerUrlRaw), ulUrlRef);
   do_check_eq(formatter.formatURL(multiUrl), multiUrlRef);
   // Encoded strings must be kept as is (Bug 427304)
   do_check_eq(formatter.formatURL(encodedUrl), encodedUrlRef);
 
   do_check_eq(formatter.formatURL(advancedUrl), advancedUrlRef);
+
+  for (let val of ["MOZILLA_API_KEY", "GOOGLE_API_KEY", "BING_API_CLIENTID", "BING_API_KEY"]) {
+    let url = "http://test.mozilla.com/?val=%" + val + "%";
+    do_check_neq(formatter.formatURL(url), url);
+  }
+
+  let url = "http://test.mozilla.com/%GOOGLE_API_KEY%/?val=%GOOGLE_API_KEY%";
+  do_check_eq(formatter.trimSensitiveURLs(formatter.formatURL(url)), "http://test.mozilla.com/[trimmed-google-api-key]/?val=[trimmed-google-api-key]");
 }
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -326,16 +326,21 @@ this.AppConstants = Object.freeze({
   INSTALL_LOCALE: "@AB_CD@",
   MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@",
   ANDROID_PACKAGE_NAME: "@ANDROID_PACKAGE_NAME@",
   MOZ_B2G_VERSION: @MOZ_B2G_VERSION@,
   MOZ_B2G_OS_NAME: @MOZ_B2G_OS_NAME@,
 
   DEBUG_JS_MODULES: "@DEBUG_JS_MODULES@",
 
+  MOZ_BING_API_CLIENTID: "@MOZ_BING_API_CLIENTID@",
+  MOZ_BING_API_KEY: "@MOZ_BING_API_KEY@",
+  MOZ_GOOGLE_API_KEY: "@MOZ_GOOGLE_API_KEY@",
+  MOZ_MOZILLA_API_KEY: "@MOZ_MOZILLA_API_KEY@",
+
   // URL to the hg revision this was built from (e.g.
   // "https://hg.mozilla.org/mozilla-central/rev/6256ec9113c1")
   // On unofficial builds, this is an empty string.
 #ifndef MOZ_SOURCE_URL
 #define MOZ_SOURCE_URL
 #endif
   SOURCE_REVISION_URL: "@MOZ_SOURCE_URL@",
 
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -292,17 +292,21 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
     EXTRA_JS_MODULES += [
         'WindowsRegistry.jsm',
     ]
 
 for var in ('ANDROID_PACKAGE_NAME',
             'MOZ_APP_NAME',
             'MOZ_APP_VERSION',
             'MOZ_APP_VERSION_DISPLAY',
+            'MOZ_BING_API_CLIENTID',
+            'MOZ_BING_API_KEY',
+            'MOZ_GOOGLE_API_KEY',
             'MOZ_MACBUNDLE_NAME',
+            'MOZ_MOZILLA_API_KEY',
             'MOZ_WIDGET_TOOLKIT',
             'DLL_PREFIX',
             'DLL_SUFFIX',
             'DEBUG_JS_MODULES'):
             DEFINES[var] = CONFIG[var]
 
 for var in ('MOZ_TOOLKIT_SEARCH',
             'MOZ_SYSTEM_NSS',
--- a/toolkit/modules/tests/browser/browser_WebRequest_filtering.js
+++ b/toolkit/modules/tests/browser/browser_WebRequest_filtering.js
@@ -105,8 +105,16 @@ add_task(async function filter_types() {
 
 function waitForLoad(browser = gBrowser.selectedBrowser) {
   return new Promise(resolve => {
     browser.addEventListener("load", function() {
       resolve();
     }, {capture: true, once: true});
   });
 }
+
+// Disable rcwn to make cache behavior deterministic.
+let rcwnEnabled = Preferences.get("network.http.rcwn.enabled");
+Preferences.set("network.http.rcwn.enabled", false);
+
+registerCleanupFunction(() => {
+  Preferences.set("network.http.rcwn.enabled", rcwnEnabled);
+});
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1672,17 +1672,17 @@ DumpHelp()
          "  --new-instance     Open new instance, not a new window in running instance.\n"
          "  --UILocale <locale> Start with <locale> resources as UI Locale.\n"
          "  --safe-mode        Disables extensions and themes for this session.\n", (const char*) gAppData->name);
 
 #if defined(XP_WIN)
   printf("  --console          Start %s with a debugging console.\n", (const char*) gAppData->name);
 #endif
 
-#ifdef MOZ_WIDGET_GTK
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
   printf("  --headless         Run without a GUI.\n");
 #endif
 
   // this works, but only after the components have registered.  so if you drop in a new command line handler, --help
   // won't not until the second run.
   // out of the bug, because we ship a component.reg file, it works correctly.
   DumpArbitraryHelp();
 }
@@ -3167,22 +3167,34 @@ XREMain::XRE_mainInit(bool* aExitFlag)
     printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
   }
 
   if (CheckArg("headless")) {
     PR_SetEnv("MOZ_HEADLESS=1");
   }
 
   if (gfxPlatform::IsHeadless()) {
-#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(XP_MACOSX)
     printf_stderr("*** You are running in headless mode.\n");
 #else
     Output(true, "Error: headless mode is not currently supported on this platform.\n");
     return 1;
 #endif
+
+#ifdef XP_MACOSX
+    // To avoid taking focus when running in headless mode immediately
+    // transition Firefox to a background application.
+    ProcessSerialNumber psn = { 0, kCurrentProcess };
+    OSStatus transformStatus = TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+    if (transformStatus != noErr) {
+      NS_ERROR("Failed to make process a background application.");
+      return 1;
+    }
+#endif
+
   }
 
   nsresult rv;
   ArgResult ar;
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
     NS_BREAK();
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -214,16 +214,17 @@
   "test_bug883784.jsm": ["Test"],
   "Timer.jsm": ["setTimeout", "clearTimeout", "setInterval", "clearInterval"],
   "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
   "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
   "tps.jsm": ["ACTIONS", "TPS"],
   "Translation.jsm": ["Translation", "TranslationTelemetry"],
   "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
   "UpdateTelemetry.jsm": ["AUSTLMY"],
+  "UpdateTopLevelContentWindowIDHelper.jsm": ["trackBrowserWindow"],
   "util.js": ["getChromeWindow", "Utils", "Svc"],
   "utils.js": ["applicationName", "assert", "Copy", "getBrowserObject", "getChromeWindow", "getWindows", "getWindowByTitle", "getWindowByType", "getWindowId", "getMethodInWindows", "getPreference", "saveDataURL", "setPreference", "sleep", "startTimer", "stopTimer", "takeScreenshot", "unwrapNode", "waitFor", "btoa", "encryptPayload", "makeIdentityConfig", "makeFxAccountsInternalMock", "configureFxAccountIdentity", "configureIdentity", "SyncTestingInfrastructure", "waitForZeroTimer", "Promise", "MockFxaStorageManager", "AccountState", "sumHistogram", "CommonUtils", "CryptoUtils", "TestingUtils", "promiseZeroTimer", "promiseNamedTimer"],
   "Utils.jsm": ["Utils", "Logger", "PivotContext", "PrefCache"],
   "VariablesView.jsm": ["VariablesView", "escapeHTML"],
   "VariablesViewController.jsm": ["VariablesViewController", "StackFrameUtils"],
   "version.jsm": ["VERSION"],
   "vtt.jsm": ["WebVTT"],
   "WebChannel.jsm": ["WebChannel", "WebChannelBroker"],
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -1468,16 +1468,23 @@ GfxInfoBase::GetActiveCrashGuards(JSCont
 NS_IMETHODIMP
 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled)
 {
   *aWebRenderEnabled = gfxVars::UseWebRender();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+GfxInfoBase::GetIsHeadless(bool* aIsHeadless)
+{
+  *aIsHeadless = gfxPlatform::IsHeadless();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 GfxInfoBase::GetContentBackend(nsAString & aContentBackend)
 {
   BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
   nsString outStr;
 
   switch (backend) {
   case BackendType::DIRECT2D1_1: {
     outStr.AppendPrintf("Direct2D 1.1");
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -57,16 +57,17 @@ public:
   NS_IMETHOD_(void) LogFailure(const nsACString &failure) override;
   NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetActiveCrashGuards(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetContentBackend(nsAString & aContentBackend) override;
   NS_IMETHOD GetUsingGPUProcess(bool *aOutValue) override;
   NS_IMETHOD GetWebRenderEnabled(bool* aWebRenderEnabled) override;
+  NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override;
 
   // Initialization function. If you override this, you must call this class's
   // version of Init first.
   // We need Init to be called separately from the constructor so we can
   // register as an observer after all derived classes have been constructed
   // and we know we have a non-zero refcount.
   // Ideally, Init() would be void-return, but the rules of
   // NS_GENERIC_FACTORY_CONSTRUCTOR_INIT require it be nsresult return.
--- a/widget/cocoa/moz.build
+++ b/widget/cocoa/moz.build
@@ -83,16 +83,17 @@ include('/ipc/chromium/chromium-config.m
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/layout/forms',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
     '/widget',
+    '/widget/headless',
 ]
 
 if CONFIG['MOZ_ENABLE_SKIA_PDF']:
     LOCAL_INCLUDES += [
         # Skia includes because widget code includes PrintTargetSkPDF.h, and that
         # includes skia headers.
         '/gfx/skia/skia/include/config',
         '/gfx/skia/skia/include/core',
--- a/widget/cocoa/nsAppShell.mm
+++ b/widget/cocoa/nsAppShell.mm
@@ -29,16 +29,17 @@
 #include "nsCocoaUtils.h"
 #include "nsChildView.h"
 #include "nsToolkit.h"
 #include "TextInputHandler.h"
 #include "mozilla/HangMonitor.h"
 #include "GeckoProfiler.h"
 #include "ScreenHelperCocoa.h"
 #include "mozilla/widget/ScreenManager.h"
+#include "HeadlessScreenHelper.h"
 #include "pratom.h"
 #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
 #include "nsSandboxViolationSink.h"
 #endif
 
 #include <IOKit/pwr_mgt/IOPMLib.h>
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
@@ -305,17 +306,22 @@ nsAppShell::Init()
   
   mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
   NS_ENSURE_STATE(mCFRunLoopSource);
 
   ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes);
 
   if (XRE_IsParentProcess()) {
     ScreenManager& screenManager = ScreenManager::GetSingleton();
-    screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperCocoa>());
+
+    if (gfxPlatform::IsHeadless()) {
+      screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
+    } else {
+      screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperCocoa>());
+    }
   }
 
   rv = nsBaseAppShell::Init();
 
   if (!gAppShellMethodsSwizzled) {
     // We should only replace the original terminate: method if we're not
     // running in a Cocoa embedder. See bug 604901.
     if (!mRunningCocoaEmbedded) {
--- a/widget/cocoa/nsWidgetFactory.mm
+++ b/widget/cocoa/nsWidgetFactory.mm
@@ -15,16 +15,18 @@
 #include "nsCocoaWindow.h"
 #include "nsAppShell.h"
 #include "nsAppShellSingleton.h"
 #include "nsFilePicker.h"
 #include "nsColorPicker.h"
 
 #include "nsClipboard.h"
 #include "nsClipboardHelper.h"
+#include "HeadlessClipboard.h"
+#include "gfxPlatform.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
 #include "nsDragService.h"
 #include "nsToolkit.h"
 
 #include "nsLookAndFeel.h"
 
 #include "nsSound.h"
@@ -38,24 +40,43 @@
 #include "nsPrintSession.h"
 #include "nsToolkitCompsCID.h"
 
 #include "mozilla/widget/ScreenManager.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
+static nsresult
+nsClipboardConstructor(nsISupports *aOuter, REFNSIID aIID,
+                            void **aResult)
+{
+  nsCOMPtr<nsIClipboard> inst;
+
+  *aResult = nullptr;
+  if (aOuter != nullptr) {
+    return NS_ERROR_NO_AGGREGATION;
+  }
+
+  if (gfxPlatform::IsHeadless()) {
+    inst = new HeadlessClipboard();
+  } else {
+    inst = new nsClipboard();
+  }
+
+  return inst->QueryInterface(aIID, aResult);
+}
+
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCocoaWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildView)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsColorPicker)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecX)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsX, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceX, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceX, nsIdleServiceX::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager, ScreenManager::GetAddRefedSingleton)
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1281,16 +1281,33 @@ nsBaseWidget::CreateCompositorSession(in
       this,
       lm,
       GetDefaultScale(),
       options,
       UseExternalCompositingSurface(),
       gfx::IntSize(aWidth, aHeight),
       &retry);
 
+    if (lm->AsWebRenderLayerManager() && mCompositorSession) {
+      TextureFactoryIdentifier textureFactoryIdentifier;
+      lm->AsWebRenderLayerManager()->Initialize(mCompositorSession->GetCompositorBridgeChild(),
+                                                wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
+                                                &textureFactoryIdentifier);
+      if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
+        retry = true;
+        DestroyCompositor();
+        // Disable WebRender
+        gfx::gfxConfig::GetFeature(gfx::Feature::WEBRENDER).ForceDisable(
+          gfx::FeatureStatus::Unavailable,
+          "WebRender initialization failed",
+          NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_INITIALIZE"));
+        gfx::gfxVars::SetUseWebRender(false);
+      }
+    }
+
     // We need to retry in a loop because the act of failing to create the
     // compositor can change our state (e.g. disable WebRender).
     if (mCompositorSession || !retry) {
       *aOptionsOut = options;
       return lm.forget();
     }
   } while (true);
 
@@ -1342,20 +1359,18 @@ void nsBaseWidget::CreateCompositor(int 
   if (mInitialZoomConstraints) {
     UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID,
                           mInitialZoomConstraints->mViewID,
                           Some(mInitialZoomConstraints->mConstraints));
     mInitialZoomConstraints.reset();
   }
 
   if (lm->AsWebRenderLayerManager()) {
-    TextureFactoryIdentifier textureFactoryIdentifier;
-    lm->AsWebRenderLayerManager()->Initialize(mCompositorBridgeChild,
-                                              wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
-                                              &textureFactoryIdentifier);
+    TextureFactoryIdentifier textureFactoryIdentifier = lm->GetTextureFactoryIdentifier();
+    MOZ_ASSERT(textureFactoryIdentifier.mParentBackend == LayersBackend::LAYERS_WR);
     ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
     gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
   }
 
   ShadowLayerForwarder* lf = lm->AsShadowForwarder();
   if (lf) {
     // lf is non-null if we are creating a ClientLayerManager above
     TextureFactoryIdentifier textureFactoryIdentifier;
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -19,16 +19,17 @@ interface nsIGfxInfo : nsISupports
   readonly attribute DOMString DWriteVersion;
   readonly attribute DOMString cleartypeParameters;
 
   /*
    * These are valid across all platforms.
    */
   readonly attribute DOMString ContentBackend;
   readonly attribute boolean WebRenderEnabled;
+  readonly attribute boolean isHeadless;
 
   // XXX: Switch to a list of devices, rather than explicitly numbering them.
 
   /**
    * The name of the display adapter.
    */
   readonly attribute DOMString adapterDescription;
   readonly attribute DOMString adapterDescription2;
new file mode 100644
--- /dev/null
+++ b/xpcom/base/SizeOfState.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SizeOfState_h
+#define SizeOfState_h
+
+#include "mozilla/fallible.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Unused.h"
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+
+// This file includes types that are useful during memory reporting, but which
+// cannot be put into mfbt/MemoryReporting.h because they depend on things that
+// are not in MFBT.
+
+namespace mozilla {
+
+// A table of seen pointers. Useful when measuring structures that contain
+// nodes that may be pointed to from multiple places, e.g. via RefPtr (in C++
+// code) or Arc (in Rust code).
+class SeenPtrs : public nsTHashtable<nsPtrHashKey<const void>>
+{
+public:
+  // Returns true if we have seen this pointer before, false otherwise. Also
+  // remembers this pointer for later queries.
+  bool HaveSeenPtr(const void* aPtr)
+  {
+    uint32_t oldCount = Count();
+
+    mozilla::Unused << PutEntry(aPtr, fallible);
+
+    // If the counts match, there are two possibilities.
+    //
+    // - Lookup succeeded: we've seen the pointer before, and didn't need to
+    //   add a new entry.
+    //
+    // - PutEntry() tried to add the entry and failed due to lack of memory. In
+    //   this case we can't tell if this pointer has been seen before (because
+    //   the table is in an unreliable state and may have dropped previous
+    //   insertions). When doing memory reporting it's better to err on the
+    //   side of under-reporting rather than over-reporting, so we assume we've
+    //   seen the pointer before.
+    //
+    return oldCount == Count();
+  }
+};
+
+// Memory reporting state. Some memory measuring functions
+// (SizeOfIncludingThis(), etc.) just need a MallocSizeOf parameter, but some
+// also need a record of pointers that have been seen and should not be
+// re-measured. This class encapsulates both of those things.
+class SizeOfState
+{
+public:
+  explicit SizeOfState(MallocSizeOf aMallocSizeOf)
+    : mMallocSizeOf(aMallocSizeOf)
+  {}
+
+  bool HaveSeenPtr(const void* aPtr) { return mSeenPtrs.HaveSeenPtr(aPtr); }
+
+  MallocSizeOf mMallocSizeOf;
+  SeenPtrs mSeenPtrs;
+};
+
+} // namespace mozilla
+
+#endif // SizeOfState_h
+
--- a/xpcom/base/moz.build
+++ b/xpcom/base/moz.build
@@ -106,16 +106,17 @@ EXPORTS.mozilla += [
     'IntentionalCrash.h',
     'JSObjectHolder.h',
     'LinuxUtils.h',
     'Logging.h',
     'MemoryReportingProcess.h',
     'nsMemoryInfoDumper.h',
     'NSPRLogModulesParser.h',
     'OwningNonNull.h',
+    'SizeOfState.h',
     'StaticMutex.h',
     'StaticPtr.h',
     'SystemMemoryReporter.h',
 ]
 
 # nsDebugImpl isn't unified because we disable PGO so that NS_ABORT_OOM isn't
 # optimized away oddly.
 SOURCES += [
--- a/xpcom/ds/nsTHashtable.h
+++ b/xpcom/ds/nsTHashtable.h
@@ -139,27 +139,32 @@ public:
   /**
    * Return true if an entry for the given key exists, false otherwise.
    * @param     aKey the key to retrieve
    * @return    true if the key exists, false if the key doesn't exist
    */
   bool Contains(KeyType aKey) const { return !!GetEntry(aKey); }
 
   /**
-   * Get the entry associated with a key, or create a new entry,
+   * Infallibly get the entry associated with a key, or create a new entry,
    * @param     aKey the key to retrieve
-   * @return    pointer to the entry class retreived; nullptr only if memory
-                can't be allocated
+   * @return    pointer to the entry retrieved; never nullptr
    */
   EntryType* PutEntry(KeyType aKey)
   {
     // infallible add
     return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey)));
   }
 
+  /**
+   * Fallibly get the entry associated with a key, or create a new entry,
+   * @param     aKey the key to retrieve
+   * @return    pointer to the entry retrieved; nullptr only if memory can't
+   *            be allocated
+   */
   MOZ_MUST_USE
   EntryType* PutEntry(KeyType aKey, const fallible_t&)
   {
     return static_cast<EntryType*>(mTable.Add(EntryType::KeyToPointer(aKey),
                                               mozilla::fallible));
   }
 
   /**
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -496,16 +496,19 @@ nsWebShellWindow::WindowDeactivated()
   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
   if (fm && window)
     fm->WindowLowered(window);
 }
 
 #ifdef USE_NATIVE_MENUS
 static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
 {
+  if (gfxPlatform::IsHeadless()) {
+    return;
+  }
   nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1");
   if (!nms) {
     return;
   }
 
   // Find the menubar tag (if there is more than one, we ignore all but
   // the first).
   nsCOMPtr<nsIDOMNodeList> menubarElements;