Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 10 Feb 2017 14:28:13 +0100
changeset 342240 9f23fb946723b47b9a1e1b1c82c6c3556e0354c7
parent 342239 2e544c9e2fdcef1f3f7259764bdf263a7121e18f (current diff)
parent 341816 b9c6246f13ead0159d8a262959fae0efcbbca407 (diff)
child 342241 9f402afa3864572af68f27eb3e16295da02841b4
push id31346
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 22:33:24 +0000
treeherdermozilla-central@7b9d9e4a82a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/formautofill/test/unit/.eslintrc
browser/extensions/formautofill/test/unit/test_populateFieldValues.js
browser/extensions/formautofill/test/unit/test_profileStorage.js
layout/base/ServoRestyleManagerInlines.h
tools/profiler/core/Sampler.cpp
--- a/accessible/ipc/ProxyAccessibleBase.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -20,34 +20,34 @@
 namespace mozilla {
 namespace a11y {
 
 template <class Derived>
 void
 ProxyAccessibleBase<Derived>::Shutdown()
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
-  NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
   xpcAccessibleDocument* xpcDoc =
     GetAccService()->GetCachedXPCDocument(Document());
   if (xpcDoc) {
     xpcDoc->NotifyOfShutdown(static_cast<Derived*>(this));
   }
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
+  uint32_t childCount = mChildren.Length();
   if (!mOuterDoc) {
-    uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
   } else {
-    if (mChildren.Length() != 1)
-      MOZ_CRASH("outer doc doesn't own adoc!");
-
-    mChildren[0]->AsDoc()->Unbind();
+    if (childCount > 1) {
+      MOZ_CRASH("outer doc has too many documents!");
+    } else if (childCount == 1) {
+      mChildren[0]->AsDoc()->Unbind();
+    }
   }
 
   mChildren.Clear();
   ProxyDestroyed(static_cast<Derived*>(this));
   mDoc->RemoveAccessible(static_cast<Derived*>(this));
 }
 
 template <class Derived>
@@ -71,19 +71,17 @@ void
 ProxyAccessibleBase<Derived>::ClearChildDoc(DocAccessibleParent* aChildDoc)
 {
   MOZ_ASSERT(aChildDoc);
   // This is possible if we're replacing one document with another: Doc 1
   // has not had a chance to remove itself, but was already replaced by Doc 2
   // in SetChildDoc(). This could result in two subsequent calls to
   // ClearChildDoc() even though mChildren.Length() == 1.
   MOZ_ASSERT(mChildren.Length() <= 1);
-  if (mChildren.RemoveElement(aChildDoc)) {
-    mOuterDoc = false;
-  }
+  mChildren.RemoveElement(aChildDoc);
 }
 
 template <class Derived>
 bool
 ProxyAccessibleBase<Derived>::MustPruneChildren() const
 {
   // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
   // kept in sync with that.
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -281,45 +281,16 @@ nsWindowsShellService::IsDefaultBrowser(
 
   *aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
   if (*aIsDefaultBrowser && aForAllTypes) {
     *aIsDefaultBrowser = IsAARDefault(pAAR, L".html");
   }
   return NS_OK;
 }
 
-static nsresult
-DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
-{
-  // shell32.dll is in the knownDLLs list so will always be loaded from the
-  // system32 directory.
-  static const wchar_t kSehllLibraryName[] =  L"shell32.dll";
-  HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
-  if (!shellDLL) {
-    return NS_ERROR_FAILURE;
-  }
-
-  decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
-    (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
-
-  if (!SHOpenWithDialogFn) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsresult rv;
-  HRESULT hr = SHOpenWithDialogFn(hwndParent, poainfo);
-  if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
-    rv = NS_OK;
-  } else {
-    rv = NS_ERROR_FAILURE;
-  }
-  FreeLibrary(shellDLL);
-  return rv;
-}
-
 nsresult
 nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
 {
   IApplicationAssociationRegistrationUI* pAARUI;
   HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
                                 NULL,
                                 CLSCTX_INPROC,
                                 IID_IApplicationAssociationRegistrationUI,
@@ -486,17 +457,22 @@ nsresult
 nsWindowsShellService::LaunchHTTPHandlerPane()
 {
   OPENASINFO info;
   info.pcszFile = L"http";
   info.pcszClass = nullptr;
   info.oaifInFlags = OAIF_FORCE_REGISTRATION |
                      OAIF_URL_PROTOCOL |
                      OAIF_REGISTER_EXT;
-  return DynSHOpenWithDialog(nullptr, &info);
+
+  HRESULT hr = SHOpenWithDialog(nullptr, &info);
+  if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
+    return NS_OK;
+  }
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
 {
   nsAutoString appHelperPath;
   if (NS_FAILED(GetHelperPath(appHelperPath)))
     return NS_ERROR_FAILURE;
--- a/browser/extensions/formautofill/.eslintrc.js
+++ b/browser/extensions/formautofill/.eslintrc.js
@@ -12,18 +12,18 @@ module.exports = { // eslint-disable-lin
     "TextDecoder": false,
     "TextEncoder": false,
   },
 
   "rules": {
     // Rules from the mozilla plugin
     "mozilla/balanced-listeners": "error",
     "mozilla/no-aArgs": "warn",
-    "mozilla/no-cpows-in-tests": "warn",
-    "mozilla/var-only-at-top-level": "warn",
+    "mozilla/no-cpows-in-tests": "error",
+    "mozilla/var-only-at-top-level": "error",
 
     "valid-jsdoc": ["error", {
       "prefer": {
         "return": "returns",
       },
       "preferType": {
         "Boolean": "boolean",
         "Number": "number",
@@ -33,442 +33,165 @@ module.exports = { // eslint-disable-lin
       "requireParamDescription": false,
       "requireReturn": false,
       "requireReturnDescription": false,
     }],
 
     // Braces only needed for multi-line arrow function blocks
     // "arrow-body-style": ["error", "as-needed"],
 
-    // Require spacing around =>
-    "arrow-spacing": "error",
-
-    // Always require spacing around a single line block
-    "block-spacing": "warn",
-
     // Forbid spaces inside the square brackets of array literals.
     "array-bracket-spacing": ["error", "never"],
 
     // Forbid spaces inside the curly brackets of object literals.
     "object-curly-spacing": ["error", "never"],
 
     // No space padding in parentheses
     "space-in-parens": ["error", "never"],
 
     // Enforce one true brace style (opening brace on the same line) and avoid
     // start and end braces on the same line.
     "brace-style": ["error", "1tbs", {"allowSingleLine": true}],
 
-    // No space before always a space after a comma
-    "comma-spacing": ["error", {"before": false, "after": true}],
-
     // Commas at the end of the line not the start
     "comma-style": "error",
 
-    // Don't require spaces around computed properties
-    "computed-property-spacing": ["warn", "never"],
-
-    // Functions are not required to consistently return something or nothing
-    "consistent-return": "off",
-
     // Require braces around blocks that start a new line
     "curly": ["error", "all"],
 
-    // Always require a trailing EOL
-    "eol-last": "error",
-
     // Require function* name()
     "generator-star-spacing": ["error", {"before": false, "after": true}],
 
     // Two space indent
     "indent": ["error", 2, {"SwitchCase": 1}],
 
     // Space after colon not before in property declarations
     "key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
 
-    // Require spaces before and after finally, catch, etc.
-    "keyword-spacing": "error",
-
-    // Unix linebreaks
-    "linebreak-style": ["error", "unix"],
-
     // Always require parenthesis for new calls
     "new-parens": "error",
 
     // Use [] instead of Array()
     "no-array-constructor": "error",
 
-    // No duplicate arguments in function declarations
-    "no-dupe-args": "error",
-
-    // No duplicate keys in object declarations
-    "no-dupe-keys": "error",
-
-    // No duplicate cases in switch statements
-    "no-duplicate-case": "error",
-
     // If an if block ends with a return no need for an else block
     // "no-else-return": "error",
 
     // Disallow empty statements. This will report an error for:
     // try { something(); } catch (e) {}
     // but will not report it for:
     // try { something(); } catch (e) { /* Silencing the error because ...*/ }
     // which is a valid use case.
     "no-empty": "error",
 
-    // No empty character classes in regex
-    "no-empty-character-class": "error",
-
-    // Disallow empty destructuring
-    "no-empty-pattern": "error",
-
-    // No assiging to exception variable
-    "no-ex-assign": "error",
-
-    // No using !! where casting to boolean is already happening
-    "no-extra-boolean-cast": "warn",
-
-    // No double semicolon
-    "no-extra-semi": "error",
-
-    // No overwriting defined functions
-    "no-func-assign": "error",
-
-    // No invalid regular expresions
-    "no-invalid-regexp": "error",
-
-    // No odd whitespace characters
-    "no-irregular-whitespace": "error",
-
-    // No single if block inside an else block
-    "no-lonely-if": "warn",
-
-    // No mixing spaces and tabs in indent
-    "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
-
-    // Disallow use of multiple spaces (sometimes used to align const values,
-    // array or object items, etc.). It's hard to maintain and doesn't add that
-    // much benefit.
-    "no-multi-spaces": "warn",
-
-    // No reassigning native JS objects
-    "no-native-reassign": "error",
-
-    // Nested ternary statements are confusing
-    "no-nested-ternary": "error",
-
-    // Use {} instead of new Object()
-    "no-new-object": "error",
-
-    // No Math() or JSON()
-    "no-obj-calls": "error",
-
-    // No octal literals
-    "no-octal": "error",
-
-    // No redeclaring variables
-    "no-redeclare": "error",
-
-    // No unnecessary comparisons
-    "no-self-compare": "error",
-
     // No spaces between function name and parentheses
     "no-spaced-func": "warn",
 
-    // No trailing whitespace
-    "no-trailing-spaces": "error",
-
-    // Error on newline where a semicolon is needed
-    "no-unexpected-multiline": "error",
-
-    // No unreachable statements
-    "no-unreachable": "error",
-
     // No expressions where a statement is expected
     "no-unused-expressions": "error",
 
     // No declaring variables that are never used
     "no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}],
 
     // No using variables before defined
     "no-use-before-define": "error",
 
-    // No using with
-    "no-with": "error",
-
     // Always require semicolon at end of statement
     "semi": ["error", "always"],
 
-    // Require space before blocks
-    "space-before-blocks": "error",
-
-    // Never use spaces before function parentheses
-    "space-before-function-paren": ["error", {"anonymous": "never", "named": "never"}],
-
     // Require spaces around operators, except for a|"off".
     "space-infix-ops": ["error", {"int32Hint": true}],
 
-    // ++ and -- should not need spacing
-    "space-unary-ops": ["warn", {"nonwords": false}],
-
-    // No comparisons to NaN
-    "use-isnan": "error",
-
-    // Only check typeof against valid results
-    "valid-typeof": "error",
-
     // Disallow using variables outside the blocks they are defined (especially
     // since only let and const are used, see "no-var").
     "block-scoped-var": "error",
 
     // Allow trailing commas for easy list extension.  Having them does not
     // impair readability, but also not required either.
     "comma-dangle": ["error", "always-multiline"],
 
     // Warn about cyclomatic complexity in functions.
     "complexity": "warn",
 
     // Don't warn for inconsistent naming when capturing this (not so important
     // with auto-binding fat arrow functions).
     // "consistent-this": ["error", "self"],
 
-    // Don't require a default case in switch statements. Avoid being forced to
-    // add a bogus default when you know all possible cases are handled.
-    "default-case": "off",
-
     // Enforce dots on the next line with property name.
     "dot-location": ["error", "property"],
 
     // Encourage the use of dot notation whenever possible.
     "dot-notation": "error",
 
-    // Allow using == instead of ===, in the interest of landing something since
-    // the devtools codebase is split on convention here.
-    "eqeqeq": "off",
-
-    // Don't require function expressions to have a name.
-    // This makes the code more verbose and hard to read. Our engine already
-    // does a fantastic job assigning a name to the function, which includes
-    // the enclosing function name, and worst case you have a line number that
-    // you can just look up.
-    "func-names": "off",
-
-    // Allow use of function declarations and expressions.
-    "func-style": "off",
-
-    // Don't enforce the maximum depth that blocks can be nested. The complexity
-    // rule is a better rule to check this.
-    "max-depth": "off",
-
     // Maximum length of a line.
-    // Disabled because we exceed this in too many places.
-    "max-len": ["off", 80],
+    // This should be 100 but too many lines were longer than that so set a
+    // conservative upper bound for now.
+    "max-len": ["error", 140],
 
     // Maximum depth callbacks can be nested.
     "max-nested-callbacks": ["error", 4],
 
-    // Don't limit the number of parameters that can be used in a function.
-    "max-params": "off",
-
-    // Don't limit the maximum number of statement allowed in a function. We
-    // already have the complexity rule that's a better measurement.
-    "max-statements": "off",
-
-    // Don't require a capital letter for constructors, only check if all new
-    // operators are followed by a capital letter. Don't warn when capitalized
-    // functions are used without the new operator.
-    "new-cap": ["off", {"capIsNew": false}],
-
-    // Allow use of bitwise operators.
-    "no-bitwise": "off",
-
     // Disallow use of arguments.caller or arguments.callee.
     "no-caller": "error",
 
-    // Disallow the catch clause parameter name being the same as a variable in
-    // the outer scope, to avoid confusion.
-    "no-catch-shadow": "off",
-
-    // Disallow assignment in conditional expressions.
-    "no-cond-assign": "error",
-
     // Disallow using the console API.
     "no-console": "error",
 
-    // Allow using constant expressions in conditions like while (true)
-    "no-constant-condition": "off",
-
-    // Allow use of the continue statement.
-    "no-continue": "off",
-
     // Disallow control characters in regular expressions.
     "no-control-regex": "error",
 
-    // Disallow use of debugger.
-    "no-debugger": "error",
-
-    // Disallow deletion of variables (deleting properties is fine).
-    "no-delete-var": "error",
-
-    // Allow division operators explicitly at beginning of regular expression.
-    "no-div-regex": "off",
-
     // Disallow use of eval(). We have other APIs to evaluate code in content.
     "no-eval": "error",
 
     // Disallow adding to native types
     "no-extend-native": "error",
 
-    // Disallow unnecessary function binding.
-    "no-extra-bind": "error",
-
-    // Allow unnecessary parentheses, as they may make the code more readable.
-    "no-extra-parens": "off",
-
     // Disallow fallthrough of case statements, except if there is a comment.
     "no-fallthrough": "error",
 
-    // Allow the use of leading or trailing decimal points in numeric literals.
-    "no-floating-decimal": "off",
-
-    // Allow comments inline after code.
-    "no-inline-comments": "off",
-
-    // Disallow use of labels for anything other then loops and switches.
-    "no-labels": ["error", {"allowLoop": true}],
-
     // Disallow use of multiline strings (use template strings instead).
     "no-multi-str": "warn",
 
     // Disallow multiple empty lines.
     "no-multiple-empty-lines": ["warn", {"max": 2}],
 
-    // Allow reassignment of function parameters.
-    "no-param-reassign": "off",
-
-    // Allow string concatenation with __dirname and __filename (not a node env).
-    "no-path-concat": "off",
-
-    // Allow use of unary operators, ++ and --.
-    "no-plusplus": "off",
-
-    // Allow using process.env (not a node environment).
-    "no-process-env": "off",
-
-    // Allow using process.exit (not a node environment).
-    "no-process-exit": "off",
-
     // Disallow usage of __proto__ property.
     "no-proto": "error",
 
-    // Disallow multiple spaces in a regular expression literal.
-    "no-regex-spaces": "error",
-
-    // Allow reserved words being used as object literal keys.
-    "no-reserved-keys": "off",
-
-    // Don't restrict usage of specified node modules (not a node environment).
-    "no-restricted-modules": "off",
-
     // Disallow use of assignment in return statement. It is preferable for a
     // single line of code to have only one easily predictable effect.
     "no-return-assign": "error",
 
-    // Don't warn about declaration of variables already declared in the outer scope.
-    "no-shadow": "off",
-
-    // Disallow shadowing of names such as arguments.
-    "no-shadow-restricted-names": "error",
-
-    // Allow use of synchronous methods (not a node environment).
-    "no-sync": "off",
-
-    // Allow the use of ternary operators.
-    "no-ternary": "off",
-
     // Disallow throwing literals (eg. throw "error" instead of
     // throw new Error("error")).
     "no-throw-literal": "error",
 
-    // Disallow use of undeclared variables unless mentioned in a /* global */
-    // block. Note that globals from head.js are automatically imported in tests
-    // by the import-headjs-globals rule form the mozilla eslint plugin.
-    "no-undef": "error",
-
-    // Allow dangling underscores in identifiers (for privates).
-    "no-underscore-dangle": "off",
-
-    // Allow use of undefined variable.
-    "no-undefined": "off",
-
     // Disallow the use of Boolean literals in conditional expressions.
     "no-unneeded-ternary": "error",
 
-    // We use var-only-at-top-level instead of no-var as we allow top level
-    // vars.
-    "no-var": "off",
-
-    // Allow using TODO/FIXME comments.
-    "no-warning-comments": "off",
-
-    // Don't require method and property shorthand syntax for object literals.
-    // We use this in the code a lot, but not consistently, and this seems more
-    // like something to check at code review time.
-    "object-shorthand": "off",
-
-    // Allow more than one variable declaration per function.
-    "one-var": "off",
-
     // Disallow padding within blocks.
     "padded-blocks": ["warn", "never"],
 
-    // Don't require quotes around object literal property names.
-    "quote-props": "off",
-
-    // Double quotes should be used.
-    "quotes": ["warn", "double", {"avoidEscape": true, "allowTemplateLiterals": true}],
-
     // Require use of the second argument for parseInt().
     "radix": "error",
 
     // Enforce spacing after semicolons.
     "semi-spacing": ["error", {"before": false, "after": true}],
 
-    // Don't require to sort variables within the same declaration block.
-    // Anyway, one-var is disabled.
-    "sort-vars": "off",
-
-    // Require a space immediately following the // in a line comment.
-    "spaced-comment": ["error", "always"],
-
     // Require "use strict" to be defined globally in the script.
     "strict": ["error", "global"],
 
-    // Allow vars to be declared anywhere in the scope.
-    "vars-on-top": "off",
-
-    // Don't require immediate function invocation to be wrapped in parentheses.
-    "wrap-iife": "off",
-
-    // Don't require regex literals to be wrapped in parentheses (which
-    // supposedly prevent them from being mistaken for division operators).
-    "wrap-regex": "off",
-
     // Disallow Yoda conditions (where literal value comes first).
     "yoda": "error",
 
     // disallow use of eval()-like methods
     "no-implied-eval": "error",
 
     // Disallow function or variable declarations in nested blocks
     "no-inner-declarations": "error",
 
-    // Disallow usage of __iterator__ property
-    "no-iterator": "error",
-
     // Disallow labels that share a name with a variable
     "no-label-var": "error",
 
     // Disallow creating new instances of String, Number, and Boolean
     "no-new-wrappers": "error",
   },
 };
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -41,17 +41,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
 
 let FormAutofillParent = {
   _profileStore: null,
 
   /**
    * Initializes ProfileStorage and registers the message handler.
    */
-  init: function() {
+  init() {
     let storePath =
       OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
 
     this._profileStore = new ProfileStorage(storePath);
     this._profileStore.initialize();
 
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
@@ -61,17 +61,17 @@ let FormAutofillParent = {
 
   /**
    * Handles the message coming from FormAutofillContent.
    *
    * @param   {string} message.name The name of the message.
    * @param   {object} message.data The data of the message.
    * @param   {nsIFrameMessageManager} message.target Caller's message manager.
    */
-  receiveMessage: function({name, data, target}) {
+  receiveMessage({name, data, target}) {
     switch (name) {
       case "FormAutofill:PopulateFieldValues":
         this._populateFieldValues(data, target);
         break;
       case "FormAutofill:GetProfiles":
         this._getProfiles(data, target);
         break;
     }
@@ -79,26 +79,26 @@ let FormAutofillParent = {
 
   /**
    * Returns the instance of ProfileStorage. To avoid syncing issues, anyone
    * who needs to access the profile should request the instance by this instead
    * of creating a new one.
    *
    * @returns {ProfileStorage}
    */
-  getProfileStore: function() {
+  getProfileStore() {
     return this._profileStore;
   },
 
   /**
    * Uninitializes FormAutofillParent. This is for testing only.
    *
    * @private
    */
-  _uninit: function() {
+  _uninit() {
     if (this._profileStore) {
       this._profileStore._saveImmediately();
       this._profileStore = null;
     }
 
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
     mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
rename from browser/extensions/formautofill/test/unit/.eslintrc
rename to browser/extensions/formautofill/test/unit/.eslintrc.js
--- a/browser/extensions/formautofill/test/unit/.eslintrc
+++ b/browser/extensions/formautofill/test/unit/.eslintrc.js
@@ -1,5 +1,7 @@
-{
+"use strict";
+
+module.exports = { // eslint-disable-line no-undef
   "extends": [
-    "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+    "../../../../../testing/xpcshell/xpcshell.eslintrc.js",
   ],
-}
+};
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -34,17 +34,17 @@ Components.manager.addBootstrappedManife
 // While the previous test file should have deleted all the temporary files it
 // used, on Windows these might still be pending deletion on the physical file
 // system.  Thus, start from a new base number every time, to make a collision
 // with a file that is still pending deletion highly unlikely.
 let gFileCounter = Math.floor(Math.random() * 1000000);
 
 function loadFormAutofillContent() {
   let facGlobal = {
-    addEventListener: function() {},
+    addEventListener() {},
   };
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Ci.mozIJSSubScriptLoader);
   loader.loadSubScriptWithOptions("chrome://formautofill/content/FormAutofillContent.js", {
     target: facGlobal,
   });
 
   return facGlobal;
--- a/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
+++ b/browser/extensions/formautofill/test/unit/test_populateFieldValues.js
@@ -1,14 +1,12 @@
 /*
  * Test for populating field values in Form Autofill Parent.
  */
 
-/* global FormAutofillParent */
-
 "use strict";
 
 Cu.import("resource://formautofill/FormAutofillParent.jsm");
 
 do_get_profile();
 
 const TEST_FIELDS = [
   {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "organization"},
@@ -55,17 +53,17 @@ add_task(function* test_populateFieldVal
   yield new Promise((resolve) => {
     FormAutofillParent.receiveMessage({
       name: "FormAutofill:PopulateFieldValues",
       data: {
         guid: TEST_GUID,
         fields: TEST_FIELDS,
       },
       target: {
-        sendAsyncMessage: function(name, data) {
+        sendAsyncMessage(name, data) {
           do_check_eq(name, "FormAutofill:fillForm");
 
           let fields = data.fields;
           do_check_eq(fields.length, TEST_FIELDS.length);
 
           for (let i = 0; i < fields.length; i++) {
             do_check_eq(fields[i].fieldName, TEST_FIELDS[i].fieldName);
             do_check_eq(fields[i].value,
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -11,17 +11,17 @@ let matchingProfiles = [{
   guid: "test-guid-2",
   organization: "Mozilla",
   streetAddress: "331 E. Evelyn Avenue",
   tel: "1-650-903-0800",
 }];
 
 let testCases = [{
   options: {},
-  matchingProfiles: matchingProfiles,
+  matchingProfiles,
   searchString: "",
   fieldName: "",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
     items: [{
       style: "autofill-profile",
       image: "",
--- a/browser/extensions/formautofill/test/unit/test_profileStorage.js
+++ b/browser/extensions/formautofill/test/unit/test_profileStorage.js
@@ -1,14 +1,12 @@
 /**
  * Tests ProfileStorage object.
  */
 
-/* global ProfileStorage */
-
 "use strict";
 
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://formautofill/ProfileStorage.jsm");
 
 const TEST_STORE_FILE_NAME = "test-profile.json";
 
 const TEST_PROFILE_1 = {
--- a/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
+++ b/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
@@ -452,17 +452,23 @@ class Viewport {
       this._nextPosition = null;
     }
 
     this._runtimePosition = this.getScrollOffset();
     this._doAction({
       type: 'viewport',
       xOffset: this._runtimePosition.x,
       yOffset: this._runtimePosition.y,
-      zoom: this._zoom
+      zoom: this._zoom,
+      // FIXME Since Chromium improves pinch-zoom for PDF. PostMessage of type
+      //       viewport takes an addition parameter pinchPhase. We workaround
+      //       here by adding a pinchPhase of value 0 to make sure that viewing
+      //       pdf works normally. More details about pinch-zoom please refer
+      //       to chromium revision: 6e1abbfb2450eedddb1ab128be1b31cc93104e41
+      pinchPhase: 0
     });
 
     let newPage = this._getMostVisiblePage();
     if (newPage != this._page) {
       this._page = newPage;
       if (typeof this.onPageChanged === 'function') {
         this.onPageChanged(newPage);
       }
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/LayerAnimationInfo.h"
 #include "mozilla/RestyleManagerHandle.h"
 #include "mozilla/RestyleManagerHandleInlines.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h"
 #include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include "nsLayoutUtils.h"
 #include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
 #include "nsRuleProcessorData.h" // For ElementRuleProcessorData etc.
 #include "nsTArray.h"
 #include <bitset>
 #include <initializer_list>
 
 using mozilla::dom::Animation;
@@ -258,17 +259,17 @@ EffectCompositor::RequestRestyle(dom::El
 
   auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
   PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
 
   if (aRestyleType == RestyleType::Throttled) {
     if (!elementsToRestyle.Contains(key)) {
       elementsToRestyle.Put(key, false);
     }
-    mPresContext->Document()->SetNeedStyleFlush();
+    mPresContext->PresShell()->SetNeedThrottledAnimationFlush();
   } else {
     // Get() returns 0 if the element is not found. It will also return
     // false if the element is found but does not have a pending restyle.
     bool hasPendingRestyle = elementsToRestyle.Get(key);
     if (!hasPendingRestyle) {
       PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
     }
     elementsToRestyle.Put(key, true);
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -442,18 +442,16 @@ public:
     SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
   void UnsetHasDirtyDescendantsForServo() {
     MOZ_ASSERT(IsStyledByServo());
     UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
-  inline bool ShouldTraverseForServo();
-
   inline void NoteDirtyDescendantsForServo();
 
 #ifdef DEBUG
   inline bool DirtyDescendantsBitIsPropagatedForServo();
 #endif
 
   bool HasServoData() {
 #ifdef MOZ_STYLO
--- a/dom/base/ElementInlines.h
+++ b/dom/base/ElementInlines.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_dom_ElementInlines_h
 #define mozilla_dom_ElementInlines_h
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/ServoBindings.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
+#include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 
 namespace mozilla {
 namespace dom {
 
 inline void
 Element::RegisterActivityObserver()
 {
   OwnerDoc()->RegisterActivityObserver(this);
@@ -44,36 +46,34 @@ Element::GetFlattenedTreeParentElementFo
   nsINode* parentNode = GetFlattenedTreeParentNodeForStyle();
   if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
     return parentNode->AsElement();
   }
 
   return nullptr;
 }
 
-inline bool
-Element::ShouldTraverseForServo()
-{
-  return HasDirtyDescendantsForServo() || Servo_Element_ShouldTraverse(this);
-}
-
 inline void
 Element::NoteDirtyDescendantsForServo()
 {
   if (!HasServoData()) {
     // The dirty descendants bit only applies to styled elements.
     return;
   }
 
   Element* curr = this;
   while (curr && !curr->HasDirtyDescendantsForServo()) {
     curr->SetHasDirtyDescendantsForServo();
     curr = curr->GetFlattenedTreeParentElementForStyle();
   }
 
+  if (nsIPresShell* shell = OwnerDoc()->GetShell()) {
+    shell->SetNeedStyleFlush();
+  }
+
   MOZ_ASSERT(DirtyDescendantsBitIsPropagatedForServo());
 }
 
 #ifdef DEBUG
 inline bool
 Element::DirtyDescendantsBitIsPropagatedForServo()
 {
   Element* curr = this;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -249,16 +249,17 @@
 #include "nsISupportsPrimitives.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
+#include "nsIPresShellInlines.h"
 
 #include "mozilla/DocLoadingTimelineMarker.h"
 
 #include "nsISpeculativeConnect.h"
 
 #include "mozilla/MediaManager.h"
 #ifdef MOZ_WEBRTC
 #include "IPeerConnection.h"
@@ -1308,18 +1309,16 @@ nsIDocument::nsIDocument()
     mAllowDNSPrefetch(true),
     mIsStaticDocument(false),
     mCreatingStaticClone(false),
     mInUnlinkOrDeletion(false),
     mHasHadScriptHandlingObject(false),
     mIsBeingUsedAsImage(false),
     mIsSyntheticDocument(false),
     mHasLinksToUpdate(false),
-    mNeedLayoutFlush(false),
-    mNeedStyleFlush(false),
     mMayHaveDOMMutationObservers(false),
     mMayHaveAnimationObservers(false),
     mHasMixedActiveContentLoaded(false),
     mHasMixedActiveContentBlocked(false),
     mHasMixedDisplayContentLoaded(false),
     mHasMixedDisplayContentBlocked(false),
     mHasMixedContentObjectSubrequest(false),
     mHasCSP(false),
@@ -1387,17 +1386,16 @@ nsDocument::nsDocument(const char* aCont
   , mHeaderData(nullptr)
   , mIsGoingAway(false)
   , mInDestructor(false)
   , mMayHaveTitleElement(false)
   , mHasWarnedAboutBoxObjects(false)
   , mDelayFrameLoaderInitialization(false)
   , mSynchronousDOMContentLoaded(false)
   , mInXBLUpdate(false)
-  , mInFlush(false)
   , mParserAborted(false)
   , mCurrentOrientationAngle(0)
   , mCurrentOrientationType(OrientationType::Portrait_primary)
   , mSSApplicableStateNotificationPending(false)
   , mReportedUseCounters(false)
   , mStyleSetFilled(false)
   , mPendingFullscreenRequests(0)
   , mXMLDeclarationBits(0)
@@ -7944,37 +7942,22 @@ nsDocument::FlushPendingNotifications(Fl
   // correct size to determine the correct style.
   if (mParentDocument && IsSafeToFlush()) {
     FlushType parentType = aType;
     if (aType >= FlushType::Style)
       parentType = std::max(FlushType::Layout, aType);
     mParentDocument->FlushPendingNotifications(parentType);
   }
 
-  // We can optimize away getting our presshell and calling
-  // FlushPendingNotifications on it if we don't need a flush of the sort we're
-  // looking at.  The one exception is if mInFlush is true, because in that
-  // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false
-  // already but the presshell hasn't actually done the corresponding work yet.
-  // So if mInFlush and reentering this code, we need to flush the presshell.
-  if (mNeedStyleFlush ||
-      (mNeedLayoutFlush && aType >= FlushType::InterruptibleLayout) ||
-      aType >= FlushType::Display ||
-      mInFlush) {
-    nsCOMPtr<nsIPresShell> shell = GetShell();
-    if (shell) {
-      mNeedStyleFlush = false;
-      mNeedLayoutFlush = mNeedLayoutFlush && (aType < FlushType::InterruptibleLayout);
-      // mInFlush is a bitfield, so can't us AutoRestore here.  But we
-      // need to keep track of multi-level reentry correctly, so need
-      // to restore the old mInFlush value.
-      bool oldInFlush = mInFlush;
-      mInFlush = true;
-      shell->FlushPendingNotifications(aType);
-      mInFlush = oldInFlush;
+  // Call nsIPresShell::NeedFlush (inline, non-virtual) to check whether we
+  // really need to flush the shell (virtual, and needs a strong reference).
+  if (nsIPresShell* shell = GetShell()) {
+    if (shell->NeedFlush(aType)) {
+      nsCOMPtr<nsIPresShell> presShell = shell;
+      presShell->FlushPendingNotifications(aType);
     }
   }
 }
 
 static bool
 Copy(nsIDocument* aDocument, void* aData)
 {
   nsTArray<nsCOMPtr<nsIDocument> >* resources =
@@ -12897,17 +12880,19 @@ nsIDocument::RebuildUserFontSet()
   if (!mGetUserFontSetCalled) {
     // We want to lazily build the user font set the first time it's
     // requested (so we don't force creation of rule cascades too
     // early), so don't do anything now.
     return;
   }
 
   mFontFaceSetDirty = true;
-  SetNeedStyleFlush();
+  if (nsIPresShell* shell = GetShell()) {
+    shell->SetNeedStyleFlush();
+  }
 
   // Somebody has already asked for the user font set, so we need to
   // post an event to rebuild it.  Setting the user font set to be dirty
   // and lazily rebuilding it isn't sufficient, since it is only the act
   // of rebuilding it that will trigger the style change reflow that
   // calls GetUserFontSet.  (This reflow causes rebuilding of text runs,
   // which starts font loads, whose completion causes another style
   // change reflow).
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1440,20 +1440,16 @@ public:
   bool mHasWarnedAboutBoxObjects:1;
 
   bool mDelayFrameLoaderInitialization:1;
 
   bool mSynchronousDOMContentLoaded:1;
 
   bool mInXBLUpdate:1;
 
-  // Whether we're currently under a FlushPendingNotifications call to
-  // our presshell.  This is used to handle flush reentry correctly.
-  bool mInFlush:1;
-
   // Parser aborted. True if the parser of this document was forcibly
   // terminated instead of letting it finish at its own pace.
   bool mParserAborted:1;
 
   friend class nsCallRequestFullScreen;
 
   // ScreenOrientation "pending promise" as described by
   // http://www.w3.org/TR/screen-orientation/
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2472,30 +2472,16 @@ public:
                      bool asError = false,
                      const char16_t **aParams = nullptr,
                      uint32_t aParamsLength = 0) const;
 
   virtual void PostVisibilityUpdateEvent() = 0;
 
   bool IsSyntheticDocument() const { return mIsSyntheticDocument; }
 
-  void SetNeedLayoutFlush() {
-    mNeedLayoutFlush = true;
-    if (mDisplayDocument) {
-      mDisplayDocument->SetNeedLayoutFlush();
-    }
-  }
-
-  void SetNeedStyleFlush() {
-    mNeedStyleFlush = true;
-    if (mDisplayDocument) {
-      mDisplayDocument->SetNeedStyleFlush();
-    }
-  }
-
   // Note: nsIDocument is a sub-class of nsINode, which has a
   // SizeOfExcludingThis function.  However, because nsIDocument objects can
   // only appear at the top of the DOM tree, we have a specialized measurement
   // function which returns multiple sizes.
   virtual void DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const;
   // DocAddSizeOfIncludingThis doesn't need to be overridden by sub-classes
   // because nsIDocument inherits from nsINode;  see the comment above the
   // declaration of nsINode::SizeOfIncludingThis.
@@ -3119,22 +3105,16 @@ protected:
 
   // True is this document is synthetic : stand alone image, video, audio
   // file, etc.
   bool mIsSyntheticDocument : 1;
 
   // True if this document has links whose state needs updating
   bool mHasLinksToUpdate : 1;
 
-  // True if a layout flush might not be a no-op
-  bool mNeedLayoutFlush : 1;
-
-  // True if a style flush might not be a no-op
-  bool mNeedStyleFlush : 1;
-
   // True if a DOMMutationObserver is perhaps attached to a node in the document.
   bool mMayHaveDOMMutationObservers : 1;
 
   // True if an nsIAnimationObserver is perhaps attached to a node in the document.
   bool mMayHaveAnimationObservers : 1;
 
   // True if a document has loaded Mixed Active Script (see nsMixedContentBlocker.cpp)
   bool mHasMixedActiveContentLoaded : 1;
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -362,18 +362,16 @@ class IDLUnresolvedIdentifier(IDLObject)
         if name == "__noSuchMethod__":
             raise WebIDLError("__noSuchMethod__ is deprecated", [location])
 
         if name[:2] == "__" and name != "__content" and not allowDoubleUnderscore:
             raise WebIDLError("Identifiers beginning with __ are reserved",
                               [location])
         if name[0] == '_' and not allowDoubleUnderscore:
             name = name[1:]
-        # TODO: Bug 872377, Restore "toJSON" to below list.
-        # We sometimes need custom serialization, so allow toJSON for now.
         if (name in ["constructor", "toString"] and
             not allowForbidden):
             raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
                               [location])
 
         self.name = name
 
     def __str__(self):
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -1230,18 +1230,16 @@ Geolocation::GetCurrentPosition(Position
   nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
                                    GeoPositionErrorCallback(aErrorCallback),
                                    Move(CreatePositionOptionsCopy(aOptions)),
                                    aCallerType);
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
-
-  return;
 }
 
 nsresult
 Geolocation::GetCurrentPosition(GeoPositionCallback callback,
                                 GeoPositionErrorCallback errorCallback,
                                 UniquePtr<PositionOptions>&& options,
                                 CallerType aCallerType)
 {
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -36,18 +36,18 @@
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
-#include "WinUtils.h"
 #include <knownfolders.h>
+#include <shlobj.h>
 #endif
 
 #ifdef MOZ_WIDGET_COCOA
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 #include "GeckoProfiler.h"
@@ -1929,18 +1929,18 @@ CreateFileAHookFn(LPCSTR fname, DWORD ac
                             ftemplate);
 }
 
 static bool
 GetLocalLowTempPath(size_t aLen, LPWSTR aPath)
 {
     NS_NAMED_LITERAL_STRING(tempname, "\\Temp");
     LPWSTR path;
-    if (SUCCEEDED(WinUtils::SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0,
-                                                 nullptr, &path))) {
+    if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0,
+                                       nullptr, &path))) {
         if (wcslen(path) + tempname.Length() < aLen) {
             wcscpy(aPath, path);
             wcscat(aPath, tempname.get());
             ::CoTaskMemFree(path);
             return true;
         }
         ::CoTaskMemFree(path);
     }
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -11,16 +11,18 @@
 #include "nsITimer.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/SVGAnimationElement.h"
 #include "nsSMILTimedElement.h"
 #include <algorithm>
 #include "mozilla/AutoRestore.h"
 #include "RestyleTracker.h"
+#include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // nsSMILAnimationController implementation
 
 //----------------------------------------------------------------------
@@ -788,10 +790,12 @@ nsSMILAnimationController::GetRefreshDri
 
   nsPresContext* context = shell->GetPresContext();
   return context ? context->RefreshDriver() : nullptr;
 }
 
 void
 nsSMILAnimationController::FlagDocumentNeedsFlush()
 {
-  mDocument->SetNeedStyleFlush();
+  if (nsIPresShell* shell = mDocument->GetShell()) {
+    shell->SetNeedStyleFlush();
+  }
 }
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -15,16 +15,17 @@
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "plstr.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include "nsIXMLContentSink.h"
 #include "nsContentCID.h"
 #include "mozilla/dom/XMLDocument.h"
 #include "nsIStreamListener.h"
 #include "ChildIterator.h"
 #include "nsITimer.h"
 
 #include "nsXBLBinding.h"
@@ -333,17 +334,19 @@ nsBindingManager::AddToAttachedQueue(nsX
 
   // If we're in the middle of processing our queue already, don't
   // bother posting the event.
   if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
     PostProcessAttachedQueueEvent();
   }
 
   // Make sure that flushes will flush out the new items as needed.
-  mDocument->SetNeedStyleFlush();
+  if (nsIPresShell* shell = mDocument->GetShell()) {
+    shell->SetNeedStyleFlush();
+  }
 
   return NS_OK;
 
 }
 
 void
 nsBindingManager::PostProcessAttachedQueueEvent()
 {
--- a/gfx/2d/NativeFontResourceMac.cpp
+++ b/gfx/2d/NativeFontResourceMac.cpp
@@ -8,16 +8,18 @@
 #include "Types.h"
 
 #include "mozilla/RefPtr.h"
 
 #ifdef MOZ_WIDGET_UIKIT
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
+#include "nsCocoaFeatures.h"
+
 // Simple helper class to automatically release a CFObject when it goes out
 // of scope.
 template<class T>
 class AutoRelease
 {
 public:
   explicit AutoRelease(T aObject)
     : mObject(aObject)
@@ -49,16 +51,22 @@ private:
 
 // This is essentially identical to the similarly-named helper function
 // in gfx/thebes/gfxMacFont.cpp. Maybe we should put it somewhere that
 // can be shared by both Moz2d and Thebes callers?
 static CFDictionaryRef
 CreateVariationDictionaryOrNull(CGFontRef aCGFont, uint32_t aVariationCount,
   const mozilla::gfx::ScaledFont::VariationSetting* aVariations)
 {
+  // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+  // versions (see bug 1331683)
+  if (!nsCocoaFeatures::OnSierraOrLater()) {
+    return nullptr;
+  }
+
   AutoRelease<CTFontRef>
     ctFont(CTFontCreateWithGraphicsFont(aCGFont, 0, nullptr, nullptr));
   AutoRelease<CFArrayRef> axes(CTFontCopyVariationAxes(ctFont));
   if (!axes) {
     return nullptr;
   }
 
   CFIndex axisCount = CFArrayGetCount(axes);
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -10,16 +10,17 @@
 #include "skia/include/core/SkPath.h"
 #include "skia/include/ports/SkTypeface_mac.h"
 #endif
 #include <vector>
 #include <dlfcn.h>
 #ifdef MOZ_WIDGET_UIKIT
 #include <CoreFoundation/CoreFoundation.h>
 #endif
+#include "nsCocoaFeatures.h"
 
 #ifdef MOZ_WIDGET_COCOA
 // prototype for private API
 extern "C" {
 CGPathRef CGFontGetGlyphPath(CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph);
 };
 #endif
 
@@ -33,16 +34,22 @@ namespace gfx {
 ScaledFontMac::CTFontDrawGlyphsFuncT* ScaledFontMac::CTFontDrawGlyphsPtr = nullptr;
 bool ScaledFontMac::sSymbolLookupDone = false;
 
 // Helper to create a CTFont from a CGFont, copying any variations that were
 // set on the original CGFont.
 static CTFontRef
 CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize)
 {
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    if (!nsCocoaFeatures::OnSierraOrLater()) {
+        return CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr);
+    }
+
     CFDictionaryRef vars = CGFontCopyVariations(aCGFont);
     CTFontRef ctFont;
     if (vars) {
         CFDictionaryRef varAttr =
             CFDictionaryCreate(nullptr,
                                (const void**)&kCTFontVariationAttribute,
                                (const void**)&vars, 1,
                                &kCFTypeDictionaryKeyCallBacks,
@@ -270,27 +277,31 @@ ScaledFontMac::GetFontFileData(FontFileD
     memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t));
     uint32_t fontChecksum = CFSwapInt32HostToBig(0xb1b0afba - CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset));
     // set checkSumAdjust to the computed checksum
     memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum, sizeof(fontChecksum));
 
     // Collect any variation settings that were incorporated into the CTFont.
     uint32_t variationCount = 0;
     VariationSetting* variations = nullptr;
-    if (mCTFont) {
-      CFDictionaryRef dict = CTFontCopyVariation(mCTFont);
-      if (dict) {
-        CFIndex count = CFDictionaryGetCount(dict);
-        if (count > 0) {
-          variations = new VariationSetting[count];
-          VariationSetting* vPtr = variations;
-          CFDictionaryApplyFunction(dict, CollectVariationSetting, &vPtr);
-          variationCount = vPtr - variations;
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    if (nsCocoaFeatures::OnSierraOrLater()) {
+      if (mCTFont) {
+        CFDictionaryRef dict = CTFontCopyVariation(mCTFont);
+        if (dict) {
+          CFIndex count = CFDictionaryGetCount(dict);
+          if (count > 0) {
+            variations = new VariationSetting[count];
+            VariationSetting* vPtr = variations;
+            CFDictionaryApplyFunction(dict, CollectVariationSetting, &vPtr);
+            variationCount = vPtr - variations;
+          }
+          CFRelease(dict);
         }
-        CFRelease(dict);
       }
     }
 
     // we always use an index of 0
     aDataCallback(buf.data, buf.offset, 0, mSize, variationCount, variations, aBaton);
     delete[] variations;
 
     return true;
--- a/gfx/cairo/cairo/src/cairo-quartz-font.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-font.c
@@ -335,16 +335,23 @@ const cairo_font_face_backend_t _cairo_q
     _cairo_quartz_font_face_scaled_font_create
 };
 
 // Helper to create a CTFont from a CGFont, copying any variations that were
 // set on the original CGFont.
 static CTFontRef
 CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize)
 {
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    // Declare helper provided by widget/cocoa/nsCocoaFeatures.mm
+    extern bool Gecko_OnSierraOrLater();
+    if (!Gecko_OnSierraOrLater()) {
+        return CTFontCreateWithGraphicsFont(aCGFont, aSize, NULL, NULL);
+    }
     CFDictionaryRef vars = CGFontCopyVariations(aCGFont);
     CTFontRef ctFont;
     if (vars) {
         CFDictionaryRef varAttr =
             CFDictionaryCreate(NULL,
                                (const void**)&kCTFontVariationAttribute,
                                (const void**)&vars, 1,
                                &kCFTypeDictionaryKeyCallBacks,
--- a/gfx/skia/skia/src/ports/SkFontHost_mac.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_mac.cpp
@@ -796,32 +796,36 @@ static CTFontRef ctfont_create_exact_cop
     // If non-nullptr then with fonts with variation axes, the copy will fail in
     // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
     // which it quite obviously is not.
 
     // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
     // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
     // the scaler context, since they aren't 'normal'.
 
-    // Not AutoCFRelease<> because CGFontCopyVariations can return null!
-    CFDictionaryRef variations = CGFontCopyVariations(baseCGFont);
-    if (variations) {
-        AutoCFRelease<CFDictionaryRef>
-            varAttr(CFDictionaryCreate(nullptr,
-                                       (const void**)&kCTFontVariationAttribute,
-                                       (const void**)&variations,
-                                       1,
-                                       &kCFTypeDictionaryKeyCallBacks,
-                                       &kCFTypeDictionaryValueCallBacks));
-        CFRelease(variations);
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    if (darwinVersion() >= 16) {
+        // Not AutoCFRelease<> because CGFontCopyVariations can return null!
+        CFDictionaryRef variations = CGFontCopyVariations(baseCGFont);
+        if (variations) {
+            AutoCFRelease<CFDictionaryRef>
+                varAttr(CFDictionaryCreate(nullptr,
+                                           (const void**)&kCTFontVariationAttribute,
+                                           (const void**)&variations,
+                                           1,
+                                           &kCFTypeDictionaryKeyCallBacks,
+                                           &kCFTypeDictionaryValueCallBacks));
+            CFRelease(variations);
 
-        AutoCFRelease<CTFontDescriptorRef>
-            varDesc(CTFontDescriptorCreateWithAttributes(varAttr));
+            AutoCFRelease<CTFontDescriptorRef>
+                varDesc(CTFontDescriptorCreateWithAttributes(varAttr));
 
-        return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, varDesc);
+            return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, varDesc);
+        }
     }
 
     return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, nullptr);
 }
 
 SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
                                          const SkScalerContextEffects& effects,
                                          const SkDescriptor* desc)
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -11,16 +11,17 @@
 #include "gfxCoreTextShaper.h"
 #include <algorithm>
 #include "gfxPlatformMac.h"
 #include "gfxContext.h"
 #include "gfxFontUtils.h"
 #include "gfxMacPlatformFontList.h"
 #include "gfxFontConstants.h"
 #include "gfxTextRun.h"
+#include "nsCocoaFeatures.h"
 
 #include "cairo-quartz.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 // Simple helper class to automatically release a CFObject when it goes out
 // of scope.
@@ -55,16 +56,22 @@ public:
 private:
     T mObject;
 };
 
 static CFDictionaryRef
 CreateVariationDictionaryOrNull(CGFontRef aCGFont,
                                 const nsTArray<gfxFontVariation>& aVariations)
 {
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    if (!nsCocoaFeatures::OnSierraOrLater()) {
+        return nullptr;
+    }
+
     AutoRelease<CTFontRef>
       ctFont(CTFontCreateWithGraphicsFont(aCGFont, 0, nullptr, nullptr));
     AutoRelease<CFArrayRef> axes(CTFontCopyVariationAxes(ctFont));
     if (!axes) {
         return nullptr;
     }
 
     CFIndex axisCount = CFArrayGetCount(axes);
@@ -526,16 +533,22 @@ gfxMacFont::GetCharWidth(CFDataRef aCmap
 }
 
 /* static */
 CTFontRef
 gfxMacFont::CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont,
                                                  CGFloat aSize,
                                                  CTFontDescriptorRef aFontDesc)
 {
+    // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
+    // versions (see bug 1331683)
+    if (!nsCocoaFeatures::OnSierraOrLater()) {
+        return CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
+    }
+
     CFDictionaryRef variations = ::CGFontCopyVariations(aCGFont);
     CTFontRef ctFont;
     if (variations) {
         CFDictionaryRef varAttr =
             ::CFDictionaryCreate(nullptr,
                                  (const void**)&kCTFontVariationAttribute,
                                  (const void**)&variations, 1,
                                  &kCFTypeDictionaryKeyCallBacks,
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -35,18 +35,16 @@
 
 #include "gfxGDIFontList.h"
 #include "gfxGDIFont.h"
 
 #include "mozilla/layers/CompositorThread.h"
 #include "DeviceManagerD3D9.h"
 #include "mozilla/layers/ReadbackManagerD3D11.h"
 
-#include "WinUtils.h"
-
 #include "gfxDWriteFontList.h"
 #include "gfxDWriteFonts.h"
 #include "gfxDWriteCommon.h"
 #include <dwrite.h>
 
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "nsWindowsHelpers.h"
@@ -55,16 +53,17 @@
 #include <string>
 
 #include <d3d10_1.h>
 
 #include "mozilla/gfx/2D.h"
 
 #include "nsMemory.h"
 
+#include <dwmapi.h>
 #include <d3d11.h>
 
 #include "nsIMemoryReporter.h"
 #include <winternl.h>
 #include "d3dkmtQueryStatistics.h"
 
 #include "base/thread.h"
 #include "gfxPrefs.h"
@@ -1614,20 +1613,19 @@ gfxWindowsPlatform::InitGPUProcessSuppor
 
   // If we're still enabled at this point, the user set the force-enabled pref.
   return gpuProc.IsEnabled();
 }
 
 bool
 gfxWindowsPlatform::DwmCompositionEnabled()
 {
-  MOZ_ASSERT(WinUtils::dwmIsCompositionEnabledPtr);
   BOOL dwmEnabled = false;
 
-  if (FAILED(WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled))) {
+  if (FAILED(DwmIsCompositionEnabled(&dwmEnabled))) {
     return false;
   }
 
   return dwmEnabled;
 }
 
 class D3DVsyncSource final : public VsyncSource
 {
@@ -1652,17 +1650,17 @@ public:
         if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
           mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
           return;
         }
 
         DWM_TIMING_INFO vblankTime;
         // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
         vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
-        HRESULT hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
+        HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
         if (SUCCEEDED(hr)) {
           UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
           // We get the rate in hertz / time, but we want the rate in ms.
           float rate = ((float) refreshRate.uiDenominator
                        / (float) refreshRate.uiNumerator) * 1000;
           mVsyncRate = TimeDuration::FromMilliseconds(rate);
         } else {
           mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
@@ -1737,17 +1735,17 @@ public:
       {
         TimeStamp vsync = TimeStamp::Now();
         TimeStamp now = vsync;
 
         DWM_TIMING_INFO vblankTime;
         // Make sure to init the cbSize, otherwise
         // GetCompositionTiming will fail
         vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
-        HRESULT hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
+        HRESULT hr = DwmGetCompositionTimingInfo(0, &vblankTime);
         if (!SUCCEEDED(hr)) {
             return vsync;
         }
 
         LARGE_INTEGER frequency;
         QueryPerformanceFrequency(&frequency);
 
         LARGE_INTEGER qpcNow;
@@ -1819,17 +1817,17 @@ public:
           // to get back to dwm rendering once it's re-enabled
           if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
             ScheduleSoftwareVsync(vsync);
             return;
           }
 
           // Using WaitForVBlank, the whole system dies because WaitForVBlank
           // only works if it's run on the same thread as the Present();
-          HRESULT hr = WinUtils::dwmFlushProcPtr();
+          HRESULT hr = DwmFlush();
           if (!SUCCEEDED(hr)) {
             // DWMFlush isn't working, fallback to software vsync.
             ScheduleSoftwareVsync(TimeStamp::Now());
             return;
           }
 
           TimeStamp now = TimeStamp::Now();
           TimeDuration flushDiff = now - flushTime;
@@ -1899,23 +1897,19 @@ private:
   }
   RefPtr<D3DVsyncDisplay> mPrimaryDisplay;
 }; // end D3DVsyncSource
 
 already_AddRefed<mozilla::gfx::VsyncSource>
 gfxWindowsPlatform::CreateHardwareVsyncSource()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread(), "GFX: Not in main thread.");
-  if (!WinUtils::dwmIsCompositionEnabledPtr) {
-    NS_WARNING("Dwm composition not available, falling back to software vsync");
-    return gfxPlatform::CreateHardwareVsyncSource();
-  }
 
   BOOL dwmEnabled = false;
-  WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled);
+  DwmIsCompositionEnabled(&dwmEnabled);
   if (!dwmEnabled) {
     NS_WARNING("DWM not enabled, falling back to software vsync");
     return gfxPlatform::CreateHardwareVsyncSource();
   }
 
   RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
   return d3dVsyncSource.forget();
 }
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -406,17 +406,17 @@ public:
                                 mozilla::ipc::Shmem* aShmem) override \
   { return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); } \
   virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override \
   { return aImplClass::DeallocShmem(aShmem); }
 
 inline bool
 LoggingEnabled()
 {
-#if defined(DEBUG)
+#if defined(DEBUG) || defined(FUZZING)
     return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
 #else
     return false;
 #endif
 }
 
 inline bool
 LoggingEnabledFor(const char *aTopLevelProtocol)
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -1,20 +1,20 @@
 // MagicNumber = 0x6d736100;
 const magic0 = 0x00;  // '\0'
 const magic1 = 0x61;  // 'a'
 const magic2 = 0x73;  // 's'
 const magic3 = 0x6d;  // 'm'
 
-// EncodingVersion (temporary; to be set to 1 at some point before release)
-const experimentalVersion = 0xd;
-const ver0 = (experimentalVersion >>>  0) & 0xff;
-const ver1 = (experimentalVersion >>>  8) & 0xff;
-const ver2 = (experimentalVersion >>> 16) & 0xff;
-const ver3 = (experimentalVersion >>> 24) & 0xff;
+// EncodingVersion
+const encodingVersion = 0x1;
+const ver0 = (encodingVersion >>>  0) & 0xff;
+const ver1 = (encodingVersion >>>  8) & 0xff;
+const ver2 = (encodingVersion >>> 16) & 0xff;
+const ver3 = (encodingVersion >>> 24) & 0xff;
 
 // Section opcodes
 const userDefinedId    = 0;
 const typeId           = 1;
 const importId         = 2;
 const functionId       = 3;
 const tableId          = 4;
 const memoryId         = 5;
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -7,17 +7,17 @@ const CompileError = WebAssembly.Compile
 const magicError = /failed to match magic number/;
 const unknownSection = /expected custom section/;
 
 function sectionError(section) {
     return RegExp(`failed to start ${section} section`);
 }
 
 function versionError(actual) {
-    var expect = experimentalVersion;
+    var expect = encodingVersion;
     var str = `binary version 0x${actual.toString(16)} does not match expected version 0x${expect.toString(16)}`;
     return RegExp(str);
 }
 
 function toU8(array) {
     for (let b of array)
         assertEq(b < 256, true);
     return Uint8Array.from(array);
@@ -59,16 +59,19 @@ assertErrorMessage(() => wasmEval(toU8([
 assertErrorMessage(() => wasmEval(toU8([42])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([1,2,3,4])), CompileError, magicError);
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, 1])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0])), CompileError, versionError(0x6d736100));
 assertErrorMessage(() => wasmEval(toU8([magic0, magic1, magic2, magic3, ver0, ver1, ver2])), CompileError, versionError(0x6d736100));
 
+// This test should be removed shortly.
+assertEq(WebAssembly.validate(toU8([magic0, magic1, magic2, magic3, 0xd, 0x0, 0x0, 0x0])), true);
+
 function moduleHeaderThen(...rest) {
     return [magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, ...rest];
 }
 
 var o = wasmEval(toU8(moduleHeaderThen()));
 assertEq(Object.getOwnPropertyNames(o).length, 0);
 
 // unfinished known sections
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -20,17 +20,21 @@
 #define wasm_binary_h
 
 #include "builtin/SIMD.h"
 
 namespace js {
 namespace wasm {
 
 static const uint32_t MagicNumber        = 0x6d736100; // "\0asm"
-static const uint32_t EncodingVersion    = 0x0d;
+static const uint32_t EncodingVersion    = 0x01;
+
+// 0xd is equivalent to 0x1 modulo unreachability validation rules, so to aid
+// transition of toolchain, accept both for a short period of time.
+static const uint32_t PrevEncodingVersion = 0x0d;
 
 static const char NameSectionName[]      = "name";
 
 enum class SectionId
 {
     Custom                               = 0,
     Type                                 = 1,
     Import                               = 2,
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -715,19 +715,20 @@ DecodePreamble(Decoder& d)
 {
     if (d.bytesRemain() > MaxModuleBytes)
         return d.fail("module too big");
 
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
         return d.fail("failed to match magic number");
 
-    if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
+    if (!d.readFixedU32(&u32) || (u32 != EncodingVersion && u32 != PrevEncodingVersion)) {
         return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
                       u32, EncodingVersion);
+    }
 
     return true;
 }
 
 static bool
 DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
 {
     uint32_t sectionStart, sectionSize;
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -791,26 +791,30 @@ nsIPresShell::nsIPresShell()
     , mIsThemeSupportDisabled(false)
     , mIsActive(false)
     , mFrozen(false)
     , mIsFirstPaint(false)
     , mObservesMutationsForPrint(false)
     , mReflowScheduled(false)
     , mSuppressInterruptibleReflows(false)
     , mScrollPositionClampingScrollPortSizeSet(false)
+    , mNeedLayoutFlush(true)
+    , mNeedStyleFlush(true)
+    , mNeedThrottledAnimationFlush(true)
     , mPresShellId(0)
     , mFontSizeInflationEmPerLine(0)
     , mFontSizeInflationMinTwips(0)
     , mFontSizeInflationLineThreshold(0)
     , mFontSizeInflationForceEnabled(false)
     , mFontSizeInflationDisabledInMasterProcess(false)
     , mFontSizeInflationEnabled(false)
     , mPaintingIsFrozen(false)
     , mFontSizeInflationEnabledIsDirty(false)
     , mIsNeverPainting(false)
+    , mInFlush(false)
   {}
 
 PresShell::PresShell()
   : mCaretEnabled(false)
 #ifdef DEBUG
   , mInVerifyReflow(false)
   , mCurrentReflowRoot(nullptr)
   , mUpdateCount(0)
@@ -937,16 +941,23 @@ PresShell::Init(nsIDocument* aDocument,
 
   if (!aDocument || !aPresContext || !aViewManager || mDocument) {
     return;
   }
 
   mDocument = aDocument;
   mViewManager = aViewManager;
 
+  // mDocument is now set.  It might have a display document whose "need layout/
+  // style" flush flags are not set, but ours will be set.  To keep these
+  // consistent, call the flag setting functions to propagate those flags up
+  // to the display document.
+  SetNeedLayoutFlush();
+  SetNeedStyleFlush();
+
   // Create our frame constructor.
   mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
 
   mFrameManager = mFrameConstructor;
 
   // The document viewer owns both view manager and pres shell.
   mViewManager->SetPresShell(this);
 
@@ -2022,17 +2033,17 @@ PresShell::ResizeReflowIgnoreOverride(ns
                                                      this, 15,
                                                      nsITimer::TYPE_ONE_SHOT);
       }
     } else {
       RefPtr<nsRunnableMethod<PresShell> > resizeEvent =
         NewRunnableMethod(this, &PresShell::FireResizeEvent);
       if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
         mResizeEvent = resizeEvent;
-        mDocument->SetNeedStyleFlush();
+        SetNeedStyleFlush();
       }
     }
   }
 
   return NS_OK; //XXX this needs to be real. MMP
 }
 
 void
@@ -2768,17 +2779,17 @@ PresShell::FrameNeedsReflow(nsIFrame *aF
     // up the tree until we reach either a frame that's already dirty or
     // a reflow root.
     nsIFrame *f = subtreeRoot;
     for (;;) {
       if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
         // we've hit a reflow root or the root frame
         if (!wasDirty) {
           mDirtyRoots.AppendElement(f);
-          mDocument->SetNeedLayoutFlush();
+          SetNeedLayoutFlush();
         }
 #ifdef DEBUG
         else {
           VerifyHasDirtyRootAncestor(f);
         }
 #endif
 
         break;
@@ -3495,17 +3506,19 @@ PresShell::ScrollContentIntoView(nsICont
   data->mContentScrollHAxis = aHorizontal;
   data->mContentToScrollToFlags = aFlags;
   if (NS_FAILED(mContentToScrollTo->SetProperty(nsGkAtoms::scrolling, data,
                                                 nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
     mContentToScrollTo = nullptr;
   }
 
   // Flush layout and attempt to scroll in the process.
-  composedDoc->SetNeedLayoutFlush();
+  if (nsIPresShell* shell = composedDoc->GetShell()) {
+    shell->SetNeedLayoutFlush();
+  }
   composedDoc->FlushPendingNotifications(FlushType::InterruptibleLayout);
 
   // If mContentToScrollTo is non-null, that means we interrupted the reflow
   // (or suppressed it altogether because we're suppressing interruptible
   // flushes right now) and won't necessarily get the position correct, but do
   // a best-effort scroll here.  The other option would be to do this inside
   // FlushPendingNotifications, but I'm not sure the repeated scrolling that
   // could trigger if reflows keep getting interrupted would be more desirable
@@ -3722,19 +3735,17 @@ PresShell::ScheduleViewManagerFlush(Pain
     }
     return;
   }
 
   nsPresContext* presContext = GetPresContext();
   if (presContext) {
     presContext->RefreshDriver()->ScheduleViewManagerFlush();
   }
-  if (mDocument) {
-    mDocument->SetNeedLayoutFlush();
-  }
+  SetNeedLayoutFlush();
 }
 
 bool
 FlushLayoutRecursive(nsIDocument* aDocument,
                      void* aData = nullptr)
 {
   MOZ_ASSERT(!aData);
   nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
@@ -4062,17 +4073,17 @@ PresShell::FlushPendingNotifications(Flu
 }
 
 void
 PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
 {
   /**
    * VERY IMPORTANT: If you add some sort of new flushing to this
    * method, make sure to add the relevant SetNeedLayoutFlush or
-   * SetNeedStyleFlush calls on the document.
+   * SetNeedStyleFlush calls on the shell.
    */
   FlushType flushType = aFlush.mFlushType;
 
 #ifdef MOZ_GECKO_PROFILER
   static const EnumeratedArray<FlushType,
                                FlushType::Count,
                                const char*> flushTypeNames = {
     "",
@@ -4096,16 +4107,30 @@ PresShell::FlushPendingNotifications(moz
     NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
                  "Flush during accessible tree update!");
   }
 #endif
 #endif
 
   NS_ASSERTION(flushType >= FlushType::Frames, "Why did we get called?");
 
+  // Record that we are in a flush, so that our optimization in
+  // nsDocument::FlushPendingNotifications doesn't skip any re-entrant
+  // calls to us.  Otherwise, we might miss some needed flushes, since
+  // we clear mNeedStyleFlush / mNeedLayoutFlush here at the top of
+  // the function but we might not have done the work yet.
+  AutoRestore<bool> guard(mInFlush);
+  mInFlush = true;
+
+  mNeedStyleFlush = false;
+  mNeedThrottledAnimationFlush =
+    mNeedThrottledAnimationFlush && !aFlush.mFlushAnimations;
+  mNeedLayoutFlush =
+    mNeedLayoutFlush && (flushType < FlushType::InterruptibleLayout);
+
   bool isSafeToFlush = IsSafeToFlush();
 
   // If layout could possibly trigger scripts, then it's only safe to flush if
   // it's safe to run script.
   bool hasHadScriptObject;
   if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
       hasHadScriptObject) {
     isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
@@ -4219,27 +4244,28 @@ PresShell::FlushPendingNotifications(moz
     if (flushType >= FlushType::Layout) {
       if (!mIsDestroying) {
         viewManager->UpdateWidgetGeometry();
       }
     }
   }
 
   if (!didStyleFlush && flushType >= FlushType::Style && !mIsDestroying) {
-    mDocument->SetNeedStyleFlush();
-  }
-
-  if (!didLayoutFlush && !mIsDestroying &&
-      (flushType >=
-       (mSuppressInterruptibleReflows ? FlushType::Layout
-                                      : FlushType::InterruptibleLayout))) {
-    // We suppressed this flush due to mSuppressInterruptibleReflows or
-    // !isSafeToFlush, but the document thinks it doesn't
-    // need to flush anymore.  Let it know what's really going on.
-    mDocument->SetNeedLayoutFlush();
+    SetNeedStyleFlush();
+    if (aFlush.mFlushAnimations) {
+      SetNeedThrottledAnimationFlush();
+    }
+  }
+
+  if (!didLayoutFlush && flushType >= FlushType::InterruptibleLayout &&
+      !mIsDestroying) {
+    // We suppressed this flush either due to it not being safe to flush,
+    // or due to mSuppressInterruptibleReflows.  Either way, the
+    // mNeedLayoutFlush flag needs to be re-set.
+    SetNeedLayoutFlush();
   }
 }
 
 void
 PresShell::CharacterDataChanged(nsIDocument *aDocument,
                                 nsIContent*  aContent,
                                 CharacterDataChangeInfo* aInfo)
 {
@@ -9289,17 +9315,17 @@ PresShell::DoReflow(nsIFrame* target, bo
         if (f == target) {
           break;
         }
       }
     }
 
     NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
     mDirtyRoots.AppendElement(target);
-    mDocument->SetNeedLayoutFlush();
+    SetNeedLayoutFlush();
 
     // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
     // assertion so that if it fails it's easier to see what's going on.
 #ifdef NOISY_INTERRUPTIBLE_REFLOW
     printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
     mFramesToDirty.Clear();
 
@@ -9426,18 +9452,18 @@ PresShell::ProcessReflowCommands(bool aI
 
       // If any new reflow commands were enqueued during the reflow, schedule
       // another reflow event to process them.  Note that we want to do this
       // after DidDoReflow(), since that method can change whether there are
       // dirty roots around by flushing, and there's no point in posting a
       // reflow event just to have the flush revoke it.
       if (!mDirtyRoots.IsEmpty()) {
         MaybeScheduleReflow();
-        // And tell our document that we might need flushing
-        mDocument->SetNeedLayoutFlush();
+        // And record that we might need flushing
+        SetNeedLayoutFlush();
       }
     }
   }
 
   if (!mIsDestroying && mShouldUnsuppressPainting &&
       mDirtyRoots.IsEmpty()) {
     // We only unlock if we're out of reflows.  It's pointless
     // to unlock if reflows are still pending, since reflows
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -259,19 +259,16 @@ public:
   // CreateNeededFrames.
   // Note: It's the caller's responsibility to make sure to wrap a
   // ProcessPendingRestyles call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
   void ProcessPendingRestyles();
 
-  // Returns whether there are any pending restyles.
-  bool HasPendingRestyles() { return mPendingRestyles.Count() != 0; }
-
 private:
   // ProcessPendingRestyles calls into one of our RestyleTracker
   // objects.  It then calls back to these functions at the beginning
   // and end of its work.
   void BeginProcessingRestyles(RestyleTracker& aRestyleTracker);
   void EndProcessingRestyles();
 
 public:
--- a/layout/base/RestyleManagerBase.cpp
+++ b/layout/base/RestyleManagerBase.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/RestyleManagerBase.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "nsIFrame.h"
+#include "nsIPresShellInlines.h"
 
 namespace mozilla {
 
 RestyleManagerBase::RestyleManagerBase(nsPresContext* aPresContext)
   : mPresContext(aPresContext)
   , mRestyleGeneration(1)
   , mHoverGeneration(0)
   , mObservingRefreshDriver(false)
@@ -213,17 +214,17 @@ RestyleManagerBase::PostRestyleEventInte
   if (!ObservingRefreshDriver() && !inRefresh) {
     SetObservingRefreshDriver(PresContext()->RefreshDriver()->
         AddStyleFlushObserver(presShell));
   }
 
   // Unconditionally flag our document as needing a flush.  The other
   // option here would be a dedicated boolean to track whether we need
   // to do so (set here and unset in ProcessPendingRestyles).
-  presShell->GetDocument()->SetNeedStyleFlush();
+  presShell->SetNeedStyleFlush();
 }
 
 /**
  * Frame construction helpers follow.
  */
 #ifdef DEBUG
 static bool gInApplyRenderingChangeToTree = false;
 #endif
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -142,17 +142,16 @@ public:
                                     int32_t aModType,
                                     const nsAttrValue* aNewValue);
     inline void AttributeChanged(dom::Element* aElement,
                                  int32_t aNameSpaceID,
                                  nsIAtom* aAttribute,
                                  int32_t aModType,
                                  const nsAttrValue* aOldValue);
     inline nsresult ReparentStyleContext(nsIFrame* aFrame);
-    inline bool HasPendingRestyles();
     inline uint64_t GetRestyleGeneration() const;
     inline uint32_t GetHoverGeneration() const;
     inline void SetObservingRefreshDriver(bool aObserving);
     inline nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
     inline void FlushOverflowChangedTracker();
     inline void NotifyDestroyingFrame(nsIFrame* aFrame);
 
   private:
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_RestyleManagerHandleInlines_h
 #define mozilla_RestyleManagerHandleInlines_h
 
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ServoRestyleManager.h"
-#include "mozilla/ServoRestyleManagerInlines.h"
 
 #define FORWARD_CONCRETE(method_, geckoargs_, servoargs_) \
   if (IsGecko()) { \
     return AsGecko()->method_ geckoargs_; \
   } else { \
     return AsServo()->method_ servoargs_; \
   }
 
@@ -153,22 +152,16 @@ RestyleManagerHandle::Ptr::AttributeChan
 }
 
 nsresult
 RestyleManagerHandle::Ptr::ReparentStyleContext(nsIFrame* aFrame)
 {
   FORWARD(ReparentStyleContext, (aFrame));
 }
 
-bool
-RestyleManagerHandle::Ptr::HasPendingRestyles()
-{
-  FORWARD(HasPendingRestyles, ());
-}
-
 uint64_t
 RestyleManagerHandle::Ptr::GetRestyleGeneration() const
 {
   FORWARD(GetRestyleGeneration, ());
 }
 
 uint32_t
 RestyleManagerHandle::Ptr::GetHoverGeneration() const
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoRestyleManager.h"
 
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoBindings.h"
-#include "mozilla/ServoRestyleManagerInlines.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
 #include "nsStyleChangeList.h"
 
 using namespace mozilla::dom;
 
@@ -39,17 +38,17 @@ ServoRestyleManager::PostRestyleEvent(El
 
   if (mInStyleRefresh && aRestyleHint == eRestyle_CSSAnimations) {
     // FIXME: This is the initial restyle for CSS animations when the animation
     // is created. We have to process this restyle if necessary. Currently we
     // skip it here and will do this restyle in the next tick.
     return;
   }
 
-  if (aRestyleHint == 0 && !aMinChangeHint && !HasPendingRestyles()) {
+  if (aRestyleHint == 0 && !aMinChangeHint) {
     return; // Nothing to do.
   }
 
   // We allow posting change hints during restyling, but not restyle hints
   // themselves, since those would require us to re-traverse the tree.
   MOZ_ASSERT_IF(mInStyleRefresh, aRestyleHint == 0);
 
   // Processing change hints sometimes causes new change hints to be generated.
@@ -318,32 +317,29 @@ ServoRestyleManager::ProcessPendingResty
     // PresShell::FlushPendingNotifications doesn't early-return in the case
     // where the PreShell hasn't yet been initialized (and therefore we haven't
     // yet done the initial style traversal of the DOM tree). We should arguably
     // fix up the callers and assert against this case, but we just detect and
     // handle it for now.
     return;
   }
 
-  if (!HasPendingRestyles()) {
-    return;
-  }
-
   // Create a AnimationsWithDestroyedFrame during restyling process to
   // stop animations and transitions on elements that have no frame at the end
   // of the restyling process.
   AnimationsWithDestroyedFrame animationsWithDestroyedFrame(this);
 
   ServoStyleSet* styleSet = StyleSet();
   nsIDocument* doc = PresContext()->Document();
 
-  // XXXbholley: Should this be while() per bug 1316247?
-  if (HasPendingRestyles()) {
-    mInStyleRefresh = true;
-    styleSet->StyleDocument();
+  mInStyleRefresh = true;
+
+  // Perform the Servo traversal, and the post-traversal if required.
+  if (styleSet->StyleDocument()) {
+
     PresContext()->EffectCompositor()->ClearElementsToRestyle();
 
     // First do any queued-up frame creation. (see bugs 827239 and 997506).
     //
     // XXXEmilio I'm calling this to avoid random behavior changes, since we
     // delay frame construction after styling we should re-check once our
     // model is more stable whether we can skip this call.
     //
@@ -372,19 +368,20 @@ ServoRestyleManager::ProcessPendingResty
         currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
                                     change.mContent, change.mHint);
       }
       newChanges.Clear();
     }
     mReentrantChanges = nullptr;
 
     styleSet->AssertTreeIsClean();
-    mInStyleRefresh = false;
   }
 
+  mInStyleRefresh = false;
+
   IncrementRestyleGeneration();
 
   // Note: We are in the scope of |animationsWithDestroyedFrame|, so
   //       |mAnimationsWithDestroyedFrame| is still valid.
   MOZ_ASSERT(mAnimationsWithDestroyedFrame);
   mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
 }
 
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -74,19 +74,16 @@ public:
                            const nsAttrValue* aNewValue);
 
   void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
                         nsIAtom* aAttribute, int32_t aModType,
                         const nsAttrValue* aOldValue);
 
   nsresult ReparentStyleContext(nsIFrame* aFrame);
 
-  inline bool HasPendingRestyles();
-
-
   /**
    * Gets the appropriate frame given a content and a pseudo-element tag.
    *
    * Right now only supports a null tag, before or after. If the pseudo-element
    * is not null, the content needs to be an element.
    */
   static nsIFrame* FrameForPseudoElement(const nsIContent* aContent,
                                          nsIAtom* aPseudoTagOrNull);
deleted file mode 100644
--- a/layout/base/ServoRestyleManagerInlines.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef ServoRestyleManagerInlines_h
-#define ServoRestyleManagerInlines_h
-
-#include "ServoRestyleManager.h"
-
-#include "mozilla/dom/ElementInlines.h"
-
-namespace mozilla {
-
-using namespace dom;
-
-inline bool
-ServoRestyleManager::HasPendingRestyles()
-{
-  nsIDocument* doc = PresContext()->Document();
-  DocumentStyleRootIterator iter(doc);
-  while (Element* root = iter.GetNextStyleRoot()) {
-    if (root->ShouldTraverseForServo()) {
-      return true;
-    }
-  }
-  return false;
-}
-
-} // namespace mozilla
-
-#endif // ServoRestyleManagerInlines_h
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -45,16 +45,17 @@ EXPORTS += [
     'nsFrameManager.h',
     'nsFrameManagerBase.h',
     'nsFrameTraversal.h',
     'nsIFrameTraversal.h',
     'nsILayoutDebugger.h',
     'nsILayoutHistoryState.h',
     'nsIPercentBSizeObserver.h',
     'nsIPresShell.h',
+    'nsIPresShellInlines.h',
     'nsIReflowCallback.h',
     'nsLayoutUtils.h',
     'nsPresArena.h',
     'nsPresArenaObjectList.h',
     'nsPresContext.h',
     'nsPresState.h',
     'nsRefreshDriver.h',
     'nsStyleChangeList.h',
@@ -74,17 +75,16 @@ EXPORTS.mozilla += [
     'OverflowChangedTracker.h',
     'PresShell.h',
     'RestyleLogging.h',
     'RestyleManager.h',
     'RestyleManagerBase.h',
     'RestyleManagerHandle.h',
     'RestyleManagerHandleInlines.h',
     'ServoRestyleManager.h',
-    'ServoRestyleManagerInlines.h',
     'ShapeUtils.h',
     'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
     'AccessibleCaretEventHub.cpp',
     'AccessibleCaretManager.cpp',
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -77,16 +77,17 @@
 #include "nsRubyBaseContainerFrame.h"
 #include "nsRubyTextFrame.h"
 #include "nsRubyTextContainerFrame.h"
 #include "nsImageFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsTArray.h"
 #include "nsGenericDOMDataNode.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ElementInlines.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsStyleStructInlines.h"
 #include "nsPageContentFrame.h"
 #include "mozilla/RestyleManagerHandle.h"
 #include "mozilla/RestyleManagerHandleInlines.h"
 #include "StickyScrollContainer.h"
 #include "nsFieldSetFrame.h"
 #include "nsInlineFrame.h"
@@ -117,16 +118,17 @@
 #include "nsMathMLParts.h"
 #include "mozilla/dom/SVGTests.h"
 #include "nsSVGUtils.h"
 
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
 #include "nsTextNode.h"
 #include "ActiveLayerTracker.h"
+#include "nsIPresShellInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // An alias for convenience.
 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
 
 nsIFrame*
@@ -12931,8 +12933,24 @@ Iterator::DeleteItemsTo(const Iterator& 
     NS_ASSERTION(!IsDone(), "Ran off end of list?");
     FrameConstructionItem* item = mCurrent;
     Next();
     item->remove();
     mList.AdjustCountsForItem(item, -1);
     delete item;
   } while (*this != aEnd);
 }
+
+void
+nsCSSFrameConstructor::QuotesDirty()
+{
+  NS_PRECONDITION(mUpdateCount != 0, "Instant quote updates are bad news");
+  mQuotesDirty = true;
+  mPresShell->SetNeedLayoutFlush();
+}
+
+void
+nsCSSFrameConstructor::CountersDirty()
+{
+  NS_PRECONDITION(mUpdateCount != 0, "Instant counter updates are bad news");
+  mCountersDirty = true;
+  mPresShell->SetNeedLayoutFlush();
+}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -2056,27 +2056,18 @@ private:
   // see if aContent and aSibling are legitimate siblings due to restrictions
   // imposed by table columns
   // XXXbz this code is generally wrong, since the frame for aContent
   // may be constructed based on tag, not based on aDisplay!
   bool IsValidSibling(nsIFrame*              aSibling,
                       nsIContent*            aContent,
                       mozilla::StyleDisplay& aDisplay);
 
-  void QuotesDirty() {
-    NS_PRECONDITION(mUpdateCount != 0, "Instant quote updates are bad news");
-    mQuotesDirty = true;
-    mDocument->SetNeedLayoutFlush();
-  }
-
-  void CountersDirty() {
-    NS_PRECONDITION(mUpdateCount != 0, "Instant counter updates are bad news");
-    mCountersDirty = true;
-    mDocument->SetNeedLayoutFlush();
-  }
+  void QuotesDirty();
+  void CountersDirty();
 
   /**
    * Add the pair (aContent, aStyleContext) to the undisplayed items
    * in aList as needed.  This method enforces the invariant that all
    * style contexts in the undisplayed content map must be non-pseudo
    * contexts and also handles unbinding undisplayed generated content
    * as needed.
    */
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -581,16 +581,45 @@ public:
    * nsIDocument::FlushPendingNotifications.
    *
    * @param aType the type of notifications to flush
    */
   virtual void FlushPendingNotifications(mozilla::FlushType aType) = 0;
   virtual void FlushPendingNotifications(mozilla::ChangesToFlush aType) = 0;
 
   /**
+   * Whether we might need a flush for the given flush type.  If this
+   * function returns false, we definitely don't need to flush.
+   *
+   * @param aFlushType The flush type to check.  This must be
+   *   >= FlushType::Style.  This also returns true if a throttled
+   *   animation flush is required.
+   */
+  bool NeedFlush(mozilla::FlushType aType) const
+  {
+    // We check mInFlush to handle re-entrant calls to FlushPendingNotifications
+    // by reporting that we always need a flush in that case.  Otherwise,
+    // we could end up missing needed flushes, since we clear the mNeedXXXFlush
+    // flags at the top of FlushPendingNotifications.
+    MOZ_ASSERT(aType >= mozilla::FlushType::Style);
+    return mNeedStyleFlush ||
+           (mNeedLayoutFlush &&
+            aType >= mozilla::FlushType::InterruptibleLayout) ||
+           aType >= mozilla::FlushType::Display ||
+           mNeedThrottledAnimationFlush ||
+           mInFlush;
+  }
+
+  inline void SetNeedStyleFlush();
+  inline void SetNeedLayoutFlush();
+  inline void SetNeedThrottledAnimationFlush();
+
+  bool NeedStyleFlush() { return mNeedStyleFlush; }
+
+  /**
    * Callbacks will be called even if reflow itself fails for
    * some reason.
    */
   virtual nsresult PostReflowCallback(nsIReflowCallback* aCallback) = 0;
   virtual void CancelReflowCallback(nsIReflowCallback* aCallback) = 0;
 
   virtual void ClearFrameRefs(nsIFrame* aFrame) = 0;
 
@@ -1800,16 +1829,26 @@ protected:
 
   // If true, we have a reflow scheduled. Guaranteed to be false if
   // mReflowContinueTimer is non-null.
   bool                      mReflowScheduled : 1;
 
   bool                      mSuppressInterruptibleReflows : 1;
   bool                      mScrollPositionClampingScrollPortSizeSet : 1;
 
+  // True if a layout flush might not be a no-op
+  bool mNeedLayoutFlush : 1;
+
+  // True if a style flush might not be a no-op
+  bool mNeedStyleFlush : 1;
+
+  // True if there are throttled animations that would be processed when
+  // performing a flush with mFlushAnimations == true.
+  bool mNeedThrottledAnimationFlush : 1;
+
   uint32_t                  mPresShellId;
 
   // List of subtrees rooted at style scope roots that need to be restyled.
   // When a change to a scoped style sheet is made, we add the style scope
   // root to this array rather than setting mStylesHaveChanged = true, since
   // we know we don't need to restyle the whole document.  However, if in the
   // same update block we have already had other changes that require
   // the whole document to be restyled (i.e., mStylesHaveChanged is already
@@ -1830,13 +1869,17 @@ protected:
 
   // Dirty bit indicating that mFontSizeInflationEnabled needs to be recomputed.
   bool mFontSizeInflationEnabledIsDirty;
 
   // If a document belongs to an invisible DocShell, this flag must be set
   // to true, so we can avoid any paint calls for widget related to this
   // presshell.
   bool mIsNeverPainting;
+
+  // Whether we're currently under a FlushPendingNotifications.
+  // This is used to handle flush reentry correctly.
+  bool mInFlush;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIPresShell, NS_IPRESSHELL_IID)
 
 #endif /* nsIPresShell_h___ */
new file mode 100644
--- /dev/null
+++ b/layout/base/nsIPresShellInlines.h
@@ -0,0 +1,39 @@
+#ifndef nsIPresShellInlines_h
+#define nsIPresShellInlines_h
+
+#include "nsIDocument.h"
+
+void
+nsIPresShell::SetNeedLayoutFlush()
+{
+  mNeedLayoutFlush = true;
+  if (nsIDocument* doc = mDocument->GetDisplayDocument()) {
+    if (nsIPresShell* shell = doc->GetShell()) {
+      shell->mNeedLayoutFlush = true;
+    }
+  }
+}
+
+void
+nsIPresShell::SetNeedStyleFlush()
+{
+  mNeedStyleFlush = true;
+  if (nsIDocument* doc = mDocument->GetDisplayDocument()) {
+    if (nsIPresShell* shell = doc->GetShell()) {
+      shell->mNeedStyleFlush = true;
+    }
+  }
+}
+
+void
+nsIPresShell::SetNeedThrottledAnimationFlush()
+{
+  mNeedThrottledAnimationFlush = true;
+  if (nsIDocument* doc = mDocument->GetDisplayDocument()) {
+    if (nsIPresShell* shell = doc->GetShell()) {
+      shell->mNeedThrottledAnimationFlush = true;
+    }
+  }
+}
+
+#endif // nsIPresShellInlines_h
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 
 #include "base/basictypes.h"
 
 #include "nsCOMPtr.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include "nsDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
@@ -71,17 +72,16 @@
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
 #include "gfxTextRun.h"
 #include "nsFontFaceUtils.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
-#include "mozilla/ServoRestyleManagerInlines.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceTiming.h"
 
 #if defined(MOZ_WIDGET_GTK)
 #include "gfxPlatformGtk.h" // xxx - for UseFcFontList
 #endif
 
@@ -2064,22 +2064,22 @@ nsPresContext::MediaFeatureValuesChanged
 }
 
 void
 nsPresContext::PostMediaFeatureValuesChangedEvent()
 {
   // FIXME: We should probably replace this event with use of
   // nsRefreshDriver::AddStyleFlushObserver (except the pres shell would
   // need to track whether it's been added).
-  if (!mPendingMediaFeatureValuesChanged) {
+  if (!mPendingMediaFeatureValuesChanged && mShell) {
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent);
     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
       mPendingMediaFeatureValuesChanged = true;
-      mDocument->SetNeedStyleFlush();
+      mShell->SetNeedStyleFlush();
     }
   }
 }
 
 void
 nsPresContext::HandleMediaFeatureValuesChangedEvent()
 {
   // Null-check mShell in case the shell has been destroyed (and the
@@ -2253,17 +2253,19 @@ void
 nsPresContext::RebuildCounterStyles()
 {
   if (mCounterStyleManager->IsInitial()) {
     // Still in its initial state, no need to reset.
     return;
   }
 
   mCounterStylesDirty = true;
-  mDocument->SetNeedStyleFlush();
+  if (mShell) {
+    mShell->SetNeedStyleFlush();
+  }
   if (!mPostedFlushCounterStyles) {
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod(this, &nsPresContext::HandleRebuildCounterStyles);
     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
       mPostedFlushCounterStyles = true;
     }
   }
 }
@@ -2771,18 +2773,18 @@ nsPresContext::NotifyFontFaceSetOnRefres
   if (set) {
     set->DidRefresh();
   }
 }
 
 bool
 nsPresContext::HasPendingRestyleOrReflow()
 {
-  return (mRestyleManager && mRestyleManager->HasPendingRestyles()) ||
-         PresShell()->HasPendingReflow();
+  nsIPresShell* shell = PresShell();
+  return shell->NeedStyleFlush() || shell->HasPendingReflow();
 }
 
 void
 nsPresContext::ReflowStarted(bool aInterruptible)
 {
 #ifdef NOISY_INTERRUPTIBLE_REFLOW
   if (!aInterruptible) {
     printf("STARTING NONINTERRUPTIBLE REFLOW\n");
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1779,16 +1779,33 @@ GetSpacingFlags(nsIFrame* aFrame, const 
     (eStyleUnit_Coord == ls.GetUnit() && ls.GetCoordValue() != 0) ||
     (eStyleUnit_Coord == ws.GetUnit() && ws.GetCoordValue() != 0) ||
     (eStyleUnit_Percent == ws.GetUnit() && ws.GetPercentValue() != 0) ||
     (eStyleUnit_Calc == ws.GetUnit() && !ws.GetCalcValue()->IsDefinitelyZero());
 
   return nonStandardSpacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0;
 }
 
+static bool
+IsBaselineAligned(const nsStyleCoord& aCoord)
+{
+  switch (aCoord.GetUnit()) {
+    case eStyleUnit_Enumerated:
+      return aCoord.GetIntValue() == NS_STYLE_VERTICAL_ALIGN_BASELINE;
+    case eStyleUnit_Coord:
+      return aCoord.GetCoordValue() == 0;
+    case eStyleUnit_Percent:
+      return aCoord.GetPercentValue() == 0;
+    case eStyleUnit_Calc:
+      return aCoord.GetCalcValue()->IsDefinitelyZero();
+    default:
+      return false;
+  }
+}
+
 bool
 BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
 {
   // We don't need to check font size inflation, since
   // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
   // ensures that text runs never cross block boundaries.  This means
   // that the font size inflation on all text frames in the text run is
   // already guaranteed to be the same as each other (and for the line
@@ -1808,32 +1825,40 @@ BuildTextRunsScanner::ContinueTextRunAcr
   // here. This avoids creating giant textruns for an entire plain text file.
   // Note that we create a single text frame for a preformatted text node,
   // even if it has newlines in it, so typically we won't see trailing newlines
   // until after reflow has broken up the frame into one (or more) frames per
   // line. That's OK though.
   if (textStyle1->NewlineIsSignificant(aFrame1) && HasTerminalNewline(aFrame1))
     return false;
 
+  if (!IsBaselineAligned(sc1->StyleDisplay()->mVerticalAlign)) {
+    return false;
+  }
+
   if (aFrame1->GetContent() == aFrame2->GetContent() &&
       aFrame1->GetNextInFlow() != aFrame2) {
     // aFrame2 must be a non-fluid continuation of aFrame1. This can happen
     // sometimes when the unicode-bidi property is used; the bidi resolver
     // breaks text into different frames even though the text has the same
     // direction. We can't allow these two frames to share the same textrun
     // because that would violate our invariant that two flows in the same
     // textrun have different content elements.
     return false;
   }
 
   nsStyleContext* sc2 = aFrame2->StyleContext();
   const nsStyleText* textStyle2 = sc2->StyleText();
   if (sc1 == sc2)
     return true;
 
+  if (!IsBaselineAligned(sc1->StyleDisplay()->mVerticalAlign)) {
+    return false;
+  }
+
   const nsStyleFont* fontStyle1 = sc1->StyleFont();
   const nsStyleFont* fontStyle2 = sc2->StyleFont();
   nscoord letterSpacing1 = LetterSpacing(aFrame1);
   nscoord letterSpacing2 = LetterSpacing(aFrame2);
   return fontStyle1->mFont == fontStyle2->mFont &&
     fontStyle1->mLanguage == fontStyle2->mLanguage &&
     textStyle1->mTextTransform == textStyle2->mTextTransform &&
     nsLayoutUtils::GetTextRunFlagsForStyle(sc1, fontStyle1, textStyle1, letterSpacing1) ==
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -15,17 +15,16 @@
  *
  * Users of this list should define a macro
  * SERVO_BINDING_FUNC(name_, return_, ...)
  * before including this file.
  */
 
 // Element data
 SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
-SERVO_BINDING_FUNC(Servo_Element_ShouldTraverse, bool, RawGeckoElementBorrowed node)
 
 // Styleset and Stylesheet management
 SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong,
                    mozilla::css::SheetParsingMode parsing_mode)
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong,
                    mozilla::css::Loader* loader,
                    mozilla::ServoStyleSheet* gecko_stylesheet,
                    const nsACString* data,
@@ -229,20 +228,19 @@ SERVO_BINDING_FUNC(Servo_ResolvePseudoSt
 // may be invalidated by already-scheduled restyles.
 //
 // The tree must be in a consistent state such that a normal traversal could be
 // performed, and this function maintains that invariant.
 SERVO_BINDING_FUNC(Servo_ResolveStyleLazily, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element, nsIAtom* pseudo_tag,
                    RawServoStyleSetBorrowed set)
 
-// Restyle the given subtree.
 // Use ServoStyleSet::PrepareAndTraverseSubtree instead of calling this
 // directly
-SERVO_BINDING_FUNC(Servo_TraverseSubtree, void,
+SERVO_BINDING_FUNC(Servo_TraverseSubtree, bool,
                    RawGeckoElementBorrowed root, RawServoStyleSetBorrowed set,
                    mozilla::TraversalRootBehavior root_behavior)
 
 // Assert that the tree has no pending or unconsumed restyles.
 SERVO_BINDING_FUNC(Servo_AssertTreeIsClean, void, RawGeckoElementBorrowed root)
 
 // Style-struct management.
 #define STYLE_STRUCT(name, checkdata_cb)                            \
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -15,16 +15,18 @@
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "nsINode.h"
+#include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include "nsIPrincipal.h"
 #include "nsMappedAttributes.h"
 #include "nsMediaFeatures.h"
 #include "nsNameSpaceManager.h"
 #include "nsNetUtil.h"
 #include "nsRuleNode.h"
 #include "nsString.h"
 #include "nsStyleStruct.h"
@@ -255,16 +257,26 @@ Gecko_SetNodeFlags(RawGeckoNodeBorrowed 
 }
 
 void
 Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed aNode, uint32_t aFlags)
 {
   const_cast<nsINode*>(aNode)->UnsetFlags(aFlags);
 }
 
+void
+Gecko_SetOwnerDocumentNeedsStyleFlush(RawGeckoElementBorrowed aElement)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (nsIPresShell* shell = aElement->OwnerDoc()->GetShell()) {
+    shell->SetNeedStyleFlush();
+  }
+}
+
 nsStyleContext*
 Gecko_GetStyleContext(RawGeckoNodeBorrowed aNode, nsIAtom* aPseudoTagOrNull)
 {
   MOZ_ASSERT(aNode->IsContent());
   nsIFrame* relevantFrame =
     ServoRestyleManager::FrameForPseudoElement(aNode->AsContent(),
                                                aPseudoTagOrNull);
   if (!relevantFrame) {
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -229,16 +229,17 @@ void Gecko_SetMozBinding(nsStyleDisplay*
                          ThreadSafeURIHolder* referrer,
                          ThreadSafePrincipalHolder* principal);
 void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
 
 // Dirtiness tracking.
 uint32_t Gecko_GetNodeFlags(RawGeckoNodeBorrowed node);
 void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
+void Gecko_SetOwnerDocumentNeedsStyleFlush(RawGeckoElementBorrowed element);
 
 // Incremental restyle.
 // Also, we might want a ComputedValues to ComputedValues API for animations?
 // Not if we do them in Gecko...
 nsStyleContext* Gecko_GetStyleContext(RawGeckoNodeBorrowed node,
                                       nsIAtom* aPseudoTagOrNull);
 nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
                                        ServoComputedValuesBorrowed newstyle);
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -182,30 +182,32 @@ ServoStyleSet::GetContext(already_AddRef
 void
 ServoStyleSet::ResolveMappedAttrDeclarationBlocks()
 {
   if (nsHTMLStyleSheet* sheet = mPresContext->Document()->GetAttributeStyleSheet()) {
     sheet->CalculateMappedServoDeclarations();
   }
 }
 
-void
+bool
 ServoStyleSet::PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
                                          mozilla::TraversalRootBehavior aRootBehavior) {
   ResolveMappedAttrDeclarationBlocks();
 
   // Get the Document's root element to ensure that the cache is valid before
   // calling into the (potentially-parallel) Servo traversal, where a cache hit
   // is necessary to avoid a data race when updating the cache.
   mozilla::Unused << aRoot->OwnerDoc()->GetRootElement();
 
   MOZ_ASSERT(!sInServoTraversal);
   sInServoTraversal = true;
-  Servo_TraverseSubtree(aRoot, mRawSet.get(), aRootBehavior);
+  bool postTraversalRequired =
+    Servo_TraverseSubtree(aRoot, mRawSet.get(), aRootBehavior);
   sInServoTraversal = false;
+  return postTraversalRequired;
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveStyleFor(Element* aElement,
                                nsStyleContext* aParentContext,
                                LazyComputeBehavior aMayCompute,
                                TreeMatchContext& aTreeMatchContext)
 {
@@ -555,40 +557,46 @@ ServoStyleSet::HasStateDependentStyle(do
                                       CSSPseudoElementType aPseudoType,
                                      dom::Element* aPseudoElement,
                                      EventStates aStateMask)
 {
   NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
   return nsRestyleHint(0);
 }
 
-void
+bool
 ServoStyleSet::StyleDocument()
 {
   // Restyle the document from the root element and each of the document level
   // NAC subtree roots.
+  bool postTraversalRequired = false;
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
-    if (root->ShouldTraverseForServo()) {
-      PrepareAndTraverseSubtree(root, TraversalRootBehavior::Normal);
+    if (PrepareAndTraverseSubtree(root, TraversalRootBehavior::Normal)) {
+      postTraversalRequired = true;
     }
   }
+  return postTraversalRequired;
 }
 
 void
 ServoStyleSet::StyleNewSubtree(Element* aRoot)
 {
   MOZ_ASSERT(!aRoot->HasServoData());
-  PrepareAndTraverseSubtree(aRoot, TraversalRootBehavior::Normal);
+  DebugOnly<bool> postTraversalRequired =
+    PrepareAndTraverseSubtree(aRoot, TraversalRootBehavior::Normal);
+  MOZ_ASSERT(!postTraversalRequired);
 }
 
 void
 ServoStyleSet::StyleNewChildren(Element* aParent)
 {
   PrepareAndTraverseSubtree(aParent, TraversalRootBehavior::UnstyledChildrenOnly);
+  // We can't assert that Servo_TraverseSubtree returns false, since aParent
+  // or some of its other children might have pending restyles.
 }
 
 void
 ServoStyleSet::NoteStyleSheetsChanged()
 {
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get());
 }
 
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -140,19 +140,21 @@ public:
   nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
                                        EventStates aStateMask);
   nsRestyleHint HasStateDependentStyle(
     dom::Element* aElement, mozilla::CSSPseudoElementType aPseudoType,
     dom::Element* aPseudoElement, EventStates aStateMask);
 
   /**
    * Performs a Servo traversal to compute style for all dirty nodes in the
-   * document. The root element must be non-null.
+   * document.  This will traverse all of the document's style roots (that
+   * is, its document element, and the roots of the document-level native
+   * anonymous content).  Returns true if a post-traversal is required.
    */
-  void StyleDocument();
+  bool StyleDocument();
 
   /**
    * Eagerly styles a subtree of unstyled nodes that was just appended to the
    * tree. This is used in situations where we need the style immediately and
    * cannot wait for a future batch restyle.
    */
   void StyleNewSubtree(Element* aRoot);
 
@@ -218,19 +220,19 @@ private:
    * Resolve all ServoDeclarationBlocks attached to mapped
    * presentation attributes cached on the document.
    * Call this before jumping into Servo's style system.
    */
   void ResolveMappedAttrDeclarationBlocks();
 
   /**
    * Perform all lazy operations required before traversing
-   * a subtree.
+   * a subtree.  Returns whether a post-traversal is required.
    */
-  void PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
+  bool PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
                                  mozilla::TraversalRootBehavior aRootBehavior);
 
   nsPresContext* mPresContext;
   UniquePtr<RawServoStyleSet> mRawSet;
   EnumeratedArray<SheetType, SheetType::Count,
                   nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
   int32_t mBatching;
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -19,16 +19,18 @@
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 #include "nsCSSRules.h"
 #include "mozilla/RestyleManager.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsDOMMutationObserver.h"
+#include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include <algorithm> // std::stable_sort
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using mozilla::dom::Animation;
 using mozilla::dom::AnimationPlayState;
 using mozilla::dom::KeyframeEffectReadOnly;
@@ -450,17 +452,17 @@ nsAnimationManager::UpdateAnimations(nsS
     newAnimations[newAnimIdx]->CancelFromStyle();
   }
 
   // We don't actually dispatch the pending events now.  We'll either
   // dispatch them the next time we get a refresh driver notification
   // or the next time somebody calls
   // nsPresShell::FlushPendingNotifications.
   if (mEventDispatcher.HasQueuedEvents()) {
-    mPresContext->Document()->SetNeedStyleFlush();
+    mPresContext->PresShell()->SetNeedStyleFlush();
   }
 }
 
 void
 nsAnimationManager::StopAnimationsForElement(
   mozilla::dom::Element* aElement,
   mozilla::CSSPseudoElementType aPseudoType)
 {
--- a/layout/style/test/test_font_loading_api.html
+++ b/layout/style/test/test_font_loading_api.html
@@ -157,16 +157,20 @@ function awaitRefresh() {
     return new Promise(function(aResolve, aReject) {
       requestAnimationFrame(aResolve);
     });
   }
 
   return awaitOneRefresh().then(awaitOneRefresh);
 }
 
+function flushStyles() {
+  getComputedStyle(document.body).color;
+}
+
 function runTest() {
   // Document and window from inside the display:none iframe.
   var nframe = document.getElementById("n");
   var ndocument = nframe.contentDocument;
   var nwindow = nframe.contentWindow;
 
   // Document and window from inside the visible iframe.
   var vframe = document.getElementById("v");
@@ -951,16 +955,17 @@ function runTest() {
     // from becoming loaded.
     var face = new FontFace("test", "url(neverending_font_load.sjs)");
     face.load();
     document.fonts.add(face);
 
     is(document.fonts.status, "loading", "FontFaceSet.status after adding a loading FontFace (TEST 32)");
 
     document.fonts.clear();
+    flushStyles();
 
     is(document.fonts.status, "loaded", "FontFaceSet.status after clearing (TEST 32)");
 
     document.fonts.add(face);
 
     is(document.fonts.status, "loading", "FontFaceSet.status after adding a loading FontFace again (TEST 32)");
 
     var div = document.querySelector("div");
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -83,17 +83,17 @@ def target_tasks_default(full_task_graph
 
 
 @_target_task('ash_tasks')
 def target_tasks_ash(full_task_graph, parameters):
     """Target tasks that only run on the ash branch."""
     def filter(task):
         platform = task.attributes.get('build_platform')
         # only select platforms
-        if platform not in ('linux64', 'linux64-asan', 'linux64-pgo'):
+        if platform not in ('linux32', 'linux32-pgo', 'linux64', 'linux64-asan', 'linux64-pgo'):
             return False
         # and none of this linux64-asan/debug stuff
         if platform == 'linux64-asan' and task.attributes['build_type'] == 'debug':
             return False
         # no non-e10s tests
         if task.attributes.get('unittest_suite'):
             if not task.attributes.get('e10s'):
                 return False
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -336,16 +336,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'winspool',
         'comdlg32',
         'imm32',
         'msimg32',
         'netapi32',
         'shlwapi',
         'ws2_32',
         'dbghelp',
+        'dwmapi',
         'rasapi32',
         'rasdlg',
         'iphlpapi',
         'uxtheme',
         'setupapi',
         'secur32',
         'sensorsapi',
         'portabledeviceguids',
--- a/tools/profiler/core/ProfileBuffer.cpp
+++ b/tools/profiler/core/ProfileBuffer.cpp
@@ -62,16 +62,17 @@ void ProfileBuffer::reset() {
 size_t
 ProfileBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   n += aMallocSizeOf(mEntries.get());
 
   // Measurement of the following members may be added later if DMD finds it
   // is worthwhile:
+  // - memory pointed to by the elements within mEntries
   // - mStoredMarkers
 
   return n;
 }
 
 #define DYNAMIC_MAX_STRING 8192
 
 char* ProfileBuffer::processDynamicTag(int readPos,
deleted file mode 100644
--- a/tools/profiler/core/Sampler.cpp
+++ /dev/null
@@ -1,1331 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <algorithm>
-#include <string>
-#include <stdio.h>
-#include <fstream>
-#include <sstream>
-#include "GeckoProfiler.h"
-#include "nsIProfileSaveEvent.h"
-#include "nsThreadUtils.h"
-#include "prenv.h"
-#include "prtime.h"
-#include "nsXULAppAPI.h"
-#include "ProfileEntry.h"
-#include "SyncProfile.h"
-#include "platform.h"
-#include "shared-libraries.h"
-#include "mozilla/StackWalk.h"
-
-// JSON
-#include "ProfileJSONWriter.h"
-
-// Meta
-#include "nsXPCOM.h"
-#include "nsXPCOMCID.h"
-#include "nsIHttpProtocolHandler.h"
-#include "nsServiceManagerUtils.h"
-#include "nsIXULRuntime.h"
-#include "nsIXULAppInfo.h"
-#include "nsDirectoryServiceUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsIObserverService.h"
-#include "mozilla/Services.h"
-#include "PlatformMacros.h"
-#include "nsTArray.h"
-
-#include "mozilla/Preferences.h"
-
-#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-  #include "FennecJNIWrappers.h"
-#endif
-
-#ifdef MOZ_TASK_TRACER
-#include "GeckoTaskTracer.h"
-#endif // MOZ_TASK_TRACER
-
-// JS
-#include "jsfriendapi.h"
-#include "js/ProfilingFrameIterator.h"
-
-#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
- #define USE_NS_STACKWALK
-#endif
-
-#if defined(XP_WIN)
-typedef CONTEXT tickcontext_t;
-#elif defined(LINUX)
-#include <ucontext.h>
-typedef ucontext_t tickcontext_t;
-#endif
-
-#if defined(__arm__) && defined(ANDROID)
- // Should also work on ARM Linux, but not tested there yet.
- #define USE_EHABI_STACKWALK
-#endif
-#ifdef USE_EHABI_STACKWALK
- #include "EHABIStackWalk.h"
-#endif
-
-#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
-# define USE_LUL_STACKWALK
-# include "lul/LulMain.h"
-# include "lul/platform-linux-lul.h"
-#endif
-
-using std::string;
-using namespace mozilla;
-
-#ifndef MAXPATHLEN
- #ifdef PATH_MAX
-  #define MAXPATHLEN PATH_MAX
- #elif defined(MAX_PATH)
-  #define MAXPATHLEN MAX_PATH
- #elif defined(_MAX_PATH)
-  #define MAXPATHLEN _MAX_PATH
- #elif defined(CCHMAXPATH)
-  #define MAXPATHLEN CCHMAXPATH
- #else
-  #define MAXPATHLEN 1024
- #endif
-#endif
-
-#ifdef MOZ_VALGRIND
-# include <valgrind/memcheck.h>
-#else
-# define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
-#endif
-
-
-///////////////////////////////////////////////////////////////////////
-// BEGIN ProfileSaveEvent
-
-class ProfileSaveEvent final : public nsIProfileSaveEvent {
-public:
-  typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
-  NS_DECL_ISUPPORTS
-
-  ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
-    : mFunc(aFunc)
-    , mClosure(aClosure)
-  {}
-
-  NS_IMETHOD AddSubProfile(const char* aProfile) override {
-    mFunc(aProfile, mClosure);
-    return NS_OK;
-  }
-private:
-  ~ProfileSaveEvent() {}
-
-  AddSubProfileFunc mFunc;
-  void* mClosure;
-};
-
-NS_IMPL_ISUPPORTS(ProfileSaveEvent, nsIProfileSaveEvent)
-
-// END ProfileSaveEvent
-///////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////
-// BEGIN SaveProfileTask et al
-
-static void
-AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
-{
-  aStream << "{";
-  aStream << "\"start\":" << aLib.GetStart();
-  aStream << ",\"end\":" << aLib.GetEnd();
-  aStream << ",\"offset\":" << aLib.GetOffset();
-  aStream << ",\"name\":\"" << aLib.GetName() << "\"";
-  const std::string &breakpadId = aLib.GetBreakpadId();
-  aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
-#ifdef XP_WIN
-  // FIXME: remove this XP_WIN code when the profiler plugin has switched to
-  // using breakpadId.
-  std::string pdbSignature = breakpadId.substr(0, 32);
-  std::string pdbAgeStr = breakpadId.substr(32,  breakpadId.size() - 1);
-
-  std::stringstream stream;
-  stream << pdbAgeStr;
-
-  unsigned pdbAge;
-  stream << std::hex;
-  stream >> pdbAge;
-
-#ifdef DEBUG
-  std::ostringstream oStream;
-  oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
-  MOZ_ASSERT(breakpadId == oStream.str());
-#endif
-
-  aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
-  aStream << ",\"pdbAge\":" << pdbAge;
-  aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
-#endif
-  aStream << "}";
-}
-
-std::string
-GetSharedLibraryInfoStringInternal()
-{
-  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
-  if (info.GetSize() == 0)
-    return "[]";
-
-  std::ostringstream os;
-  os << "[";
-  AddSharedLibraryInfoToStream(os, info.GetEntry(0));
-
-  for (size_t i = 1; i < info.GetSize(); i++) {
-    os << ",";
-    AddSharedLibraryInfoToStream(os, info.GetEntry(i));
-  }
-
-  os << "]";
-  return os.str();
-}
-
-static bool
-hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
-  for(size_t i = 0; i < aFeatureCount; i++) {
-    if (strcmp(aFeatures[i], aFeature) == 0)
-      return true;
-  }
-  return false;
-}
-
-Sampler::Sampler(double aInterval, int aEntrySize,
-                 const char** aFeatures, uint32_t aFeatureCount,
-                 uint32_t aFilterCount)
-
-  : interval_(aInterval)
-  , paused_(false)
-  , active_(false)
-  , mBuffer(new ProfileBuffer(aEntrySize))
-{
-  MOZ_COUNT_CTOR(Sampler);
-
-  mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
-
-  mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
-  mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
-  // Users sometimes ask to filter by a list of threads but forget to request
-  // profiling non main threads. Let's make it implificit if we have a filter
-  mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
-  mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
-  mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
-  mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
-  mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
-  mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
-  mLayersDump = hasFeature(aFeatures, aFeatureCount, "layersdump");
-  mDisplayListDump = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
-  mProfileRestyle = hasFeature(aFeatures, aFeatureCount, "restyle");
-
-#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-  mProfileJava = mozilla::jni::IsFennec() &&
-      hasFeature(aFeatures, aFeatureCount, "java");
-#else
-  mProfileJava = false;
-#endif
-
-  bool ignore;
-  sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
-
-  {
-    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-    // Set up profiling for each registered thread, if appropriate
-    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
-      ThreadInfo* info = sRegisteredThreads->at(i);
-
-      RegisterThread(info);
-    }
-  }
-
-#ifdef MOZ_TASK_TRACER
-  if (mTaskTracer) {
-    mozilla::tasktracer::StartLogging();
-  }
-#endif
-}
-
-Sampler::~Sampler()
-{
-  MOZ_COUNT_DTOR(Sampler);
-
-  if (IsActive())
-    Stop();
-
-  // Destroy ThreadInfo for all threads
-  {
-    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
-      ThreadInfo* info = sRegisteredThreads->at(i);
-      // We've stopped profiling. We no longer need to retain
-      // information for an old thread.
-      if (info->IsPendingDelete()) {
-        // The stack was nulled when SetPendingDelete() was called.
-        MOZ_ASSERT(!info->Stack());
-        delete info;
-        sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
-        i--;
-      }
-    }
-  }
-
-#ifdef MOZ_TASK_TRACER
-  if (mTaskTracer) {
-    mozilla::tasktracer::StopLogging();
-  }
-#endif
-}
-
-void
-Sampler::DeleteExpiredMarkers()
-{
-  mBuffer->deleteExpiredStoredMarkers();
-}
-
-void
-Sampler::StreamTaskTracer(SpliceableJSONWriter& aWriter)
-{
-#ifdef MOZ_TASK_TRACER
-  aWriter.StartArrayProperty("data");
-    UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime);
-    for (uint32_t i = 0; i < data->Length(); ++i) {
-      aWriter.StringElement((data->ElementAt(i)).get());
-    }
-  aWriter.EndArray();
-
-  aWriter.StartArrayProperty("threads");
-  {
-    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-    for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
-      // Thread meta data
-      ThreadInfo* info = sRegisteredThreads->at(i);
-      aWriter.StartObjectElement();
-      {
-        if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
-          // TODO Add the proper plugin name
-          aWriter.StringProperty("name", "Plugin");
-        } else {
-          aWriter.StringProperty("name", info->Name());
-        }
-        aWriter.IntProperty("tid", static_cast<int>(info->ThreadId()));
-      }
-      aWriter.EndObject();
-    }
-  }
-  aWriter.EndArray();
-
-  aWriter.DoubleProperty("start", static_cast<double>(mozilla::tasktracer::GetStartTime()));
-#endif
-}
-
-
-void
-Sampler::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
-{
-  aWriter.IntProperty("version", 3);
-  aWriter.DoubleProperty("interval", interval());
-  aWriter.IntProperty("stackwalk", mUseStackWalk);
-
-#ifdef DEBUG
-  aWriter.IntProperty("debug", 1);
-#else
-  aWriter.IntProperty("debug", 0);
-#endif
-
-  aWriter.IntProperty("gcpoison", JS::IsGCPoisoning() ? 1 : 0);
-
-  bool asyncStacks = Preferences::GetBool("javascript.options.asyncstack");
-  aWriter.IntProperty("asyncstack", asyncStacks);
-
-  mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
-  aWriter.DoubleProperty("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
-
-  aWriter.IntProperty("processType", XRE_GetProcessType());
-
-  nsresult res;
-  nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
-  if (!NS_FAILED(res)) {
-    nsAutoCString string;
-
-    res = http->GetPlatform(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("platform", string.Data());
-
-    res = http->GetOscpu(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("oscpu", string.Data());
-
-    res = http->GetMisc(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("misc", string.Data());
-  }
-
-  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
-  if (runtime) {
-    nsAutoCString string;
-
-    res = runtime->GetXPCOMABI(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("abi", string.Data());
-
-    res = runtime->GetWidgetToolkit(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("toolkit", string.Data());
-  }
-
-  nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
-  if (appInfo) {
-    nsAutoCString string;
-
-    res = appInfo->GetName(string);
-    if (!NS_FAILED(res))
-      aWriter.StringProperty("product", string.Data());
-  }
-}
-
-void
-Sampler::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
-{
-  SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
-  StreamJSON(b, aSinceTime);
-}
-
-JSObject*
-Sampler::ToJSObject(JSContext *aCx, double aSinceTime)
-{
-  JS::RootedValue val(aCx);
-  {
-    UniquePtr<char[]> buf = ToJSON(aSinceTime);
-    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
-    MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
-                                 js_string.Length(), &val));
-  }
-  return &val.toObject();
-}
-
-UniquePtr<char[]>
-Sampler::ToJSON(double aSinceTime)
-{
-  SpliceableChunkedJSONWriter b;
-  StreamJSON(b, aSinceTime);
-  return b.WriteFunc()->CopyData();
-}
-
-struct SubprocessClosure {
-  explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
-    : mWriter(aWriter)
-  {}
-
-  SpliceableJSONWriter* mWriter;
-};
-
-void SubProcessCallback(const char* aProfile, void* aClosure)
-{
-  // Called by the observer to get their profile data included
-  // as a sub profile
-  SubprocessClosure* closure = (SubprocessClosure*)aClosure;
-
-  // Add the string profile into the profile
-  closure->mWriter->StringElement(aProfile);
-}
-
-
-#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-static
-void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
-{
-  aWriter.StringProperty("name", "Java Main Thread");
-
-  aWriter.StartArrayProperty("samples");
-
-    // for each sample
-    for (int sampleId = 0; true; sampleId++) {
-      bool firstRun = true;
-      // for each frame
-      for (int frameId = 0; true; frameId++) {
-        jni::String::LocalRef frameName =
-            java::GeckoJavaSampler::GetFrameName(0, sampleId, frameId);
-        // when we run out of frames, we stop looping
-        if (!frameName) {
-          // if we found at least one frame, we have objects to close
-          if (!firstRun) {
-              aWriter.EndArray();
-            aWriter.EndObject();
-          }
-          break;
-        }
-        // the first time around, open the sample object and frames array
-        if (firstRun) {
-          firstRun = false;
-
-          double sampleTime =
-              java::GeckoJavaSampler::GetSampleTime(0, sampleId);
-
-          aWriter.StartObjectElement();
-            aWriter.DoubleProperty("time", sampleTime);
-
-            aWriter.StartArrayProperty("frames");
-        }
-        // add a frame to the sample
-        aWriter.StartObjectElement();
-          aWriter.StringProperty("location",
-                                 frameName->ToCString().BeginReading());
-        aWriter.EndObject();
-      }
-      // if we found no frames for this sample, we are done
-      if (firstRun) {
-        break;
-      }
-    }
-
-  aWriter.EndArray();
-}
-#endif
-
-void
-Sampler::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
-{
-  aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
-  {
-    // Put shared library info
-    aWriter.StringProperty("libs", GetSharedLibraryInfoStringInternal().c_str());
-
-    // Put meta data
-    aWriter.StartObjectProperty("meta");
-      StreamMetaJSCustomObject(aWriter);
-    aWriter.EndObject();
-
-    // Data of TaskTracer doesn't belong in the circular buffer.
-    if (TaskTracer()) {
-      aWriter.StartObjectProperty("tasktracer");
-      StreamTaskTracer(aWriter);
-      aWriter.EndObject();
-    }
-
-    // Lists the samples for each thread profile
-    aWriter.StartArrayProperty("threads");
-    {
-      SetPaused(true);
-
-      {
-        StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-        for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
-          // Thread not being profiled, skip it
-          ThreadInfo* info = sRegisteredThreads->at(i);
-          if (!info->hasProfile()) {
-            continue;
-          }
-
-          // Note that we intentionally include thread profiles which
-          // have been marked for pending delete.
-
-          MutexAutoLock lock(info->GetMutex());
-
-          info->StreamJSON(aWriter, aSinceTime);
-        }
-      }
-
-      if (Sampler::CanNotifyObservers()) {
-        // Send a event asking any subprocesses (plugins) to
-        // give us their information
-        SubprocessClosure closure(&aWriter);
-        nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-        if (os) {
-          RefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
-          os->NotifyObservers(pse, "profiler-subprocess", nullptr);
-        }
-      }
-
-  #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-      if (ProfileJava()) {
-        java::GeckoJavaSampler::Pause();
-
-        aWriter.Start();
-        {
-          BuildJavaThreadJSObject(aWriter);
-        }
-        aWriter.End();
-
-        java::GeckoJavaSampler::Unpause();
-      }
-  #endif
-
-      SetPaused(false);
-    }
-    aWriter.EndArray();
-  }
-  aWriter.End();
-}
-
-void
-Sampler::FlushOnJSShutdown(JSContext* aContext)
-{
-  SetPaused(true);
-
-  {
-    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-    for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
-      // Thread not being profiled, skip it.
-      ThreadInfo* info = sRegisteredThreads->at(i);
-      if (!info->hasProfile() || info->IsPendingDelete()) {
-        continue;
-      }
-
-      // Thread not profiling the context that's going away, skip it.
-      if (info->Stack()->mContext != aContext) {
-        continue;
-      }
-
-      MutexAutoLock lock(info->GetMutex());
-      info->FlushSamplesAndMarkers();
-    }
-  }
-
-  SetPaused(false);
-}
-
-void PseudoStack::flushSamplerOnJSShutdown()
-{
-  MOZ_ASSERT(mContext);
-
-  // XXX: this function should handle being called by any thread, but it
-  // currently doesn't because it accesses gSampler, which is main-thread-only.
-  if (!NS_IsMainThread()) {
-    return;
-  }
-
-  if (gSampler) {
-    gSampler->FlushOnJSShutdown(mContext);
-  }
-}
-
-// END SaveProfileTask et al
-////////////////////////////////////////////////////////////////////////
-
-static
-void addDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr)
-{
-  aInfo.addTag(ProfileEntry::CodeLocation(""));
-  // Add one to store the null termination
-  size_t strLen = strlen(aStr) + 1;
-  for (size_t j = 0; j < strLen;) {
-    // Store as many characters in the void* as the platform allows
-    char text[sizeof(void*)];
-    size_t len = sizeof(void*)/sizeof(char);
-    if (j+len >= strLen) {
-      len = strLen - j;
-    }
-    memcpy(text, &aStr[j], len);
-    j += sizeof(void*)/sizeof(char);
-    // Cast to *((void**) to pass the text data to a void*
-    aInfo.addTag(ProfileEntry::EmbeddedString(*((void**)(&text[0]))));
-  }
-}
-
-static
-void addPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
-                    PseudoStack* stack, void* lastpc)
-{
-  // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations
-  // and should not be recorded in the profile.
-  if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS))
-    return;
-
-  int lineno = -1;
-
-  // First entry has kind CodeLocation
-  // Check for magic pointer bit 1 to indicate copy
-  const char* sampleLabel = entry.label();
-  if (entry.isCopyLabel()) {
-    // Store the string using 1 or more EmbeddedString tags
-    // that will happen to the preceding tag
-
-    addDynamicCodeLocationTag(aInfo, sampleLabel);
-    if (entry.isJs()) {
-      JSScript* script = entry.script();
-      if (script) {
-        if (!entry.pc()) {
-          // The JIT only allows the top-most entry to have a nullptr pc
-          MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
-          // If stack-walking was disabled, then that's just unfortunate
-          if (lastpc) {
-            jsbytecode *jspc = js::ProfilingGetPC(stack->mContext, script,
-                                                  lastpc);
-            if (jspc) {
-              lineno = JS_PCToLineNumber(script, jspc);
-            }
-          }
-        } else {
-          lineno = JS_PCToLineNumber(script, entry.pc());
-        }
-      }
-    } else {
-      lineno = entry.line();
-    }
-  } else {
-    aInfo.addTag(ProfileEntry::CodeLocation(sampleLabel));
-
-    // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
-    // line for js entries as well.
-    if (entry.isCpp()) {
-      lineno = entry.line();
-    }
-  }
-
-  if (lineno != -1) {
-    aInfo.addTag(ProfileEntry::LineNumber(lineno));
-  }
-
-  uint32_t category = entry.category();
-  MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
-  MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
-
-  if (category) {
-    aInfo.addTag(ProfileEntry::Category((int)category));
-  }
-}
-
-struct NativeStack
-{
-  void** pc_array;
-  void** sp_array;
-  size_t size;
-  size_t count;
-};
-
-mozilla::Atomic<bool> WALKING_JS_STACK(false);
-
-struct AutoWalkJSStack {
-  bool walkAllowed;
-
-  AutoWalkJSStack() : walkAllowed(false) {
-    walkAllowed = WALKING_JS_STACK.compareExchange(false, true);
-  }
-
-  ~AutoWalkJSStack() {
-    if (walkAllowed)
-        WALKING_JS_STACK = false;
-  }
-};
-
-static void
-mergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
-                       NativeStack& aNativeStack)
-{
-  PseudoStack* pseudoStack = aInfo.Stack();
-  volatile StackEntry *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;
-  if (aSample->isSamplingCurrentThread) {
-    startBufferGen = UINT32_MAX;
-  } else {
-    startBufferGen = aInfo.bufferGeneration();
-  }
-  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;
-
-      JS::ProfilingFrameIterator jsIter(pseudoStack->mContext,
-                                        registerState,
-                                        startBufferGen);
-      for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
-        // See note below regarding 'J' entries.
-        if (aSample->isSamplingCurrentThread || jsIter.isWasm()) {
-          uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
-          jsCount += extracted;
-          if (jsCount == maxFrames)
-            break;
-        } else {
-          mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
-            jsIter.getPhysicalFrameWithoutLabel();
-          if (frame.isSome())
-            jsFrames[jsCount++] = frame.value();
-        }
-      }
-    }
-  }
-
-  // Start the sample with a root entry.
-  aInfo.addTag(ProfileEntry::Sample("(root)"));
-
-  // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
-  // native arrays are ordered youngest-to-oldest. We must add frames to
-  // aInfo oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
-  // and JS and native arrays backwards. Note: this means the terminating
-  // condition jsIndex and nativeIndex is being < 0.
-  uint32_t pseudoIndex = 0;
-  int32_t jsIndex = jsCount - 1;
-  int32_t nativeIndex = aNativeStack.count - 1;
-
-  uint8_t *lastPseudoCppStackAddr = nullptr;
-
-  // Iterate as long as there is at least one frame remaining.
-  while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
-    // There are 1 to 3 frames available. Find and add the oldest.
-
-    uint8_t *pseudoStackAddr = nullptr;
-    uint8_t *jsStackAddr = nullptr;
-    uint8_t *nativeStackAddr = nullptr;
-
-    if (pseudoIndex != pseudoCount) {
-      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
-
-      if (pseudoFrame.isCpp())
-        lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();
-
-      // Skip any pseudo-stack JS frames which are marked isOSR
-      // Pseudostack frames are marked isOSR when the JS interpreter
-      // enters a jit frame on a loop edge (via on-stack-replacement,
-      // or OSR).  To avoid both the pseudoframe and jit frame being
-      // recorded (and showing up twice), the interpreter marks the
-      // interpreter pseudostack entry with the OSR flag to ensure that
-      // it doesn't get counted.
-      if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
-          pseudoIndex++;
-          continue;
-      }
-
-      MOZ_ASSERT(lastPseudoCppStackAddr);
-      pseudoStackAddr = lastPseudoCppStackAddr;
-    }
-
-    if (jsIndex >= 0)
-      jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;
-
-    if (nativeIndex >= 0)
-      nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];
-
-    // If there's a native stack entry which has the same SP as a
-    // pseudo stack entry, pretend we didn't see the native stack
-    // entry.  Ditto for a native stack entry which has the same SP as
-    // a JS stack entry.  In effect this means pseudo or JS entries
-    // trump conflicting native entries.
-    if (nativeStackAddr && (pseudoStackAddr == nativeStackAddr || jsStackAddr == nativeStackAddr)) {
-      nativeStackAddr = nullptr;
-      nativeIndex--;
-      MOZ_ASSERT(pseudoStackAddr || jsStackAddr);
-    }
-
-    // Sanity checks.
-    MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
-                                   pseudoStackAddr != nativeStackAddr);
-    MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
-                               jsStackAddr != nativeStackAddr);
-    MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
-                                   nativeStackAddr != jsStackAddr);
-
-    // Check to see if pseudoStack frame is top-most.
-    if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
-      MOZ_ASSERT(pseudoIndex < pseudoCount);
-      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
-      addPseudoEntry(pseudoFrame, aInfo, pseudoStack, nullptr);
-      pseudoIndex++;
-      continue;
-    }
-
-    // Check to see if JS jit stack frame is top-most
-    if (jsStackAddr > nativeStackAddr) {
-      MOZ_ASSERT(jsIndex >= 0);
-      const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
-
-      // Stringifying non-wasm JIT frames is delayed until streaming
-      // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
-      // store the JIT code address (OptInfoAddr) in the circular buffer.
-      //
-      // Note that we cannot do this when we are sychronously sampling the
-      // 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 ||
-          jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
-        addDynamicCodeLocationTag(aInfo, jsFrame.label);
-      } else {
-        MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
-                   jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
-        aInfo.addTag(ProfileEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
-      }
-
-      jsIndex--;
-      continue;
-    }
-
-    // If we reach here, there must be a native stack entry and it must be the
-    // greatest entry.
-    if (nativeStackAddr) {
-      MOZ_ASSERT(nativeIndex >= 0);
-      aInfo
-        .addTag(ProfileEntry::NativeLeafAddr((void*)aNativeStack.pc_array[nativeIndex]));
-    }
-    if (nativeIndex >= 0) {
-      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) {
-    MOZ_ASSERT(aInfo.bufferGeneration() >= startBufferGen);
-    uint32_t lapCount = aInfo.bufferGeneration() - startBufferGen;
-    JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
-                                               aInfo.bufferGeneration(),
-                                               lapCount);
-  }
-}
-
-#ifdef USE_NS_STACKWALK
-static
-void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
-                       void* aClosure)
-{
-  NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
-  MOZ_ASSERT(nativeStack->count < nativeStack->size);
-  nativeStack->sp_array[nativeStack->count] = aSP;
-  nativeStack->pc_array[nativeStack->count] = aPC;
-  nativeStack->count++;
-}
-
-void
-Sampler::doNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
-{
-  void* pc_array[1000];
-  void* sp_array[1000];
-  NativeStack nativeStack = {
-    pc_array,
-    sp_array,
-    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(/* frameNumber */ 0, aSample->pc, aSample->sp, &nativeStack);
-
-  uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
-  // win X64 doesn't support disabling frame pointers emission so we need
-  // to fallback to using StackWalk64 which is slower.
-#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(V8_HOST_ARCH_X64))
-  void* stackEnd = aSample->threadInfo->StackTop();
-  if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd) {
-    FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
-                          &nativeStack, reinterpret_cast<void**>(aSample->fp),
-                          stackEnd);
-  }
-#else
-  void *platformData = nullptr;
-
-  uintptr_t thread = GetThreadHandle(aSample->threadInfo->GetPlatformData());
-  MOZ_ASSERT(thread);
-  MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack,
-               thread, platformData);
-#endif
-
-  mergeStacksIntoProfile(aInfo, aSample, nativeStack);
-}
-#endif
-
-
-#ifdef USE_EHABI_STACKWALK
-void
-Sampler::doNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
-{
-  void *pc_array[1000];
-  void *sp_array[1000];
-  NativeStack nativeStack = {
-    pc_array,
-    sp_array,
-    mozilla::ArrayLength(pc_array),
-    0
-  };
-
-  const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
-  mcontext_t savedContext;
-  PseudoStack* pseudoStack = aInfo.Stack();
-
-  nativeStack.count = 0;
-  // 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).
-    volatile StackEntry &entry = pseudoStack->mStack[i - 1];
-    if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
-      // Found JIT entry frame.  Unwind up to that point (i.e., force
-      // the stack walk to stop before the block of saved registers;
-      // note that it yields nondecreasing stack pointers), then restore
-      // the saved state.
-      uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
-
-      nativeStack.count += EHABIStackWalk(*mcontext,
-                                          /* stackBase = */ vSP,
-                                          sp_array + nativeStack.count,
-                                          pc_array + nativeStack.count,
-                                          nativeStack.size - nativeStack.count);
-
-      memset(&savedContext, 0, sizeof(savedContext));
-      // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
-      savedContext.arm_r4 = *vSP++;
-      savedContext.arm_r5 = *vSP++;
-      savedContext.arm_r6 = *vSP++;
-      savedContext.arm_r7 = *vSP++;
-      savedContext.arm_r8 = *vSP++;
-      savedContext.arm_r9 = *vSP++;
-      savedContext.arm_r10 = *vSP++;
-      savedContext.arm_fp = *vSP++;
-      savedContext.arm_lr = *vSP++;
-      savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
-      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(),
-                                      sp_array + nativeStack.count,
-                                      pc_array + nativeStack.count,
-                                      nativeStack.size - nativeStack.count);
-
-  mergeStacksIntoProfile(aInfo, aSample, nativeStack);
-}
-#endif
-
-
-#ifdef USE_LUL_STACKWALK
-void
-Sampler::doNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
-{
-  const mcontext_t* mc
-    = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
-
-  lul::UnwindRegs startRegs;
-  memset(&startRegs, 0, sizeof(startRegs));
-
-# if defined(SPS_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]);
-# elif defined(SPS_PLAT_arm_android)
-  startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
-  startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
-  startRegs.r13 = lul::TaggedUWord(mc->arm_sp);
-  startRegs.r12 = lul::TaggedUWord(mc->arm_ip);
-  startRegs.r11 = lul::TaggedUWord(mc->arm_fp);
-  startRegs.r7  = lul::TaggedUWord(mc->arm_r7);
-# elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
-  startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
-  startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
-  startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
-# else
-#   error "Unknown plat"
-# endif
-
-  /* Copy up to N_STACK_BYTES from rsp-REDZONE upwards, but not
-     going past the stack's registered top point.  Do some basic
-     sanity checks too.  This assumes that the TaggedUWord holding
-     the stack pointer value is valid, but it should be, since it
-     was constructed that way in the code just above. */
-
-  lul::StackImage stackImg;
-
-  {
-#   if defined(SPS_PLAT_amd64_linux)
-    uintptr_t rEDZONE_SIZE = 128;
-    uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
-#   elif defined(SPS_PLAT_arm_android)
-    uintptr_t rEDZONE_SIZE = 0;
-    uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
-#   elif defined(SPS_PLAT_x86_linux) || defined(SPS_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>(aInfo.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;
-    }
-    MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
-    stackImg.mLen       = nToCopy;
-    stackImg.mStartAvma = start;
-    if (nToCopy > 0) {
-      memcpy(&stackImg.mContents[0], (void*)start, nToCopy);
-      (void)VALGRIND_MAKE_MEM_DEFINED(&stackImg.mContents[0], nToCopy);
-    }
-  }
-
-  // The maximum number of frames that LUL will produce.  Setting it
-  // too high gives a risk of it wasting a lot of time looping on
-  // corrupted stacks.
-  const int MAX_NATIVE_FRAMES = 256;
-
-  size_t scannedFramesAllowed = 0;
-
-  uintptr_t framePCs[MAX_NATIVE_FRAMES];
-  uintptr_t frameSPs[MAX_NATIVE_FRAMES];
-  size_t framesAvail = mozilla::ArrayLength(framePCs);
-  size_t framesUsed  = 0;
-  size_t scannedFramesAcquired = 0;
-  sLUL->Unwind( &framePCs[0], &frameSPs[0],
-                &framesUsed, &scannedFramesAcquired,
-                framesAvail, scannedFramesAllowed,
-                &startRegs, &stackImg );
-
-  NativeStack nativeStack = {
-    reinterpret_cast<void**>(framePCs),
-    reinterpret_cast<void**>(frameSPs),
-    mozilla::ArrayLength(framePCs),
-    0
-  };
-
-  nativeStack.count = framesUsed;
-
-  mergeStacksIntoProfile(aInfo, aSample, nativeStack);
-
-  // Update stats in the LUL stats object.  Unfortunately this requires
-  // three global memory operations.
-  sLUL->mStats.mContext += 1;
-  sLUL->mStats.mCFI     += framesUsed - 1 - scannedFramesAcquired;
-  sLUL->mStats.mScanned += scannedFramesAcquired;
-}
-#endif
-
-
-static void
-doSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample,
-                   bool aAddLeafAddresses)
-{
-  NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
-  mergeStacksIntoProfile(aInfo, aSample, nativeStack);
-
-#ifdef ENABLE_LEAF_DATA
-  if (aSample && aAddLeafAddresses) {
-    aInfo.addTag(ProfileEntry::NativeLeafAddr((void*)aSample->pc));
-  }
-#endif
-}
-
-void
-Sampler::Tick(TickSample* sample)
-{
-  // Don't allow for ticks to happen within other ticks.
-  InplaceTick(sample);
-}
-
-void
-Sampler::InplaceTick(TickSample* sample)
-{
-  ThreadInfo& currThreadInfo = *sample->threadInfo;
-
-  currThreadInfo.addTag(ProfileEntry::ThreadId(currThreadInfo.ThreadId()));
-
-  mozilla::TimeDuration delta = sample->timestamp - sStartTime;
-  currThreadInfo.addTag(ProfileEntry::Time(delta.ToMilliseconds()));
-
-  PseudoStack* stack = currThreadInfo.Stack();
-
-#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
-    defined(USE_LUL_STACKWALK)
-  if (mUseStackWalk) {
-    doNativeBacktrace(currThreadInfo, sample);
-  } else {
-    doSampleStackTrace(currThreadInfo, sample, mAddLeafAddresses);
-  }
-#else
-  doSampleStackTrace(currThreadInfo, sample, mAddLeafAddresses);
-#endif
-
-  // Don't process the PeudoStack's markers if we're
-  // synchronously sampling the current thread.
-  if (!sample->isSamplingCurrentThread) {
-    ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
-    while (pendingMarkersList && pendingMarkersList->peek()) {
-      ProfilerMarker* marker = pendingMarkersList->popHead();
-      currThreadInfo.addStoredMarker(marker);
-      currThreadInfo.addTag(ProfileEntry::Marker(marker));
-    }
-  }
-
-  if (currThreadInfo.GetThreadResponsiveness()->HasData()) {
-    mozilla::TimeDuration delta = currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
-    currThreadInfo.addTag(ProfileEntry::Responsiveness(delta.ToMilliseconds()));
-  }
-
-  // rssMemory is equal to 0 when we are not recording.
-  if (sample->rssMemory != 0) {
-    currThreadInfo.addTag(ProfileEntry::ResidentMemory(static_cast<double>(sample->rssMemory)));
-  }
-
-  // ussMemory is equal to 0 when we are not recording.
-  if (sample->ussMemory != 0) {
-    currThreadInfo.addTag(ProfileEntry::UnsharedMemory(static_cast<double>(sample->ussMemory)));
-  }
-
-  if (gLastFrameNumber != gFrameNumber) {
-    currThreadInfo.addTag(ProfileEntry::FrameNumber(gFrameNumber));
-    gLastFrameNumber = gFrameNumber;
-  }
-}
-
-namespace {
-
-SyncProfile* NewSyncProfile()
-{
-  PseudoStack* stack = tlsPseudoStack.get();
-  if (!stack) {
-    MOZ_ASSERT(stack);
-    return nullptr;
-  }
-  Thread::tid_t tid = Thread::GetCurrentId();
-
-  return new SyncProfile(tid, stack);
-}
-
-} // namespace
-
-SyncProfile*
-Sampler::GetBacktrace()
-{
-  SyncProfile* profile = NewSyncProfile();
-
-  TickSample sample;
-  sample.threadInfo = profile;
-
-#if defined(HAVE_NATIVE_UNWIND) || defined(USE_LUL_STACKWALK)
-#if defined(XP_WIN) || defined(LINUX)
-  tickcontext_t context;
-  sample.PopulateContext(&context);
-#elif defined(XP_MACOSX)
-  sample.PopulateContext(nullptr);
-#endif
-#endif
-
-  sample.isSamplingCurrentThread = true;
-  sample.timestamp = mozilla::TimeStamp::Now();
-
-  profile->BeginUnwind();
-  Tick(&sample);
-  profile->EndUnwind();
-
-  return profile;
-}
-
-void
-Sampler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
-                       uint32_t *aGeneration)
-{
-  *aCurrentPosition = mBuffer->mWritePos;
-  *aTotalSize = gEntrySize;
-  *aGeneration = mBuffer->mGeneration;
-}
-
-static bool
-ThreadSelected(const char* aThreadName)
-{
-  StaticMutexAutoLock lock(gThreadNameFiltersMutex);
-
-  if (gThreadNameFilters.empty()) {
-    return true;
-  }
-
-  std::string name = aThreadName;
-  std::transform(name.begin(), name.end(), name.begin(), ::tolower);
-
-  for (uint32_t i = 0; i < gThreadNameFilters.length(); ++i) {
-    std::string filter = gThreadNameFilters[i];
-    std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
-
-    // Crude, non UTF-8 compatible, case insensitive substring search
-    if (name.find(filter) != std::string::npos) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-void
-Sampler::RegisterThread(ThreadInfo* aInfo)
-{
-  if (!aInfo->IsMainThread() && !mProfileThreads) {
-    return;
-  }
-
-  if (!ThreadSelected(aInfo->Name())) {
-    return;
-  }
-
-  aInfo->SetProfile(mBuffer);
-}
-
-size_t
-Sampler::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
-  size_t n = aMallocSizeOf(this);
-  n += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
-
-  {
-    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
-
-    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
-      ThreadInfo* info = sRegisteredThreads->at(i);
-
-      n += info->SizeOfIncludingThis(aMallocSizeOf);
-    }
-  }
-
-  // Measurement of the following members may be added later if DMD finds it
-  // is worthwhile:
-  // - memory pointed to by the elements within mBuffer
-  // - sRegisteredThreads
-  // - mThreadNameFilters
-  // - mFeatures
-
-  return n;
-}
--- a/tools/profiler/core/ThreadInfo.cpp
+++ b/tools/profiler/core/ThreadInfo.cpp
@@ -10,17 +10,17 @@
 
 ThreadInfo::ThreadInfo(const char* aName, int aThreadId,
                        bool aIsMainThread, PseudoStack* aPseudoStack,
                        void* aStackTop)
   : mName(strdup(aName))
   , mThreadId(aThreadId)
   , mIsMainThread(aIsMainThread)
   , mPseudoStack(aPseudoStack)
-  , mPlatformData(Sampler::AllocPlatformData(aThreadId))
+  , mPlatformData(AllocPlatformData(aThreadId))
   , mStackTop(aStackTop)
   , mPendingDelete(false)
   , mMutex(MakeUnique<mozilla::Mutex>("ThreadInfo::mMutex"))
 #ifdef XP_LINUX
   , mRssMemory(0)
   , mUssMemory(0)
 #endif
 {
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -36,17 +36,17 @@ class ThreadInfo {
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
  private:
   mozilla::UniqueFreePtr<char> mName;
   int mThreadId;
   const bool mIsMainThread;
   PseudoStack* mPseudoStack;
-  Sampler::UniquePlatformData mPlatformData;
+  UniquePlatformData mPlatformData;
   void* mStackTop;
 
   // May be null for the main thread if the profiler was started during startup.
   nsCOMPtr<nsIThread> mThread;
 
   bool mPendingDelete;
 
   //
--- a/tools/profiler/core/platform-linux.cc
+++ b/tools/profiler/core/platform-linux.cc
@@ -65,26 +65,16 @@
 #include <stdarg.h>
 
 #include "prenv.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/LinuxSignal.h"
 #include "mozilla/DebugOnly.h"
 #include "ProfileEntry.h"
 
-#if defined(__ARM_EABI__) && defined(ANDROID)
- // Should also work on other Android and ARM Linux, but not tested there yet.
-# define USE_EHABI_STACKWALK
-# include "EHABIStackWalk.h"
-#elif defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
-# define USE_LUL_STACKWALK
-# include "lul/LulMain.h"
-# include "lul/platform-linux-lul.h"
-#endif
-
 // Memory profile
 #include "nsMemoryReporterManager.h"
 
 #include <string.h>
 #include <list>
 
 using namespace mozilla;
 
@@ -95,17 +85,17 @@ static struct sigaction gOldSigprofSigna
 
 // All accesses to these two variables are on the main thread, so no locking is
 // needed.
 static bool gHasSignalSenderLaunched;
 static pthread_t gSignalSenderThread;
 
 #if defined(USE_LUL_STACKWALK)
 // A singleton instance of the library.  It is initialised at first
-// use.  Currently only the main thread can call Sampler::Start, so
+// use.  Currently only the main thread can call PlatformStart(), so
 // there is no need for a mechanism to ensure that it is only
 // created once in a multi-thread-use situation.
 lul::LUL* sLUL = nullptr;
 
 // This is the sLUL initialization routine.
 static void sLUL_initialization_routine(void)
 {
   MOZ_ASSERT(!sLUL);
@@ -130,37 +120,31 @@ Thread::GetCurrentId()
 // it to fail and be restarted; hence forward progress is delayed a
 // great deal.  A side effect of this is to permanently disable
 // sampling in the child process.  See bug 837390.
 
 // Unfortunately this is only doable on non-Android, since Bionic
 // doesn't have pthread_atfork.
 
 // This records the current state at the time we paused it.
-static bool was_paused = false;
+static bool gWasPaused = false;
 
 // In the parent, just before the fork, record the pausedness state,
 // and then pause.
 static void paf_prepare(void) {
-  // XXX: this is an off-main-thread(?) use of gSampler
-  if (gSampler) {
-    was_paused = gSampler->IsPaused();
-    gSampler->SetPaused(true);
-  } else {
-    was_paused = false;
-  }
+  // This function can run off the main thread.
+  gWasPaused = gIsPaused;
+  gIsPaused = true;
 }
 
 // In the parent, just after the fork, return pausedness to the
 // pre-fork state.
 static void paf_parent(void) {
-  // XXX: this is an off-main-thread(?) use of gSampler
-  if (gSampler) {
-    gSampler->SetPaused(was_paused);
-  }
+  // This function can run off the main thread.
+  gIsPaused = gWasPaused;
 }
 
 // Set up the fork handlers.
 static void* setup_atfork() {
   pthread_atfork(paf_prepare, paf_parent, NULL);
   return NULL;
 }
 #endif /* !defined(ANDROID) */
@@ -227,31 +211,29 @@ void ProfilerSignalHandler(int signal, s
 
   // Extract the current pc and sp.
   SetSampleContext(sample, context);
   sample->threadInfo = sCurrentThreadInfo;
   sample->timestamp = mozilla::TimeStamp::Now();
   sample->rssMemory = sample->threadInfo->mRssMemory;
   sample->ussMemory = sample->threadInfo->mUssMemory;
 
-  // XXX: this is an off-main-thread(?) use of gSampler
-  gSampler->Tick(sample);
+  Tick(sample);
 
   sCurrentThreadInfo = NULL;
   sem_post(&sSignalHandlingDone);
   errno = savedErrno;
 }
 
 } // namespace
 
 static void
 ProfilerSignalThread(ThreadInfo* aInfo, bool aIsFirstProfiledThread)
 {
-  // XXX: this is an off-main-thread(?) use of gSampler
-  if (aIsFirstProfiledThread && gSampler->ProfileMemory()) {
+  if (aIsFirstProfiledThread && gProfileMemory) {
     aInfo->mRssMemory = nsMemoryReporterManager::ResidentFast();
     aInfo->mUssMemory = nsMemoryReporterManager::ResidentUnique();
   } else {
     aInfo->mRssMemory = 0;
     aInfo->mUssMemory = 0;
   }
 }
 
@@ -267,44 +249,45 @@ class PlatformData {
   }
 
   ~PlatformData()
   {
     MOZ_COUNT_DTOR(PlatformData);
   }
 };
 
-/* static */ auto
-Sampler::AllocPlatformData(int aThreadId) -> UniquePlatformData
+UniquePlatformData
+AllocPlatformData(int aThreadId)
 {
   return UniquePlatformData(new PlatformData);
 }
 
 void
-Sampler::PlatformDataDestructor::operator()(PlatformData* aData)
+PlatformDataDestructor::operator()(PlatformData* aData)
 {
   delete aData;
 }
 
 static void* SignalSender(void* arg) {
+  // This function runs on its own thread.
+
   // Taken from platform_thread_posix.cc
   prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0);
 
   int vm_tgid_ = getpid();
   DebugOnly<int> my_tid = gettid();
 
   unsigned int nSignalsSent = 0;
 
   TimeDuration lastSleepOverhead = 0;
   TimeStamp sampleStart = TimeStamp::Now();
-  // XXX: this loop is an off-main-thread use of gSampler
-  while (gSampler->IsActive()) {
-    gSampler->DeleteExpiredMarkers();
+  while (gIsActive) {
+    gBuffer->deleteExpiredStoredMarkers();
 
-    if (!gSampler->IsPaused()) {
+    if (!gIsPaused) {
       StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
       bool isFirstProfiledThread = true;
       for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
         ThreadInfo* info = (*sRegisteredThreads)[i];
 
         // This will be null if we're not interested in profiling this thread.
         if (!info->hasProfile() || info->IsPendingDelete()) {
@@ -355,37 +338,42 @@ static void* SignalSender(void* arg) {
         if ((++nSignalsSent & 0xF) == 0) {
 #          if defined(USE_LUL_STACKWALK)
            sLUL->MaybeShowStats();
 #          endif
         }
       }
     }
 
+    // This off-main-thread use of gInterval is safe due to implicit
+    // synchronization -- this function cannot run at the same time as
+    // profiler_{start,stop}(), where gInterval is set.
     TimeStamp targetSleepEndTime =
-      sampleStart + TimeDuration::FromMicroseconds(gSampler->interval() * 1000);
+      sampleStart + TimeDuration::FromMicroseconds(gInterval * 1000);
     TimeStamp beforeSleep = TimeStamp::Now();
     TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
     double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
     OS::SleepMicro(sleepTime);
     sampleStart = TimeStamp::Now();
     lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
   }
   return 0;
 }
 
-void Sampler::Start() {
+static void
+PlatformStart()
+{
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   LOG("Sampler started");
 
 #if defined(USE_EHABI_STACKWALK)
   mozilla::EHABIStackWalkInit();
 #elif defined(USE_LUL_STACKWALK)
-  // NOTE: this isn't thread-safe.  But we expect Sampler::Start to be
+  // NOTE: this isn't thread-safe.  But we expect PlatformStart() to be
   // called only from the main thread, so this is OK in general.
   if (!sLUL) {
      sLUL_initialization_routine();
   }
 #endif
 
   // Initialize signal handler communication
   sCurrentThreadInfo = nullptr;
@@ -418,28 +406,31 @@ void Sampler::Start() {
      int nTests = 0, nTestsPassed = 0;
      RunLulUnitTests(&nTests, &nTestsPassed, sLUL);
   }
 #endif
 
   // Start a thread that sends SIGPROF signal to VM thread.
   // Sending the signal ourselves instead of relying on itimer provides
   // much better accuracy.
-  SetActive(true);
+  MOZ_ASSERT(!gIsActive);
+  gIsActive = true;
   if (pthread_create(&gSignalSenderThread, NULL, SignalSender, NULL) == 0) {
     gHasSignalSenderLaunched = true;
   }
   LOG("Profiler thread started");
 }
 
-
-void Sampler::Stop() {
+static void
+PlatformStop()
+{
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  SetActive(false);
+  MOZ_ASSERT(gIsActive);
+  gIsActive = false;
 
   // Wait for signal sender termination (it will exit after setting
   // active_ to false).
   if (gHasSignalSenderLaunched) {
     pthread_join(gSignalSenderThread, NULL);
     gHasSignalSenderLaunched = false;
   }
 
--- a/tools/profiler/core/platform-macos.cc
+++ b/tools/profiler/core/platform-macos.cc
@@ -68,24 +68,24 @@ class PlatformData {
 
  private:
   // Note: for profiled_thread_ Mach primitives are used instead of PThread's
   // because the latter doesn't provide thread manipulation primitives required.
   // For details, consult "Mac OS X Internals" book, Section 7.3.
   thread_act_t profiled_thread_;
 };
 
-/* static */ auto
-Sampler::AllocPlatformData(int aThreadId) -> UniquePlatformData
+UniquePlatformData
+AllocPlatformData(int aThreadId)
 {
   return UniquePlatformData(new PlatformData);
 }
 
 void
-Sampler::PlatformDataDestructor::operator()(PlatformData* aData)
+PlatformDataDestructor::operator()(PlatformData* aData)
 {
   delete aData;
 }
 
 // SamplerThread objects are used for creating and running threads. When the
 // Start() method is called the new thread starts running the Run() method in
 // the new thread. The SamplerThread object should not be deallocated before
 // the thread has terminated.
@@ -131,36 +131,39 @@ public:
   void Join() {
     pthread_join(mThread, NULL);
   }
 
   static void AddActiveSampler() {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
     if (mInstance == NULL) {
-      mInstance = new SamplerThread(gSampler->interval());
+      mInstance = new SamplerThread(gInterval);
       mInstance->Start();
     }
   }
 
   static void RemoveActiveSampler() {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
     mInstance->Join();
     delete mInstance;
     mInstance = NULL;
   }
 
   void Run() {
+    // This function runs on the sampler thread.
+
     TimeDuration lastSleepOverhead = 0;
     TimeStamp sampleStart = TimeStamp::Now();
 
-    // XXX: this loop is an off-main-thread use of gSampler
-    while (gSampler->IsActive()) {
-      gSampler->DeleteExpiredMarkers();
+    while (gIsActive) {
+      gBuffer->deleteExpiredStoredMarkers();
 
-      if (!gSampler->IsPaused()) {
+      if (!gIsPaused) {
         StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*sRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->hasProfile() || info->IsPendingDelete()) {
@@ -197,18 +200,17 @@ public:
 
     TickSample sample_obj;
     TickSample* sample = &sample_obj;
 
     // Unique Set Size is not supported on Mac.
     sample->ussMemory = 0;
     sample->rssMemory = 0;
 
-    // XXX: this is an off-main-thread use of gSampler
-    if (isFirstProfiledThread && gSampler->ProfileMemory()) {
+    if (isFirstProfiledThread && gProfileMemory) {
       sample->rssMemory = nsMemoryReporterManager::ResidentFast();
     }
 
     // We're using thread_suspend on OS X because pthread_kill (which is what
     // we're using on Linux) has less consistent performance and causes
     // strange crashes, see bug 1166778 and bug 1166808.
 
     if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
@@ -240,18 +242,17 @@ public:
                          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));
       sample->timestamp = mozilla::TimeStamp::Now();
       sample->threadInfo = aThreadInfo;
 
-      // XXX: this is an off-main-thread use of gSampler
-      gSampler->Tick(sample);
+      Tick(sample);
     }
     thread_resume(profiled_thread);
   }
 
 private:
   pthread_t mThread;
 
   int mIntervalMicro;
@@ -260,25 +261,29 @@ private:
 
   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
 };
 
 #undef REGISTER_FIELD
 
 SamplerThread* SamplerThread::mInstance = NULL;
 
-void Sampler::Start() {
-  MOZ_ASSERT(!IsActive());
-  SetActive(true);
+static void
+PlatformStart()
+{
+  MOZ_ASSERT(!gIsActive);
+  gIsActive = true;
   SamplerThread::AddActiveSampler();
 }
 
-void Sampler::Stop() {
-  MOZ_ASSERT(IsActive());
-  SetActive(false);
+static void
+PlatformStop()
+{
+  MOZ_ASSERT(gIsActive);
+  gIsActive = false;
   SamplerThread::RemoveActiveSampler();
 }
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return gettid();
 }
--- a/tools/profiler/core/platform-win32.cc
+++ b/tools/profiler/core/platform-win32.cc
@@ -59,30 +59,30 @@ class PlatformData {
   }
 
   HANDLE profiled_thread() { return profiled_thread_; }
 
  private:
   HANDLE profiled_thread_;
 };
 
-/* static */ auto
-Sampler::AllocPlatformData(int aThreadId) -> UniquePlatformData
+UniquePlatformData
+AllocPlatformData(int aThreadId)
 {
   return UniquePlatformData(new PlatformData(aThreadId));
 }
 
 void
-Sampler::PlatformDataDestructor::operator()(PlatformData* aData)
+PlatformDataDestructor::operator()(PlatformData* aData)
 {
   delete aData;
 }
 
 uintptr_t
-Sampler::GetThreadHandle(PlatformData* aData)
+GetThreadHandle(PlatformData* aData)
 {
   return (uintptr_t) aData->profiled_thread();
 }
 
 static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
 
 // SamplerThread objects are used for creating and running threads. When the
 // Start() method is called the new thread starts running the Run() method in
@@ -135,41 +135,44 @@ class SamplerThread
       WaitForSingleObject(mThread, INFINITE);
     }
   }
 
   static void StartSampler() {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
     if (mInstance == NULL) {
-      mInstance = new SamplerThread(gSampler->interval());
+      mInstance = new SamplerThread(gInterval);
       mInstance->Start();
     } else {
-      MOZ_ASSERT(mInstance->mInterval == gSampler->interval());
+      MOZ_ASSERT(mInstance->mInterval == gInterval);
     }
   }
 
   static void StopSampler() {
+    MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
     mInstance->Join();
     delete mInstance;
     mInstance = NULL;
   }
 
   void Run() {
+    // This function runs on the sampler thread.
+
     // By default we'll not adjust the timer resolution which tends to be around
     // 16ms. However, if the requested interval is sufficiently low we'll try to
     // adjust the resolution to match.
     if (mInterval < 10)
         ::timeBeginPeriod(mInterval);
 
-    // XXX: this loop is an off-main-thread use of gSampler
-    while (gSampler->IsActive()) {
-      gSampler->DeleteExpiredMarkers();
+    while (gIsActive) {
+      gBuffer->deleteExpiredStoredMarkers();
 
-      if (!gSampler->IsPaused()) {
+      if (!gIsPaused) {
         mozilla::StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
           ThreadInfo* info = (*sRegisteredThreads)[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->hasProfile() || info->IsPendingDelete()) {
@@ -193,34 +196,33 @@ class SamplerThread
 
     // disable any timer resolution changes we've made
     if (mInterval < 10)
         ::timeEndPeriod(mInterval);
   }
 
   void SampleContext(ThreadInfo* aThreadInfo, bool isFirstProfiledThread)
   {
-    uintptr_t thread = Sampler::GetThreadHandle(aThreadInfo->GetPlatformData());
+    uintptr_t thread = GetThreadHandle(aThreadInfo->GetPlatformData());
     HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
     if (profiled_thread == NULL)
       return;
 
     // Context used for sampling the register state of the profiled thread.
     CONTEXT context;
     memset(&context, 0, sizeof(context));
 
     TickSample sample_obj;
     TickSample* sample = &sample_obj;
 
     // Grab the timestamp before pausing the thread, to avoid deadlocks.
     sample->timestamp = mozilla::TimeStamp::Now();
     sample->threadInfo = aThreadInfo;
 
-    // XXX: this is an off-main-thread use of gSampler
-    if (isFirstProfiledThread && gSampler->ProfileMemory()) {
+    if (isFirstProfiledThread && gProfileMemory) {
       sample->rssMemory = nsMemoryReporterManager::ResidentFast();
     } else {
       sample->rssMemory = 0;
     }
 
     // Unique Set Size is not supported on Windows.
     sample->ussMemory = 0;
 
@@ -274,18 +276,17 @@ class SamplerThread
 #else
     sample->pc = reinterpret_cast<Address>(context.Eip);
     sample->sp = reinterpret_cast<Address>(context.Esp);
     sample->fp = reinterpret_cast<Address>(context.Ebp);
 #endif
 
     sample->context = &context;
 
-    // XXX: this is an off-main-thread use of gSampler
-    gSampler->Tick(sample);
+    Tick(sample);
 
     ResumeThread(profiled_thread);
   }
 
 private:
   int mStackSize;
   HANDLE mThread;
   Thread::tid_t mThreadId;
@@ -295,25 +296,29 @@ private:
   // Protects the process wide state below.
   static SamplerThread* mInstance;
 
   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
 };
 
 SamplerThread* SamplerThread::mInstance = NULL;
 
-void Sampler::Start() {
-  MOZ_ASSERT(!IsActive());
-  SetActive(true);
+static void
+PlatformStart()
+{
+  MOZ_ASSERT(!gIsActive);
+  gIsActive = true;
   SamplerThread::StartSampler();
 }
 
-void Sampler::Stop() {
-  MOZ_ASSERT(IsActive());
-  SetActive(false);
+static void
+PlatformStop()
+{
+  MOZ_ASSERT(gIsActive);
+  gIsActive = false;
   SamplerThread::StopSampler();
 }
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return GetCurrentThreadId();
 }
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -7,55 +7,85 @@
 #include <ostream>
 #include <fstream>
 #include <sstream>
 #include <errno.h>
 
 #include "platform.h"
 #include "PlatformMacros.h"
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Atomics.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Vector.h"
 #include "GeckoProfiler.h"
 #include "ProfilerIOInterposeObserver.h"
+#include "mozilla/StackWalk.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StaticPtr.h"
 #include "PseudoStack.h"
 #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 "nsXULAppAPI.h"
 #include "nsProfilerStartParams.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
 #include "mozilla/ProfileGatherer.h"
 #include "ProfilerMarkers.h"
+#include "shared-libraries.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 #endif
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   #include "FennecJNIWrappers.h"
 #endif
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
 #include "FennecJNINatives.h"
 #endif
 
+#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
+# define USE_NS_STACKWALK
+#endif
+
+// This should also work on ARM Linux, but not tested there yet.
+#if defined(__arm__) && defined(ANDROID)
+# define USE_EHABI_STACKWALK
+# include "EHABIStackWalk.h"
+#endif
+
 #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
 # define USE_LUL_STACKWALK
 # include "lul/LulMain.h"
 # include "lul/platform-linux-lul.h"
 #endif
 
+#ifdef MOZ_VALGRIND
+# include <valgrind/memcheck.h>
+#else
+# define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
+#endif
+
+#if defined(XP_WIN)
+typedef CONTEXT tickcontext_t;
+#elif defined(LINUX)
+#include <ucontext.h>
+typedef ucontext_t tickcontext_t;
+#endif
+
 using namespace mozilla;
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
 class GeckoJavaSampler : public mozilla::java::GeckoJavaSampler::Natives<GeckoJavaSampler>
 {
 private:
   GeckoJavaSampler();
 
@@ -72,52 +102,81 @@ public:
 MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
 
 static Sampler* gSampler;
 
 static std::vector<ThreadInfo*>* sRegisteredThreads = nullptr;
 static mozilla::StaticMutex sRegisteredThreadsMutex;
 
 // All accesses to gGatherer are on the main thread, so no locking is needed.
-static mozilla::StaticRefPtr<mozilla::ProfileGatherer> gGatherer;
+static StaticRefPtr<mozilla::ProfileGatherer> gGatherer;
+
+// XXX: gBuffer is used on multiple threads -- including via copies in
+// ThreadInfo::mBuffer -- without any apparent synchronization(!)
+static StaticRefPtr<ProfileBuffer> gBuffer;
 
 // gThreadNameFilters is accessed from multiple threads. All accesses to it
 // must be guarded by gThreadNameFiltersMutex.
 static Vector<std::string> gThreadNameFilters;
 static mozilla::StaticMutex gThreadNameFiltersMutex;
 
 // All accesses to gFeatures are on the main thread, so no locking is needed.
 static Vector<std::string> gFeatures;
 
 // All accesses to gEntrySize are on the main thread, so no locking is needed.
 static int gEntrySize = 0;
 
+// This variable is set on the main thread in profiler_{start,stop}(), and
+// mostly read on the main thread. There is one read off the main thread in
+// SignalSender() in platform-linux.cc which is safe because there is implicit
+// synchronization between that function and the set points in
+// profiler_{start,stop}().
+static double gInterval = 0;
+
+// XXX: These two variables are used extensively both on and off the main
+// thread. It's possible that code that checks them then unsafely assumes their
+// values don't subsequently change.
+static Atomic<bool> gIsActive(false);
+static Atomic<bool> gIsPaused(false);
+
 // We need to track whether we've been initialized otherwise
 // we end up using tlsStack without initializing it.
 // Because tlsStack is totally opaque to us we can't reuse
 // it as the flag itself.
 bool stack_key_initialized;
 
 // XXX: This is set by profiler_init() and profiler_start() on the main thread.
-// It is read off the main thread, e.g. by InplaceTick(). It might require more
+// It is read off the main thread, e.g. by Tick(). It might require more
 // inter-thread synchronization than it currently has.
 mozilla::TimeStamp sStartTime;
 
 // XXX: These are accessed by multiple threads and might require more
 // inter-thread synchronization than they currently have.
 static int gFrameNumber = 0;
 static int gLastFrameNumber = 0;
 
 int sInitCount = 0; // Each init must have a matched shutdown.
 
 static bool sIsProfiling = false; // is raced on
-static bool sIsGPUProfiling = false; // is raced on
-static bool sIsLayersDump = false; // is raced on
-static bool sIsDisplayListDump = false; // is raced on
-static bool sIsRestyleProfiling = false; // is raced on
+
+// All accesses to these are on the main thread, so no locking is needed.
+static bool gProfileJava = false;
+static bool gProfileJS = false;
+static bool gTaskTracer = false;
+
+// XXX: These are all accessed by multiple threads and might require more
+// inter-thread synchronization than they currently have.
+static Atomic<bool> gAddLeafAddresses(false);
+static Atomic<bool> gDisplayListDump(false);
+static Atomic<bool> gLayersDump(false);
+static Atomic<bool> gProfileGPU(false);
+static Atomic<bool> gProfileMemory(false);
+static Atomic<bool> gProfileRestyle(false);
+static Atomic<bool> gProfileThreads(false);
+static Atomic<bool> gUseStackWalk(false);
 
 // Environment variables to control the profiler
 const char* PROFILER_HELP = "MOZ_PROFILER_HELP";
 const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL";
 const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES";
 const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN";
 const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES";
 
@@ -133,16 +192,1064 @@ static int sProfileEntries;   /* how man
 
 static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
                                                             sInterposeObserver;
 
 // The name that identifies the gecko thread for calls to
 // profiler_register_thread.
 static const char * gGeckoThreadName = "GeckoMain";
 
+static bool
+CanNotifyObservers()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+#if defined(SPS_OS_android)
+  // Android ANR reporter uses the profiler off the main thread.
+  return NS_IsMainThread();
+#else
+  return true;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+// 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)
+  {}
+
+  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;
+};
+
+static void
+AddDynamicCodeLocationTag(ThreadInfo& aInfo, const char* aStr)
+{
+  aInfo.addTag(ProfileEntry::CodeLocation(""));
+
+  size_t strLen = strlen(aStr) + 1;   // +1 for the null terminator
+  for (size_t j = 0; j < strLen; ) {
+    // Store as many characters in the void* as the platform allows.
+    char text[sizeof(void*)];
+    size_t len = sizeof(void*) / sizeof(char);
+    if (j+len >= strLen) {
+      len = strLen - j;
+    }
+    memcpy(text, &aStr[j], len);
+    j += sizeof(void*) / sizeof(char);
+
+    // Cast to *((void**) to pass the text data to a void*.
+    aInfo.addTag(ProfileEntry::EmbeddedString(*((void**)(&text[0]))));
+  }
+}
+
+static void
+AddPseudoEntry(volatile StackEntry& entry, ThreadInfo& aInfo,
+               PseudoStack* stack, void* lastpc)
+{
+  // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations and
+  // should not be recorded in the profile.
+  if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS)) {
+    return;
+  }
+
+  int lineno = -1;
+
+  // First entry has kind CodeLocation. Check for magic pointer bit 1 to
+  // indicate copy.
+  const char* sampleLabel = entry.label();
+
+  if (entry.isCopyLabel()) {
+    // Store the string using 1 or more EmbeddedString tags.
+    // That will happen to the preceding tag.
+    AddDynamicCodeLocationTag(aInfo, sampleLabel);
+    if (entry.isJs()) {
+      JSScript* script = entry.script();
+      if (script) {
+        if (!entry.pc()) {
+          // The JIT only allows the top-most entry to have a nullptr pc.
+          MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
+
+          // If stack-walking was disabled, then that's just unfortunate.
+          if (lastpc) {
+            jsbytecode* jspc = js::ProfilingGetPC(stack->mContext, script,
+                                                  lastpc);
+            if (jspc) {
+              lineno = JS_PCToLineNumber(script, jspc);
+            }
+          }
+        } else {
+          lineno = JS_PCToLineNumber(script, entry.pc());
+        }
+      }
+    } else {
+      lineno = entry.line();
+    }
+  } else {
+    aInfo.addTag(ProfileEntry::CodeLocation(sampleLabel));
+
+    // XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
+    // js entries as well.
+    if (entry.isCpp()) {
+      lineno = entry.line();
+    }
+  }
+
+  if (lineno != -1) {
+    aInfo.addTag(ProfileEntry::LineNumber(lineno));
+  }
+
+  uint32_t category = entry.category();
+  MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
+  MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
+
+  if (category) {
+    aInfo.addTag(ProfileEntry::Category((int)category));
+  }
+}
+
+struct NativeStack
+{
+  void** pc_array;
+  void** sp_array;
+  size_t size;
+  size_t count;
+};
+
+mozilla::Atomic<bool> WALKING_JS_STACK(false);
+
+struct AutoWalkJSStack
+{
+  bool walkAllowed;
+
+  AutoWalkJSStack() : walkAllowed(false) {
+    walkAllowed = WALKING_JS_STACK.compareExchange(false, true);
+  }
+
+  ~AutoWalkJSStack() {
+    if (walkAllowed) {
+      WALKING_JS_STACK = false;
+    }
+  }
+};
+
+static void
+MergeStacksIntoProfile(ThreadInfo& aInfo, TickSample* aSample,
+                       NativeStack& aNativeStack)
+{
+  PseudoStack* pseudoStack = aInfo.Stack();
+  volatile StackEntry* 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
+                 ? UINT32_MAX
+                 : aInfo.bufferGeneration();
+  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;
+
+      JS::ProfilingFrameIterator jsIter(pseudoStack->mContext,
+                                        registerState,
+                                        startBufferGen);
+      for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
+        // See note below regarding 'J' entries.
+        if (aSample->isSamplingCurrentThread || jsIter.isWasm()) {
+          uint32_t extracted =
+            jsIter.extractStack(jsFrames, jsCount, maxFrames);
+          jsCount += extracted;
+          if (jsCount == maxFrames) {
+            break;
+          }
+        } else {
+          mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
+            jsIter.getPhysicalFrameWithoutLabel();
+          if (frame.isSome()) {
+            jsFrames[jsCount++] = frame.value();
+          }
+        }
+      }
+    }
+  }
+
+  // Start the sample with a root entry.
+  aInfo.addTag(ProfileEntry::Sample("(root)"));
+
+  // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
+  // native arrays are ordered youngest-to-oldest. We must add frames to aInfo
+  // oldest-to-youngest. Thus, iterate over the pseudo-stack forwards and JS
+  // and native arrays backwards. Note: this means the terminating condition
+  // jsIndex and nativeIndex is being < 0.
+  uint32_t pseudoIndex = 0;
+  int32_t jsIndex = jsCount - 1;
+  int32_t nativeIndex = aNativeStack.count - 1;
+
+  uint8_t* lastPseudoCppStackAddr = nullptr;
+
+  // Iterate as long as there is at least one frame remaining.
+  while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
+    // There are 1 to 3 frames available. Find and add the oldest.
+    uint8_t* pseudoStackAddr = nullptr;
+    uint8_t* jsStackAddr = nullptr;
+    uint8_t* nativeStackAddr = nullptr;
+
+    if (pseudoIndex != pseudoCount) {
+      volatile StackEntry& pseudoFrame = pseudoFrames[pseudoIndex];
+
+      if (pseudoFrame.isCpp()) {
+        lastPseudoCppStackAddr = (uint8_t*) pseudoFrame.stackAddress();
+      }
+
+      // Skip any pseudo-stack JS frames which are marked isOSR. Pseudostack
+      // frames are marked isOSR when the JS interpreter enters a jit frame on
+      // a loop edge (via on-stack-replacement, or OSR). To avoid both the
+      // pseudoframe and jit frame being recorded (and showing up twice), the
+      // interpreter marks the interpreter pseudostack entry with the OSR flag
+      // to ensure that it doesn't get counted.
+      if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
+          pseudoIndex++;
+          continue;
+      }
+
+      MOZ_ASSERT(lastPseudoCppStackAddr);
+      pseudoStackAddr = lastPseudoCppStackAddr;
+    }
+
+    if (jsIndex >= 0) {
+      jsStackAddr = (uint8_t*) jsFrames[jsIndex].stackAddress;
+    }
+
+    if (nativeIndex >= 0) {
+      nativeStackAddr = (uint8_t*) aNativeStack.sp_array[nativeIndex];
+    }
+
+    // If there's a native stack entry which has the same SP as a pseudo stack
+    // entry, pretend we didn't see the native stack entry.  Ditto for a native
+    // stack entry which has the same SP as a JS stack entry.  In effect this
+    // means pseudo or JS entries trump conflicting native entries.
+    if (nativeStackAddr && (pseudoStackAddr == nativeStackAddr ||
+                            jsStackAddr == nativeStackAddr)) {
+      nativeStackAddr = nullptr;
+      nativeIndex--;
+      MOZ_ASSERT(pseudoStackAddr || jsStackAddr);
+    }
+
+    // Sanity checks.
+    MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
+                                   pseudoStackAddr != nativeStackAddr);
+    MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
+                               jsStackAddr != nativeStackAddr);
+    MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
+                                   nativeStackAddr != jsStackAddr);
+
+    // Check to see if pseudoStack frame is top-most.
+    if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
+      MOZ_ASSERT(pseudoIndex < pseudoCount);
+      volatile StackEntry& pseudoFrame = pseudoFrames[pseudoIndex];
+      AddPseudoEntry(pseudoFrame, aInfo, pseudoStack, nullptr);
+      pseudoIndex++;
+      continue;
+    }
+
+    // Check to see if JS jit stack frame is top-most
+    if (jsStackAddr > nativeStackAddr) {
+      MOZ_ASSERT(jsIndex >= 0);
+      const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
+
+      // Stringifying non-wasm JIT frames is delayed until streaming time. To
+      // re-lookup the entry in the JitcodeGlobalTable, we need to store the
+      // JIT code address (OptInfoAddr) in the circular buffer.
+      //
+      // Note that we cannot do this when we are sychronously sampling the
+      // 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 ||
+          jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
+        AddDynamicCodeLocationTag(aInfo, jsFrame.label);
+      } else {
+        MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
+                   jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
+        aInfo.addTag(
+          ProfileEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
+      }
+
+      jsIndex--;
+      continue;
+    }
+
+    // If we reach here, there must be a native stack entry and it must be the
+    // greatest entry.
+    if (nativeStackAddr) {
+      MOZ_ASSERT(nativeIndex >= 0);
+      void* addr = (void*)aNativeStack.pc_array[nativeIndex];
+      aInfo.addTag(ProfileEntry::NativeLeafAddr(addr));
+    }
+    if (nativeIndex >= 0) {
+      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) {
+    MOZ_ASSERT(aInfo.bufferGeneration() >= startBufferGen);
+    uint32_t lapCount = aInfo.bufferGeneration() - startBufferGen;
+    JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
+                                               aInfo.bufferGeneration(),
+                                               lapCount);
+  }
+}
+
+#ifdef XP_WIN
+static uintptr_t GetThreadHandle(PlatformData* aData);
+#endif
+
+#ifdef USE_NS_STACKWALK
+static void
+StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+  NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
+  MOZ_ASSERT(nativeStack->count < nativeStack->size);
+  nativeStack->sp_array[nativeStack->count] = aSP;
+  nativeStack->pc_array[nativeStack->count] = aPC;
+  nativeStack->count++;
+}
+
+static void
+DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+{
+  void* pc_array[1000];
+  void* sp_array[1000];
+  NativeStack nativeStack = {
+    pc_array,
+    sp_array,
+    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);
+
+  uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
+
+#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(V8_HOST_ARCH_X64))
+  void* stackEnd = aSample->threadInfo->StackTop();
+  if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd) {
+    FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
+                          &nativeStack, reinterpret_cast<void**>(aSample->fp),
+                          stackEnd);
+  }
+#else
+  // Win64 always omits frame pointers so for it we use the slower
+  // MozStackWalk().
+  uintptr_t thread = GetThreadHandle(aSample->threadInfo->GetPlatformData());
+  MOZ_ASSERT(thread);
+  MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, &nativeStack,
+               thread, /* platformData */ nullptr);
+#endif
+
+  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+}
+#endif
+
+#ifdef USE_EHABI_STACKWALK
+static void
+DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+{
+  void* pc_array[1000];
+  void* sp_array[1000];
+  NativeStack nativeStack = {
+    pc_array,
+    sp_array,
+    mozilla::ArrayLength(pc_array),
+    0
+  };
+
+  const mcontext_t* mcontext =
+    &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
+  mcontext_t savedContext;
+  PseudoStack* pseudoStack = aInfo.Stack();
+
+  nativeStack.count = 0;
+
+  // 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).
+    volatile StackEntry& entry = pseudoStack->mStack[i - 1];
+    if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
+      // Found JIT entry frame.  Unwind up to that point (i.e., force
+      // the stack walk to stop before the block of saved registers;
+      // note that it yields nondecreasing stack pointers), then restore
+      // the saved state.
+      uint32_t* vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
+
+      nativeStack.count += EHABIStackWalk(*mcontext,
+                                          /* stackBase = */ vSP,
+                                          sp_array + nativeStack.count,
+                                          pc_array + nativeStack.count,
+                                          nativeStack.size - nativeStack.count);
+
+      memset(&savedContext, 0, sizeof(savedContext));
+
+      // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
+      savedContext.arm_r4  = *vSP++;
+      savedContext.arm_r5  = *vSP++;
+      savedContext.arm_r6  = *vSP++;
+      savedContext.arm_r7  = *vSP++;
+      savedContext.arm_r8  = *vSP++;
+      savedContext.arm_r9  = *vSP++;
+      savedContext.arm_r10 = *vSP++;
+      savedContext.arm_fp  = *vSP++;
+      savedContext.arm_lr  = *vSP++;
+      savedContext.arm_sp  = reinterpret_cast<uint32_t>(vSP);
+      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(),
+                                      sp_array + nativeStack.count,
+                                      pc_array + nativeStack.count,
+                                      nativeStack.size - nativeStack.count);
+
+  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+}
+#endif
+
+#ifdef USE_LUL_STACKWALK
+static void
+DoNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample)
+{
+  const mcontext_t* mc =
+    &reinterpret_cast<ucontext_t*>(aSample->context)->uc_mcontext;
+
+  lul::UnwindRegs startRegs;
+  memset(&startRegs, 0, sizeof(startRegs));
+
+#if defined(SPS_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]);
+#elif defined(SPS_PLAT_arm_android)
+  startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
+  startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
+  startRegs.r13 = lul::TaggedUWord(mc->arm_sp);
+  startRegs.r12 = lul::TaggedUWord(mc->arm_ip);
+  startRegs.r11 = lul::TaggedUWord(mc->arm_fp);
+  startRegs.r7  = lul::TaggedUWord(mc->arm_r7);
+#elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
+  startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
+  startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
+  startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
+#else
+# error "Unknown plat"
+#endif
+
+  // Copy up to N_STACK_BYTES from rsp-REDZONE upwards, but not going past the
+  // stack's registered top point.  Do some basic sanity checks too.  This
+  // assumes that the TaggedUWord holding the stack pointer value is valid, but
+  // it should be, since it was constructed that way in the code just above.
+
+  lul::StackImage stackImg;
+
+  {
+#if defined(SPS_PLAT_amd64_linux)
+    uintptr_t rEDZONE_SIZE = 128;
+    uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
+#elif defined(SPS_PLAT_arm_android)
+    uintptr_t rEDZONE_SIZE = 0;
+    uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
+#elif defined(SPS_PLAT_x86_linux) || defined(SPS_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>(aInfo.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;
+    }
+    MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
+    stackImg.mLen       = nToCopy;
+    stackImg.mStartAvma = start;
+    if (nToCopy > 0) {
+      memcpy(&stackImg.mContents[0], (void*)start, nToCopy);
+      (void)VALGRIND_MAKE_MEM_DEFINED(&stackImg.mContents[0], nToCopy);
+    }
+  }
+
+  // The maximum number of frames that LUL will produce.  Setting it
+  // too high gives a risk of it wasting a lot of time looping on
+  // corrupted stacks.
+  const int MAX_NATIVE_FRAMES = 256;
+
+  size_t scannedFramesAllowed = 0;
+
+  uintptr_t framePCs[MAX_NATIVE_FRAMES];
+  uintptr_t frameSPs[MAX_NATIVE_FRAMES];
+  size_t framesAvail = mozilla::ArrayLength(framePCs);
+  size_t framesUsed  = 0;
+  size_t scannedFramesAcquired = 0;
+  sLUL->Unwind(&framePCs[0], &frameSPs[0],
+               &framesUsed, &scannedFramesAcquired,
+               framesAvail, scannedFramesAllowed,
+               &startRegs, &stackImg );
+
+  NativeStack nativeStack = {
+    reinterpret_cast<void**>(framePCs),
+    reinterpret_cast<void**>(frameSPs),
+    mozilla::ArrayLength(framePCs),
+    0
+  };
+
+  nativeStack.count = framesUsed;
+
+  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+
+  // Update stats in the LUL stats object.  Unfortunately this requires
+  // three global memory operations.
+  sLUL->mStats.mContext += 1;
+  sLUL->mStats.mCFI     += framesUsed - 1 - scannedFramesAcquired;
+  sLUL->mStats.mScanned += scannedFramesAcquired;
+}
+#endif
+
+static void
+DoSampleStackTrace(ThreadInfo& aInfo, TickSample* aSample,
+                   bool aAddLeafAddresses)
+{
+  NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
+  MergeStacksIntoProfile(aInfo, aSample, nativeStack);
+
+#ifdef ENABLE_LEAF_DATA
+  if (aSample && aAddLeafAddresses) {
+    aInfo.addTag(ProfileEntry::NativeLeafAddr((void*)aSample->pc));
+  }
+#endif
+}
+
+// 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(TickSample* aSample)
+{
+  ThreadInfo& currThreadInfo = *aSample->threadInfo;
+
+  currThreadInfo.addTag(ProfileEntry::ThreadId(currThreadInfo.ThreadId()));
+
+  mozilla::TimeDuration delta = aSample->timestamp - sStartTime;
+  currThreadInfo.addTag(ProfileEntry::Time(delta.ToMilliseconds()));
+
+  PseudoStack* stack = currThreadInfo.Stack();
+
+#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
+    defined(USE_LUL_STACKWALK)
+  if (gUseStackWalk) {
+    DoNativeBacktrace(currThreadInfo, aSample);
+  } else {
+    DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+  }
+#else
+  DoSampleStackTrace(currThreadInfo, aSample, gAddLeafAddresses);
+#endif
+
+  // Don't process the PeudoStack's markers if we're synchronously sampling the
+  // current thread.
+  if (!aSample->isSamplingCurrentThread) {
+    ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
+    while (pendingMarkersList && pendingMarkersList->peek()) {
+      ProfilerMarker* marker = pendingMarkersList->popHead();
+      currThreadInfo.addStoredMarker(marker);
+      currThreadInfo.addTag(ProfileEntry::Marker(marker));
+    }
+  }
+
+  if (currThreadInfo.GetThreadResponsiveness()->HasData()) {
+    mozilla::TimeDuration delta =
+      currThreadInfo.GetThreadResponsiveness()->GetUnresponsiveDuration(
+        aSample->timestamp);
+    currThreadInfo.addTag(ProfileEntry::Responsiveness(delta.ToMilliseconds()));
+  }
+
+  // rssMemory is equal to 0 when we are not recording.
+  if (aSample->rssMemory != 0) {
+    currThreadInfo.addTag(ProfileEntry::ResidentMemory(
+      static_cast<double>(aSample->rssMemory)));
+  }
+
+  // ussMemory is equal to 0 when we are not recording.
+  if (aSample->ussMemory != 0) {
+    currThreadInfo.addTag(ProfileEntry::UnsharedMemory(
+      static_cast<double>(aSample->ussMemory)));
+  }
+
+  if (gLastFrameNumber != gFrameNumber) {
+    currThreadInfo.addTag(ProfileEntry::FrameNumber(gFrameNumber));
+    gLastFrameNumber = gFrameNumber;
+  }
+}
+
+// END tick/unwinding code
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// BEGIN saving/streaming code
+
+class ProfileSaveEvent final : public nsIProfileSaveEvent
+{
+public:
+  typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
+  NS_DECL_ISUPPORTS
+
+  ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
+    : mFunc(aFunc)
+    , mClosure(aClosure)
+  {}
+
+  NS_IMETHOD AddSubProfile(const char* aProfile) override {
+    mFunc(aProfile, mClosure);
+    return NS_OK;
+  }
+
+private:
+  ~ProfileSaveEvent() {}
+
+  AddSubProfileFunc mFunc;
+  void* mClosure;
+};
+
+NS_IMPL_ISUPPORTS(ProfileSaveEvent, nsIProfileSaveEvent)
+
+static void
+AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
+{
+  aStream << "{";
+  aStream << "\"start\":" << aLib.GetStart();
+  aStream << ",\"end\":" << aLib.GetEnd();
+  aStream << ",\"offset\":" << aLib.GetOffset();
+  aStream << ",\"name\":\"" << aLib.GetName() << "\"";
+  const std::string& breakpadId = aLib.GetBreakpadId();
+  aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
+
+#ifdef XP_WIN
+  // FIXME: remove this XP_WIN code when the profiler plugin has switched to
+  // using breakpadId.
+  std::string pdbSignature = breakpadId.substr(0, 32);
+  std::string pdbAgeStr = breakpadId.substr(32,  breakpadId.size() - 1);
+
+  std::stringstream stream;
+  stream << pdbAgeStr;
+
+  unsigned pdbAge;
+  stream << std::hex;
+  stream >> pdbAge;
+
+#ifdef DEBUG
+  std::ostringstream oStream;
+  oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
+  MOZ_ASSERT(breakpadId == oStream.str());
+#endif
+
+  aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
+  aStream << ",\"pdbAge\":" << pdbAge;
+  aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
+#endif
+
+  aStream << "}";
+}
+
+static std::string
+GetSharedLibraryInfoStringInternal()
+{
+  SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
+  if (info.GetSize() == 0) {
+    return "[]";
+  }
+
+  std::ostringstream os;
+  os << "[";
+  AddSharedLibraryInfoToStream(os, info.GetEntry(0));
+
+  for (size_t i = 1; i < info.GetSize(); i++) {
+    os << ",";
+    AddSharedLibraryInfoToStream(os, info.GetEntry(i));
+  }
+
+  os << "]";
+  return os.str();
+}
+
+static void
+StreamTaskTracer(SpliceableJSONWriter& aWriter)
+{
+#ifdef MOZ_TASK_TRACER
+  aWriter.StartArrayProperty("data");
+  {
+    UniquePtr<nsTArray<nsCString>> data =
+      mozilla::tasktracer::GetLoggedData(sStartTime);
+    for (uint32_t i = 0; i < data->Length(); ++i) {
+      aWriter.StringElement((data->ElementAt(i)).get());
+    }
+  }
+  aWriter.EndArray();
+
+  aWriter.StartArrayProperty("threads");
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+    for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
+      // Thread meta data
+      ThreadInfo* info = sRegisteredThreads->at(i);
+      aWriter.StartObjectElement();
+      {
+        if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
+          // TODO Add the proper plugin name
+          aWriter.StringProperty("name", "Plugin");
+        } else {
+          aWriter.StringProperty("name", info->Name());
+        }
+        aWriter.IntProperty("tid", static_cast<int>(info->ThreadId()));
+      }
+      aWriter.EndObject();
+    }
+  }
+  aWriter.EndArray();
+
+  aWriter.DoubleProperty(
+    "start", static_cast<double>(mozilla::tasktracer::GetStartTime()));
+#endif
+}
+
+static void
+StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  aWriter.IntProperty("version", 3);
+  aWriter.DoubleProperty("interval", gInterval);
+  aWriter.IntProperty("stackwalk", gUseStackWalk);
+
+#ifdef DEBUG
+  aWriter.IntProperty("debug", 1);
+#else
+  aWriter.IntProperty("debug", 0);
+#endif
+
+  aWriter.IntProperty("gcpoison", JS::IsGCPoisoning() ? 1 : 0);
+
+  bool asyncStacks = Preferences::GetBool("javascript.options.asyncstack");
+  aWriter.IntProperty("asyncstack", asyncStacks);
+
+  mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
+  aWriter.DoubleProperty(
+    "startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
+
+  aWriter.IntProperty("processType", XRE_GetProcessType());
+
+  nsresult res;
+  nsCOMPtr<nsIHttpProtocolHandler> http =
+    do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
+
+  if (!NS_FAILED(res)) {
+    nsAutoCString string;
+
+    res = http->GetPlatform(string);
+    if (!NS_FAILED(res)) {
+      aWriter.StringProperty("platform", string.Data());
+    }
+
+    res = http->GetOscpu(string);
+    if (!NS_FAILED(res)) {
+      aWriter.StringProperty("oscpu", string.Data());
+    }
+
+    res = http->GetMisc(string);
+    if (!NS_FAILED(res)) {
+      aWriter.StringProperty("misc", string.Data());
+    }
+  }
+
+  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+  if (runtime) {
+    nsAutoCString string;
+
+    res = runtime->GetXPCOMABI(string);
+    if (!NS_FAILED(res))
+      aWriter.StringProperty("abi", string.Data());
+
+    res = runtime->GetWidgetToolkit(string);
+    if (!NS_FAILED(res))
+      aWriter.StringProperty("toolkit", string.Data());
+  }
+
+  nsCOMPtr<nsIXULAppInfo> appInfo =
+    do_GetService("@mozilla.org/xre/app-info;1");
+
+  if (appInfo) {
+    nsAutoCString string;
+    res = appInfo->GetName(string);
+    if (!NS_FAILED(res))
+      aWriter.StringProperty("product", string.Data());
+  }
+}
+
+struct SubprocessClosure
+{
+  explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
+    : mWriter(aWriter)
+  {}
+
+  SpliceableJSONWriter* mWriter;
+};
+
+static void
+SubProcessCallback(const char* aProfile, void* aClosure)
+{
+  // Called by the observer to get their profile data included as a sub profile.
+  SubprocessClosure* closure = (SubprocessClosure*)aClosure;
+
+  // Add the string profile into the profile.
+  closure->mWriter->StringElement(aProfile);
+}
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+static void
+BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
+{
+  aWriter.StringProperty("name", "Java Main Thread");
+
+  aWriter.StartArrayProperty("samples");
+  {
+    for (int sampleId = 0; true; sampleId++) {
+      bool firstRun = true;
+      for (int frameId = 0; true; frameId++) {
+        jni::String::LocalRef frameName =
+            java::GeckoJavaSampler::GetFrameName(0, sampleId, frameId);
+
+        // When we run out of frames, we stop looping.
+        if (!frameName) {
+          // If we found at least one frame, we have objects to close.
+          if (!firstRun) {
+            aWriter.EndArray();
+            aWriter.EndObject();
+          }
+          break;
+        }
+        // The first time around, open the sample object and frames array.
+        if (firstRun) {
+          firstRun = false;
+
+          double sampleTime =
+              java::GeckoJavaSampler::GetSampleTime(0, sampleId);
+
+          aWriter.StartObjectElement();
+            aWriter.DoubleProperty("time", sampleTime);
+
+            aWriter.StartArrayProperty("frames");
+        }
+
+        // Add a frame to the sample.
+        aWriter.StartObjectElement();
+        {
+          aWriter.StringProperty("location",
+                                 frameName->ToCString().BeginReading());
+        }
+        aWriter.EndObject();
+      }
+
+      // If we found no frames for this sample, we are done.
+      if (firstRun) {
+        break;
+      }
+    }
+  }
+  aWriter.EndArray();
+}
+#endif
+
+static void
+StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
+  {
+    // Put shared library info
+    aWriter.StringProperty("libs",
+                           GetSharedLibraryInfoStringInternal().c_str());
+
+    // Put meta data
+    aWriter.StartObjectProperty("meta");
+    {
+      StreamMetaJSCustomObject(aWriter);
+    }
+    aWriter.EndObject();
+
+    // Data of TaskTracer doesn't belong in the circular buffer.
+    if (gTaskTracer) {
+      aWriter.StartObjectProperty("tasktracer");
+      StreamTaskTracer(aWriter);
+      aWriter.EndObject();
+    }
+
+    // Lists the samples for each thread profile
+    aWriter.StartArrayProperty("threads");
+    {
+      gIsPaused = true;
+
+      {
+        StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+        for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
+          // Thread not being profiled, skip it
+          ThreadInfo* info = sRegisteredThreads->at(i);
+          if (!info->hasProfile()) {
+            continue;
+          }
+
+          // Note that we intentionally include thread profiles which
+          // have been marked for pending delete.
+
+          MutexAutoLock lock(info->GetMutex());
+
+          info->StreamJSON(aWriter, aSinceTime);
+        }
+      }
+
+      if (CanNotifyObservers()) {
+        // Send a event asking any subprocesses (plugins) to
+        // give us their information
+        SubprocessClosure closure(&aWriter);
+        nsCOMPtr<nsIObserverService> os =
+          mozilla::services::GetObserverService();
+        if (os) {
+          RefPtr<ProfileSaveEvent> pse =
+            new ProfileSaveEvent(SubProcessCallback, &closure);
+          os->NotifyObservers(pse, "profiler-subprocess", nullptr);
+        }
+      }
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+      if (gProfileJava) {
+        java::GeckoJavaSampler::Pause();
+
+        aWriter.Start();
+        {
+          BuildJavaThreadJSObject(aWriter);
+        }
+        aWriter.End();
+
+        java::GeckoJavaSampler::Unpause();
+      }
+#endif
+
+      gIsPaused = false;
+    }
+    aWriter.EndArray();
+  }
+  aWriter.End();
+}
+
+UniquePtr<char[]>
+ToJSON(double aSinceTime)
+{
+  SpliceableChunkedJSONWriter b;
+  StreamJSON(b, aSinceTime);
+  return b.WriteFunc()->CopyData();
+}
+
+static JSObject*
+ToJSObject(JSContext* aCx, double aSinceTime)
+{
+  JS::RootedValue val(aCx);
+  {
+    UniquePtr<char[]> buf = ToJSON(aSinceTime);
+    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
+    MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
+                                 js_string.Length(), &val));
+  }
+  return &val.toObject();
+}
+
+static void
+ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0)
+{
+  SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
+  StreamJSON(b, aSinceTime);
+}
+
+// END saving/streaming code
+////////////////////////////////////////////////////////////////////////
+
 ProfilerMarker::ProfilerMarker(const char* aMarkerName,
                                ProfilerMarkerPayload* aPayload,
                                double aTime)
   : mMarkerName(strdup(aMarkerName))
   , mPayload(aPayload)
   , mTime(aTime)
 {
 }
@@ -411,37 +1518,88 @@ profiler_log(const char* fmt, va_list ar
 MOZ_DEFINE_MALLOC_SIZE_OF(GeckoProfilerMallocSizeOf);
 
 NS_IMETHODIMP
 GeckoProfilerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
                                       nsISupports* aData, bool aAnonymize)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
-  if (gSampler) {
-    size_t n = gSampler->SizeOfIncludingThis(GeckoProfilerMallocSizeOf);
+  size_t n = 0;
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = sRegisteredThreads->at(i);
+
+      n += info->SizeOfIncludingThis(GeckoProfilerMallocSizeOf);
+    }
+  }
+  MOZ_COLLECT_REPORT(
+    "explicit/profiler/thread-info", KIND_HEAP, UNITS_BYTES, n,
+    "Memory used by the Gecko Profiler's ThreadInfo objects.");
+
+  if (gBuffer) {
+    n = gBuffer->SizeOfIncludingThis(GeckoProfilerMallocSizeOf);
     MOZ_COLLECT_REPORT(
-      "explicit/profiler/sampler", KIND_HEAP, UNITS_BYTES, n,
-      "Memory used by the Gecko Profiler's Sampler object.");
+      "explicit/profiler/profile-buffer", KIND_HEAP, UNITS_BYTES, n,
+      "Memory used by the Gecko Profiler's ProfileBuffer object.");
   }
 
 #if defined(USE_LUL_STACKWALK)
-  {
-    size_t n = sLUL ? sLUL->SizeOfIncludingThis(GeckoProfilerMallocSizeOf) : 0;
-    MOZ_COLLECT_REPORT(
-      "explicit/profiler/lul", KIND_HEAP, UNITS_BYTES, n,
-      "Memory used by LUL, a stack unwinder used by the Gecko Profiler.");
-  }
+  n = sLUL ? sLUL->SizeOfIncludingThis(GeckoProfilerMallocSizeOf) : 0;
+  MOZ_COLLECT_REPORT(
+    "explicit/profiler/lul", KIND_HEAP, UNITS_BYTES, n,
+    "Memory used by LUL, a stack unwinder used by the Gecko Profiler.");
 #endif
 
+  // Measurement of the following things may be added later if DMD finds it
+  // is worthwhile:
+  // - gThreadNameFilters
+  // - gFeatures
+
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(GeckoProfilerReporter, nsIMemoryReporter)
 
+static bool
+ThreadSelected(const char* aThreadName)
+{
+  StaticMutexAutoLock lock(gThreadNameFiltersMutex);
+
+  if (gThreadNameFilters.empty()) {
+    return true;
+  }
+
+  std::string name = aThreadName;
+  std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+
+  for (uint32_t i = 0; i < gThreadNameFilters.length(); ++i) {
+    std::string filter = gThreadNameFilters[i];
+    std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
+
+    // Crude, non UTF-8 compatible, case insensitive substring search
+    if (name.find(filter) != std::string::npos) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+static void
+MaybeSetProfile(ThreadInfo* aInfo)
+{
+  if ((aInfo->IsMainThread() || gProfileThreads) &&
+      ThreadSelected(aInfo->Name())) {
+    aInfo->SetProfile(gBuffer);
+  }
+}
+
 static void
 RegisterCurrentThread(const char* aName, PseudoStack* aPseudoStack,
                       bool aIsMainThread, void* stackTop)
 {
   StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
   if (!sRegisteredThreads) {
     return;
@@ -457,20 +1615,17 @@ RegisterCurrentThread(const char* aName,
       MOZ_ASSERT(false);
       return;
     }
   }
 
   ThreadInfo* info =
     new ThreadInfo(aName, id, aIsMainThread, aPseudoStack, stackTop);
 
-  // XXX: this is an off-main-thread use of gSampler
-  if (gSampler) {
-    gSampler->RegisterThread(info);
-  }
+  MaybeSetProfile(info);
 
   sRegisteredThreads->push_back(info);
 }
 
 void
 profiler_init(void* stackTop)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -499,17 +1654,17 @@ profiler_init(void* stackTop)
 
     sRegisteredThreads = new std::vector<ThreadInfo*>();
   }
 
   // (Linux-only) We could create the sLUL object and read unwind info into it
   // at this point. That would match the lifetime implied by destruction of it
   // in profiler_shutdown() just below. However, that gives a big delay on
   // startup, even if no profiling is actually to be done. So, instead, sLUL is
-  // created on demand at the first call to Sampler::Start.
+  // created on demand at the first call to PlatformStart().
 
   PseudoStack* stack = new PseudoStack();
   tlsPseudoStack.set(stack);
 
   bool isMainThread = true;
   RegisterCurrentThread(gGeckoThreadName, stack, isMainThread, stackTop);
 
   // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan
@@ -568,17 +1723,17 @@ profiler_shutdown()
 
   // Save the profile on shutdown if requested.
   if (gSampler) {
     const char *val = getenv("MOZ_PROFILER_SHUTDOWN");
     if (val) {
       std::ofstream stream;
       stream.open(val);
       if (stream.is_open()) {
-        gSampler->ToStreamAsJSON(stream);
+        ToStreamAsJSON(stream);
         stream.close();
       }
     }
   }
 
   profiler_stop();
 
   set_stderr_callback(nullptr);
@@ -619,29 +1774,29 @@ mozilla::UniquePtr<char[]>
 profiler_get_profile(double aSinceTime)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!gSampler) {
     return nullptr;
   }
 
-  return gSampler->ToJSON(aSinceTime);
+  return ToJSON(aSinceTime);
 }
 
 JSObject*
 profiler_get_profile_jsobject(JSContext *aCx, double aSinceTime)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!gSampler) {
     return nullptr;
   }
 
-  return gSampler->ToJSObject(aCx, aSinceTime);
+  return ToJSObject(aCx, aSinceTime);
 }
 
 void
 profiler_get_profile_jsobject_async(double aSinceTime,
                                     mozilla::dom::Promise* aPromise)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
@@ -675,22 +1830,18 @@ profiler_get_start_params(int* aEntrySiz
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!aEntrySize) || NS_WARN_IF(!aInterval) ||
       NS_WARN_IF(!aFilters) || NS_WARN_IF(!aFeatures)) {
     return;
   }
 
-  if (NS_WARN_IF(!gSampler)) {
-    return;
-  }
-
   *aEntrySize = gEntrySize;
-  *aInterval = gSampler->interval();
+  *aInterval = gInterval;
 
   {
     StaticMutexAutoLock lock(gThreadNameFiltersMutex);
 
     MOZ_ALWAYS_TRUE(aFilters->resize(gThreadNameFilters.length()));
     for (uint32_t i = 0; i < gThreadNameFilters.length(); ++i) {
       (*aFilters)[i] = gThreadNameFilters[i].c_str();
     }
@@ -731,17 +1882,17 @@ profiler_save_profile_to_file(const char
 
   if (!gSampler) {
     return;
   }
 
   std::ofstream stream;
   stream.open(aFilename);
   if (stream.is_open()) {
-    gSampler->ToStreamAsJSON(stream);
+    ToStreamAsJSON(stream);
     stream.close();
     LOGF("Saved to %s", aFilename);
   } else {
     LOG("Fail to open profile log file.");
   }
 }
 
 const char**
@@ -804,23 +1955,43 @@ profiler_get_buffer_info_helper(uint32_t
   // This function is called by profiler_get_buffer_info(), which has already
   // zeroed the outparams.
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!stack_key_initialized)
     return;
 
-  if (!gSampler) {
+  if (!gBuffer) {
     return;
   }
 
-  gSampler->GetBufferInfo(aCurrentPosition, aTotalSize, aGeneration);
+  *aCurrentPosition = gBuffer->mWritePos;
+  *aTotalSize = gEntrySize;
+  *aGeneration = gBuffer->mGeneration;
 }
 
+static bool
+hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
+{
+  for (size_t i = 0; i < aFeatureCount; i++) {
+    if (strcmp(aFeatures[i], aFeature) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Platform-specific start/stop actions.
+static void PlatformStart();
+static void PlatformStop();
+
+// XXX: an empty class left behind after refactoring. Will be removed soon.
+class Sampler {};
+
 // Values are only honored on the first start
 void
 profiler_start(int aProfileEntries, double aInterval,
                const char** aFeatures, uint32_t aFeatureCount,
                const char** aThreadNameFilters, uint32_t aFilterCount)
 
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
@@ -837,85 +2008,129 @@ profiler_start(int aProfileEntries, doub
 
   /* If the entry count was set using env vars, use that, too: */
   if (sProfileEntries > 0)
     aProfileEntries = sProfileEntries;
 
   // Reset the current state if the profiler is running
   profiler_stop();
 
-  // Deep copy aThreadNameFilters. Must happen before Sampler's constructor
-  // calls RegisterThread().
+  // Deep copy aThreadNameFilters. Must happen before the MaybeSetProfile()
+  // calls below.
   {
     StaticMutexAutoLock lock(gThreadNameFiltersMutex);
 
     MOZ_ALWAYS_TRUE(gThreadNameFilters.resize(aFilterCount));
     for (uint32_t i = 0; i < aFilterCount; ++i) {
       gThreadNameFilters[i] = aThreadNameFilters[i];
     }
   }
 
   // Deep copy aFeatures.
   MOZ_ALWAYS_TRUE(gFeatures.resize(aFeatureCount));
   for (uint32_t i = 0; i < aFeatureCount; ++i) {
     gFeatures[i] = aFeatures[i];
   }
 
   gEntrySize = aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY;
-  gSampler =
-    new Sampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, gEntrySize,
-                aFeatures, aFeatureCount, aFilterCount);
+  gInterval = aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL;
+  gBuffer = new ProfileBuffer(gEntrySize);
+  gSampler = new Sampler();
+
+  bool ignore;
+  sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
+
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+    // Set up profiling for each registered thread, if appropriate
+    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = sRegisteredThreads->at(i);
+
+      MaybeSetProfile(info);
+    }
+  }
+
+#ifdef MOZ_TASK_TRACER
+  if (mTaskTracer) {
+    mozilla::tasktracer::StartLogging();
+  }
+#endif
+
   gGatherer = new mozilla::ProfileGatherer(gSampler);
 
-  gSampler->Start();
-  if (gSampler->ProfileJS() || gSampler->InPrivacyMode()) {
+  bool mainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
+  bool privacyMode  = hasFeature(aFeatures, aFeatureCount, "privacy");
+
+#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
+  gProfileJava = mozilla::jni::IsFennec() &&
+                      hasFeature(aFeatures, aFeatureCount, "java");
+#else
+  gProfileJava = false;
+#endif
+  gProfileJS   = hasFeature(aFeatures, aFeatureCount, "js");
+  gTaskTracer  = hasFeature(aFeatures, aFeatureCount, "tasktracer");
+
+  gAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
+  gDisplayListDump  = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
+  gLayersDump       = hasFeature(aFeatures, aFeatureCount, "layersdump");
+  gProfileGPU       = hasFeature(aFeatures, aFeatureCount, "gpu");
+  gProfileMemory    = hasFeature(aFeatures, aFeatureCount, "memory");
+  gProfileRestyle   = hasFeature(aFeatures, aFeatureCount, "restyle");
+  // Profile non-main threads if we have a filter, because users sometimes ask
+  // to filter by a list of threads but forget to explicitly request.
+  gProfileThreads   = hasFeature(aFeatures, aFeatureCount, "threads") ||
+                      aFilterCount > 0;
+  gUseStackWalk     = hasFeature(aFeatures, aFeatureCount, "stackwalk");
+
+  MOZ_ASSERT(!gIsActive && !gIsPaused);
+  PlatformStart();
+  MOZ_ASSERT(gIsActive && !gIsPaused);  // PlatformStart() sets gIsActive.
+
+  if (gProfileJS || privacyMode) {
     mozilla::StaticMutexAutoLock lock(sRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = (*sRegisteredThreads)[i];
       if (info->IsPendingDelete() || !info->hasProfile()) {
         continue;
       }
       info->Stack()->reinitializeOnResume();
-      if (gSampler->ProfileJS()) {
+      if (gProfileJS) {
         info->Stack()->enableJSSampling();
       }
-      if (gSampler->InPrivacyMode()) {
+      if (privacyMode) {
         info->Stack()->mPrivacyMode = true;
       }
     }
   }
 
 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-  if (gSampler->ProfileJava()) {
+  if (gProfileJava) {
     int javaInterval = aInterval;
     // Java sampling doesn't accuratly keep up with 1ms sampling
     if (javaInterval < 10) {
       aInterval = 10;
     }
     mozilla::java::GeckoJavaSampler::Start(javaInterval, 1000);
   }
 #endif
 
-  if (gSampler->AddMainThreadIO()) {
+  if (mainThreadIO) {
     if (!sInterposeObserver) {
       // Lazily create IO interposer observer
       sInterposeObserver = new mozilla::ProfilerIOInterposeObserver();
     }
     mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
                                     sInterposeObserver);
   }
 
   sIsProfiling = true;
-  sIsGPUProfiling = gSampler->ProfileGPU();
-  sIsLayersDump = gSampler->LayersDump();
-  sIsDisplayListDump = gSampler->DisplayListDump();
-  sIsRestyleProfiling = gSampler->ProfileRestyle();
-
-  if (Sampler::CanNotifyObservers()) {
+
+  if (CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       nsTArray<nsCString> featuresArray;
       nsTArray<nsCString> threadNameFiltersArray;
 
       for (size_t i = 0; i < aFeatureCount; ++i) {
         featuresArray.AppendElement(aFeatures[i]);
       }
@@ -945,50 +2160,90 @@ profiler_stop()
   if (!stack_key_initialized)
     return;
 
   if (!gSampler) {
     LOG("END   profiler_stop-early");
     return;
   }
 
-  bool disableJS = gSampler->ProfileJS();
+  bool disableJS = gProfileJS;
 
   {
     StaticMutexAutoLock lock(gThreadNameFiltersMutex);
     gThreadNameFilters.clear();
   }
   gFeatures.clear();
 
-  gSampler->Stop();
+  PlatformStop();
+  MOZ_ASSERT(!gIsActive && !gIsPaused);   // PlatformStop() clears these.
+
+  gProfileJava      = false;
+  gProfileJS        = false;
+  gTaskTracer       = false;
+
+  gAddLeafAddresses = false;
+  gDisplayListDump  = false;
+  gLayersDump       = false;
+  gProfileGPU       = false;
+  gProfileMemory    = false;
+  gProfileRestyle   = false;
+  gProfileThreads   = false;
+  gUseStackWalk     = false;
+
+  if (gIsActive)
+    PlatformStop();
+
+  // Destroy ThreadInfo for all threads
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+    for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
+      ThreadInfo* info = sRegisteredThreads->at(i);
+      // We've stopped profiling. We no longer need to retain
+      // information for an old thread.
+      if (info->IsPendingDelete()) {
+        // The stack was nulled when SetPendingDelete() was called.
+        MOZ_ASSERT(!info->Stack());
+        delete info;
+        sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
+        i--;
+      }
+    }
+  }
+
+#ifdef MOZ_TASK_TRACER
+  if (mTaskTracer) {
+    mozilla::tasktracer::StopLogging();
+  }
+#endif
+
   delete gSampler;
   gSampler = nullptr;
+  gBuffer = nullptr;
   gEntrySize = 0;
+  gInterval = 0;
 
   // Cancel any in-flight async profile gatherering requests.
   gGatherer->Cancel();
   gGatherer = nullptr;
 
   if (disableJS) {
     PseudoStack *stack = tlsPseudoStack.get();
     MOZ_ASSERT(stack != nullptr);
     stack->disableJSSampling();
   }
 
   mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
                                     sInterposeObserver);
   sInterposeObserver = nullptr;
 
   sIsProfiling = false;
-  sIsGPUProfiling = false;
-  sIsLayersDump = false;
-  sIsDisplayListDump = false;
-  sIsRestyleProfiling = false;
-
-  if (Sampler::CanNotifyObservers()) {
+
+  if (CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os)
       os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
   }
 
   LOG("END   profiler_stop");
 }
 
@@ -996,78 +2251,78 @@ bool
 profiler_is_paused()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!gSampler) {
     return false;
   }
 
-  return gSampler->IsPaused();
+  return gIsPaused;
 }
 
 void
 profiler_pause()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!gSampler) {
     return;
   }
 
-  gSampler->SetPaused(true);
-  if (Sampler::CanNotifyObservers()) {
+  gIsPaused = true;
+  if (CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->NotifyObservers(nullptr, "profiler-paused", nullptr);
     }
   }
 }
 
 void
 profiler_resume()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   if (!gSampler) {
     return;
   }
 
-  gSampler->SetPaused(false);
-  if (Sampler::CanNotifyObservers()) {
+  gIsPaused = false;
+  if (CanNotifyObservers()) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->NotifyObservers(nullptr, "profiler-resumed", nullptr);
     }
   }
 }
 
 bool
 profiler_feature_active(const char* aName)
 {
   // This function runs both on and off the main thread.
 
-  if (!profiler_is_active()) {
+  if (!sIsProfiling) {
     return false;
   }
 
   if (strcmp(aName, "gpu") == 0) {
-    return sIsGPUProfiling;
+    return gProfileGPU;
   }
 
   if (strcmp(aName, "layersdump") == 0) {
-    return sIsLayersDump;
+    return gLayersDump;
   }
 
   if (strcmp(aName, "displaylistdump") == 0) {
-    return sIsDisplayListDump;
+    return gDisplayListDump;
   }
 
   if (strcmp(aName, "restyle") == 0) {
-    return sIsRestyleProfiling;
+    return gProfileRestyle;
   }
 
   return false;
 }
 
 bool
 profiler_is_active()
 {
@@ -1281,18 +2536,45 @@ profiler_get_backtrace()
   if (profiler_in_privacy_mode()) {
     return nullptr;
   }
 
   if (!gSampler) {
     return nullptr;
   }
 
-  return UniqueProfilerBacktrace(
-    new ProfilerBacktrace(gSampler->GetBacktrace()));
+  PseudoStack* stack = tlsPseudoStack.get();
+  if (!stack) {
+    MOZ_ASSERT(stack);
+    return nullptr;
+  }
+  Thread::tid_t tid = Thread::GetCurrentId();
+
+  SyncProfile* profile = new SyncProfile(tid, stack);
+
+  TickSample sample;
+  sample.threadInfo = profile;
+
+#if defined(HAVE_NATIVE_UNWIND) || defined(USE_LUL_STACKWALK)
+#if defined(XP_WIN) || defined(LINUX)
+  tickcontext_t context;
+  sample.PopulateContext(&context);
+#elif defined(XP_MACOSX)
+  sample.PopulateContext(nullptr);
+#endif
+#endif
+
+  sample.isSamplingCurrentThread = true;
+  sample.timestamp = mozilla::TimeStamp::Now();
+
+  profile->BeginUnwind();
+  Tick(&sample);
+  profile->EndUnwind();
+
+  return UniqueProfilerBacktrace(new ProfilerBacktrace(profile));
 }
 
 void
 ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace)
 {
   delete aBacktrace;
 }
 
@@ -1389,20 +2671,48 @@ profiler_add_marker(const char *aMarker,
                      aPayload->GetStartTime() : mozilla::TimeStamp::Now();
   mozilla::TimeDuration delta = origin - sStartTime;
   stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds());
 }
 
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
 
-// XXX: Sampler will eventually be merged with this file. In the meantime,
-// we #include it directly so that declarations can be shared between the files
-// without having to copy all the code from that file into this one.
-#include "Sampler.cpp"
+void PseudoStack::flushSamplerOnJSShutdown()
+{
+  MOZ_ASSERT(mContext);
+
+  if (!gIsActive) {
+    return;
+  }
+
+  gIsPaused = true;
+
+  {
+    StaticMutexAutoLock lock(sRegisteredThreadsMutex);
+
+    for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
+      // Thread not being profiled, skip it.
+      ThreadInfo* info = sRegisteredThreads->at(i);
+      if (!info->hasProfile() || info->IsPendingDelete()) {
+        continue;
+      }
+
+      // Thread not profiling the context that's going away, skip it.
+      if (info->Stack()->mContext != mContext) {
+        continue;
+      }
+
+      MutexAutoLock lock(info->GetMutex());
+      info->FlushSamplesAndMarkers();
+    }
+  }
+
+  gIsPaused = false;
+}
 
 // We #include these files directly because it means those files can use
 // declarations from this file trivially.
 #if defined(SPS_OS_windows)
 # include "platform-win32.cc"
 #elif defined(SPS_OS_darwin)
 # include "platform-macos.cc"
 #elif defined(SPS_OS_linux) || defined(SPS_OS_android)
--- a/tools/profiler/core/platform.h
+++ b/tools/profiler/core/platform.h
@@ -101,18 +101,16 @@ bool profiler_verbose();
     } while (0)
 
 #endif
 
 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX)
 #define ENABLE_LEAF_DATA
 #endif
 
-typedef int32_t Atomic32;
-
 extern mozilla::TimeStamp sStartTime;
 
 typedef uint8_t* Address;
 
 // ----------------------------------------------------------------------------
 // OS
 //
 // This class has static methods for the different platform specific
@@ -180,180 +178,25 @@ void profiler_usage();
 
 // Helper methods to expose modifying profiler behavior
 bool set_profiler_interval(const char*);
 bool set_profiler_entries(const char*);
 bool set_profiler_scan(const char*);
 bool is_native_unwinding_avail();
 
 // ----------------------------------------------------------------------------
-// Sampler
-//
-// A sampler periodically samples the state of the VM and optionally
-// (if used for profiling) the program counter and stack pointer for
-// the thread that created it.
+// Miscellaneous
 
-class ThreadInfo;
+class PlatformData;
 
-// 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)
-  {}
-
-  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;
+// We can't new/delete the type safely without defining it
+// (-Wdelete-incomplete).  Use these to hide the details from clients.
+struct PlatformDataDestructor {
+  void operator()(PlatformData*);
 };
 
-struct JSContext;
-class JSObject;
-class PlatformData;
-class ProfileBuffer;
-struct PseudoStack;
-class SpliceableJSONWriter;
-class SyncProfile;
-
-namespace mozilla {
-namespace dom {
-class Promise;
-}
-}
-
-class Sampler {
-public:
-  // Initialize sampler.
-  Sampler(double aInterval, int aEntrySize,
-          const char** aFeatures, uint32_t aFeatureCount,
-          uint32_t aFilterCount);
-  ~Sampler();
-
-  double interval() const { return interval_; }
-
-  // This method is called for each sampling period with the current
-  // program counter. This function must be re-entrant.
-  void Tick(TickSample* sample);
-
-  // Immediately captures the calling thread's call stack and returns it.
-  SyncProfile* GetBacktrace();
-
-  // Delete markers which are no longer part of the profile due to buffer
-  // wraparound.
-  void DeleteExpiredMarkers();
-
-  // Start and stop sampler.
-  void Start();
-  void Stop();
-
-  // Whether the sampler is running (that is, consumes resources).
-  bool IsActive() const { return active_; }
-
-  // Low overhead way to stop the sampler from ticking
-  bool IsPaused() const { return paused_; }
-  void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
-
-  // We can't new/delete the type safely without defining it
-  // (-Wdelete-incomplete).  Use these to hide the details from
-  // clients.
-  struct PlatformDataDestructor {
-    void operator()(PlatformData*);
-  };
-
-  typedef mozilla::UniquePtr<PlatformData, PlatformDataDestructor>
-    UniquePlatformData;
-  static UniquePlatformData AllocPlatformData(int aThreadId);
-
-  // If we move the backtracing code into the platform files we won't
-  // need to have these hacks
-#ifdef XP_WIN
-  // xxxehsan sucky hack :(
-  static uintptr_t GetThreadHandle(PlatformData*);
-#endif
+typedef mozilla::UniquePtr<PlatformData, PlatformDataDestructor>
+  UniquePlatformData;
+UniquePlatformData AllocPlatformData(int aThreadId);
 
-  static bool CanNotifyObservers() {
-#ifdef MOZ_WIDGET_GONK
-    // We use profile.sh on b2g to manually select threads and options per process.
-    return false;
-#elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
-    // Android ANR reporter uses the profiler off the main thread
-    return NS_IsMainThread();
-#else
-    MOZ_ASSERT(NS_IsMainThread());
-    return true;
-#endif
-  }
-
-  void RegisterThread(ThreadInfo* aInfo);
-
-  bool ProfileJS() const { return mProfileJS; }
-  bool ProfileJava() const { return mProfileJava; }
-  bool ProfileGPU() const { return mProfileGPU; }
-  bool ProfileThreads() const { return mProfileThreads; }
-  bool InPrivacyMode() const { return mPrivacyMode; }
-  bool AddMainThreadIO() const { return mAddMainThreadIO; }
-  bool ProfileMemory() const { return mProfileMemory; }
-  bool TaskTracer() const { return mTaskTracer; }
-  bool LayersDump() const { return mLayersDump; }
-  bool DisplayListDump() const { return mDisplayListDump; }
-  bool ProfileRestyle() const { return mProfileRestyle; }
-
-  void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
-  JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
-  mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
-  void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
-  void StreamTaskTracer(SpliceableJSONWriter& aWriter);
-  void FlushOnJSShutdown(JSContext* aContext);
-
-  void GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration);
-
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
-
-private:
-  // Not implemented on platforms which do not support backtracing
-  void doNativeBacktrace(ThreadInfo& aInfo, TickSample* aSample);
-
-  void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
-
-  // Called within a signal. This function must be reentrant
-  void InplaceTick(TickSample* sample);
-
-  void SetActive(bool value) { NoBarrier_Store(&active_, value); }
-
-  const double interval_;
-  Atomic32 paused_;
-  Atomic32 active_;
-
-  RefPtr<ProfileBuffer> mBuffer;
-  bool mAddLeafAddresses;
-  bool mUseStackWalk;
-  bool mProfileJS;
-  bool mProfileGPU;
-  bool mProfileThreads;
-  bool mProfileJava;
-  bool mLayersDump;
-  bool mDisplayListDump;
-  bool mProfileRestyle;
-  bool mPrivacyMode;
-  bool mAddMainThreadIO;
-  bool mProfileMemory;
-  bool mTaskTracer;
-};
+mozilla::UniquePtr<char[]> ToJSON(double aSinceTime);
 
 #endif /* ndef TOOLS_PLATFORM_H_ */
--- a/tools/profiler/gecko/ProfileGatherer.cpp
+++ b/tools/profiler/gecko/ProfileGatherer.cpp
@@ -146,17 +146,17 @@ ProfileGatherer::Finish()
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mSampler) {
     // We somehow got called after we were cancelled! This shouldn't
     // be possible, but doing a belt-and-suspenders check to be sure.
     return;
   }
 
-  UniquePtr<char[]> buf = mSampler->ToJSON(mSinceTime);
+  UniquePtr<char[]> buf = ToJSON(mSinceTime);
 
   if (mFile) {
     nsCOMPtr<nsIFileOutputStream> of =
       do_CreateInstance("@mozilla.org/network/file-output-stream;1");
     of->Init(mFile, -1, -1, 0);
     uint32_t sz;
     of->Write(buf.get(), strlen(buf.get()), &sz);
     of->Close();
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -9,16 +9,17 @@
 #include "nsICategoryManager.h"
 #include "nsIObserverService.h"
 #include "nsIWebProgress.h"
 #include "nsCURILoader.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsIURL.h"
 #include "nsISimpleEnumerator.h"
+#include "nsISupportsPriority.h"
 #include "nsNetUtil.h"
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsAutoPtr.h"
 #include "prtime.h"
 #include "mozilla/Logging.h"
@@ -151,16 +152,22 @@ nsPrefetchNode::OpenChannel()
     if (httpChannel) {
         httpChannel->SetReferrerWithPolicy(mReferrerURI, referrerPolicy);
         httpChannel->SetRequestHeader(
             NS_LITERAL_CSTRING("X-Moz"),
             NS_LITERAL_CSTRING("prefetch"),
             false);
     }
 
+    // Reduce the priority of prefetch network requests.
+    nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
+    if (priorityChannel) {
+      priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
+    }
+
     rv = mChannel->AsyncOpen2(this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // Drop the ref to the channel, because we don't want to end up with
       // cycles through it.
       mChannel = nullptr;
     }
     return rv;
 }
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -12,16 +12,17 @@
 #include "nsView.h"
 #include "nsCOMPtr.h"
 #include "mozilla/MouseEvents.h"
 #include "nsRegion.h"
 #include "nsCOMArray.h"
 #include "nsIPluginWidget.h"
 #include "nsXULPopupManager.h"
 #include "nsIPresShell.h"
+#include "nsIPresShellInlines.h"
 #include "nsPresContext.h"
 #include "mozilla/StartupTimeline.h"
 #include "GeckoProfiler.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h" // for nsAutoScriptBlocker
 #include "nsLayoutUtils.h"
 #include "Layers.h"
@@ -224,20 +225,19 @@ nsViewManager::SetWindowDimensions(nscoo
         // request a resize reflow (which would correct it). See bug 617076.
         mDelayedResize = nsSize(aWidth, aHeight);
         FlushDelayedResize(false);
       }
       mDelayedResize.SizeTo(NSCOORD_NONE, NSCOORD_NONE);
       DoSetWindowDimensions(aWidth, aHeight);
     } else {
       mDelayedResize.SizeTo(aWidth, aHeight);
-      if (mPresShell && mPresShell->GetDocument()) {
-        nsIDocument* doc = mPresShell->GetDocument();
-        doc->SetNeedStyleFlush();
-        doc->SetNeedLayoutFlush();
+      if (mPresShell) {
+        mPresShell->SetNeedStyleFlush();
+        mPresShell->SetNeedLayoutFlush();
       }
     }
   }
 }
 
 void
 nsViewManager::FlushDelayedResize(bool aDoReflow)
 {
--- a/widget/cocoa/nsCocoaFeatures.h
+++ b/widget/cocoa/nsCocoaFeatures.h
@@ -34,9 +34,15 @@ public:
   static int32_t ExtractMinorVersion(int32_t aVersion);
   static int32_t ExtractBugFixVersion(int32_t aVersion);
 
 private:
   static void InitializeVersionNumbers();
 
   static int32_t mOSXVersion;
 };
+
+// C-callable helper for cairo-quartz-font.c
+extern "C" {
+    bool Gecko_OnSierraOrLater();
+}
+
 #endif // nsCocoaFeatures_h_
--- a/widget/cocoa/nsCocoaFeatures.mm
+++ b/widget/cocoa/nsCocoaFeatures.mm
@@ -162,13 +162,20 @@ nsCocoaFeatures::OnElCapitanOrLater()
 }
 
 /* static */ bool
 nsCocoaFeatures::OnSierraOrLater()
 {
     return (OSXVersion() >= MAC_OS_X_VERSION_10_12_HEX);
 }
 
+/* Version of OnSierraOrLater as a global function callable from C (cairo) */
+bool
+Gecko_OnSierraOrLater()
+{
+    return nsCocoaFeatures::OnSierraOrLater();
+}
+
 /* static */ bool
 nsCocoaFeatures::IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix)
 {
     return OSXVersion() >= GetVersion(aMajor, aMinor, aBugFix);
 }
--- a/widget/windows/JumpListItem.cpp
+++ b/widget/windows/JumpListItem.cpp
@@ -554,17 +554,17 @@ nsresult JumpListLink::GetShellItem(nsCO
   rv = link->GetUri(getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString spec;
   rv = uri->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create the IShellItem
-  if (FAILED(WinUtils::SHCreateItemFromParsingName(
+  if (FAILED(SHCreateItemFromParsingName(
                NS_ConvertASCIItoUTF16(spec).get(),
                nullptr, IID_PPV_ARGS(&psi)))) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // Set the title
   nsAutoString linkTitle;
   link->GetUriTitle(linkTitle);
--- a/widget/windows/TaskbarPreview.cpp
+++ b/widget/windows/TaskbarPreview.cpp
@@ -134,17 +134,17 @@ TaskbarPreview::Invalidate() {
   if (!mVisible)
     return NS_OK;
 
   // DWM Composition is required for previews
   if (!nsUXThemeData::CheckForCompositor())
     return NS_OK;
 
   HWND previewWindow = PreviewWindow();
-  return FAILED(WinUtils::dwmInvalidateIconicBitmapsPtr(previewWindow))
+  return FAILED(DwmInvalidateIconicBitmaps(previewWindow))
        ? NS_ERROR_FAILURE
        : NS_OK;
 }
 
 nsresult
 TaskbarPreview::UpdateTaskbarProperties() {
   nsresult rv = UpdateTooltip();
 
@@ -279,23 +279,23 @@ TaskbarPreview::GetWindowHook() {
   NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!");
 
   return window->GetWindowHook();
 }
 
 void
 TaskbarPreview::EnableCustomDrawing(HWND aHWND, bool aEnable) {
   BOOL enabled = aEnable;
-  WinUtils::dwmSetWindowAttributePtr(
+  DwmSetWindowAttribute(
       aHWND,
       DWMWA_FORCE_ICONIC_REPRESENTATION,
       &enabled,
       sizeof(enabled));
 
-  WinUtils::dwmSetWindowAttributePtr(
+  DwmSetWindowAttribute(
       aHWND,
       DWMWA_HAS_ICONIC_BITMAP,
       &enabled,
       sizeof(enabled));
 }
 
 
 nsresult
@@ -380,21 +380,21 @@ TaskbarPreviewCallback::Done(nsISupports
 
   HDC hDC = target->GetDC();
   HBITMAP hBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP);
 
   DWORD flags = aDrawBorder ? DWM_SIT_DISPLAYFRAME : 0;
   POINT pptClient = { 0, 0 };
   HRESULT hr;
   if (!mIsThumbnail) {
-    hr = WinUtils::dwmSetIconicLivePreviewBitmapPtr(mPreview->PreviewWindow(),
-                                                    hBitmap, &pptClient, flags);
+    hr = DwmSetIconicLivePreviewBitmap(mPreview->PreviewWindow(),
+                                       hBitmap, &pptClient, flags);
   } else {
-    hr = WinUtils::dwmSetIconicThumbnailPtr(mPreview->PreviewWindow(),
-                                            hBitmap, flags);
+    hr = DwmSetIconicThumbnail(mPreview->PreviewWindow(),
+                               hBitmap, flags);
   }
   MOZ_ASSERT(SUCCEEDED(hr));
   return NS_OK;
 }
 
 /* static */
 bool
 TaskbarPreview::MainWindowHook(void *aContext,
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -917,18 +917,18 @@ IMEHandler::ShowOnScreenKeyboard()
         ::GetEnvironmentVariableW(L"CommonProgramW6432",
                                   commonProgramFilesPathW6432.data(),
                                   bufferSize);
         commonProgramFilesPath =
           std::wstring(commonProgramFilesPathW6432.data());
       } else {
         PWSTR path = nullptr;
         HRESULT hres =
-          WinUtils::SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0,
-                                         nullptr, &path);
+          SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0,
+                               nullptr, &path);
         if (FAILED(hres) || !path) {
           return;
         }
         commonProgramFilesPath =
           static_cast<const wchar_t*>(nsDependentString(path).get());
         ::CoTaskMemFree(path);
       }
       wstrpath.replace(commonProgramFilesOffset,
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -412,38 +412,16 @@ NS_IMPL_ISUPPORTS(AsyncFaviconDataReady,
 NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
 NS_IMPL_ISUPPORTS(AsyncDeleteIconFromDisk, nsIRunnable)
 NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
 
 
 const char FaviconHelper::kJumpListCacheDir[] = "jumpListCache";
 const char FaviconHelper::kShortcutCacheDir[] = "shortcutCache";
 
-// apis available on vista and up.
-WinUtils::SHCreateItemFromParsingNamePtr WinUtils::sCreateItemFromParsingName = nullptr;
-WinUtils::SHGetKnownFolderPathPtr WinUtils::sGetKnownFolderPath = nullptr;
-
-// We just leak these DLL HMODULEs. There's no point in calling FreeLibrary
-// on them during shutdown anyway.
-static const wchar_t kShellLibraryName[] =  L"shell32.dll";
-static HMODULE sShellDll = nullptr;
-static const wchar_t kDwmLibraryName[] = L"dwmapi.dll";
-static HMODULE sDwmDll = nullptr;
-
-WinUtils::DwmExtendFrameIntoClientAreaProc WinUtils::dwmExtendFrameIntoClientAreaPtr = nullptr;
-WinUtils::DwmIsCompositionEnabledProc WinUtils::dwmIsCompositionEnabledPtr = nullptr;
-WinUtils::DwmSetIconicThumbnailProc WinUtils::dwmSetIconicThumbnailPtr = nullptr;
-WinUtils::DwmSetIconicLivePreviewBitmapProc WinUtils::dwmSetIconicLivePreviewBitmapPtr = nullptr;
-WinUtils::DwmGetWindowAttributeProc WinUtils::dwmGetWindowAttributePtr = nullptr;
-WinUtils::DwmSetWindowAttributeProc WinUtils::dwmSetWindowAttributePtr = nullptr;
-WinUtils::DwmInvalidateIconicBitmapsProc WinUtils::dwmInvalidateIconicBitmapsPtr = nullptr;
-WinUtils::DwmDefWindowProcProc WinUtils::dwmDwmDefWindowProcPtr = nullptr;
-WinUtils::DwmGetCompositionTimingInfoProc WinUtils::dwmGetCompositionTimingInfoPtr = nullptr;
-WinUtils::DwmFlushProc WinUtils::dwmFlushProcPtr = nullptr;
-
 // Prefix for path used by NT calls.
 const wchar_t kNTPrefix[] = L"\\??\\";
 const size_t kNTPrefixLen = ArrayLength(kNTPrefix) - 1;
 
 struct CoTaskMemFreePolicy
 {
   void operator()(void* aPtr) {
     ::CoTaskMemFree(aPtr);
@@ -457,33 +435,16 @@ typedef NTSTATUS (NTAPI* NtTestAlertPtr)
 static NtTestAlertPtr sNtTestAlert = nullptr;
 #endif
 
 
 /* static */
 void
 WinUtils::Initialize()
 {
-  if (!sDwmDll) {
-    sDwmDll = ::LoadLibraryW(kDwmLibraryName);
-
-    if (sDwmDll) {
-      dwmExtendFrameIntoClientAreaPtr = (DwmExtendFrameIntoClientAreaProc)::GetProcAddress(sDwmDll, "DwmExtendFrameIntoClientArea");
-      dwmIsCompositionEnabledPtr = (DwmIsCompositionEnabledProc)::GetProcAddress(sDwmDll, "DwmIsCompositionEnabled");
-      dwmSetIconicThumbnailPtr = (DwmSetIconicThumbnailProc)::GetProcAddress(sDwmDll, "DwmSetIconicThumbnail");
-      dwmSetIconicLivePreviewBitmapPtr = (DwmSetIconicLivePreviewBitmapProc)::GetProcAddress(sDwmDll, "DwmSetIconicLivePreviewBitmap");
-      dwmGetWindowAttributePtr = (DwmGetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmGetWindowAttribute");
-      dwmSetWindowAttributePtr = (DwmSetWindowAttributeProc)::GetProcAddress(sDwmDll, "DwmSetWindowAttribute");
-      dwmInvalidateIconicBitmapsPtr = (DwmInvalidateIconicBitmapsProc)::GetProcAddress(sDwmDll, "DwmInvalidateIconicBitmaps");
-      dwmDwmDefWindowProcPtr = (DwmDefWindowProcProc)::GetProcAddress(sDwmDll, "DwmDefWindowProc");
-      dwmGetCompositionTimingInfoPtr = (DwmGetCompositionTimingInfoProc)::GetProcAddress(sDwmDll, "DwmGetCompositionTimingInfo");
-      dwmFlushProcPtr = (DwmFlushProc)::GetProcAddress(sDwmDll, "DwmFlush");
-    }
-  }
-
   if (IsWin10OrLater()) {
     HMODULE user32Dll = ::GetModuleHandleW(L"user32");
     if (user32Dll) {
       sEnableNonClientDpiScaling = (EnableNonClientDpiScalingProc)
         ::GetProcAddress(user32Dll, "EnableNonClientDpiScaling");
       sSetThreadDpiAwarenessContext = (SetThreadDpiAwarenessContextProc)
         ::GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext");
     }
@@ -1150,66 +1111,16 @@ WinUtils::InitMSG(UINT aMessage, WPARAM 
   MSG msg;
   msg.message = aMessage;
   msg.wParam  = wParam;
   msg.lParam  = lParam;
   msg.hwnd    = aWnd;
   return msg;
 }
 
-/* static */
-HRESULT
-WinUtils::SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc,
-                                      REFIID riid, void **ppv)
-{
-  if (sCreateItemFromParsingName) {
-    return sCreateItemFromParsingName(pszPath, pbc, riid, ppv);
-  }
-
-  if (!sShellDll) {
-    sShellDll = ::LoadLibraryW(kShellLibraryName);
-    if (!sShellDll) {
-      return false;
-    }
-  }
-
-  sCreateItemFromParsingName = (SHCreateItemFromParsingNamePtr)
-    GetProcAddress(sShellDll, "SHCreateItemFromParsingName");
-  if (!sCreateItemFromParsingName)
-    return E_FAIL;
-
-  return sCreateItemFromParsingName(pszPath, pbc, riid, ppv);
-}
-
-/* static */
-HRESULT 
-WinUtils::SHGetKnownFolderPath(REFKNOWNFOLDERID rfid,
-                               DWORD dwFlags,
-                               HANDLE hToken,
-                               PWSTR *ppszPath)
-{
-  if (sGetKnownFolderPath) {
-    return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
-  }
-
-  if (!sShellDll) {
-    sShellDll = ::LoadLibraryW(kShellLibraryName);
-    if (!sShellDll) {
-      return false;
-    }
-  }
-
-  sGetKnownFolderPath = (SHGetKnownFolderPathPtr)
-    GetProcAddress(sShellDll, "SHGetKnownFolderPath");
-  if (!sGetKnownFolderPath)
-    return E_FAIL;
-
-  return sGetKnownFolderPath(rfid, dwFlags, hToken, ppszPath);
-}
-
 static BOOL
 WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam)
 {
   *((HWND*)lParam) = hwnd;
   return FALSE;
 }
 
 /* static */
@@ -1911,18 +1822,18 @@ typedef struct REPARSE_DATA_BUFFER {
 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
 #pragma pack(pop)
 
 /* static */
 bool
 WinUtils::ResolveMovedUsersFolder(std::wstring& aPath)
 {
   wchar_t* usersPath;
-  if (FAILED(WinUtils::SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, nullptr,
-                                            &usersPath))) {
+  if (FAILED(SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, nullptr,
+                                  &usersPath))) {
     return false;
   }
 
   // Ensure usersPath gets freed properly.
   UniquePtr<wchar_t, CoTaskMemFreePolicy> autoFreePath(usersPath);
 
   // Is aPath in Users folder?
   size_t usersLen = wcslen(usersPath);
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -387,31 +387,16 @@ public:
    * retrieve their pointer ID on receiving mouse events as well. Please refer to
    * https://msdn.microsoft.com/en-us/library/windows/desktop/ms703320(v=vs.85).aspx
    */
   static uint16_t GetMousePointerID();
 
   static bool GetIsMouseFromTouch(EventMessage aEventType);
 
   /**
-   * SHCreateItemFromParsingName() calls native SHCreateItemFromParsingName()
-   * API which is available on Vista and up.
-   */
-  static HRESULT SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc,
-                                             REFIID riid, void **ppv);
-
-  /**
-   * SHGetKnownFolderPath() calls native SHGetKnownFolderPath()
-   * API which is available on Vista and up.
-   */
-  static HRESULT SHGetKnownFolderPath(REFKNOWNFOLDERID rfid,
-                                      DWORD dwFlags,
-                                      HANDLE hToken,
-                                      PWSTR *ppszPath);
-  /**
    * GetShellItemPath return the file or directory path of a shell item.
    * Internally calls IShellItem's GetDisplayName.
    *
    * aItem  the shell item containing the path.
    * aResultString  the resulting string path.
    * returns  true if a path was retreived.
    */
   static bool GetShellItemPath(IShellItem* aItem,
@@ -473,41 +458,16 @@ public:
    *
    * @param aPath path to be resolved.
    * @return true if successful, including if nothing needs to be changed.
    *         false if something failed or aPath does not exist, aPath will
    *               remain unchanged.
    */
   static bool ResolveMovedUsersFolder(std::wstring& aPath);
 
-  /**
-  * dwmapi.dll function typedefs and declarations
-  */
-  typedef HRESULT (WINAPI*DwmExtendFrameIntoClientAreaProc)(HWND hWnd, const MARGINS *pMarInset);
-  typedef HRESULT (WINAPI*DwmIsCompositionEnabledProc)(BOOL *pfEnabled);
-  typedef HRESULT (WINAPI*DwmSetIconicThumbnailProc)(HWND hWnd, HBITMAP hBitmap, DWORD dwSITFlags);
-  typedef HRESULT (WINAPI*DwmSetIconicLivePreviewBitmapProc)(HWND hWnd, HBITMAP hBitmap, POINT *pptClient, DWORD dwSITFlags);
-  typedef HRESULT (WINAPI*DwmGetWindowAttributeProc)(HWND hWnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
-  typedef HRESULT (WINAPI*DwmSetWindowAttributeProc)(HWND hWnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
-  typedef HRESULT (WINAPI*DwmInvalidateIconicBitmapsProc)(HWND hWnd);
-  typedef HRESULT (WINAPI*DwmDefWindowProcProc)(HWND hWnd, UINT msg, LPARAM lParam, WPARAM wParam, LRESULT *aRetValue);
-  typedef HRESULT (WINAPI*DwmGetCompositionTimingInfoProc)(HWND hWnd, DWM_TIMING_INFO *info);
-  typedef HRESULT (WINAPI*DwmFlushProc)(void);
-
-  static DwmExtendFrameIntoClientAreaProc dwmExtendFrameIntoClientAreaPtr;
-  static DwmIsCompositionEnabledProc dwmIsCompositionEnabledPtr;
-  static DwmSetIconicThumbnailProc dwmSetIconicThumbnailPtr;
-  static DwmSetIconicLivePreviewBitmapProc dwmSetIconicLivePreviewBitmapPtr;
-  static DwmGetWindowAttributeProc dwmGetWindowAttributePtr;
-  static DwmSetWindowAttributeProc dwmSetWindowAttributePtr;
-  static DwmInvalidateIconicBitmapsProc dwmInvalidateIconicBitmapsPtr;
-  static DwmDefWindowProcProc dwmDwmDefWindowProcPtr;
-  static DwmGetCompositionTimingInfoProc dwmGetCompositionTimingInfoPtr;
-  static DwmFlushProc dwmFlushProcPtr;
-
   static void Initialize();
 
   static bool ShouldHideScrollbars();
 
   /**
    * This function normalizes the input path, converts short filenames to long
    * filenames, and substitutes environment variables for system paths.
    * The resulting output string length is guaranteed to be <= MAX_PATH.
@@ -519,27 +479,16 @@ public:
    */
   static bool GetAppInitDLLs(nsAString& aOutput);
 
 #ifdef ACCESSIBILITY
   static void SetAPCPending();
 #endif
 
 private:
-  typedef HRESULT (WINAPI * SHCreateItemFromParsingNamePtr)(PCWSTR pszPath,
-                                                            IBindCtx *pbc,
-                                                            REFIID riid,
-                                                            void **ppv);
-  static SHCreateItemFromParsingNamePtr sCreateItemFromParsingName;
-  typedef HRESULT (WINAPI * SHGetKnownFolderPathPtr)(REFKNOWNFOLDERID rfid,
-                                                     DWORD dwFlags,
-                                                     HANDLE hToken,
-                                                     PWSTR *ppszPath);
-  static SHGetKnownFolderPathPtr sGetKnownFolderPath;
-
   static void GetWhitelistedPaths(
       nsTArray<mozilla::Pair<nsString,nsDependentString>>& aOutput);
 };
 
 #ifdef MOZ_PLACES
 class AsyncFaviconDataReady final : public nsIFaviconDataCallback
 {
 public:
--- a/widget/windows/nsFilePicker.cpp
+++ b/widget/windows/nsFilePicker.cpp
@@ -368,19 +368,19 @@ nsFilePicker::ShowFolderPicker(const nsS
 
   if (!mOkButtonLabel.IsEmpty()) {
     dialog->SetOkButtonLabel(mOkButtonLabel.get());
   }
 
   if (!aInitialDir.IsEmpty()) {
     RefPtr<IShellItem> folder;
     if (SUCCEEDED(
-          WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
-                                                IID_IShellItem,
-                                                getter_AddRefs(folder)))) {
+          SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
+                                      IID_IShellItem,
+                                      getter_AddRefs(folder)))) {
       dialog->SetFolder(folder);
     }
   }
 
   AutoDestroyTmpWindow adtw((HWND)(mParentWidget.get() ?
     mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr));
  
   // display
@@ -509,19 +509,19 @@ nsFilePicker::ShowFilePicker(const nsStr
   } else if (IsDefaultPathHtml()) {
     dialog->SetDefaultExtension(htmExt.get());
   }
 
   // initial location
   if (!aInitialDir.IsEmpty()) {
     RefPtr<IShellItem> folder;
     if (SUCCEEDED(
-          WinUtils::SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
-                                                IID_IShellItem,
-                                                getter_AddRefs(folder)))) {
+          SHCreateItemFromParsingName(aInitialDir.get(), nullptr,
+                                      IID_IShellItem,
+                                      getter_AddRefs(folder)))) {
       dialog->SetFolder(folder);
     }
   }
 
   // filter types and the default index
   if (!mComFilterList.IsEmpty()) {
     dialog->SetFileTypes(mComFilterList.Length(), mComFilterList.get());
     dialog->SetFileTypeIndex(mSelectedType);
--- a/widget/windows/nsUXThemeData.cpp
+++ b/widget/windows/nsUXThemeData.cpp
@@ -151,20 +151,20 @@ nsUXThemeData::InitTitlebarInfo()
 void
 nsUXThemeData::UpdateTitlebarInfo(HWND aWnd)
 {
   if (!aWnd)
     return;
 
   if (!sTitlebarInfoPopulatedAero && nsUXThemeData::CheckForCompositor()) {
     RECT captionButtons;
-    if (SUCCEEDED(WinUtils::dwmGetWindowAttributePtr(aWnd,
-                                                     DWMWA_CAPTION_BUTTON_BOUNDS,
-                                                     &captionButtons,
-                                                     sizeof(captionButtons)))) {
+    if (SUCCEEDED(DwmGetWindowAttribute(aWnd,
+                                        DWMWA_CAPTION_BUTTON_BOUNDS,
+                                        &captionButtons,
+                                        sizeof(captionButtons)))) {
       sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx = captionButtons.right - captionButtons.left - 3;
       sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy = (captionButtons.bottom - captionButtons.top) - 1;
       sTitlebarInfoPopulatedAero = true;
     }
   }
 
   // NB: sTitlebarInfoPopulatedThemed is always true pre-vista.
   if (sTitlebarInfoPopulatedThemed || IsWin8OrLater())
@@ -288,18 +288,18 @@ bool nsUXThemeData::IsHighContrastOn()
 {
   return sIsHighContrastOn;
 }
 
 // static
 bool nsUXThemeData::CheckForCompositor(bool aUpdateCache)
 {
   static BOOL sCachedValue = FALSE;
-  if (aUpdateCache && WinUtils::dwmIsCompositionEnabledPtr) {
-    WinUtils::dwmIsCompositionEnabledPtr(&sCachedValue);
+  if (aUpdateCache) {
+    DwmIsCompositionEnabled(&sCachedValue);
   }
   return sCachedValue;
 }
 
 // static
 void
 nsUXThemeData::UpdateNativeThemeInfo()
 {
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -841,25 +841,25 @@ nsWindow::Create(nsIWidget* aParent,
                            nsToolkit::mDllInstance,
                            nullptr);
 
   if (!mWnd) {
     NS_WARNING("nsWindow CreateWindowEx failed.");
     return NS_ERROR_FAILURE;
   }
 
-  if (mIsRTL && WinUtils::dwmSetWindowAttributePtr) {
+  if (mIsRTL) {
     DWORD dwAttribute = TRUE;    
-    WinUtils::dwmSetWindowAttributePtr(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
-  }
-
-  if (mOpeningAnimationSuppressed && WinUtils::dwmSetWindowAttributePtr) {
+    DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
+  }
+
+  if (mOpeningAnimationSuppressed) {
     DWORD dwAttribute = TRUE;
-    WinUtils::dwmSetWindowAttributePtr(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
-                                       &dwAttribute, sizeof dwAttribute);
+    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
+                          &dwAttribute, sizeof dwAttribute);
   }
 
   if (!IsPlugin() &&
       mWindowType != eWindowType_invisible &&
       MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
     // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
     //
     // We create two zero-sized windows as descendants of the top-level window,
@@ -1627,20 +1627,20 @@ nsWindow::Show(bool bState)
   if (!wasVisible && bState) {
     Invalidate();
     if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
       ::UpdateWindow(mWnd);
     }
   }
 #endif
 
-  if (mOpeningAnimationSuppressed && WinUtils::dwmSetWindowAttributePtr) {
+  if (mOpeningAnimationSuppressed) {
     DWORD dwAttribute = FALSE;
-    WinUtils::dwmSetWindowAttributePtr(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
-                                       &dwAttribute, sizeof dwAttribute);
+    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
+                          &dwAttribute, sizeof dwAttribute);
   }
 }
 
 /**************************************************************
  *
  * SECTION: nsIWidget::IsVisible
  *
  * Returns the visibility state.
@@ -3127,18 +3127,18 @@ void nsWindow::UpdateGlass()
 
   MOZ_LOG(gWindowsLog, LogLevel::Info,
          ("glass margins: left:%d top:%d right:%d bottom:%d\n",
           margins.cxLeftWidth, margins.cyTopHeight,
           margins.cxRightWidth, margins.cyBottomHeight));
 
   // Extends the window frame behind the client area
   if (nsUXThemeData::CheckForCompositor()) {
-    WinUtils::dwmExtendFrameIntoClientAreaPtr(mWnd, &margins);
-    WinUtils::dwmSetWindowAttributePtr(mWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof policy);
+    DwmExtendFrameIntoClientArea(mWnd, &margins);
+    DwmSetWindowAttribute(mWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof policy);
   }
 }
 #endif
 
 /**************************************************************
  *
  * SECTION: nsIWidget::HideWindowChrome
  *
@@ -5000,17 +5000,17 @@ nsWindow::ProcessMessage(UINT msg, WPARA
 
   // Glass hit testing w/custom transparent margins
   LRESULT dwmHitResult;
   if (mCustomNonClient &&
       nsUXThemeData::CheckForCompositor() &&
       /* We don't do this for win10 glass with a custom titlebar,
        * in order to avoid the caption buttons breaking. */
       !(IsWin10OrLater() && HasGlass()) &&
-      WinUtils::dwmDwmDefWindowProcPtr(mWnd, msg, wParam, lParam, &dwmHitResult)) {
+      DwmDefWindowProc(mWnd, msg, wParam, lParam, &dwmHitResult)) {
     *aRetValue = dwmHitResult;
     return true;
   }
 
   // (Large blocks of code should be broken out into OnEvent handlers.)
   switch (msg) {
     // WM_QUERYENDSESSION must be handled by all windows.
     // Otherwise Windows thinks the window can just be killed at will.
--- a/xpcom/build/nsWindowsDllInterceptor.h
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -106,17 +106,17 @@ private:
 
 class WindowsDllNopSpacePatcher
 {
   typedef uint8_t* byteptr_t;
   HMODULE mModule;
 
   // Dumb array for remembering the addresses of functions we've patched.
   // (This should be nsTArray, but non-XPCOM code uses this class.)
-  static const size_t maxPatchedFns = 128;
+  static const size_t maxPatchedFns = 16;
   byteptr_t mPatchedFns[maxPatchedFns];
   int mPatchedFnsLen;
 
 public:
   WindowsDllNopSpacePatcher()
     : mModule(0)
     , mPatchedFnsLen(0)
   {}
@@ -244,20 +244,17 @@ public:
 
     if (!IsCompatible()) {
 #if defined(MOZILLA_INTERNAL_API)
       NS_WARNING("NOP space patching is unavailable for compatibility reasons");
 #endif
       return false;
     }
 
-    if (mPatchedFnsLen == maxPatchedFns) {
-      // printf ("No space for hook in mPatchedFns.\n");
-      return false;
-    }
+    MOZ_RELEASE_ASSERT(mPatchedFnsLen < maxPatchedFns, "No room for the hook");
 
     byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
     if (!fn) {
       //printf ("GetProcAddress failed\n");
       return false;
     }
 
     fn = ResolveRedirectedAddress(fn);
--- a/xpcom/tests/gtest/TestThreadUtils.cpp
+++ b/xpcom/tests/gtest/TestThreadUtils.cpp
@@ -159,113 +159,133 @@ struct TestCopyMove
   int* mMoveCounter;
 };
 
 static void Expect(const char* aContext, int aCounter, int aMaxExpected)
 {
   EXPECT_LE(aCounter, aMaxExpected) << aContext;
 }
 
-TEST(ThreadUtils, NewRunnableFunction)
+static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName)
+{
+  nsAutoCString name;
+  EXPECT_TRUE(NS_SUCCEEDED(aRunnable->GetName(name))) << "Runnable::GetName()";
+#ifdef RELEASE_OR_BETA
+  EXPECT_TRUE(name.IsEmpty()) << "Runnable name shall be empty in RELEASE or BETA!";
+#else
+  EXPECT_TRUE(name.EqualsASCII(aExpectedName)) << "Verify Runnable name";
+#endif
+}
+
+static void TestNewRunnableFunction(bool aNamed)
 {
   // Test NS_NewRunnableFunction with copyable-only function object.
   {
     int copyCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyWithNoMove tracker(&copyCounter);
-        trackedRunnable = NS_NewRunnableFunction(tracker);
+        trackedRunnable = aNamed ? NS_NewRunnableFunction("unused", tracker) :
+                                   NS_NewRunnableFunction(tracker);
         // Original 'tracker' is destroyed here.
       }
       // Verify that the runnable contains a non-destroyed function object.
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable-only (and no move) function, copies",
            copyCounter, 1);
   }
   {
     int copyCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         // Passing as rvalue, but using copy.
         // (TestCopyWithDeletedMove wouldn't allow this.)
-        trackedRunnable = NS_NewRunnableFunction(TestCopyWithNoMove(&copyCounter));
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", TestCopyWithNoMove(&copyCounter)) :
+          NS_NewRunnableFunction(TestCopyWithNoMove(&copyCounter));
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable-only (and no move) function rvalue, copies",
            copyCounter, 1);
   }
   {
     int copyCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyWithDeletedMove tracker(&copyCounter);
-        trackedRunnable = NS_NewRunnableFunction(tracker);
+        trackedRunnable = aNamed ? NS_NewRunnableFunction("unused", tracker) :
+                                   NS_NewRunnableFunction(tracker);
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable-only (and deleted move) function, copies",
            copyCounter, 1);
   }
 
   // Test NS_NewRunnableFunction with movable-only function object.
   {
     int moveCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestMove tracker(&moveCounter);
-        trackedRunnable = NS_NewRunnableFunction(Move(tracker));
+        trackedRunnable = aNamed ? NS_NewRunnableFunction("unused", Move(tracker)) :
+                                   NS_NewRunnableFunction(Move(tracker));
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with movable-only function, moves",
            moveCounter, 1);
   }
   {
     int moveCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
-        trackedRunnable = NS_NewRunnableFunction(TestMove(&moveCounter));
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", TestMove(&moveCounter)) :
+          NS_NewRunnableFunction(TestMove(&moveCounter));
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
            moveCounter, 1);
   }
 
   // Test NS_NewRunnableFunction with copyable&movable function object.
   {
     int copyCounter = 0;
     int moveCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyMove tracker(&copyCounter, &moveCounter);
-        trackedRunnable = NS_NewRunnableFunction(Move(tracker));
+        trackedRunnable = aNamed ? NS_NewRunnableFunction("unused", Move(tracker)) :
+                                   NS_NewRunnableFunction(Move(tracker));
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable&movable function, copies",
            copyCounter, 0);
     Expect("NS_NewRunnableFunction with copyable&movable function, moves",
            moveCounter, 1);
   }
   {
     int copyCounter = 0;
     int moveCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
-        trackedRunnable =
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", TestCopyMove(&copyCounter, &moveCounter)) :
           NS_NewRunnableFunction(TestCopyMove(&copyCounter, &moveCounter));
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable&movable function rvalue, copies",
            copyCounter, 0);
     Expect("NS_NewRunnableFunction with copyable&movable function rvalue, moves",
            moveCounter, 1);
@@ -274,31 +294,35 @@ TEST(ThreadUtils, NewRunnableFunction)
   // Test NS_NewRunnableFunction with copyable-only lambda capture.
   {
     int copyCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyWithNoMove tracker(&copyCounter);
         // Expect 2 copies (here -> local lambda -> runnable lambda).
-        trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", [tracker]() mutable { tracker(); }) :
+          NS_NewRunnableFunction([tracker]() mutable { tracker(); });
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable-only (and no move) capture, copies",
            copyCounter, 2);
   }
   {
     int copyCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyWithDeletedMove tracker(&copyCounter);
         // Expect 2 copies (here -> local lambda -> runnable lambda).
-        trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", [tracker]() mutable { tracker(); }) :
+          NS_NewRunnableFunction([tracker]() mutable { tracker(); });
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable-only (and deleted move) capture, copies",
            copyCounter, 2);
   }
 
   // Note: Not possible to use move-only captures.
@@ -307,62 +331,104 @@ TEST(ThreadUtils, NewRunnableFunction)
   // Test NS_NewRunnableFunction with copyable&movable lambda capture.
   {
     int copyCounter = 0;
     int moveCounter = 0;
     {
       nsCOMPtr<nsIRunnable> trackedRunnable;
       {
         TestCopyMove tracker(&copyCounter, &moveCounter);
-        trackedRunnable = NS_NewRunnableFunction([tracker]() mutable { tracker(); });
+        trackedRunnable = aNamed ?
+          NS_NewRunnableFunction("unused", [tracker]() mutable { tracker(); }) :
+          NS_NewRunnableFunction([tracker]() mutable { tracker(); });
         // Expect 1 copy (here -> local lambda) and 1 move (local -> runnable lambda).
       }
       trackedRunnable->Run();
     }
     Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
            copyCounter, 1);
     Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
            moveCounter, 1);
   }
 }
 
-TEST(ThreadUtils, RunnableMethod)
+TEST(ThreadUtils, NewRunnableFunction)
+{
+  TestNewRunnableFunction(/*aNamed*/false);
+}
+
+TEST(ThreadUtils, NewNamedRunnableFunction)
+{
+  // The named overload shall behave identical to the non-named counterpart.
+  TestNewRunnableFunction(/*aNamed*/true);
+
+  // Test naming.
+  {
+    const char* expectedName = "NamedRunnable";
+    RefPtr<Runnable> NamedRunnable = NS_NewRunnableFunction(expectedName, []{});
+    ExpectRunnableName(NamedRunnable, expectedName);
+  }
+}
+
+static void TestNewRunnableMethod(bool aNamed)
 {
   memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
   // Scope the smart ptrs so that the runnables need to hold on to whatever they need
   {
     RefPtr<nsFoo> foo = new nsFoo();
     RefPtr<nsBar> bar = new nsBar();
     RefPtr<const nsBar> constBar = bar;
 
     // This pointer will be freed at the end of the block
     // Do not dereference this pointer in the runnable method!
     RefPtr<nsFoo> rawFoo = new nsFoo();
 
     // Read only string. Dereferencing in runnable method to check this works.
     char* message = (char*)"Test message";
 
-    NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1));
-    NS_DispatchToMainThread(NewRunnableMethod(constBar, &nsBar::DoBar1Const));
-    NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2));
-    NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
-      (bar, &nsBar::DoBar3, foo));
-    NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
-      (bar, &nsBar::DoBar4, foo));
-    NS_DispatchToMainThread(NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5, rawFoo));
-    NS_DispatchToMainThread(NewRunnableMethod<char*>(bar, &nsBar::DoBar6, message));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod("unused", bar, &nsBar::DoBar1) :
+      NewRunnableMethod(bar, &nsBar::DoBar1));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod("unused", constBar, &nsBar::DoBar1Const) :
+      NewRunnableMethod(constBar, &nsBar::DoBar1Const));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod("unused", bar, &nsBar::DoBar2) :
+      NewRunnableMethod(bar, &nsBar::DoBar2));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar3, foo) :
+      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar3, foo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar4, foo) :
+      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar4, foo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5, rawFoo) :
+      NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5, rawFoo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6, message) :
+      NewRunnableMethod<char*>(bar, &nsBar::DoBar6, message));
 #ifdef HAVE_STDCALL
-    NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar1std));
-    NS_DispatchToMainThread(NewRunnableMethod(bar, &nsBar::DoBar2std));
-    NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
-      (bar, &nsBar::DoBar3std, foo));
-    NS_DispatchToMainThread(NewRunnableMethod<RefPtr<nsFoo>>
-      (bar, &nsBar::DoBar4std, foo));
-    NS_DispatchToMainThread(NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5std, rawFoo));
-    NS_DispatchToMainThread(NewRunnableMethod<char*>(bar, &nsBar::DoBar6std, message));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod("unused", bar, &nsBar::DoBar1std) :
+      NewRunnableMethod(bar, &nsBar::DoBar1std));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod("unused", bar, &nsBar::DoBar2std) :
+      NewRunnableMethod(bar, &nsBar::DoBar2std));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar3std, foo) :
+      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar3std, foo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<RefPtr<nsFoo>>("unused", bar, &nsBar::DoBar4std, foo) :
+      NewRunnableMethod<RefPtr<nsFoo>>(bar, &nsBar::DoBar4std, foo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<nsFoo*>("unused", bar, &nsBar::DoBar5std, rawFoo) :
+      NewRunnableMethod<nsFoo*>(bar, &nsBar::DoBar5std, rawFoo));
+    NS_DispatchToMainThread(aNamed ?
+      NewRunnableMethod<char*>("unused", bar, &nsBar::DoBar6std, message) :
+      NewRunnableMethod<char*>(bar, &nsBar::DoBar6std, message));
 #endif
   }
 
   // Spin the event loop
   NS_ProcessPendingEvents(nullptr);
 
   // Now test a suicidal event in NS_New(Named)Thread
   nsCOMPtr<nsIThread> thread;
@@ -373,16 +439,37 @@ TEST(ThreadUtils, RunnableMethod)
     NS_ProcessPendingEvents(nullptr);
   }
 
   for (uint32_t i = 0; i < MAX_TESTS; i++) {
     EXPECT_TRUE(gRunnableExecuted[i]) << "Error in test " << i;
   }
 }
 
+TEST(ThreadUtils, RunnableMethod)
+{
+  TestNewRunnableMethod(/* aNamed */false);
+}
+
+TEST(ThreadUtils, NamedRunnableMethod)
+{
+  // The named overloads shall behave identical to the non-named counterparts.
+  TestNewRunnableMethod(/* aNamed */true);
+
+  // Test naming.
+  {
+    RefPtr<nsFoo> foo = new nsFoo();
+    const char* expectedName = "NamedRunnable";
+    bool unused;
+    RefPtr<Runnable> NamedRunnable =
+      NewRunnableMethod<bool*>(expectedName, foo, &nsFoo::DoFoo, &unused);
+    ExpectRunnableName(NamedRunnable, expectedName);
+  }
+}
+
 // {9e70a320-be02-11d1-8031-006008159b5a}
 #define NS_IFOO_IID \
   {0x9e70a320, 0xbe02, 0x11d1,    \
     {0x80, 0x31, 0x00, 0x60, 0x08, 0x15, 0x9b, 0x5a}}
 
 TEST(ThreadUtils, TypeTraits)
 {
   static_assert(!mozilla::IsRefcountedSmartPointer<int>::value,
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -313,16 +313,32 @@ public:
                   "The lambda must return void!");
     mFunction();
     return NS_OK;
   }
 private:
   StoredFunction mFunction;
 };
 
+// Type alias for NS_NewRunnableFunction
+template<typename Function>
+using RunnableFunctionImpl =
+  // Make sure we store a non-reference in nsRunnableFunction.
+  typename detail::RunnableFunction<typename RemoveReference<Function>::Type>;
+
+template <typename T>
+inline already_AddRefed<T>
+SetRunnableName(already_AddRefed<T>&& aObj, const char* aName)
+{
+  MOZ_RELEASE_ASSERT(aName);
+  RefPtr<T> ref(aObj);
+  ref->SetName(aName);
+  return ref.forget();
+}
+
 } // namespace detail
 
 namespace detail {
 
 template<typename CVRemoved>
 struct IsRefcountedSmartPointerHelper : FalseType {};
 
 template<typename Pointee>
@@ -405,21 +421,28 @@ ToSupports(mozilla::Runnable *p)
 {
   return static_cast<nsIRunnable*>(p);
 }
 
 template<typename Function>
 already_AddRefed<mozilla::Runnable>
 NS_NewRunnableFunction(Function&& aFunction)
 {
-  return do_AddRef(new mozilla::detail::RunnableFunction
-                   // Make sure we store a non-reference in nsRunnableFunction.
-                   <typename mozilla::RemoveReference<Function>::Type>
-                   // But still forward aFunction to move if possible.
-                   (mozilla::Forward<Function>(aFunction)));
+  // We store a non-reference in RunnableFunction, but still forward aFunction
+  // to move if possible.
+  return do_AddRef(new mozilla::detail::RunnableFunctionImpl<Function>
+    (mozilla::Forward<Function>(aFunction)));
+}
+
+template<typename Function>
+already_AddRefed<mozilla::Runnable>
+NS_NewRunnableFunction(const char* aName, Function&& aFunction)
+{
+  return mozilla::detail::SetRunnableName(
+    NS_NewRunnableFunction(mozilla::Forward<Function>(aFunction)), aName);
 }
 
 // An event that can be used to call a method on a class.  The class type must
 // support reference counting. This event supports Revoke for use
 // with nsRevocableEventPtr.
 template<class ClassType,
          typename ReturnType = void,
          bool Owning = true,
@@ -871,138 +894,226 @@ public:
   nsresult Cancel() {
     static_assert(Cancelable, "Don't use me!");
     Revoke();
     return NS_OK;
   }
   void Revoke() { mReceiver.Revoke(); }
 };
 
+// Type aliases for NewRunnableMethod.
+template<typename PtrType, typename Method>
+using OwningRunnableMethod = typename ::nsRunnableMethodTraits<
+  typename RemoveReference<PtrType>::Type, Method, true, false>::base_type;
+template<typename PtrType, typename Method, typename... Storages>
+using OwningRunnableMethodImpl = RunnableMethodImpl<
+  typename RemoveReference<PtrType>::Type, Method, true, false, Storages...>;
+
+// Type aliases for NewCancelableRunnableMethod.
+template<typename PtrType, typename Method>
+using CancelableRunnableMethod = typename ::nsRunnableMethodTraits<
+  typename RemoveReference<PtrType>::Type, Method, true, true>::base_type;
+template<typename PtrType, typename Method, typename... Storages>
+using CancelableRunnableMethodImpl = RunnableMethodImpl<
+  typename RemoveReference<PtrType>::Type, Method, true, true, Storages...>;
+
+// Type aliases for NewNonOwningRunnableMethod.
+template<typename PtrType, typename Method>
+using NonOwningRunnableMethod = typename ::nsRunnableMethodTraits<
+  typename RemoveReference<PtrType>::Type, Method, false, false>::base_type;
+template<typename PtrType, typename Method, typename... Storages>
+using NonOwningRunnableMethodImpl = RunnableMethodImpl<
+  typename RemoveReference<PtrType>::Type, Method, false, false, Storages...>;
+
+// Type aliases for NonOwningCancelableRunnableMethod
+template<typename PtrType, typename Method>
+using NonOwningCancelableRunnableMethod = typename ::nsRunnableMethodTraits<
+  typename RemoveReference<PtrType>::Type, Method, false, true>::base_type;
+template<typename PtrType, typename Method, typename... Storages>
+using NonOwningCancelableRunnableMethodImpl = RunnableMethodImpl<
+  typename RemoveReference<PtrType>::Type, Method, false, true, Storages...>;
+
 } // namespace detail
 
 // Use this template function like so:
 //
 //   nsCOMPtr<nsIRunnable> event =
 //     mozilla::NewRunnableMethod(myObject, &MyClass::HandleEvent);
 //   NS_DispatchToCurrentThread(event);
 //
 // Statically enforced constraints:
 //  - myObject must be of (or implicitly convertible to) type MyClass
 //  - MyClass must define AddRef and Release methods
 //
 
 template<typename PtrType, typename Method>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, true, false>::base_type>
+already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>>
 NewRunnableMethod(PtrType&& aPtr, Method aMethod)
 {
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, true, false>
+    new detail::OwningRunnableMethodImpl<PtrType, Method>
+      (Forward<PtrType>(aPtr), aMethod));
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>>
+NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod)
+{
+  return detail::SetRunnableName(
+    NewRunnableMethod(Forward<PtrType>(aPtr), aMethod), aName);
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>>
+NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
+{
+  return do_AddRef(
+    new detail::CancelableRunnableMethodImpl<PtrType, Method>
       (Forward<PtrType>(aPtr), aMethod));
 }
 
 template<typename PtrType, typename Method>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, true, true>::base_type>
-NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
+already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>>
+NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod)
+{
+  return detail::SetRunnableName(
+    NewCancelableRunnableMethod(Forward<PtrType>(aPtr), aMethod), aName);
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>>
+NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
 {
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, true, true>
+    new detail::NonOwningRunnableMethodImpl<PtrType, Method>
       (Forward<PtrType>(aPtr), aMethod));
 }
 
 template<typename PtrType, typename Method>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, false, false>::base_type>
-NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
+already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>>
+NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod)
+{
+  return detail::SetRunnableName(
+    NewNonOwningRunnableMethod(Forward<PtrType>(aPtr), aMethod), aName);
+}
+
+template<typename PtrType, typename Method>
+already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>>
+NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
 {
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, false, false>
+    new detail::NonOwningCancelableRunnableMethodImpl<PtrType, Method>
       (Forward<PtrType>(aPtr), aMethod));
 }
 
 template<typename PtrType, typename Method>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, false, true>::base_type>
-NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
+already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>>
+NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr,
+                                     Method aMethod)
 {
-  return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, false, true>
-      (Forward<PtrType>(aPtr), aMethod));
+  return detail::SetRunnableName(
+    NewNonOwningCancelableRunnableMethod(Forward<PtrType>(aPtr), aMethod), aName);
 }
 
 // Similar to NewRunnableMethod. Call like so:
 // nsCOMPtr<nsIRunnable> event =
 //   NewRunnableMethod<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...);
 // 'Types' are the stored type for each argument, see ParameterStorage for details.
 template<typename... Storages, typename PtrType, typename Method, typename... Args>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, true, false>::base_type>
+already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>>
 NewRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
 {
   static_assert(sizeof...(Storages) == sizeof...(Args),
                 "<Storages...> size should be equal to number of arguments");
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, true, false, Storages...>
+    new detail::OwningRunnableMethodImpl<PtrType, Method, Storages...>
       (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...));
 }
 
 template<typename... Storages, typename PtrType, typename Method, typename... Args>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, false, false>::base_type>
+already_AddRefed<detail::OwningRunnableMethod<PtrType, Method>>
+NewRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod, Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return detail::SetRunnableName(
+    NewRunnableMethod<Storages...>
+      (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...), aName);
+}
+
+template<typename... Storages, typename PtrType, typename Method, typename... Args>
+already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>>
 NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
 {
   static_assert(sizeof...(Storages) == sizeof...(Args),
                 "<Storages...> size should be equal to number of arguments");
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, false, false, Storages...>
+      new detail::NonOwningRunnableMethodImpl<PtrType, Method, Storages...>
       (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...));
 }
 
 template<typename... Storages, typename PtrType, typename Method, typename... Args>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, true, true>::base_type>
+already_AddRefed<detail::NonOwningRunnableMethod<PtrType, Method>>
+NewNonOwningRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod,
+                           Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return detail::SetRunnableName(
+    NewNonOwningRunnableMethod<Storages...>
+      (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...), aName);
+}
+
+template<typename... Storages, typename PtrType, typename Method, typename... Args>
+already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>>
 NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
 {
   static_assert(sizeof...(Storages) == sizeof...(Args),
                 "<Storages...> size should be equal to number of arguments");
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, true, true, Storages...>
+    new detail::CancelableRunnableMethodImpl<PtrType, Method, Storages...>
       (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...));
 }
 
 template<typename... Storages, typename PtrType, typename Method, typename... Args>
-already_AddRefed<
-  typename ::nsRunnableMethodTraits<
-    typename RemoveReference<PtrType>::Type, Method, false, true>::base_type>
+already_AddRefed<detail::CancelableRunnableMethod<PtrType, Method>>
+NewCancelableRunnableMethod(const char* aName, PtrType&& aPtr, Method aMethod,
+                            Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return detail::SetRunnableName(
+    NewCancelableRunnableMethod<Storages...>
+      (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...), aName);
+}
+
+template<typename... Storages, typename PtrType, typename Method, typename... Args>
+already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>>
 NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod,
-                                                Args&&... aArgs)
+                                     Args&&... aArgs)
 {
   static_assert(sizeof...(Storages) == sizeof...(Args),
                 "<Storages...> size should be equal to number of arguments");
   return do_AddRef(
-    new detail::RunnableMethodImpl
-      <typename RemoveReference<PtrType>::Type, Method, false, true, Storages...>
+    new detail::NonOwningCancelableRunnableMethodImpl<PtrType, Method, Storages...>
       (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...));
 }
 
+template<typename... Storages, typename PtrType, typename Method, typename... Args>
+already_AddRefed<detail::NonOwningCancelableRunnableMethod<PtrType, Method>>
+NewNonOwningCancelableRunnableMethod(const char* aName, PtrType&& aPtr,
+                                     Method aMethod, Args&&... aArgs)
+{
+  static_assert(sizeof...(Storages) == sizeof...(Args),
+                "<Storages...> size should be equal to number of arguments");
+  return detail::SetRunnableName(
+    NewNonOwningCancelableRunnableMethod<Storages...>
+      (Forward<PtrType>(aPtr), aMethod, mozilla::Forward<Args>(aArgs)...), aName);
+}
+
 } // namespace mozilla
 
 #endif  // XPCOM_GLUE_AVOID_NSPR
 
 // This class is designed to be used when you have an event class E that has a
 // pointer back to resource class R.  If R goes away while E is still pending,
 // then it is important to "revoke" E so that it does not try use R after R has
 // been destroyed.  nsRevocableEventPtr makes it easy for R to manage such