merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 27 Mar 2017 12:57:16 +0200
changeset 397881 9577ddeaafd85554c2a855f385a87472a089d5c0
parent 397861 7ac8812719a11344a4cbcb5b1f6d55b5a68de0e6 (current diff)
parent 397880 8e3e5dc30f91015a3a33bdc184c06cd2e0a219d3 (diff)
child 397896 04bfc9efd61601638d9a91e777453a3a527dea59
child 397918 bd80c3154d6ee152eb90d2d075dffe4244a340da
child 398026 7d3e8986a676a085a65e8bf0e060b66b8c344cd2
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
9577ddeaafd8 / 55.0a1 / 20170327110256 / files
nightly linux64
9577ddeaafd8 / 55.0a1 / 20170327110256 / files
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
merge mozilla-inbound to mozilla-central a=merge
testing/web-platform/meta/XMLHttpRequest/send-network-error-sync-events.sub.htm.ini
toolkit/themes/windows/global/checkbox/cbox-check-dis.gif
toolkit/themes/windows/global/checkbox/cbox-check.gif
--- a/addon-sdk/source/lib/sdk/core/heritage.js
+++ b/addon-sdk/source/lib/sdk/core/heritage.js
@@ -3,182 +3,171 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 var getPrototypeOf = Object.getPrototypeOf;
-var getNames = x => [...Object.getOwnPropertyNames(x),
-                     ...Object.getOwnPropertySymbols(x)];
+function* getNames(x) {
+  yield* Object.getOwnPropertyNames(x);
+  yield* Object.getOwnPropertySymbols(x);
+}
 var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
-var create = Object.create;
 var freeze = Object.freeze;
-var unbind = Function.call.bind(Function.bind, Function.call);
 
 // This shortcut makes sure that we do perform desired operations, even if
 // associated methods have being overridden on the used object.
-var owns = unbind(Object.prototype.hasOwnProperty);
-var apply = unbind(Function.prototype.apply);
-var slice = Array.slice || unbind(Array.prototype.slice);
-var reduce = Array.reduce || unbind(Array.prototype.reduce);
-var map = Array.map || unbind(Array.prototype.map);
-var concat = Array.concat || unbind(Array.prototype.concat);
+var hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);
 
 // Utility function to get own properties descriptor map.
-function getOwnPropertyDescriptors(object) {
-  return reduce(getNames(object), function(descriptor, name) {
-    descriptor[name] = getOwnPropertyDescriptor(object, name);
-    return descriptor;
-  }, {});
+function getOwnPropertyDescriptors(...objects) {
+  let descriptors = {};
+  for (let object of objects)
+    for (let name of getNames(object))
+      descriptors[name] = getOwnPropertyDescriptor(object, name);
+  return descriptors;
 }
 
 function isDataProperty(property) {
-  var value = property.value;
   var type = typeof(property.value);
   return "value" in property &&
-         (type !== "object" || value === null) &&
-         type !== "function";
+         type !== "function" &&
+         (type !== "object" || property.value === null);
 }
 
 function getDataProperties(object) {
   var properties = getOwnPropertyDescriptors(object);
-  return getNames(properties).reduce(function(result, name) {
+  let result = {};
+  for (let name of getNames(properties)) {
     var property = properties[name];
     if (isDataProperty(property)) {
       result[name] = {
         value: property.value,
         writable: true,
         configurable: true,
         enumerable: false
       };
     }
-    return result;
-  }, {})
+  }
+  return result;
 }
 
 /**
  * Takes `source` object as an argument and returns identical object
  * with the difference that all own properties will be non-enumerable
  */
-function obscure(source) {
-  var descriptor = reduce(getNames(source), function(descriptor, name) {
-    var property = getOwnPropertyDescriptor(source, name);
+function obscure(source, prototype = getPrototypeOf(source)) {
+  let descriptors = {};
+  for (let name of getNames(source)) {
+    let property = getOwnPropertyDescriptor(source, name);
     property.enumerable = false;
-    descriptor[name] = property;
-    return descriptor;
-  }, {});
-  return create(getPrototypeOf(source), descriptor);
+    descriptors[name] = property;
+  }
+  return Object.create(prototype, descriptors);
 }
 exports.obscure = obscure;
 
 /**
  * Takes arbitrary number of source objects and returns fresh one, that
  * inherits from the same prototype as a first argument and implements all
  * own properties of all argument objects. If two or more argument objects
  * have own properties with the same name, the property is overridden, with
  * precedence from right to left, implying, that properties of the object on
  * the left are overridden by a same named property of the object on the right.
  */
-var mix = function(source) {
-  var descriptor = reduce(slice(arguments), function(descriptor, source) {
-    return reduce(getNames(source), function(descriptor, name) {
-      descriptor[name] = getOwnPropertyDescriptor(source, name);
-      return descriptor;
-    }, descriptor);
-  }, {});
-
-  return create(getPrototypeOf(source), descriptor);
+var mix = function(...sources) {
+  return Object.create(getPrototypeOf(sources[0]),
+                       getOwnPropertyDescriptors(...sources));
 };
 exports.mix = mix;
 
 /**
  * Returns a frozen object with that inherits from the given `prototype` and
  * implements all own properties of the given `properties` object.
  */
 function extend(prototype, properties) {
-  return create(prototype, getOwnPropertyDescriptors(properties));
+  return Object.create(prototype,
+                       getOwnPropertyDescriptors(properties));
 }
 exports.extend = extend;
 
+function prototypeOf(input) {
+  return typeof(input) === 'function' ? input.prototype : input;
+}
+
 /**
  * Returns a constructor function with a proper `prototype` setup. Returned
  * constructor's `prototype` inherits from a given `options.extends` or
  * `Class.prototype` if omitted and implements all the properties of the
  * given `option`. If `options.implemens` array is passed, it's elements
  * will be mixed into prototype as well. Also, `options.extends` can be
  * a function or a prototype. If function than it's prototype is used as
  * an ancestor of the prototype, if it's an object that it's used directly.
  * Also `options.implements` may contain functions or objects, in case of
  * functions their prototypes are used for mixing.
  */
-var Class = new function() {
-  function prototypeOf(input) {
-    return typeof(input) === 'function' ? input.prototype : input;
-  }
-  var none = freeze([]);
+function Class(options) {
+  // Create descriptor with normalized `options.extends` and
+  // `options.implements`.
+  var descriptor = {
+    // Normalize extends property of `options.extends` to a prototype object
+    // in case it's constructor. If property is missing that fallback to
+    // `Type.prototype`.
+    extends: hasOwnProperty(options, 'extends') ?
+             prototypeOf(options.extends) : Class.prototype,
 
-  return function Class(options) {
-    // Create descriptor with normalized `options.extends` and
-    // `options.implements`.
-    var descriptor = {
-      // Normalize extends property of `options.extends` to a prototype object
-      // in case it's constructor. If property is missing that fallback to
-      // `Type.prototype`.
-      extends: owns(options, 'extends') ?
-               prototypeOf(options.extends) : Class.prototype,
-      // Normalize `options.implements` to make sure that it's array of
-      // prototype objects instead of constructor functions.
-      implements: owns(options, 'implements') ?
-                  freeze(map(options.implements, prototypeOf)) : none
-    };
+    // Normalize `options.implements` to make sure that it's array of
+    // prototype objects instead of constructor functions.
+    implements: freeze(hasOwnProperty(options, 'implements') ?
+                       options.implements.map(prototypeOf) : []),
+  };
 
-    // Create array of property descriptors who's properties will be defined
-    // on the resulting prototype. Note: Using reflection `concat` instead of
-    // method as it may be overridden.
-    var descriptors = concat(descriptor.implements, options, descriptor, {
-      constructor: constructor
-    });
+  // Create array of property descriptors who's properties will be defined
+  // on the resulting prototype.
+  var descriptors = [].concat(descriptor.implements, options, descriptor,
+                              { constructor });
 
-    // Note: we use reflection `apply` in the constructor instead of method
-    // call since later may be overridden.
-    function constructor() {
-      var instance = create(prototype, attributes);
-      if (initialize) apply(initialize, instance, arguments);
-      return instance;
-    }
-    // Create `prototype` that inherits from given ancestor passed as
-    // `options.extends`, falling back to `Type.prototype`, implementing all
-    // properties of given `options.implements` and `options` itself.
-    var prototype = extend(descriptor.extends, mix.apply(mix, descriptors));
-    var initialize = prototype.initialize;
+  // Note: we use reflection `apply` in the constructor instead of method
+  // call since later may be overridden.
+  function constructor() {
+    var instance = Object.create(prototype, attributes);
+    if (initialize)
+      Reflect.apply(initialize, instance, arguments);
+    return instance;
+  }
+  // Create `prototype` that inherits from given ancestor passed as
+  // `options.extends`, falling back to `Type.prototype`, implementing all
+  // properties of given `options.implements` and `options` itself.
+  var prototype = Object.create(descriptor.extends,
+                                getOwnPropertyDescriptors(...descriptors));
+  var initialize = prototype.initialize;
 
-    // Combine ancestor attributes with prototype's attributes so that
-    // ancestors attributes also become initializeable.
-    var attributes = mix(descriptor.extends.constructor.attributes || {},
-                         getDataProperties(prototype));
+  // Combine ancestor attributes with prototype's attributes so that
+  // ancestors attributes also become initializeable.
+  var attributes = mix(descriptor.extends.constructor.attributes || {},
+                       getDataProperties(prototype));
 
-    constructor.attributes = attributes;
-    Object.defineProperty(constructor, 'prototype', {
-      configurable: false,
-      writable: false,
-      value: prototype
-    });
-    return constructor;
-  };
+  constructor.attributes = attributes;
+  Object.defineProperty(constructor, 'prototype', {
+    configurable: false,
+    writable: false,
+    value: prototype
+  });
+  return constructor;
 }
-Class.prototype = extend(null, obscure({
+Class.prototype = obscure({
   constructor: function constructor() {
     this.initialize.apply(this, arguments);
     return this;
   },
   initialize: function initialize() {
     // Do your initialization logic here
   },
   // Copy useful properties from `Object.prototype`.
   toString: Object.prototype.toString,
   toLocaleString: Object.prototype.toLocaleString,
   toSource: Object.prototype.toSource,
   valueOf: Object.prototype.valueOf,
   isPrototypeOf: Object.prototype.isPrototypeOf
-}));
+}, null);
 exports.Class = freeze(Class);
--- a/addon-sdk/source/lib/sdk/event/core.js
+++ b/addon-sdk/source/lib/sdk/event/core.js
@@ -5,69 +5,66 @@
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.';
 const BAD_LISTENER = 'The event listener must be a function.';
 
-const { ns } = require('../core/namespace');
-
-const event = ns();
+const { DefaultMap, DefaultWeakMap } = require('../util/object');
 
 const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/;
 exports.EVENT_TYPE_PATTERN = EVENT_TYPE_PATTERN;
 
-// Utility function to access given event `target` object's event listeners for
-// the specific event `type`. If listeners for this type does not exists they
-// will be created.
-const observers = function observers(target, type) {
-  if (!target) throw TypeError("Event target must be an object");
-  let listeners = event(target);
-  return type in listeners ? listeners[type] : listeners[type] = [];
-};
+// Count of total listeners ever added.
+// This is used to keep track of when a listener was added, which can
+// have an effect on when it is and isn't dispatched. See comments in
+// emitOnObject for more details.
+let listenerCount = 0;
+
+const observers = new DefaultWeakMap(() => {
+  return new DefaultMap(() => new Map());
+});
 
 /**
  * Registers an event `listener` that is called every time events of
  * specified `type` is emitted on the given event `target`.
  * @param {Object} target
  *    Event target object.
  * @param {String} type
  *    The type of event.
  * @param {Function} listener
  *    The listener function that processes the event.
  */
 function on(target, type, listener) {
   if (typeof(listener) !== 'function')
     throw new Error(BAD_LISTENER);
 
-  let listeners = observers(target, type);
-  if (!~listeners.indexOf(listener))
-    listeners.push(listener);
+  observers.get(target).get(type).set(listener, listenerCount++);
 }
 exports.on = on;
 
 
+// Map of wrapper functions for listeners added using `once`.
 var onceWeakMap = new WeakMap();
 
-
 /**
  * Registers an event `listener` that is called only the next time an event
  * of the specified `type` is emitted on the given event `target`.
  * @param {Object} target
  *    Event target object.
  * @param {String} type
  *    The type of the event.
  * @param {Function} listener
  *    The listener function that processes the event.
  */
 function once(target, type, listener) {
-  let replacement = function observer(...args) {
-    off(target, type, observer);
+  function replacement(...args) {
+    off(target, type, replacement);
     onceWeakMap.delete(listener);
     listener.apply(target, args);
   };
   onceWeakMap.set(listener, replacement);
   on(target, type, replacement);
 }
 exports.once = once;
 
@@ -89,43 +86,46 @@ function emit (target, type, ...args) {
   emitOnObject(target, type, target, ...args);
 }
 exports.emit = emit;
 
 /**
  * A variant of emit that allows setting the this property for event listeners
  */
 function emitOnObject(target, type, thisArg, ...args) {
-  let all = observers(target, '*').length;
-  let state = observers(target, type);
-  let listeners = state.slice();
-  let count = listeners.length;
-  let index = 0;
+  let allListeners = observers.get(target);
+  let listeners = allListeners.get(type);
 
   // If error event and there are no handlers (explicit or catch-all)
   // then print error message to the console.
-  if (count === 0 && type === 'error' && all === 0)
+  if (type === 'error' && !listeners.size && !allListeners.get('*').size)
     console.exception(args[0]);
-  while (index < count) {
+
+  let count = listenerCount;
+  for (let [listener, added] of listeners)
     try {
-      let listener = listeners[index];
-      // Dispatch only if listener is still registered.
-      if (~state.indexOf(listener))
-        listener.apply(thisArg, args);
+      // Since our contract unfortuantely requires that we not dispatch to
+      // this event to listeners that were either added or removed during this
+      // dispatch, we need to check when each listener was added.
+      if (added >= count)
+        break;
+      listener.apply(thisArg, args);
     }
     catch (error) {
       // If exception is not thrown by a error listener and error listener is
       // registered emit `error` event. Otherwise dump exception to the console.
-      if (type !== 'error') emit(target, 'error', error);
-      else console.exception(error);
+      if (type !== 'error')
+        emitOnObject(target, 'error', target, error);
+      else
+        console.exception(error);
     }
-    index++;
-  }
-   // Also emit on `"*"` so that one could listen for all events.
-  if (type !== '*') emit(target, '*', type, ...args);
+
+  // Also emit on `"*"` so that one could listen for all events.
+  if (type !== '*' && allListeners.get('*').size)
+    emitOnObject(target, '*', target, type, ...args);
 }
 exports.emitOnObject = emitOnObject;
 
 /**
  * Removes an event `listener` for the given event `type` on the given event
  * `target`. If no `listener` is passed removes all listeners of the given
  * `type`. If `type` is not passed removes all the listeners of the given
  * event `target`.
@@ -135,41 +135,41 @@ exports.emitOnObject = emitOnObject;
  *    The type of event.
  * @param {Function} listener
  *    The listener function that processes the event.
  */
 function off(target, type, listener) {
   let length = arguments.length;
   if (length === 3) {
     if (onceWeakMap.has(listener)) {
-      listener = onceWeakMap.get(listener);
+      observers.get(target).get(type)
+               .delete(onceWeakMap.get(listener));
       onceWeakMap.delete(listener);
     }
 
-    let listeners = observers(target, type);
-    let index = listeners.indexOf(listener);
-    if (~index)
-      listeners.splice(index, 1);
+    observers.get(target).get(type).delete(listener);
   }
   else if (length === 2) {
-    observers(target, type).splice(0);
+    observers.get(target).get(type).clear();
+    observers.get(target).delete(type);
   }
   else if (length === 1) {
-    let listeners = event(target);
-    Object.keys(listeners).forEach(type => delete listeners[type]);
+    for (let listeners of observers.get(target).values())
+      listeners.clear();
+    observers.delete(target);
   }
 }
 exports.off = off;
 
 /**
  * Returns a number of event listeners registered for the given event `type`
  * on the given event `target`.
  */
 function count(target, type) {
-  return observers(target, type).length;
+  return observers.get(target).get(type).size;
 }
 exports.count = count;
 
 /**
  * Registers listeners on the given event `target` from the given `listeners`
  * dictionary. Iterates over the listeners and if property name matches name
  * pattern `onEventType` and property is a function, then registers it as
  * an `eventType` listener on `target`.
@@ -178,16 +178,17 @@ exports.count = count;
  *    The type of event.
  * @param {Object} listeners
  *    Dictionary of listeners.
  */
 function setListeners(target, listeners) {
   Object.keys(listeners || {}).forEach(key => {
     let match = EVENT_TYPE_PATTERN.exec(key);
     let type = match && match[1].toLowerCase();
-    if (!type) return;
+    if (!type)
+      return;
 
     let listener = listeners[key];
     if (typeof(listener) === 'function')
       on(target, type, listener);
   });
 }
 exports.setListeners = setListeners;
--- a/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
@@ -96,20 +96,18 @@ const Tabs = Class({
     if (window)
       return window.tabs.open(options);
 
     return openNewWindowWithTab();
   }
 });
 
 const allTabs = new Tabs();
-// Export a new object with allTabs as the prototype, otherwise allTabs becomes
-// frozen and addListItem and removeListItem don't work correctly.
-module.exports = Object.create(allTabs);
-pipe(tabEvents, module.exports);
+module.exports = allTabs;
+pipe(tabEvents, allTabs);
 
 function addWindowTab(window, tabElement) {
   let tab = new Tab(tabElement);
   if (window)
     addListItem(window.tabs, tab);
   addListItem(allTabs, tab);
   emit(allTabs, "open", tab);
 }
--- a/addon-sdk/source/lib/sdk/util/object.js
+++ b/addon-sdk/source/lib/sdk/util/object.js
@@ -8,16 +8,50 @@ module.metadata = {
 };
 
 const { flatten } = require('./array');
 
 // Create a shortcut for Array.prototype.slice.call().
 const unbind = Function.call.bind(Function.bind, Function.call);
 const slice = unbind(Array.prototype.slice);
 
+class DefaultWeakMap extends WeakMap {
+  constructor(createItem, items = undefined) {
+    super(items);
+
+    this.createItem = createItem;
+  }
+
+  get(key) {
+    if (!this.has(key)) {
+      this.set(key, this.createItem(key));
+    }
+
+    return super.get(key);
+  }
+}
+
+class DefaultMap extends Map {
+  constructor(createItem, items = undefined) {
+    super(items);
+
+    this.createItem = createItem;
+  }
+
+  get(key) {
+    if (!this.has(key)) {
+      this.set(key, this.createItem(key));
+    }
+
+    return super.get(key);
+  }
+}
+
+Object.assign(exports, {DefaultMap, DefaultWeakMap});
+
 /**
  * Merges all the properties of all arguments into first argument. If two or
  * more argument objects have own properties with the same name, the property
  * is overridden, with precedence from right to left, implying, that properties
  * of the object on the left are overridden by a same named property of the
  * object on the right.
  *
  * Any argument given with "falsy" value - commonly `null` and `undefined` in
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -488,16 +488,17 @@ const load = iced(function load(loader, 
       metadata: {
         addonID: loader.id,
         URI: module.uri
       }
     });
   }
   sandboxes[module.uri] = sandbox;
 
+  let originalExports = module.exports;
   try {
     evaluate(sandbox, module.uri);
   }
   catch (error) {
     let { message, fileName, lineNumber } = error;
     let stack = error.stack || Error().stack;
     let frames = parseStack(stack).filter(isntLoaderFrame);
     let toString = String(error);
@@ -540,17 +541,20 @@ const load = iced(function load(loader, 
 
   if (loader.checkCompatibility) {
     let err = XulApp.incompatibility(module);
     if (err) {
       throw err;
     }
   }
 
-  if (module.exports && typeof(module.exports) === 'object')
+  // Only freeze the exports object if we created it ourselves. Modules
+  // which completely replace the exports object and still want it
+  // frozen need to freeze it themselves.
+  if (module.exports === originalExports)
     freeze(module.exports);
 
   return module;
 });
 Loader.load = load;
 
 // Utility function to normalize module `uri`s so they have `.js` extension.
 function normalizeExt(uri) {
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -1213,17 +1213,17 @@ exports['test active tab properties defi
       });
     }
   });
 };
 
 // related to bugs 922956 and 989288
 // https://bugzilla.mozilla.org/show_bug.cgi?id=922956
 // https://bugzilla.mozilla.org/show_bug.cgi?id=989288
-exports["test tabs ready and close after window.open"] = function*(assert, done) {
+if (0) exports["test tabs ready and close after window.open"] = function*(assert, done) {
   // ensure popups open in a new window and disable popup blocker
   setPref(OPEN_IN_NEW_WINDOW_PREF, 2);
   setPref(DISABLE_POPUP_PREF, false);
 
   // open windows to trigger observers
   tabs.activeTab.attach({
     contentScript: "window.open('about:blank');" +
                    "window.open('about:blank', '', " +
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -90,20 +90,16 @@ var whitelist = new Set([
   {file: "chrome://global/skin/arrow/arrow-rit-sharp.gif",
    platforms: ["linux", "win"]},
   {file: "chrome://global/skin/arrow/arrow-up-sharp.gif",
    platforms: ["linux", "win"]},
   {file: "chrome://global/skin/arrow/panelarrow-horizontal.svg",
    platforms: ["linux"]},
   {file: "chrome://global/skin/arrow/panelarrow-vertical.svg",
    platforms: ["linux"]},
-  // Bug 1348529
-  {file: "chrome://global/skin/checkbox/cbox-check-dis.gif",
-   platforms: ["linux"]},
-  {file: "chrome://global/skin/checkbox/cbox-check.gif", platforms: ["linux"]},
   // Bug 1348359
   {file: "chrome://global/skin/dirListing/folder.png", platforms: ["linux"]},
   {file: "chrome://global/skin/dirListing/local.png", platforms: ["linux", "win"]},
   {file: "chrome://global/skin/dirListing/remote.png"},
   {file: "chrome://global/skin/dirListing/up.png", platforms: ["linux"]},
   // Bug 1348362
   {file: "chrome://global/skin/icons/Close.gif", platforms: ["win"]},
   {file: "chrome://global/skin/icons/Error.png", platforms: ["linux", "macosx"]},
--- a/dom/svg/SVGContentUtils.cpp
+++ b/dom/svg/SVGContentUtils.cpp
@@ -857,43 +857,8 @@ SVGContentUtils::GetPath(const nsAString
   return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1);
 }
 
 bool
 SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {
   return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle,
                                                   nsGkAtoms::ellipse);
 }
-
-gfxMatrix
-SVGContentUtils::PrependLocalTransformsTo(
-  const gfxMatrix &aMatrix,
-  SVGTransformTypes aWhich,
-  const gfx::Matrix* aAnimateMotionTransform,
-  const nsSVGAnimatedTransformList* aTransforms)
-{
-  gfxMatrix result(aMatrix);
-
-  if (aWhich == eChildToUserSpace) {
-    // We don't have anything to prepend.
-    // eChildToUserSpace is not the common case, which is why we return
-    // 'result' to benefit from NRVO rather than returning aMatrix before
-    // creating 'result'.
-    return result;
-  }
-
-  MOZ_ASSERT(aWhich == eAllTransforms || aWhich == eUserSpaceToParent,
-             "Unknown TransformTypes");
-
-  // animateMotion's resulting transform is supposed to apply *on top of*
-  // any transformations from the |transform| attribute. So since we're
-  // PRE-multiplying, we need to apply the animateMotion transform *first*.
-  if (aAnimateMotionTransform) {
-    result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform));
-  }
-
-  if (aTransforms) {
-    result.PreMultiply(
-      aTransforms->GetAnimValue().GetConsolidationMatrix());
-  }
-
-  return result;
-}
--- a/dom/svg/SVGContentUtils.h
+++ b/dom/svg/SVGContentUtils.h
@@ -379,22 +379,11 @@ public:
   static already_AddRefed<mozilla::gfx::Path>
   GetPath(const nsAString& aPathString);
 
   /**
    *  Returns true if aContent is one of the elements whose stroke is guaranteed
    *  to have no corners: circle or ellipse
    */
   static bool ShapeTypeHasNoCorners(const nsIContent* aContent);
-
-  /**
-   *  Prepends an element's local transforms to the transform matrix.
-   *  This is a helper for nsSVGElement::PrependLocalTransformsTo.
-   *  Any callers probably really want to call that method instead of this one.
-   */
-  static gfxMatrix PrependLocalTransformsTo(
-    const gfxMatrix &aMatrix,
-    SVGTransformTypes aWhich,
-    const Matrix* aAnimateMotionTransform,
-    const mozilla::nsSVGAnimatedTransformList* aTransforms);
 };
 
 #endif
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -949,49 +949,62 @@ SVGSVGElement::GetLength(uint8_t aCtxTyp
   }
   return 0;
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
-SVGSVGElement::PrependLocalTransformsTo(
-  const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
+SVGSVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
+                                        SVGTransformTypes aWhich) const
 {
   // 'transform' attribute (or an override from a fragment identifier):
-  gfxMatrix fromUserSpace =
-    SVGContentUtils::PrependLocalTransformsTo(
-      aMatrix, aWhich, mAnimateMotionTransform,
-      mSVGView && mSVGView->mTransforms ? mSVGView->mTransforms : mTransforms);
+  gfxMatrix userToParent;
 
-  if (aWhich == eUserSpaceToParent) {
-    return fromUserSpace;
+  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
+    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
+                                            mSVGView && mSVGView->mTransforms
+                                              ? mSVGView->mTransforms
+                                              : mTransforms);
+    if (aWhich == eUserSpaceToParent) {
+      return userToParent * aMatrix;
+    }
   }
 
+  gfxMatrix childToUser;
+
   if (IsInner()) {
     float x, y;
     const_cast<SVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
-    if (aWhich == eAllTransforms) {
-      // the common case
-      return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * fromUserSpace;
-    }
-    MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
-    return ThebesMatrix(GetViewBoxTransform()) * gfxMatrix::Translation(x, y) * aMatrix;
+    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
+  } else if (IsRoot()) {
+    childToUser = ThebesMatrix(GetViewBoxTransform()
+                                 .PostScale(mCurrentScale, mCurrentScale)
+                                 .PostTranslate(mCurrentTranslate.GetX(),
+                                                mCurrentTranslate.GetY()));
+  } else {
+    // outer-<svg>, but inline in some other content:
+    childToUser = ThebesMatrix(GetViewBoxTransform());
   }
 
-  if (IsRoot()) {
-    gfxMatrix zoomPanTM;
-    zoomPanTM.Translate(gfxPoint(mCurrentTranslate.GetX(), mCurrentTranslate.GetY()));
-    zoomPanTM.Scale(mCurrentScale, mCurrentScale);
-    return ThebesMatrix(GetViewBoxTransform()) * zoomPanTM * fromUserSpace;
+  if (aWhich == eAllTransforms) {
+    return childToUser * userToParent * aMatrix;
   }
 
-  // outer-<svg>, but inline in some other content:
-  return ThebesMatrix(GetViewBoxTransform()) * fromUserSpace;
+  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
+
+  // The following may look broken because pre-multiplying our eChildToUserSpace
+  // transform with another matrix without including our eUserSpaceToParent
+  // transform between the two wouldn't make sense.  We don't expect that to
+  // ever happen though.  We get here either when the identity matrix has been
+  // passed because our caller just wants our eChildToUserSpace transform, or
+  // when our eUserSpaceToParent transform has already been multiplied into the
+  // matrix that our caller passes (such as when we're called from PaintSVG).
+  return childToUser * aMatrix;
 }
 
 nsSVGAnimatedTransformList*
 SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
 {
   if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
     return mSVGView->mTransforms;
   }
--- a/dom/svg/SVGTransformableElement.cpp
+++ b/dom/svg/SVGTransformableElement.cpp
@@ -96,22 +96,25 @@ SVGTransformableElement::IsEventAttribut
 {
   return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement overrides
 
 gfxMatrix
-SVGTransformableElement::PrependLocalTransformsTo(
-  const gfxMatrix &aMatrix,
-  SVGTransformTypes aWhich) const
+SVGTransformableElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
+                                                  SVGTransformTypes aWhich) const
 {
-  return SVGContentUtils::PrependLocalTransformsTo(
-    aMatrix, aWhich, mAnimateMotionTransform, mTransforms);
+  if (aWhich == eChildToUserSpace) {
+    // We don't have any eUserSpaceToParent transforms. (Sub-classes that do
+    // must override this function and handle that themselves.)
+    return aMatrix;
+  }
+  return GetUserToParentTransform(mAnimateMotionTransform, mTransforms) * aMatrix;
 }
 
 const gfx::Matrix*
 SVGTransformableElement::GetAnimateMotionTransform() const
 {
   return mAnimateMotionTransform.get();
 }
 
@@ -250,11 +253,29 @@ SVGTransformableElement::GetTransformToE
   }
   RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv);
   if (rv.Failed()) return nullptr;
 
   RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM);
   return mat.forget();
 }
 
+/* static */ gfxMatrix
+SVGTransformableElement::GetUserToParentTransform(
+                           const gfx::Matrix* aAnimateMotionTransform,
+                           const nsSVGAnimatedTransformList* aTransforms)
+{
+  gfxMatrix result;
+
+  if (aAnimateMotionTransform) {
+    result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform));
+  }
+
+  if (aTransforms) {
+    result.PreMultiply(aTransforms->GetAnimValue().GetConsolidationMatrix());
+  }
+
+  return result;
+}
+
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/svg/SVGTransformableElement.h
+++ b/dom/svg/SVGTransformableElement.h
@@ -64,16 +64,27 @@ public:
     GetAnimatedTransformList(uint32_t aFlags = 0) override;
   virtual nsIAtom* GetTransformListAttrName() const override {
     return nsGkAtoms::transform;
   }
 
   virtual bool IsTransformable() override { return true; }
 
 protected:
+  /**
+   * Helper for overrides of PrependLocalTransformsTo.  If both arguments are
+   * provided they are multiplied in the order in which the arguments appear,
+   * and the result is returned.  If neither argument is provided, the identity
+   * matrix is returned.  If only one argument is provided its transform is
+   * returned.
+   */
+  static gfxMatrix GetUserToParentTransform(
+                     const gfx::Matrix* aAnimateMotionTransform,
+                     const nsSVGAnimatedTransformList* aTransforms);
+
   nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
 
   // XXX maybe move this to property table, to save space on un-animated elems?
   nsAutoPtr<gfx::Matrix> mAnimateMotionTransform;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -450,30 +450,46 @@ SVGUseElement::UnlinkSource()
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
 SVGUseElement::PrependLocalTransformsTo(
   const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
 {
   // 'transform' attribute:
-  gfxMatrix fromUserSpace =
-    SVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich);
-  if (aWhich == eUserSpaceToParent) {
-    return fromUserSpace;
+  gfxMatrix userToParent;
+
+  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
+    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
+                                            mTransforms);
+    if (aWhich == eUserSpaceToParent) {
+      return userToParent * aMatrix;
+    }
   }
+
   // our 'x' and 'y' attributes:
   float x, y;
   const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
-  gfxMatrix toUserSpace = gfxMatrix::Translation(x, y);
-  if (aWhich == eChildToUserSpace) {
-    return toUserSpace * aMatrix;
+
+  gfxMatrix childToUser = gfxMatrix::Translation(x, y);
+
+  if (aWhich == eAllTransforms) {
+    return childToUser * userToParent * aMatrix;
   }
-  MOZ_ASSERT(aWhich == eAllTransforms, "Unknown TransformTypes");
-  return toUserSpace * fromUserSpace;
+
+  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
+
+  // The following may look broken because pre-multiplying our eChildToUserSpace
+  // transform with another matrix without including our eUserSpaceToParent
+  // transform between the two wouldn't make sense.  We don't expect that to
+  // ever happen though.  We get here either when the identity matrix has been
+  // passed because our caller just wants our eChildToUserSpace transform, or
+  // when our eUserSpaceToParent transform has already been multiplied into the
+  // matrix that our caller passes (such as when we're called from PaintSVG).
+  return childToUser * aMatrix;
 }
 
 /* virtual */ bool
 SVGUseElement::HasValidDimensions() const
 {
   return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
            mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
          (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2729,16 +2729,17 @@ XMLHttpRequestMainThread::InitiateFetch(
     // ref to us to be extra safe.
     mChannel->SetNotificationCallbacks(mNotificationCallbacks);
     mChannel = nullptr;
 
     mErrorLoad = true;
 
     // Per spec, we throw on sync errors, but not async.
     if (mFlagSynchronous) {
+      mState = State::done;
       return NS_ERROR_DOM_NETWORK_ERR;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -3013,16 +3014,17 @@ XMLHttpRequestMainThread::SendInternal(c
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     // Per spec, silently fail on async request failures; throw for sync.
     if (mFlagSynchronous) {
+      mState = State::done;
       return NS_ERROR_DOM_NETWORK_ERR;
     } else {
       // Defer the actual sending of async events just in case listeners
       // are attached after the send() method is called.
       NS_DispatchToCurrentThread(
         NewRunnableMethod<ProgressEventType>(this,
           &XMLHttpRequestMainThread::CloseRequestWithError,
           ProgressEventType::error));
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -342,16 +342,17 @@ RasterImage::LookupFrame(const IntSize& 
 
   if (result.Type() == MatchType::NOT_FOUND ||
       result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
       ((aFlags & FLAG_SYNC_DECODE) && !result)) {
     // We don't have a copy of this frame, and there's no decoder working on
     // one. (Or we're sync decoding and the existing decoder hasn't even started
     // yet.) Trigger decoding so it'll be available next time.
     MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
+               gfxPrefs::ImageMemAnimatedDiscardable() ||
                !mAnimationState || mAnimationState->KnownFrameCount() < 1,
                "Animated frames should be locked");
 
     bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
 
     // If we can or did sync decode, we should already have the frame.
     if (ranSync || (aFlags & FLAG_SYNC_DECODE)) {
       result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
@@ -409,17 +410,17 @@ RasterImage::IsOpaque()
 
 NS_IMETHODIMP_(bool)
 RasterImage::WillDrawOpaqueNow()
 {
   if (!IsOpaque()) {
     return false;
   }
 
-  if (mAnimationState) {
+  if (mAnimationState && !gfxPrefs::ImageMemAnimatedDiscardable()) {
     // We never discard frames of animated images.
     return true;
   }
 
   // If we are not locked our decoded data could get discard at any time (ie
   // between the call to this function and when we are asked to draw), so we
   // have to return false if we are unlocked.
   if (IsUnlocked()) {
@@ -753,19 +754,21 @@ RasterImage::SetMetadata(const ImageMeta
     mHasSize = true;
   }
 
   if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
     // We're becoming animated, so initialize animation stuff.
     mAnimationState.emplace(mAnimationMode);
     mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
 
-    // We don't support discarding animated images (See bug 414259).
-    // Lock the image and throw away the key.
-    LockImage();
+    if (!gfxPrefs::ImageMemAnimatedDiscardable()) {
+      // We don't support discarding animated images (See bug 414259).
+      // Lock the image and throw away the key.
+      LockImage();
+    }
 
     if (!aFromMetadataDecode) {
       // The metadata decode reported that this image isn't animated, but we
       // discovered that it actually was during the full decode. This is a
       // rare failure that only occurs for corrupt images. To recover, we need
       // to discard all existing surfaces and redecode.
       return false;
     }
@@ -1070,35 +1073,37 @@ RasterImage::GetKeys(uint32_t* count, ch
   return mProperties->GetKeys(count, keys);
 }
 
 void
 RasterImage::Discard()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
-  MOZ_ASSERT(!mAnimationState, "Asked to discard for animated image");
+  MOZ_ASSERT(!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable(),
+    "Asked to discard for animated image");
 
   // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   if (mAnimationState) {
     mAnimationState->UpdateState(mAnimationFinished, this, mSize);
   }
 
   // Notify that we discarded.
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
   }
 }
 
 bool
 RasterImage::CanDiscard() {
-  return mAllSourceData &&       // ...have the source data...
-         !mAnimationState;       // Can never discard animated images
+  return mAllSourceData &&
+         // Can discard animated images if the pref is set
+         (!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable());
 }
 
 NS_IMETHODIMP
 RasterImage::StartDecoding(uint32_t aFlags)
 {
   if (mError) {
     return NS_ERROR_FAILURE;
   }
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -316,17 +316,19 @@ private:
 
   void UpdateImageContainer();
 
   // We would like to just check if we have a zero lock count, but we can't do
   // that for animated images because in EnsureAnimExists we lock the image and
   // never unlock so that animated images always have their lock count >= 1. In
   // that case we use our animation consumers count as a proxy for lock count.
   bool IsUnlocked() {
-    return (mLockCount == 0 || (mAnimationState && mAnimationConsumers == 0));
+    return (mLockCount == 0 ||
+            (!gfxPrefs::ImageMemAnimatedDiscardable() &&
+             (mAnimationState && mAnimationConsumers == 0)));
   }
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
   /**
    * Creates and runs a decoder, either synchronously or asynchronously
copy from image/test/crashtests/1249576-1.png
copy to image/test/mochitest/infinite-apng.png
--- a/image/test/mochitest/mochitest.ini
+++ b/image/test/mochitest/mochitest.ini
@@ -54,16 +54,17 @@ support-files =
   first-frame-padding.gif
   green.png
   green-background.html
   grey.png
   ico-bmp-opaque.ico
   ico-bmp-transparent.ico
   iframe.html
   imgutils.js
+  infinite-apng.png
   invalid.jpg
   keep.gif
   keep.png
   lime100x100.svg
   lime-anim-100x100.svg
   lime-anim-100x100-2.svg
   lime-css-anim-100x100.svg
   opaque.bmp
@@ -124,16 +125,17 @@ skip-if = os == 'android'
 [test_bug1180105.html]
 [test_bug1217571.html]
 [test_bullet_animation.html]
 skip-if = os == 'android'
 [test_changeOfSource.html]
 skip-if = os == 'android'
 [test_changeOfSource2.html]
 skip-if = os == 'android'
+[test_discardAnimatedImage.html]
 [test_drawDiscardedImage.html]
 [test_error_events.html]
 [test_image_crossorigin_data_url.html]
 [test_ImageContentLoaded.html]
 [test_has_transparency.html]
 skip-if = os == 'android'
 [test_net_failedtoprocess.html]
 skip-if = os == 'android'
new file mode 100644
--- /dev/null
+++ b/image/test/mochitest/test_discardAnimatedImage.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=686905
+-->
+<head>
+  <title>Test that animated images can be discarded</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="imgutils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686905">Mozilla Bug 686905</a>
+<p id="display"></p>
+<div id="content">
+  <div id="container">
+    <canvas id="canvas" width="100" height="100"></canvas>
+    <img id="infinitepng" src="infinite-apng.png">
+    <img id="infinitegif" src="animated1.gif">
+    <img id="finitepng" src="restore-previous.png">
+    <img id="finitegif" src="restore-previous.gif">
+  </div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 686905. **/
+SimpleTest.waitForExplicitFinish();
+
+var gNumDiscards = 0;
+
+window.onload = function() {
+  // Enable discarding for the test.
+  SpecialPowers.pushPrefEnv({
+    'set':[['image.mem.discardable',true]]
+  }, runTest);
+}
+
+var gImgs = ['finitepng', 'finitegif', 'infinitepng', 'infinitegif'];
+
+function runTest() {
+  var animatedDiscardable =
+    SpecialPowers.getBoolPref('image.mem.animated.discardable');
+  if (!animatedDiscardable) {
+    ok(true, "discarding of animated images is disabled, nothing to test")
+    SimpleTest.finish();
+    return;
+  }
+
+  // Draw the images to canvas to force them to be decoded.
+  for (var i = 0; i < gImgs.length; i++) {
+    drawCanvas(document.getElementById(gImgs[i]));
+  }
+
+  for (var i = 0; i < gImgs.length; i++) {
+    addCallback(document.getElementById(gImgs[i]));
+  }
+
+  document.getElementById("container").style.display = "none";
+
+  for (var i = 0; i < gImgs.length; i++) {
+    requestDiscard(document.getElementById(gImgs[i]));
+  }
+}
+
+function step2() {
+  document.getElementById("container").style.display = "";
+
+  // Draw the images to canvas to force them to be decoded again.
+  for (var i = 0; i < gImgs.length; i++) {
+    drawCanvas(document.getElementById(gImgs[i]));
+  }
+
+  SimpleTest.finish();
+}
+
+function addCallback(anImage) {
+  var observer = new ImageDecoderObserverStub();
+  observer.discard = function () {
+    imgLoadingContent.removeObserver(scriptedObserver);
+    gNumDiscards++;
+    ok(true, "got image discard");
+    if (gNumDiscards == gImgs.length) {
+      step2();
+    }
+  }
+  observer = SpecialPowers.wrapCallbackObject(observer);
+
+  var scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                           .getService(SpecialPowers.Ci.imgITools)
+                           .createScriptedObserver(observer);
+
+  var imgLoadingContent =
+    SpecialPowers.wrap(anImage)
+                 .QueryInterface(SpecialPowers.Ci.nsIImageLoadingContent);
+  imgLoadingContent.addObserver(scriptedObserver);
+}
+
+function requestDiscard(anImage) {
+  var request = SpecialPowers.wrap(anImage)
+      .QueryInterface(SpecialPowers.Ci.nsIImageLoadingContent)
+      .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+  setTimeout(() => request.requestDiscard(), 0);
+}
+
+function drawCanvas(anImage) {
+  dump(anImage + "\n");
+  var canvas = document.getElementById('canvas');
+  var context = canvas.getContext('2d');
+
+  context.drawImage(anImage, 0, 0);
+  ok(true, "we got through the drawImage call without an exception being thrown");
+}
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/layout/svg/SVGContextPaint.cpp
+++ b/layout/svg/SVGContextPaint.cpp
@@ -274,38 +274,59 @@ AutoSetRestoreSVGContextPaint::~AutoSetR
 
     NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to restore context paint");
   }
 }
 
 
 // SVGEmbeddingContextPaint
 
-void
-SVGEmbeddingContextPaint::SetFill(nscolor aFill)
+already_AddRefed<gfxPattern>
+SVGEmbeddingContextPaint::GetFillPattern(const DrawTarget* aDrawTarget,
+                                         float aFillOpacity,
+                                         const gfxMatrix& aCTM)
 {
-  mFill = new gfxPattern(ToDeviceColor(aFill));
+  if (!mFill) {
+    return nullptr;
+  }
+  // The gfxPattern that we create below depends on aFillOpacity, and since
+  // different elements in the SVG image may pass in different values for
+  // fill opacities we don't try to cache the gfxPattern that we create.
+  Color fill = *mFill;
+  fill.a *= aFillOpacity;
+  return do_AddRef(new gfxPattern(fill));
 }
 
-void
-SVGEmbeddingContextPaint::SetStroke(nscolor aStroke)
+already_AddRefed<gfxPattern>
+SVGEmbeddingContextPaint::GetStrokePattern(const DrawTarget* aDrawTarget,
+                                           float aStrokeOpacity,
+                                           const gfxMatrix& aCTM)
 {
-  mStroke = new gfxPattern(ToDeviceColor(aStroke));
+  if (!mStroke) {
+    return nullptr;
+  }
+  Color stroke = *mStroke;
+  stroke.a *= aStrokeOpacity;
+  return do_AddRef(new gfxPattern(stroke));
 }
 
 uint32_t
 SVGEmbeddingContextPaint::Hash() const
 {
   uint32_t hash = 0;
 
-  Color color;
+  if (mFill) {
+    hash = HashGeneric(hash, mFill->ToABGR());
+  } else {
+    // Arbitrary number, just to avoid trivial hash collisions between pairs of
+    // instances where one embedding context has fill set to the same value as
+    // another context has stroke set to.
+    hash = 1;
+  }
 
-  if (mFill && mFill->GetSolidColor(color)) {
-    hash = HashGeneric(hash, color.ToABGR());
-  }
-  if (mStroke && mStroke->GetSolidColor(color)) {
-    hash = HashGeneric(hash, color.ToABGR());
+  if (mStroke) {
+    hash = HashGeneric(hash, mStroke->ToABGR());
   }
 
   return hash;
 }
 
 } // namespace mozilla
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZILLA_SVGCONTEXTPAINT_H_
 #define MOZILLA_SVGCONTEXTPAINT_H_
 
 #include "DrawMode.h"
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "gfxTypes.h"
+#include "gfxUtils.h"
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/gfx/2D.h"
 #include "nsColor.h"
 #include "nsStyleStruct.h"
 #include "nsTArray.h"
 
 class gfxContext;
@@ -214,48 +215,55 @@ public:
 /**
  * This class is used to pass context paint to an SVG image when an element
  * references that image (e.g. via HTML <img> or SVG <image>, or by referencing
  * it from a CSS property such as 'background-image').  In this case we only
  * support context colors and not paint servers.
  */
 class SVGEmbeddingContextPaint : public SVGContextPaint
 {
+  typedef gfx::Color Color;
+
 public:
   SVGEmbeddingContextPaint() {}
 
-  void SetFill(nscolor aFill);
-  void SetStroke(nscolor aStroke);
-
-  already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
-                                              float aOpacity,
-                                              const gfxMatrix& aCTM) override {
-    return do_AddRef(mFill);
+  void SetFill(nscolor aFill) {
+    mFill.emplace(gfx::ToDeviceColor(aFill));
+  }
+  void SetStroke(nscolor aStroke) {
+    mStroke.emplace(gfx::ToDeviceColor(aStroke));
   }
 
+  /**
+   * Returns a pattern of type PatternType::COLOR, or else nullptr.
+   */
+  already_AddRefed<gfxPattern> GetFillPattern(const DrawTarget* aDrawTarget,
+                                              float aFillOpacity,
+                                              const gfxMatrix& aCTM) override;
+
+  /**
+   * Returns a pattern of type PatternType::COLOR, or else nullptr.
+   */
   already_AddRefed<gfxPattern> GetStrokePattern(const DrawTarget* aDrawTarget,
-                                                float aOpacity,
-                                                const gfxMatrix& aCTM) override {
-    return do_AddRef(mStroke);
-  }
+                                                float aStrokeOpacity,
+                                                const gfxMatrix& aCTM) override;
 
   float GetFillOpacity() const override {
     // Always 1.0f since we don't currently allow 'context-fill-opacity'
     return 1.0f;
   };
 
   float GetStrokeOpacity() const override {
     // Always 1.0f since we don't currently allow 'context-stroke-opacity'
     return 1.0f;
   };
 
   uint32_t Hash() const override;
 
 private:
-  // Note: if these are set at all, they'll have type PatternType::COLOR.
-  RefPtr<gfxPattern> mFill;
-  RefPtr<gfxPattern> mStroke;
+  Maybe<Color> mFill;
+  Maybe<Color> mStroke;
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_SVGCONTEXTPAINT_H_
 
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -378,38 +378,16 @@ SVGGeometryFrame::GetFrameForPoint(const
   }
 
   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
     return this;
 
   return nullptr;
 }
 
-nsRect
-SVGGeometryFrame::GetCoveredRegion()
-{
-  gfxMatrix canvasTM = GetCanvasTM();
-  if (canvasTM.PreservesAxisAlignedRectangles()) {
-    return nsSVGUtils::TransformFrameRectToOuterSVG(
-             mRect, canvasTM, PresContext());
-  }
-
-  // To get tight bounds we need to compute directly in outer SVG coordinates
-  uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
-                   nsSVGUtils::eBBoxIncludeStroke |
-                   nsSVGUtils::eBBoxIncludeMarkers;
-  gfxRect extent =
-    GetBBoxContribution(ToMatrix(canvasTM), flags).ToThebesRect();
-  nsRect region = nsLayoutUtils::RoundGfxRectToAppRect(
-                    extent, PresContext()->AppUnitsPerCSSPixel());
-
-  return nsSVGUtils::TransformFrameRectToOuterSVG(
-                       region, gfxMatrix(), PresContext());
-}
-
 void
 SVGGeometryFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probably a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
@@ -504,16 +482,23 @@ SVGGeometryFrame::GetBBoxContribution(co
 {
   SVGBBox bbox;
 
   if (aToBBoxUserspace.IsSingular()) {
     // XXX ReportToConsole
     return bbox;
   }
 
+  if ((aFlags & nsSVGUtils::eForGetClientRects) &&
+      aToBBoxUserspace.PreservesAxisAlignedRectangles()) {
+    Rect rect = NSRectToRect(mRect, PresContext()->AppUnitsPerCSSPixel());
+    bbox = aToBBoxUserspace.TransformBounds(rect);
+    return bbox;
+  }
+
   SVGGeometryElement* element =
     static_cast<SVGGeometryElement*>(mContent);
 
   bool getFill = (aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
                  ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
                   StyleSVG()->mFill.Type() != eStyleSVGPaintType_None);
 
   bool getStroke = (aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
--- a/layout/svg/SVGGeometryFrame.h
+++ b/layout/svg/SVGGeometryFrame.h
@@ -100,17 +100,16 @@ public:
   // SVGGeometryFrame methods
   gfxMatrix GetCanvasTM();
 protected:
   // nsSVGDisplayableFrame interface:
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
-  virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return false; }
 
   /**
    * This function returns a set of bit flags indicating which parts of the
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3755,23 +3755,16 @@ SVGTextFrame::GetFrameForPoint(const gfx
 
     if (Inside(frameRect, pointInRunUserSpace)) {
       hit = run.mFrame;
     }
   }
   return hit;
 }
 
-nsRect
-SVGTextFrame::GetCoveredRegion()
-{
-  return nsSVGUtils::TransformFrameRectToOuterSVG(
-           mRect, GetCanvasTM(), PresContext());
-}
-
 void
 SVGTextFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probaby a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
@@ -3875,16 +3868,25 @@ TextRenderedRunFlagsForBBoxContribution(
 }
 
 SVGBBox
 SVGTextFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
                                   uint32_t aFlags)
 {
   NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame");
   SVGBBox bbox;
+
+  if (aFlags & nsSVGUtils::eForGetClientRects) {
+    Rect rect = NSRectToRect(mRect, PresContext()->AppUnitsPerCSSPixel());
+    if (!rect.IsEmpty()) {
+      bbox = aToBBoxUserspace.TransformBounds(rect);
+    }
+    return bbox;
+  }
+
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (kid && NS_SUBTREE_DIRTY(kid)) {
     // Return an empty bbox if our kid's subtree is dirty. This may be called
     // in that situation, e.g. when we're building a display list after an
     // interrupted reflow. This can also be called during reflow before we've
     // been reflowed, e.g. if an earlier sibling is calling FinishAndStoreOverflow and
     // needs our parent's perspective matrix, which depends on the SVG bbox
     // contribution of this frame. In the latter situation, when all siblings have
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -254,17 +254,16 @@ public:
 
   // nsSVGDisplayableFrame interface:
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
-  virtual nsRect GetCoveredRegion() override;
   virtual SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
   
   // SVG DOM text methods:
   uint32_t GetNumberOfChars(nsIContent* aContent);
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -306,22 +306,16 @@ nsSVGDisplayContainerFrame::GetFrameForP
 {
   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only hit-testing of a "
                "clipPath's contents should take this code path");
   return nsSVGUtils::HitTestChildren(this, aPoint);
 }
 
-nsRect
-nsSVGDisplayContainerFrame::GetCoveredRegion()
-{
-  return nsSVGUtils::GetCoveredRegion(mFrames);
-}
-
 void
 nsSVGDisplayContainerFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probably a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
--- a/layout/svg/nsSVGContainerFrame.h
+++ b/layout/svg/nsSVGContainerFrame.h
@@ -139,17 +139,16 @@ public:
   virtual bool IsSVGTransformed(Matrix *aOwnTransform = nullptr,
                                 Matrix *aFromParentTransform = nullptr) const override;
 
   // nsSVGDisplayableFrame interface:
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect *aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
-  virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return true; }
 };
 
 #endif
--- a/layout/svg/nsSVGDisplayableFrame.h
+++ b/layout/svg/nsSVGDisplayableFrame.h
@@ -89,19 +89,16 @@ public:
   /**
    * Returns the frame that should handle pointer events at aPoint.  aPoint is
    * expected to be in the SVG user space of the frame on which this method is
    * called.  The frame returned may be the frame on which this method is
    * called, any of its descendants or else nullptr.
    */
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) = 0;
 
-  // Get bounds in our nsSVGOuterSVGFrame's coordinates space (in app units)
-  virtual nsRect GetCoveredRegion()=0;
-
   // Called on SVG child frames (except NS_FRAME_IS_NONDISPLAY frames)
   // to update and then invalidate their cached bounds. This method is not
   // called until after the nsSVGOuterSVGFrame has had its initial reflow
   // (i.e. once the SVG viewport dimensions are known). It should also only
   // be called by nsSVGOuterSVGFrame during its reflow.
   virtual void ReflowSVG()=0;
 
   // Flags to pass to NotifySVGChange:
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -318,30 +318,16 @@ nsSVGForeignObjectFrame::GetFrameForPoin
   // viewport that's established by the foreignObject element:
 
   gfxPoint pt = (aPoint + gfxPoint(x, y)) * nsPresContext::AppUnitsPerCSSPixel();
   nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
 
   return nsLayoutUtils::GetFrameForPoint(kid, point);
 }
 
-nsRect
-nsSVGForeignObjectFrame::GetCoveredRegion()
-{
-  float x, y, w, h;
-  static_cast<SVGForeignObjectElement*>(mContent)->
-    GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
-  if (w < 0.0f) w = 0.0f;
-  if (h < 0.0f) h = 0.0f;
-  // GetCanvasTM includes the x,y translation
-  return nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h),
-                                    GetCanvasTM(),
-                                    PresContext());
-}
-
 void
 nsSVGForeignObjectFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probably a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
--- a/layout/svg/nsSVGForeignObjectFrame.h
+++ b/layout/svg/nsSVGForeignObjectFrame.h
@@ -73,17 +73,16 @@ public:
   }
 #endif
 
   // nsSVGDisplayableFrame interface:
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
-  virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return true; }
 
   gfxMatrix GetCanvasTM();
 
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -83,35 +83,16 @@ nsSVGInnerSVGFrame::PaintSVG(gfxContext&
     gfxRect clipRect =
       nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
     nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
   }
 
   return nsSVGDisplayContainerFrame::PaintSVG(aContext, aTransform, aDirtyRect);
 }
 
-nsRect
-nsSVGInnerSVGFrame::GetCoveredRegion()
-{
-  float x, y, w, h;
-  static_cast<SVGSVGElement*>(mContent)->
-    GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
-  if (w < 0.0f) w = 0.0f;
-  if (h < 0.0f) h = 0.0f;
-  // GetCanvasTM includes the x,y translation
-  nsRect bounds = nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h),
-                                             GetCanvasTM(),
-                                             PresContext());
-
-  if (!StyleDisplay()->IsScrollableOverflow()) {
-    bounds.UnionRect(bounds, nsSVGUtils::GetCoveredRegion(mFrames));
-  }
-  return bounds;
-}
-
 void
 nsSVGInnerSVGFrame::ReflowSVG()
 {
   // mRect must be set before FinishAndStoreOverflow is called in order
   // for our overflow areas to be clipped correctly.
   float x, y, width, height;
   static_cast<SVGSVGElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
@@ -180,16 +161,55 @@ nsSVGInnerSVGFrame::NotifySVGChanged(uin
   if (aFlags & TRANSFORM_CHANGED) {
     // make sure our cached transform matrix gets (lazily) updated
     mCanvasTM = nullptr;
   }
 
   nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
 }
 
+SVGBBox
+nsSVGInnerSVGFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
+                                        uint32_t aFlags)
+{
+  // XXXjwatt It seems like authors would want the result to be clipped by the
+  // viewport we establish if IsScrollableOverflow() is true.  We should
+  // consider doing that.  See bug 1350755.
+
+  SVGBBox bbox;
+
+  if (aFlags & nsSVGUtils::eForGetClientRects) {
+    // XXXjwatt For consistency with the old code this code includes the
+    // viewport we establish in the result, but only includes the bounds of our
+    // descendants if they are not clipped to that viewport.  However, this is
+    // both inconsistent with Chrome and with the specs.  See bug 1350755.
+    // Ideally getClientRects/getBoundingClientRect should be consistent with
+    // getBBox.
+    float x, y, w, h;
+    static_cast<SVGSVGElement*>(mContent)->
+      GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
+    if (w < 0.0f) w = 0.0f;
+    if (h < 0.0f) h = 0.0f;
+    Rect viewport(x, y, w, h);
+    bbox = aToBBoxUserspace.TransformBounds(viewport);
+    if (StyleDisplay()->IsScrollableOverflow()) {
+      return bbox;
+    }
+    // Else we're not clipping to our viewport so we fall through and include
+    // the bounds of our children.
+  }
+
+  SVGBBox descendantsBbox =
+    nsSVGDisplayContainerFrame::GetBBoxContribution(aToBBoxUserspace, aFlags);
+
+  bbox.UnionEdges(descendantsBbox);
+
+  return bbox;
+}
+
 nsresult
 nsSVGInnerSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t  aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
 
--- a/layout/svg/nsSVGInnerSVGFrame.h
+++ b/layout/svg/nsSVGInnerSVGFrame.h
@@ -50,19 +50,20 @@ public:
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType) override;
 
   // nsSVGDisplayableFrame interface:
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect *aDirtyRect = nullptr) override;
-  virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
+  SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
+                              uint32_t aFlags) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
 
   virtual bool HasChildrenOnlyTransform(Matrix *aTransform) const override;
 
   // nsISVGSVGFrame interface:
--- a/layout/svg/nsSVGSwitchFrame.cpp
+++ b/layout/svg/nsSVGSwitchFrame.cpp
@@ -48,17 +48,16 @@ public:
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   // nsSVGDisplayableFrame interface:
   virtual DrawResult PaintSVG(gfxContext& aContext,
                               const gfxMatrix& aTransform,
                               const nsIntRect* aDirtyRect = nullptr) override;
   nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
-  nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
 private:
   nsIFrame *GetActiveChildFrame();
 };
 
@@ -155,29 +154,16 @@ nsSVGSwitchFrame::GetFrameForPoint(const
       point = m.Transform(point);
     }
     return svgFrame->GetFrameForPoint(point);
   }
 
   return nullptr;
 }
 
-nsRect
-nsSVGSwitchFrame::GetCoveredRegion()
-{
-  nsRect rect;
-
-  nsIFrame *kid = GetActiveChildFrame();
-  nsSVGDisplayableFrame* child = do_QueryFrame(kid);
-  if (child) {
-    rect = child->GetCoveredRegion();
-  }
-  return rect;
-}
-
 void
 nsSVGSwitchFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probably a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -380,20 +380,33 @@ nsSVGUtils::GetOuterSVGFrameAndCoveredRe
 {
   nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
   if (!svg)
     return nullptr;
   nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
   if (outer == svg) {
     return nullptr;
   }
-  nsMargin bp = outer->GetUsedBorderAndPadding();
-  *aRect = ((aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
-             nsRect(0, 0, 0, 0) : svg->GetCoveredRegion()) +
-                 nsPoint(bp.left, bp.top);
+
+  if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
+    *aRect = nsRect(0, 0, 0, 0);
+  } else {
+    uint32_t flags = nsSVGUtils::eForGetClientRects |
+                     nsSVGUtils::eBBoxIncludeFill |
+                     nsSVGUtils::eBBoxIncludeStroke |
+                     nsSVGUtils::eBBoxIncludeMarkers;
+    gfxMatrix m = nsSVGUtils::GetUserToCanvasTM(aFrame);
+    SVGBBox bbox = nsSVGUtils::GetBBox(aFrame, flags, &m);
+    nsRect bounds =
+      nsLayoutUtils::RoundGfxRectToAppRect(bbox,
+                       aFrame->PresContext()->AppUnitsPerDevPixel());
+    nsMargin bp = outer->GetUsedBorderAndPadding();
+    *aRect = bounds + nsPoint(bp.left, bp.top);
+  }
+
   return outer;
 }
 
 gfxMatrix
 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
 {
   // XXX yuck, we really need a common interface for GetCanvasTM
 
@@ -841,17 +854,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
 
   /* Paint the child */
   if (effectProperties.HasValidFilter()) {
     nsRegion* dirtyRegion = nullptr;
     nsRegion tmpDirtyRegion;
     if (aDirtyRect) {
       // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
       // it in frame space.
-      gfxMatrix userToDeviceSpace = GetUserToCanvasTM(aFrame);
+      gfxMatrix userToDeviceSpace = aTransform;
       if (userToDeviceSpace.IsSingular()) {
         return DrawResult::SUCCESS;
       }
       gfxMatrix deviceToUserSpace = userToDeviceSpace;
       deviceToUserSpace.Invert();
       gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
                               gfxRect(aDirtyRect->x, aDirtyRect->y,
                                       aDirtyRect->width, aDirtyRect->height));
@@ -975,34 +988,16 @@ nsSVGUtils::HitTestChildren(nsSVGDisplay
 
   if (result && !HitTestClip(aFrame, aPoint))
     result = nullptr;
 
   return result;
 }
 
 nsRect
-nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
-{
-  nsRect rect;
-
-  for (nsIFrame* kid = aFrames.FirstChild();
-       kid;
-       kid = kid->GetNextSibling()) {
-    nsSVGDisplayableFrame* child = do_QueryFrame(kid);
-    if (child) {
-      nsRect childRect = child->GetCoveredRegion();
-      rect.UnionRect(rect, childRect);
-    }
-  }
-
-  return rect;
-}
-
-nsRect
 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
                                          const gfxMatrix& aMatrix,
                                          nsPresContext* aPresContext)
 {
   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
   return nsLayoutUtils::RoundGfxRectToAppRect(
     aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
@@ -1095,17 +1090,18 @@ nsSVGUtils::SetClipRect(gfxContext *aCon
     return;
 
   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
   aContext->Multiply(aCTM);
   aContext->Clip(aRect);
 }
 
 gfxRect
-nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
+nsSVGUtils::GetBBox(nsIFrame* aFrame, uint32_t aFlags,
+                    const gfxMatrix* aToBoundsSpace)
 {
   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
     aFrame = aFrame->GetParent();
   }
 
   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
     // It is possible to apply a gradient, pattern, clipping path, mask or
     // filter to text. When one of these facilities is applied to text
@@ -1142,24 +1138,30 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, ui
   nsIContent* content = aFrame->GetContent();
   if (content->IsSVGElement() &&
       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
     return gfxRect();
   }
 
   FrameProperties props = aFrame->Properties();
 
-  if (aFlags == eBBoxIncludeFillGeometry) {
+  if (aFlags == eBBoxIncludeFillGeometry &&
+      // We only cache bbox in element's own user space
+      !aToBoundsSpace) {
     gfxRect* prop = props.Get(ObjectBoundingBoxProperty());
     if (prop) {
       return *prop;
     }
   }
 
   gfxMatrix matrix;
+  if (aToBoundsSpace) {
+    matrix = *aToBoundsSpace;
+  }
+
   if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
     // The spec says getBBox "Returns the tight bounding box in *current user
     // space*". So we should really be doing this for all elements, but that
     // needs investigation to check that we won't break too much content.
     // NOTE: When changing this to apply to other frame types, make sure to
     // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
     MOZ_ASSERT(content->IsSVGElement(), "bad cast");
     nsSVGElement *element = static_cast<nsSVGElement*>(content);
@@ -1214,17 +1216,19 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, ui
       }
 
       if (bbox.IsEmpty()) {
         bbox = gfxRect(0, 0, 0, 0);
       }
     }
   }
 
-  if (aFlags == eBBoxIncludeFillGeometry) {
+  if (aFlags == eBBoxIncludeFillGeometry &&
+      // We only cache bbox in element's own user space
+      !aToBoundsSpace) {
     // Obtaining the bbox for objectBoundingBox calculations is common so we
     // cache the result for future calls, since calculation can be expensive:
     props.Set(ObjectBoundingBoxProperty(), new gfxRect(bbox));
   }
 
   return bbox;
 }
 
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -87,16 +87,20 @@ public:
     : mIsEmpty(true) {}
 
   MOZ_IMPLICIT SVGBBox(const Rect& aRect)
     : mBBox(aRect), mIsEmpty(false) {}
 
   MOZ_IMPLICIT SVGBBox(const gfxRect& aRect)
     : mBBox(ToRect(aRect)), mIsEmpty(false) {}
 
+  operator const Rect& () {
+    return mBBox;
+  }
+
   gfxRect ToThebesRect() const {
     return ThebesRect(mBBox);
   }
 
   bool IsEmpty() const {
     return mIsEmpty;
   }
 
@@ -322,21 +326,16 @@ public:
   static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame);
 
   /**
    * Notify the descendants of aFrame of a change to one of their ancestors
    * that might affect them.
    */
   static void NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags);
 
-  /*
-   * Get frame's covered region by walking the children and doing union.
-   */
-  static nsRect GetCoveredRegion(const nsFrameList &aFrames);
-
   static nsRect TransformFrameRectToOuterSVG(const nsRect& aRect,
                                              const gfxMatrix& aMatrix,
                                              nsPresContext* aPresContext);
 
   /*
    * Convert a surface size to an integer for use by thebes
    * possibly making it smaller in the process so the surface does not
    * use excessive memory.
@@ -395,26 +394,42 @@ public:
     eBBoxIncludeFillGeometry   = 1 << 1,
     eBBoxIncludeStroke         = 1 << 2,
     eBBoxIncludeStrokeGeometry = 1 << 3,
     eBBoxIncludeMarkers        = 1 << 4,
     eBBoxIncludeClipped        = 1 << 5,
     // Normally a getBBox call on outer-<svg> should only return the
     // bounds of the elements children.  This flag will cause the
     // element's bounds to be returned instead.
-    eUseFrameBoundsForOuterSVG = 1 << 6
+    eUseFrameBoundsForOuterSVG = 1 << 6,
+    // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
+    eForGetClientRects         = 1 << 7,
   };
   /**
-   * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in
-   * aFrame's userspace.
+   * This function in primarily for implementing the SVG DOM function getBBox()
+   * and the SVG attribute value 'objectBoundingBox'.  However, it has been
+   * extended with various extra parameters in order to become more of a
+   * general purpose getter of all sorts of bounds that we might need to obtain
+   * for SVG elements, or even for other elements that have SVG effects applied
+   * to them.
+   *
+   * @param aFrame The frame of the element for which the bounds are to be
+   *   obtained.
+   * @param aFlags One or more of the BBoxFlags values defined above.
+   * @param aToBoundsSpace If not specified the returned rect is in aFrame's
+   *   element's "user space".  A matrix can optionally be pass to specify a
+   *   transform from aFrame's user space to the bounds space of interest
+   *   (typically this will be the ancestor nsSVGOuterSVGFrame, but it could be
+   *   to any other coordinate space).
    */
   static gfxRect GetBBox(nsIFrame *aFrame,
                          // If the default arg changes, update the handling for
                          // ObjectBoundingBoxProperty() in the implementation.
-                         uint32_t aFlags = eBBoxIncludeFillGeometry);
+                         uint32_t aFlags = eBBoxIncludeFillGeometry,
+                         const gfxMatrix* aToBoundsSpace = nullptr);
 
   /*
    * "User space" is the space that the frame's BBox (as calculated by
    * nsSVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
    * at the top left of the union of the frame's border-box rects over all
    * continuations.
    * This function returns the offset one needs to add to something in frame
    * space in order to get its coordinates in user space.
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -92,20 +92,20 @@ nsImageBoxFrameEvent::Run()
 
   event.mFlags.mBubbles = false;
   EventDispatcher::Dispatch(mContent, pres_context, &event, nullptr, &status);
   return NS_OK;
 }
 
 // Fire off an event that'll asynchronously call the image elements
 // onload handler once handled. This is needed since the image library
-// can't decide if it wants to call it's observer methods
+// can't decide if it wants to call its observer methods
 // synchronously or asynchronously. If an image is loaded from the
 // cache the notifications come back synchronously, but if the image
-// is loaded from the netswork the notifications come back
+// is loaded from the network the notifications come back
 // asynchronously.
 
 void
 FireImageDOMEvent(nsIContent* aContent, EventMessage aMessage)
 {
   NS_ASSERTION(aMessage == eLoad || aMessage == eLoadError,
                "invalid message");
 
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/send-network-error-sync-events.sub.htm.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[send-network-error-sync-events.sub.htm]
-  type: testharness
-  [XmlHttpRequest: The send() method: Throw a "throw an "NetworkError" exception when Network error happens (synchronous flag is set)]
-    expected: FAIL
-
-  [XMLHttpRequest: The send() method: Throw a "throw an "NetworkError" exception when Network error happens (synchronous flag is set)]
-    expected: FAIL
-
--- a/testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
+++ b/testing/web-platform/meta/pointerevents/extension/idlharness.html.ini
@@ -1,75 +1,7 @@
 [idlharness.html]
   type: testharness
-  [PointerEvent interface: existence and properties of interface object]
-    expected:
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PointerEvent interface object length]
-    expected:
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PointerEvent interface object name]
-    expected:
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PointerEvent interface: existence and properties of interface prototype object]
-    expected:
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [PointerEvent interface: existence and properties of interface prototype object's "constructor" property]
-    expected:
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if not debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL
-      if debug and not e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
+  prefs: [dom.w3c_pointer_events.enabled:true]
 
   [PointerEvent interface: operation getCoalescedEvents()]
     expected: FAIL
 
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1480,18 +1480,18 @@ MINIDUMP_TYPE GetMinidumpType()
       WORD major = HIWORD(file_info->dwFileVersionMS),
         minor = LOWORD(file_info->dwFileVersionMS),
         revision = HIWORD(file_info->dwFileVersionLS);
       if (major > 6 || (major == 6 && minor > 1) ||
           (major == 6 && minor == 1 && revision >= 7600)) {
         minidump_type = MiniDumpWithFullMemoryInfo;
       }
 #ifdef NIGHTLY_BUILD
-      // TODO: Remove the NIGHTLY_BUILD wrapping if the increased size is
-      // accetable.
+      // This is Nightly only because this doubles the size of minidumps based
+      // on the experimental data.
       if (major > 5 || (major == 5 && minor > 1)) {
         minidump_type = static_cast<MINIDUMP_TYPE>(minidump_type |
             MiniDumpWithUnloadedModules |
             MiniDumpWithProcessThreadData);
       }
 #endif
     }
   }
--- a/toolkit/mozapps/extensions/AddonPathService.cpp
+++ b/toolkit/mozapps/extensions/AddonPathService.cpp
@@ -166,16 +166,38 @@ ResolveURI(nsIURI* aURI, nsAString& out)
     rv = irph->ResolveURI(aURI, spec);
     if (NS_WARN_IF(NS_FAILED(rv)))
       return rv;
 
     rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
     if (NS_WARN_IF(NS_FAILED(rv)))
       return rv;
   } else if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &equals)) && equals) {
+    // Going through the Chrome Registry may be prohibitively slow for many of
+    // the well-known chrome:// URI packages, so check for a few of them here
+    // first in order to fail early if we don't have a chrome:// URI which
+    // could have been provided by an add-on.
+    nsAutoCString package;
+    rv = aURI->GetHostPort(package);
+    if (NS_WARN_IF(NS_FAILED(rv)) ||
+        package.EqualsLiteral("branding") ||
+        package.EqualsLiteral("browser") ||
+        package.EqualsLiteral("branding") ||
+        package.EqualsLiteral("global") ||
+        package.EqualsLiteral("global-platform") ||
+        package.EqualsLiteral("mozapps") ||
+        package.EqualsLiteral("necko") ||
+        package.EqualsLiteral("passwordmgr") ||
+        package.EqualsLiteral("pippki") ||
+        package.EqualsLiteral("pipnss")) {
+      // Returning a failure code means the URI isn't associated with an add-on
+      // ID.
+      return NS_ERROR_FAILURE;
+    }
+
     nsCOMPtr<nsIChromeRegistry> chromeReg =
       mozilla::services::GetChromeRegistryService();
     if (NS_WARN_IF(!chromeReg))
       return NS_ERROR_UNEXPECTED;
 
     rv = chromeReg->ConvertChromeURL(aURI, getter_AddRefs(uri));
     if (NS_WARN_IF(NS_FAILED(rv)))
       return rv;
rename from toolkit/themes/windows/global/checkbox/cbox-check-dis.gif
rename to toolkit/themes/faststripe/global/checkbox/cbox-check-dis.gif
rename from toolkit/themes/windows/global/checkbox/cbox-check.gif
rename to toolkit/themes/faststripe/global/checkbox/cbox-check.gif
--- a/toolkit/themes/faststripe/global/jar.mn
+++ b/toolkit/themes/faststripe/global/jar.mn
@@ -11,8 +11,10 @@ toolkit.jar:
    skin/classic/global/groupbox.css
    skin/classic/global/menu.css
    skin/classic/global/menulist.css
    skin/classic/global/popup.css
    skin/classic/global/radio.css
    skin/classic/global/tabbox.css
    skin/classic/global/textbox.css
    skin/classic/global/scrollbars.css                       (xulscrollbars.css)
+   skin/classic/global/checkbox/cbox-check.gif              (checkbox/cbox-check.gif)
+   skin/classic/global/checkbox/cbox-check-dis.gif          (checkbox/cbox-check-dis.gif)
--- a/toolkit/themes/shared/non-mac.jar.inc.mn
+++ b/toolkit/themes/shared/non-mac.jar.inc.mn
@@ -46,18 +46,16 @@
   skin/classic/global/arrow/arrow-up-dis.gif               (../../windows/global/arrow/arrow-up-dis.gif)
   skin/classic/global/arrow/arrow-up-hov.gif               (../../windows/global/arrow/arrow-up-hov.gif)
   skin/classic/global/arrow/arrow-up-sharp.gif             (../../windows/global/arrow/arrow-up-sharp.gif)
   skin/classic/global/arrow/panelarrow-horizontal.svg      (../../windows/global/arrow/panelarrow-horizontal.svg)
   skin/classic/global/arrow/panelarrow-vertical.svg        (../../windows/global/arrow/panelarrow-vertical.svg)
   skin/classic/global/arrow/panelarrow-horizontal-themed.svg (../../windows/global/arrow/panelarrow-horizontal-themed.svg)
   skin/classic/global/arrow/panelarrow-vertical-themed.svg   (../../windows/global/arrow/panelarrow-vertical-themed.svg)
 
-  skin/classic/global/checkbox/cbox-check.gif              (../../windows/global/checkbox/cbox-check.gif)
-  skin/classic/global/checkbox/cbox-check-dis.gif          (../../windows/global/checkbox/cbox-check-dis.gif)
 * skin/classic/global/dirListing/dirListing.css            (../../windows/global/dirListing/dirListing.css)
   skin/classic/global/dirListing/folder.png                (../../windows/global/dirListing/folder.png)
   skin/classic/global/dirListing/local.png                 (../../windows/global/dirListing/local.png)
   skin/classic/global/dirListing/up.png                    (../../windows/global/dirListing/up.png)
   skin/classic/global/icons/Close.gif                      (../../windows/global/icons/Close.gif)
   skin/classic/global/icons/close.png                      (../../windows/global/icons/close.png)
   skin/classic/global/icons/close@2x.png                   (../../windows/global/icons/close@2x.png)
   skin/classic/global/icons/close-inverted.png             (../../windows/global/icons/close-inverted.png)
--- a/toolkit/themes/windows/global/checkbox.css
+++ b/toolkit/themes/windows/global/checkbox.css
@@ -31,51 +31,26 @@ checkbox {
 /* ..... focused state ..... */
 
 checkbox:-moz-focusring > .checkbox-label-box {
   outline: 1px dotted;
 }
 
 /* ..... disabled state ..... */
 
-checkbox[disabled="true"] > .checkbox-check {
-  background-color: -moz-Dialog;
-}
-
 checkbox[disabled="true"] {
   color: GrayText;
 }
 
 checkbox[disabled="true"]:-moz-system-metric(windows-classic) {
   color: ThreeDShadow;
   text-shadow: 1px 1px ThreeDHighlight;
 }
 
 /* ::::: checkmark image ::::: */
 
 .checkbox-check {
   -moz-appearance: checkbox;
   -moz-box-align: center;
-  border: 2px solid;
-  -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
-  -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
-  -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
-  -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
   min-width: 13px;
   min-height: 13px;
-  background: -moz-Field no-repeat 50% 50%;
   margin-inline-end: 3px;
 }
-
-checkbox:hover:active > .checkbox-check {
-  background-color: -moz-Dialog;
-}
-
-/* ..... checked state ..... */
-
-checkbox[checked="true"] > .checkbox-check {
-  background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
-}
-
-checkbox[checked="true"][disabled="true"] > .checkbox-check {
-  background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif") !important
-}
-
--- a/tools/profiler/core/platform-linux-android.cpp
+++ b/tools/profiler/core/platform-linux-android.cpp
@@ -60,47 +60,44 @@
 #include <errno.h>
 #include <stdarg.h>
 
 #include "prenv.h"
 #include "mozilla/LinuxSignal.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/DebugOnly.h"
 
-// Memory profile
-#include "nsMemoryReporterManager.h"
-
 #include <string.h>
 #include <list>
 
 using namespace mozilla;
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return gettid();
 }
 
 static void
 SetSampleContext(TickSample* sample, mcontext_t& mcontext)
 {
   // Extracting the sample from the context is extremely machine dependent.
 #if defined(GP_ARCH_x86)
-  sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
-  sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
-  sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
+  sample->mPC = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+  sample->mSP = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
+  sample->mFP = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
 #elif defined(GP_ARCH_amd64)
-  sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
-  sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
-  sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
+  sample->mPC = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
+  sample->mSP = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
+  sample->mFP = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
 #elif defined(GP_ARCH_arm)
-  sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
-  sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
-  sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
-  sample->lr = reinterpret_cast<Address>(mcontext.arm_lr);
+  sample->mPC = reinterpret_cast<Address>(mcontext.arm_pc);
+  sample->mSP = reinterpret_cast<Address>(mcontext.arm_sp);
+  sample->mFP = reinterpret_cast<Address>(mcontext.arm_fp);
+  sample->mLR = reinterpret_cast<Address>(mcontext.arm_lr);
 #else
 # error "bad platform"
 #endif
 }
 
 #if defined(GP_OS_android)
 # define SYS_tgkill __NR_tgkill
 #endif
@@ -351,44 +348,27 @@ SamplerThread::Stop(PS::LockRef aLock)
   // we do it now, while gPSMutex is locked. It's safe to do this now even
   // though this SamplerThread is still alive, because the next time the main
   // loop of Run() iterates it won't get past the mActivityGeneration check,
   // and so won't send any signals.
   sigaction(SIGPROF, &mOldSigprofHandler, 0);
 }
 
 void
-SamplerThread::SuspendAndSampleAndResumeThread(
-  PS::LockRef aLock, ThreadInfo* aThreadInfo, bool aIsFirstProfiledThread)
+SamplerThread::SuspendAndSampleAndResumeThread(PS::LockRef aLock,
+                                               TickSample* aSample)
 {
   // Only one sampler thread can be sampling at once.  So we expect to have
   // complete control over |sSigHandlerCoordinator|.
   MOZ_ASSERT(!sSigHandlerCoordinator);
 
-  int sampleeTid = aThreadInfo->ThreadId();
+  int sampleeTid = aSample->mThreadInfo->ThreadId();
   MOZ_RELEASE_ASSERT(sampleeTid != mSamplerTid);
 
   //----------------------------------------------------------------//
-  // Collect auxiliary information whilst the samplee thread is still
-  // running.
-
-  int64_t rssMemory = 0;
-  int64_t ussMemory = 0;
-  if (aIsFirstProfiledThread && gPS->FeatureMemory(aLock)) {
-    rssMemory = nsMemoryReporterManager::ResidentFast();
-    ussMemory = nsMemoryReporterManager::ResidentUnique();
-  }
-
-  TickSample sample;
-  sample.threadInfo = aThreadInfo;
-  sample.timestamp = mozilla::TimeStamp::Now();
-  sample.rssMemory = rssMemory;
-  sample.ussMemory = ussMemory;
-
-  //----------------------------------------------------------------//
   // Suspend the samplee thread and get its context.
 
   SigHandlerCoordinator coord;   // on sampler thread's stack
   sSigHandlerCoordinator = &coord;
 
   // Send message 1 to the samplee (the thread to be sampled), by
   // signalling at it.
   int r = tgkill(mMyPid, sampleeTid, SIGPROF);
@@ -418,23 +398,23 @@ SamplerThread::SuspendAndSampleAndResume
   // sampled has been suspended at some entirely arbitrary point, and we have
   // no idea which unsharable resources (locks, essentially) it holds.  So any
   // attempt to acquire any lock, including the implied locks used by the
   // malloc implementation, risks deadlock.
 
   // The samplee thread is now frozen and sSigHandlerCoordinator->mUContext is
   // valid.  We can poke around in it and unwind its stack as we like.
 
-  sample.context = &sSigHandlerCoordinator->mUContext;
+  aSample->mContext = &sSigHandlerCoordinator->mUContext;
 
   // Extract the current pc and sp.
-  SetSampleContext(&sample,
+  SetSampleContext(aSample,
                    sSigHandlerCoordinator->mUContext.uc_mcontext);
 
-  Tick(aLock, gPS->Buffer(aLock), &sample);
+  Tick(aLock, gPS->Buffer(aLock), aSample);
 
   //----------------------------------------------------------------//
   // Resume the target thread.
 
   // Send message 3 to the samplee, which tells it to resume.
   r = sem_post(&sSigHandlerCoordinator->mMessage3);
   MOZ_ASSERT(r == 0);
 
@@ -635,19 +615,17 @@ PlatformInit(PS::LockRef aLock)
 {
   // Set up the fork handlers.
   pthread_atfork(paf_prepare, paf_parent, nullptr);
 }
 
 #endif
 
 void
-TickSample::PopulateContext(void* aContext)
+TickSample::PopulateContext(ucontext_t* aContext)
 {
   MOZ_ASSERT(aContext);
-  ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
-  if (!getcontext(pContext)) {
-    context = pContext;
-    SetSampleContext(this,
-                     reinterpret_cast<ucontext_t*>(aContext)->uc_mcontext);
+  if (!getcontext(aContext)) {
+    mContext = aContext;
+    SetSampleContext(this, aContext->uc_mcontext);
   }
 }
 
--- a/tools/profiler/core/platform-macos.cpp
+++ b/tools/profiler/core/platform-macos.cpp
@@ -26,19 +26,16 @@
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <math.h>
 
-// Memory profile
-#include "nsMemoryReporterManager.h"
-
 // this port is based off of v8 svn revision 9837
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return gettid();
 }
 
@@ -125,35 +122,21 @@ SamplerThread::~SamplerThread()
 
 void
 SamplerThread::Stop(PS::LockRef aLock)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 }
 
 void
-SamplerThread::SuspendAndSampleAndResumeThread(
-  PS::LockRef aLock, ThreadInfo* aThreadInfo, bool aIsFirstProfiledThread)
+SamplerThread::SuspendAndSampleAndResumeThread(PS::LockRef aLock,
+                                               TickSample* aSample)
 {
   thread_act_t samplee_thread =
-    aThreadInfo->GetPlatformData()->profiled_thread();
-
-  //----------------------------------------------------------------//
-  // Collect auxiliary information whilst the samplee thread is still
-  // running.
-
-  TickSample sample;
-  sample.threadInfo = aThreadInfo;
-  sample.timestamp = mozilla::TimeStamp::Now();
-
-  // Unique Set Size is not supported on Mac.
-  sample.rssMemory = (aIsFirstProfiledThread && gPS->FeatureMemory(aLock))
-                   ? nsMemoryReporterManager::ResidentFast()
-                   : 0;
-  sample.ussMemory = 0;
+    aSample->mThreadInfo->GetPlatformData()->profiled_thread();
 
   //----------------------------------------------------------------//
   // Suspend the samplee thread and get its context.
 
   // We're using thread_suspend on OS X because pthread_kill (which is what we
   // at one time used on Linux) has less consistent performance and causes
   // strange crashes, see bug 1166778 and bug 1166808.  thread_suspend
   // is also just a lot simpler to use.
@@ -194,21 +177,21 @@ SamplerThread::SuspendAndSampleAndResume
 #else
 # error Unsupported Mac OS X host architecture.
 #endif  // GP_ARCH_*
 
   if (thread_get_state(samplee_thread,
                        flavor,
                        reinterpret_cast<natural_t*>(&state),
                        &count) == KERN_SUCCESS) {
-    sample.pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
-    sample.sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
-    sample.fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
+    aSample->mPC = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
+    aSample->mSP = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
+    aSample->mFP = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
 
-    Tick(aLock, gPS->Buffer(aLock), &sample);
+    Tick(aLock, gPS->Buffer(aLock), aSample);
   }
 
 #undef REGISTER_FIELD
 
   //----------------------------------------------------------------//
   // Resume the target thread.
 
   thread_resume(samplee_thread);
@@ -234,30 +217,30 @@ TickSample::PopulateContext(void* aConte
 #if defined(GP_ARCH_amd64)
   asm (
       // Compute caller's %rsp by adding to %rbp:
       // 8 bytes for previous %rbp, 8 bytes for return address
       "leaq 0x10(%%rbp), %0\n\t"
       // Dereference %rbp to get previous %rbp
       "movq (%%rbp), %1\n\t"
       :
-      "=r"(sp),
-      "=r"(fp)
+      "=r"(mSP),
+      "=r"(mFP)
   );
 #elif defined(GP_ARCH_x86)
   asm (
       // Compute caller's %esp by adding to %ebp:
       // 4 bytes for aContext + 4 bytes for return address +
       // 4 bytes for previous %ebp
       "leal 0xc(%%ebp), %0\n\t"
       // Dereference %ebp to get previous %ebp
       "movl (%%ebp), %1\n\t"
       :
-      "=r"(sp),
-      "=r"(fp)
+      "=r"(mSP),
+      "=r"(mFP)
   );
 #else
 # error "Unsupported architecture"
 #endif
-  pc = reinterpret_cast<Address>(__builtin_extract_return_addr(
+  mPC = reinterpret_cast<Address>(__builtin_extract_return_addr(
                                     __builtin_return_address(0)));
 }
 
--- a/tools/profiler/core/platform-win32.cpp
+++ b/tools/profiler/core/platform-win32.cpp
@@ -27,19 +27,16 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 // SUCH DAMAGE.
 
 #include <windows.h>
 #include <mmsystem.h>
 #include <process.h>
 
-// Memory profile
-#include "nsMemoryReporterManager.h"
-
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return GetCurrentThreadId();
 }
 
 static void
 SleepMicro(int aMicroseconds)
@@ -155,45 +152,29 @@ SamplerThread::Stop(PS::LockRef aLock)
   // the mActivityGeneration check, and so it won't make any more ::Sleep()
   // calls.
   if (mIntervalMicroseconds < 10 * 1000) {
     ::timeEndPeriod(mIntervalMicroseconds / 1000);
   }
 }
 
 void
-SamplerThread::SuspendAndSampleAndResumeThread(
-  PS::LockRef aLock, ThreadInfo* aThreadInfo, bool aIsFirstProfiledThread)
+SamplerThread::SuspendAndSampleAndResumeThread(PS::LockRef aLock,
+                                               TickSample* aSample)
 {
-  uintptr_t thread = GetThreadHandle(aThreadInfo->GetPlatformData());
+  uintptr_t thread = GetThreadHandle(aSample->mThreadInfo->GetPlatformData());
   HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
   if (profiled_thread == nullptr)
     return;
 
   // Context used for sampling the register state of the profiled thread.
   CONTEXT context;
   memset(&context, 0, sizeof(context));
 
   //----------------------------------------------------------------//
-  // Collect auxiliary information whilst the samplee thread is still
-  // running.
-
-  TickSample sample;
-
-  // Grab the timestamp before pausing the thread, to avoid deadlocks.
-  sample.threadInfo = aThreadInfo;
-  sample.timestamp = mozilla::TimeStamp::Now();
-
-  // Unique Set Size is not supported on Windows.
-  sample.rssMemory = (aIsFirstProfiledThread && gPS->FeatureMemory(aLock))
-                   ? nsMemoryReporterManager::ResidentFast()
-                   : 0;
-  sample.ussMemory = 0;
-
-  //----------------------------------------------------------------//
   // Suspend the samplee thread and get its context.
 
   static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
   if (SuspendThread(profiled_thread) == kSuspendFailed) {
     return;
   }
 
   // SuspendThread is asynchronous, so the thread may still be running.
@@ -217,28 +198,28 @@ SamplerThread::SuspendAndSampleAndResume
 
   // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   //
   // The profiler's "critical section" begins here.  We must be very careful
   // what we do here, or risk deadlock.  See the corresponding comment in
   // platform-linux-android.cpp for details.
 
 #if defined(GP_ARCH_amd64)
-  sample.pc = reinterpret_cast<Address>(context.Rip);
-  sample.sp = reinterpret_cast<Address>(context.Rsp);
-  sample.fp = reinterpret_cast<Address>(context.Rbp);
+  aSample->mPC = reinterpret_cast<Address>(context.Rip);
+  aSample->mSP = reinterpret_cast<Address>(context.Rsp);
+  aSample->mFP = reinterpret_cast<Address>(context.Rbp);
 #else
-  sample.pc = reinterpret_cast<Address>(context.Eip);
-  sample.sp = reinterpret_cast<Address>(context.Esp);
-  sample.fp = reinterpret_cast<Address>(context.Ebp);
+  aSample->mPC = reinterpret_cast<Address>(context.Eip);
+  aSample->mSP = reinterpret_cast<Address>(context.Esp);
+  aSample->mFP = reinterpret_cast<Address>(context.Ebp);
 #endif
 
-  sample.context = &context;
+  aSample->mContext = &context;
 
-  Tick(aLock, gPS->Buffer(aLock), &sample);
+  Tick(aLock, gPS->Buffer(aLock), aSample);
 
   //----------------------------------------------------------------//
   // Resume the target thread.
 
   ResumeThread(profiled_thread);
 
   // The profiler's critical section ends here.
   //
@@ -249,26 +230,25 @@ SamplerThread::SuspendAndSampleAndResume
 ////////////////////////////////////////////////////////////////////////
 
 static void
 PlatformInit(PS::LockRef aLock)
 {
 }
 
 void
-TickSample::PopulateContext(void* aContext)
+TickSample::PopulateContext(CONTEXT* aContext)
 {
   MOZ_ASSERT(aContext);
-  CONTEXT* pContext = reinterpret_cast<CONTEXT*>(aContext);
-  context = pContext;
-  RtlCaptureContext(pContext);
+  mContext = aContext;
+  RtlCaptureContext(aContext);
 
 #if defined(GP_ARCH_amd64)
-  pc = reinterpret_cast<Address>(pContext->Rip);
-  sp = reinterpret_cast<Address>(pContext->Rsp);
-  fp = reinterpret_cast<Address>(pContext->Rbp);
+  mPC = reinterpret_cast<Address>(aContext->Rip);
+  mSP = reinterpret_cast<Address>(aContext->Rsp);
+  mFP = reinterpret_cast<Address>(aContext->Rbp);
 #elif defined(GP_ARCH_x86)
-  pc = reinterpret_cast<Address>(pContext->Eip);
-  sp = reinterpret_cast<Address>(pContext->Esp);
-  fp = reinterpret_cast<Address>(pContext->Ebp);
+  mPC = reinterpret_cast<Address>(aContext->Eip);
+  mSP = reinterpret_cast<Address>(aContext->Esp);
+  mFP = reinterpret_cast<Address>(aContext->Ebp);
 #endif
 }
 
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -28,16 +28,17 @@
 #include "ThreadInfo.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIObserverService.h"
 #include "nsIProfileSaveEvent.h"
 #include "nsIXULAppInfo.h"
 #include "nsIXULRuntime.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsMemoryReporterManager.h"
 #include "nsXULAppAPI.h"
 #include "nsProfilerStartParams.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "ProfileGatherer.h"
 #include "ProfilerMarkers.h"
 #include "shared-libraries.h"
 
@@ -51,17 +52,17 @@
 #endif
 
 #if defined(MOZ_PROFILING) && \
     (defined(GP_OS_windows) || defined(GP_OS_darwin))
 # define USE_NS_STACKWALK
 #endif
 
 // This should also work on ARM Linux, but not tested there yet.
-#if defined(GP_arm_android)
+#if defined(GP_PLAT_arm_android)
 # define USE_EHABI_STACKWALK
 # include "EHABIStackWalk.h"
 #endif
 
 #if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux)
 # define USE_LUL_STACKWALK
 # include "lul/LulMain.h"
 # include "lul/platform-linux-lul.h"
@@ -69,20 +70,22 @@
 
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #else
 # define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
 #endif
 
 #if defined(GP_OS_windows)
-typedef CONTEXT tickcontext_t;
+typedef CONTEXT tick_context_t;
+#elif defined(GP_OS_darwin)
+typedef void tick_context_t;   // this type isn't used meaningfully on Mac
 #elif defined(GP_OS_linux) || defined(GP_OS_android)
 #include <ucontext.h>
-typedef ucontext_t tickcontext_t;
+typedef ucontext_t tick_context_t;
 #endif
 
 using namespace mozilla;
 
 mozilla::LazyLogModule gProfilerLog("prof");
 
 #if defined(PROFILE_JAVA)
 class GeckoJavaSampler : public mozilla::java::GeckoJavaSampler::Natives<GeckoJavaSampler>
@@ -360,41 +363,42 @@ CanNotifyObservers()
 }
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN tick/unwinding code
 
 // TickSample captures the information collected for each sample.
 class TickSample {
 public:
-  TickSample()
-    : pc(NULL)
-    , sp(NULL)
-    , fp(NULL)
-    , lr(NULL)
-    , context(NULL)
-    , isSamplingCurrentThread(false)
-    , threadInfo(nullptr)
-    , rssMemory(0)
-    , ussMemory(0)
+  explicit TickSample(ThreadInfo* aThreadInfo)
+    : mPC(nullptr)
+    , mSP(nullptr)
+    , mFP(nullptr)
+    , mLR(nullptr)
+    , mContext(nullptr)
+    , mIsSamplingCurrentThread(false)
+    , mThreadInfo(aThreadInfo)
+    , mTimeStamp(mozilla::TimeStamp::Now())
+    , mRSSMemory(0)
+    , mUSSMemory(0)
   {}
 
-  void PopulateContext(void* aContext);
-
-  Address pc;  // Instruction pointer.
-  Address sp;  // Stack pointer.
-  Address fp;  // Frame pointer.
-  Address lr;  // ARM link register
-  void* context;   // The context from the signal handler, if available. On
-                   // Win32 this may contain the windows thread context.
-  bool isSamplingCurrentThread;
-  ThreadInfo* threadInfo;
-  mozilla::TimeStamp timestamp;
-  int64_t rssMemory;
-  int64_t ussMemory;
+  void PopulateContext(tick_context_t* aContext);
+
+  Address mPC;    // Instruction pointer.
+  Address mSP;    // Stack pointer.
+  Address mFP;    // Frame pointer.
+  Address mLR;    // ARM link register.
+  void* mContext; // The context from the signal handler, if available. On
+                  // Win32 this may contain the windows thread context.
+  bool mIsSamplingCurrentThread;
+  ThreadInfo* mThreadInfo;
+  mozilla::TimeStamp mTimeStamp;
+  int64_t mRSSMemory;
+  int64_t mUSSMemory;
 };
 
 static void
 AddDynamicCodeLocationTag(ProfileBuffer* aBuffer, const char* aStr)
 {
   aBuffer->addTag(ProfileBufferEntry::CodeLocation(""));
 
   size_t strLen = strlen(aStr) + 1;   // +1 for the null terminator
@@ -513,54 +517,54 @@ struct AutoWalkJSStack
     }
   }
 };
 
 static void
 MergeStacksIntoProfile(ProfileBuffer* aBuffer, TickSample* aSample,
                        NativeStack& aNativeStack)
 {
-  NotNull<PseudoStack*> pseudoStack = aSample->threadInfo->Stack();
+  NotNull<PseudoStack*> pseudoStack = aSample->mThreadInfo->Stack();
   volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
   // ProfilingFrameIterator to avoid incorrectly resetting the generation of
   // sampled JIT entries inside the JS engine. See note below concerning 'J'
   // entries.
   uint32_t startBufferGen;
-  startBufferGen = aSample->isSamplingCurrentThread
+  startBufferGen = aSample->mIsSamplingCurrentThread
                  ? UINT32_MAX
                  : aBuffer->mGeneration;
   uint32_t jsCount = 0;
   JS::ProfilingFrameIterator::Frame jsFrames[1000];
 
   // Only walk jit stack if profiling frame iterator is turned on.
   if (pseudoStack->mContext &&
       JS::IsProfilingEnabledForContext(pseudoStack->mContext)) {
     AutoWalkJSStack autoWalkJSStack;
     const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
 
     if (aSample && autoWalkJSStack.walkAllowed) {
       JS::ProfilingFrameIterator::RegisterState registerState;
-      registerState.pc = aSample->pc;
-      registerState.sp = aSample->sp;
-      registerState.lr = aSample->lr;
-      registerState.fp = aSample->fp;
+      registerState.pc = aSample->mPC;
+      registerState.sp = aSample->mSP;
+      registerState.lr = aSample->mLR;
+      registerState.fp = aSample->mFP;
 
       JS::ProfilingFrameIterator jsIter(pseudoStack->mContext,
                                         registerState,
                                         startBufferGen);
       for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
         // See note below regarding 'J' entries.
-        if (aSample->isSamplingCurrentThread || jsIter.isWasm()) {
+        if (aSample->mIsSamplingCurrentThread || jsIter.isWasm()) {
           uint32_t extracted =
             jsIter.extractStack(jsFrames, jsCount, maxFrames);
           jsCount += extracted;
           if (jsCount == maxFrames) {
             break;
           }
         } else {
           mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
@@ -665,17 +669,17 @@ MergeStacksIntoProfile(ProfileBuffer* aB
       // current thread; that is, when called from profiler_get_backtrace. The
       // captured backtrace is usually externally stored for an indeterminate
       // amount of time, such as in nsRefreshDriver. Problematically, the
       // stored backtrace may be alive across a GC during which the profiler
       // itself is disabled. In that case, the JS engine is free to discard its
       // JIT code. This means that if we inserted such OptInfoAddr entries into
       // the buffer, nsRefreshDriver would now be holding on to a backtrace
       // with stale JIT code return addresses.
-      if (aSample->isSamplingCurrentThread ||
+      if (aSample->mIsSamplingCurrentThread ||
           jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
         AddDynamicCodeLocationTag(aBuffer, jsFrame.label);
       } else {
         MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
                    jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
         aBuffer->addTag(
           ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
       }
@@ -695,17 +699,17 @@ MergeStacksIntoProfile(ProfileBuffer* aB
       nativeIndex--;
     }
   }
 
   // Update the JS context with the current profile sample buffer generation.
   //
   // Do not do this for synchronous sampling, which create their own
   // ProfileBuffers.
-  if (!aSample->isSamplingCurrentThread && pseudoStack->mContext) {
+  if (!aSample->mIsSamplingCurrentThread && pseudoStack->mContext) {
     MOZ_ASSERT(aBuffer->mGeneration >= startBufferGen);
     uint32_t lapCount = aBuffer->mGeneration - startBufferGen;
     JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
                                                aBuffer->mGeneration,
                                                lapCount);
   }
 }
 
@@ -736,31 +740,31 @@ DoNativeBacktrace(PS::LockRef aLock, Pro
     mozilla::ArrayLength(pc_array),
     0
   };
 
   // Start with the current function. We use 0 as the frame number here because
   // the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
   // This is a bit weird but it doesn't matter because StackWalkCallback()
   // doesn't use the frame number argument.
-  StackWalkCallback(/* frameNum */ 0, aSample->pc, aSample->sp, &nativeStack);
+  StackWalkCallback(/* frameNum */ 0, aSample->mPC, aSample->mSP, &nativeStack);
 
   uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
 
 #if defined(GP_OS_darwin) || (defined(GP_PLAT_x86_windows))
-  void* stackEnd = aSample->threadInfo->StackTop();
-  if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd) {
+  void* stackEnd = aSample->mThreadInfo->StackTop();
+  if (aSample->mFP >= aSample->mSP && aSample->mFP <= stackEnd) {
     FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
-                          &nativeStack, reinterpret_cast<void**>(aSample->fp),
+                          &nativeStack, reinterpret_cast<void**>(aSample->mFP),
                           stackEnd);
   }
 #else
   // Win64 always omits frame pointers so for it we use the slower
   // MozStackWalk().
-  uintptr_t thread = GetThreadHandle(aSample->threadInfo->GetPlatformData());
+  uintptr_t thread = GetThreadHandle(aSample->mThreadInfo->GetPlatformData());
   MOZ_ASSERT(thread);
   MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack,
                thread, /* platformData */ nullptr);
 #endif
 
   MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 }
 #endif
@@ -775,21 +779,19 @@ DoNativeBacktrace(PS::LockRef aLock, Pro
   NativeStack nativeStack = {
     pc_array,
     sp_array,
     mozilla::ArrayLength(pc_array),
     0
   };
 
   const mcontext_t* mcontext =
-    &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
+    &reinterpret_cast<ucontext_t*>(aSample->mContext)->uc_mcontext;
   mcontext_t savedContext;
-  NotNull<PseudoStack*> pseudoStack = aInfo.Stack();
-
-  nativeStack.count = 0;
+  NotNull<PseudoStack*> pseudoStack = aSample->mThreadInfo->Stack();
 
   // The pseudostack contains an "EnterJIT" frame whenever we enter
   // JIT code with profiling enabled; the stack pointer value points
   // the saved registers.  We use this to unwind resume unwinding
   // after encounting JIT code.
   for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
     // The pseudostack grows towards higher indices, so we iterate
     // backwards (from callee to caller).
@@ -823,32 +825,32 @@ DoNativeBacktrace(PS::LockRef aLock, Pro
       savedContext.arm_pc  = savedContext.arm_lr;
       mcontext = &savedContext;
     }
   }
 
   // Now unwind whatever's left (starting from either the last EnterJIT frame
   // or, if no EnterJIT was found, the original registers).
   nativeStack.count += EHABIStackWalk(*mcontext,
-                                      aInfo.StackTop(),
+                                      aSample->mThreadInfo->StackTop(),
                                       sp_array + nativeStack.count,
                                       pc_array + nativeStack.count,
                                       nativeStack.size - nativeStack.count);
 
-  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+  MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 }
 #endif
 
 #ifdef USE_LUL_STACKWALK
 static void
 DoNativeBacktrace(PS::LockRef aLock, ProfileBuffer* aBuffer,
                   TickSample* aSample)
 {
   const mcontext_t* mc =
-    &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
+    &reinterpret_cast<ucontext_t*>(aSample->mContext)->uc_mcontext;
 
   lul::UnwindRegs startRegs;
   memset(&startRegs, 0, sizeof(startRegs));
 
 #if defined(GP_PLAT_amd64_linux)
   startRegs.xip = lul::TaggedUWord(mc->gregs[REG_RIP]);
   startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_RSP]);
   startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_RBP]);
@@ -882,17 +884,17 @@ DoNativeBacktrace(PS::LockRef aLock, Pro
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
 #elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
     uintptr_t rEDZONE_SIZE = 0;
     uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
 #else
 #   error "Unknown plat"
 #endif
-    uintptr_t end = reinterpret_cast<uintptr_t>(aSample->threadInfo->StackTop());
+    uintptr_t end = reinterpret_cast<uintptr_t>(aSample->mThreadInfo->StackTop());
     uintptr_t ws  = sizeof(void*);
     start &= ~(ws-1);
     end   &= ~(ws-1);
     uintptr_t nToCopy = 0;
     if (start < end) {
       nToCopy = end - start;
       if (nToCopy > lul::N_STACK_BYTES)
         nToCopy = lul::N_STACK_BYTES;
@@ -946,31 +948,31 @@ DoNativeBacktrace(PS::LockRef aLock, Pro
 static void
 DoSampleStackTrace(PS::LockRef aLock, ProfileBuffer* aBuffer,
                    TickSample* aSample)
 {
   NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
   MergeStacksIntoProfile(aBuffer, aSample, nativeStack);
 
   if (gPS->FeatureLeaf(aLock)) {
-    aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->pc));
+    aBuffer->addTag(ProfileBufferEntry::NativeLeafAddr((void*)aSample->mPC));
   }
 }
 
 // This function is called for each sampling period with the current program
 // counter. It is called within a signal and so must be re-entrant.
 static void
 Tick(PS::LockRef aLock, ProfileBuffer* aBuffer, TickSample* aSample)
 {
-  ThreadInfo& threadInfo = *aSample->threadInfo;
+  ThreadInfo& threadInfo = *aSample->mThreadInfo;
 
   MOZ_ASSERT(threadInfo.LastSample().mThreadId == threadInfo.ThreadId());
   aBuffer->addTagThreadId(threadInfo.LastSample());
 
-  mozilla::TimeDuration delta = aSample->timestamp - gPS->StartTime(aLock);
+  mozilla::TimeDuration delta = aSample->mTimeStamp - gPS->StartTime(aLock);
   aBuffer->addTag(ProfileBufferEntry::Time(delta.ToMilliseconds()));
 
   NotNull<PseudoStack*> stack = threadInfo.Stack();
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
     defined(USE_LUL_STACKWALK)
   if (gPS->FeatureStackWalk(aLock)) {
     DoNativeBacktrace(aLock, aBuffer, aSample);
@@ -978,41 +980,41 @@ Tick(PS::LockRef aLock, ProfileBuffer* a
     DoSampleStackTrace(aLock, aBuffer, aSample);
   }
 #else
   DoSampleStackTrace(aLock, aBuffer, aSample);
 #endif
 
   // Don't process the PeudoStack's markers if we're synchronously sampling the
   // current thread.
-  if (!aSample->isSamplingCurrentThread) {
+  if (!aSample->mIsSamplingCurrentThread) {
     ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
     while (pendingMarkersList && pendingMarkersList->peek()) {
       ProfilerMarker* marker = pendingMarkersList->popHead();
       aBuffer->addStoredMarker(marker);
       aBuffer->addTag(ProfileBufferEntry::Marker(marker));
     }
   }
 
   if (threadInfo.GetThreadResponsiveness()->HasData()) {
     mozilla::TimeDuration delta =
       threadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
-        aSample->timestamp);
+        aSample->mTimeStamp);
     aBuffer->addTag(ProfileBufferEntry::Responsiveness(delta.ToMilliseconds()));
   }
 
   // rssMemory is equal to 0 when we are not recording.
-  if (aSample->rssMemory != 0) {
-    double rssMemory = static_cast<double>(aSample->rssMemory);
+  if (aSample->mRSSMemory != 0) {
+    double rssMemory = static_cast<double>(aSample->mRSSMemory);
     aBuffer->addTag(ProfileBufferEntry::ResidentMemory(rssMemory));
   }
 
   // ussMemory is equal to 0 when we are not recording.
-  if (aSample->ussMemory != 0) {
-    double ussMemory = static_cast<double>(aSample->ussMemory);
+  if (aSample->mUSSMemory != 0) {
+    double ussMemory = static_cast<double>(aSample->mUSSMemory);
     aBuffer->addTag(ProfileBufferEntry::UnsharedMemory(ussMemory));
   }
 
   int frameNumber = gPS->FrameNumber(aLock);
   if (frameNumber != gPS->LatestRecordedFrameNumber(aLock)) {
     aBuffer->addTag(ProfileBufferEntry::FrameNumber(frameNumber));
     gPS->SetLatestRecordedFrameNumber(aLock, frameNumber);
   }
@@ -1556,19 +1558,17 @@ class SamplerThread
 public:
   // Creates a sampler thread, but doesn't start it.
   SamplerThread(PS::LockRef aLock, uint32_t aActivityGeneration,
                 double aIntervalMilliseconds);
   ~SamplerThread();
 
   // This runs on the sampler thread.  It suspends and resumes the samplee
   // threads.
-  void SuspendAndSampleAndResumeThread(PS::LockRef aLock,
-                                       ThreadInfo* aThreadInfo,
-                                       bool aIsFirstProfiledThread);
+  void SuspendAndSampleAndResumeThread(PS::LockRef aLock, TickSample* aSample);
 
   // This runs on (is!) the sampler thread.
   void Run();
 
   // This runs on the main thread.
   void Stop(PS::LockRef aLock);
 
 private:
@@ -1634,18 +1634,16 @@ SamplerThread::Run()
       // static.)
       if (PS::ActivityGeneration(lock) != mActivityGeneration) {
         return;
       }
 
       gPS->Buffer(lock)->deleteExpiredStoredMarkers();
 
       if (!gPS->IsPaused(lock)) {
-        bool isFirstProfiledThread = true;
-
         const PS::ThreadVector& threads = gPS->Threads(lock);
         for (uint32_t i = 0; i < threads.size(); i++) {
           ThreadInfo* info = threads[i];
 
           if (!info->HasProfile() || info->IsPendingDelete()) {
             // We are not interested in profiling this thread.
             continue;
           }
@@ -1659,19 +1657,27 @@ SamplerThread::Run()
                                                      info->LastSample());
             if (dup_ok) {
               continue;
             }
           }
 
           info->UpdateThreadResponsiveness();
 
-          SuspendAndSampleAndResumeThread(lock, info, isFirstProfiledThread);
-
-          isFirstProfiledThread = false;
+          TickSample sample(info);
+
+          // We only get the memory measurements once for all threads.
+          if (i == 0 && gPS->FeatureMemory(lock)) {
+            sample.mRSSMemory = nsMemoryReporterManager::ResidentFast();
+#if defined(GP_OS_linux) || defined(GP_OS_android)
+            sample.mUSSMemory = nsMemoryReporterManager::ResidentUnique();
+#endif
+          }
+
+          SuspendAndSampleAndResumeThread(lock, &sample);
         }
 
 #if defined(USE_LUL_STACKWALK)
         // The LUL unwind object accumulates frame statistics. Periodically we
         // should poke it to give it a chance to print those statistics.  This
         // involves doing I/O (fprintf, __android_log_print, etc.) and so
         // can't safely be done from the critical section inside
         // SuspendAndSampleAndResumeThread, which is why it is done here.
@@ -2940,33 +2946,30 @@ profiler_get_backtrace()
   Thread::tid_t tid = Thread::GetCurrentId();
 
   ProfileBuffer* buffer = new ProfileBuffer(GET_BACKTRACE_DEFAULT_ENTRIES);
   ThreadInfo* threadInfo =
     new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), WrapNotNull(stack),
                    /* stackTop */ nullptr);
   threadInfo->SetHasProfile();
 
-  TickSample sample;
-  sample.threadInfo = threadInfo;
+  TickSample sample(threadInfo);
+  sample.mIsSamplingCurrentThread = true;
 
 #if defined(HAVE_NATIVE_UNWIND)
 #if defined(GP_OS_windows) || defined(GP_OS_linux) || defined(GP_OS_android)
-  tickcontext_t context;
+  tick_context_t context;
   sample.PopulateContext(&context);
 #elif defined(GP_OS_darwin)
   sample.PopulateContext(nullptr);
 #else
 # error "unknown platform"
 #endif
 #endif
 
-  sample.isSamplingCurrentThread = true;
-  sample.timestamp = mozilla::TimeStamp::Now();
-
   Tick(lock, buffer, &sample);
 
   return UniqueProfilerBacktrace(new ProfilerBacktrace(buffer, threadInfo));
 }
 
 void
 ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace)
 {