Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 27 Apr 2017 12:44:03 -0700
changeset 355384 ffdedb9c5aadf033a6a230bbc0338bc0e0760db0
parent 355287 c5c2b95e9d7b3c56cab54b3b8ca3ad11ee343646 (current diff)
parent 355383 87bba05a5669aadbc0e4bb23fb7157c2ebf75877 (diff)
child 355385 2cca333f546f38860f84940d4c72d7470a3410f4
push id89659
push userkwierso@gmail.com
push dateThu, 27 Apr 2017 20:29:29 +0000
treeherdermozilla-inbound@f700a9bf4fd1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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 autoland to central, a=merge MozReview-Commit-ID: 2pgVZdnDKno
dom/ipc/ContentParent.cpp
dom/ipc/PContent.ipdl
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsDisplayList.cpp
modules/libpref/init/all.js
security/manager/ssl/StaticHPKPins.h
testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py
--- a/.gitignore
+++ b/.gitignore
@@ -30,16 +30,19 @@ ID
 /.machrc
 
 # Empty marker file that's generated when we check out NSS
 security/manager/.nss.checkout
 
 # Build directories
 /obj*/
 
+# gecko.log is generated by various test harnesses
+/gecko.log
+
 # Build directories for js shell
 _DBG.OBJ/
 _OPT.OBJ/
 
 # SpiderMonkey configury
 js/src/configure
 js/src/old-configure
 js/src/autom4te.cache
--- a/.hgignore
+++ b/.hgignore
@@ -27,16 +27,19 @@
 ^\.?machrc$
 
 # Empty marker file that's generated when we check out NSS
 ^security/manager/\.nss\.checkout$
 
 # Build directories
 ^obj
 
+# gecko.log is generated by various test harnesses
+^gecko\.log
+
 # Build directories for js shell
 _DBG\.OBJ/
 _OPT\.OBJ/
 ^js/src/.*-obj/
 
 # SpiderMonkey configury
 ^js/src/configure$
 ^js/src/old-configure$
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -186,17 +186,17 @@ XULTreeAccessible::ChildAtPoint(int32_t 
     return nullptr;
 
   nsPresContext *presContext = frame->PresContext();
   nsIPresShell* presShell = presContext->PresShell();
 
   nsIFrame *rootFrame = presShell->GetRootFrame();
   NS_ENSURE_TRUE(rootFrame, nullptr);
 
-  nsIntRect rootRect = rootFrame->GetScreenRect();
+  CSSIntRect rootRect = rootFrame->GetScreenRect();
 
   int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
   int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;
 
   int32_t row = -1;
   nsCOMPtr<nsITreeColumn> column;
   nsAutoString childEltUnused;
   mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -320,17 +320,17 @@ XULTreeGridRowAccessible::ChildAtPoint(i
     return nullptr;
 
   nsPresContext *presContext = frame->PresContext();
   nsIPresShell* presShell = presContext->PresShell();
 
   nsIFrame *rootFrame = presShell->GetRootFrame();
   NS_ENSURE_TRUE(rootFrame, nullptr);
 
-  nsIntRect rootRect = rootFrame->GetScreenRect();
+  CSSIntRect rootRect = rootFrame->GetScreenRect();
 
   int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
   int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;
 
   int32_t row = -1;
   nsCOMPtr<nsITreeColumn> column;
   nsAutoString childEltUnused;
   mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -65,16 +65,27 @@ pref("extensions.systemAddon.update.url"
 
 // Disable screenshots for now, Shield will enable this.
 pref("extensions.screenshots.system-disabled", true);
 
 // Disable add-ons that are not installed by the user in all scopes by default.
 // See the SCOPE constants in AddonManager.jsm for values to use here.
 pref("extensions.autoDisableScopes", 15);
 
+// This is where the profiler WebExtension API will look for breakpad symbols.
+// NOTE: deliberately http right now since https://symbols.mozilla.org is not supported.
+pref("extensions.geckoProfiler.symbols.url", "http://symbols.mozilla.org/");
+pref("extensions.geckoProfiler.acceptedExtensionIds", "geckoprofiler@mozilla.com");
+#if defined(XP_LINUX) || defined (XP_MACOSX)
+pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad,nm");
+#else // defined(XP_WIN)
+pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad");
+#endif
+
+
 // Add-on content security policies.
 pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
 pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
 
 // Require signed add-ons by default
 pref("xpinstall.signatures.required", true);
 pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
 
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -282,17 +282,16 @@ var FullZoom = {
    * @return A promise which resolves when the zoom reset has been applied.
    */
   reset: function FullZoom_reset(browser = gBrowser.selectedBrowser) {
     let token = this._getBrowserToken(browser);
     let result = this._getGlobalValue(browser).then(value => {
       if (token.isCurrent) {
         ZoomManager.setZoomForBrowser(browser, value === undefined ? 1 : value);
         this._ignorePendingZoomAccesses(browser);
-        Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset");
       }
     });
     this._removePref(browser);
     return result;
   },
 
   /**
    * Set the zoom level for a given browser.
@@ -350,17 +349,16 @@ var FullZoom = {
 
   /**
    * Saves the zoom level of the page in the given browser to the content
    * prefs store.
    *
    * @param browser  The zoom of this browser will be saved.  Required.
    */
   _applyZoomToPref: function FullZoom__applyZoomToPref(browser) {
-    Services.obs.notifyObservers(browser, "browser-fullZoom:zoomChange");
     if (!this.siteSpecific ||
         gInPrintPreviewMode ||
         browser.isSyntheticDocument)
       return;
 
     this._cps2.set(browser.currentURI.spec, this.name,
                    ZoomManager.getZoomForBrowser(browser),
                    this._loadContextFromBrowser(browser), {
@@ -371,17 +369,16 @@ var FullZoom = {
   },
 
   /**
    * Removes from the content prefs store the zoom level of the given browser.
    *
    * @param browser  The zoom of this browser will be removed.  Required.
    */
   _removePref: function FullZoom__removePref(browser) {
-    Services.obs.notifyObservers(browser, "browser-fullZoom:zoomReset");
     if (browser.isSyntheticDocument)
       return;
     let ctxt = this._loadContextFromBrowser(browser);
     this._cps2.removeByDomainAndName(browser.currentURI.spec, this.name, ctxt, {
       handleCompletion: () => {
         this._isNextContentPrefChangeInternal = true;
       },
     });
--- a/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
+++ b/browser/components/customizableui/test/browser_934951_zoom_in_toolbar.js
@@ -19,29 +19,29 @@ add_task(function*() {
   registerCleanupFunction(() => {
     info("Cleaning up.");
     CustomizableUI.reset();
     gBrowser.removeTab(tab2);
     gBrowser.removeTab(tab1);
   });
 
   is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
-  let zoomChangePromise = promiseObserverNotification("browser-fullZoom:zoomChange");
+  let zoomChangePromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
   FullZoom.enlarge();
   yield zoomChangePromise;
   is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
 
   let tabSelectPromise = promiseObserverNotification("browser-fullZoom:location-change");
   gBrowser.selectedTab = tab2;
   yield tabSelectPromise;
   yield new Promise(resolve => executeSoon(resolve));
   is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:robots");
 
   gBrowser.selectedTab = tab1;
-  let zoomResetPromise = promiseObserverNotification("browser-fullZoom:zoomReset");
+  let zoomResetPromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
   FullZoom.reset();
   yield zoomResetPromise;
   is(parseInt(zoomResetButton.label, 10), 100, "Default zoom is 100% for about:mozilla");
 
   // Test zoom label updates while navigating pages in the same tab.
   FullZoom.enlarge();
   yield zoomChangePromise;
   is(parseInt(zoomResetButton.label, 10), 110, "Zoom is changed to 110% for about:mozilla");
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ParseSymbols-worker.js
@@ -0,0 +1,59 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* eslint-env worker */
+/* globals OS, ParseSymbols */
+
+"use strict";
+
+importScripts("resource://gre/modules/osfile.jsm");
+importScripts("resource:///modules/ParseSymbols.jsm");
+
+async function fetchSymbolFile(url) {
+  const response = await fetch(url);
+
+  if (!response.ok) {
+    throw new Error(`got error status ${response.status}`);
+  }
+
+  return response.text();
+}
+
+function parse(text) {
+  const syms = new Map();
+
+  // Lines look like this:
+  //
+  // PUBLIC 3fc74 0 test_public_symbol
+  //
+  // FUNC 40330 8e 0 test_func_symbol
+  const symbolRegex = /\nPUBLIC ([0-9a-f]+) [0-9a-f]+ (.*)|\nFUNC ([0-9a-f]+) [0-9a-f]+ [0-9a-f]+ (.*)/g;
+
+  let match;
+  let approximateLength = 0;
+  while ((match = symbolRegex.exec(text))) {
+    const [address0, symbol0, address1, symbol1] = match.slice(1);
+    const address = parseInt(address0 || address1, 16);
+    const sym = (symbol0 || symbol1).trimRight();
+    syms.set(address, sym);
+    approximateLength += sym.length;
+  }
+
+  return ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
+}
+
+onmessage = async e => {
+  try {
+    let text;
+    if (e.data.filepath) {
+      text = await OS.File.read(e.data.filepath, {encoding: "utf-8"});
+    } else if (e.data.url) {
+      text = await fetchSymbolFile(e.data.url);
+    }
+
+    const result = parse(text);
+    postMessage({result}, result.map(r => r.buffer));
+  } catch (error) {
+    postMessage({error: error.toString()});
+  }
+  close();
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ParseSymbols.jsm
@@ -0,0 +1,49 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* exported ParseSymbols */
+
+var EXPORTED_SYMBOLS = ["ParseSymbols"];
+
+function convertStringArrayToUint8BufferWithIndex(array, approximateLength) {
+  const index = new Uint32Array(array.length + 1);
+
+  const textEncoder = new TextEncoder();
+  let buffer = new Uint8Array(approximateLength);
+  let pos = 0;
+
+  for (let i = 0; i < array.length; i++) {
+    const encodedString = textEncoder.encode(array[i]);
+
+    let size = pos + buffer.length;
+    if (size < buffer.length) {
+      size = 2 << Math.log(size) / Math.log(2);
+      let newBuffer = new Uint8Array(size);
+      newBuffer.set(buffer);
+      buffer = newBuffer;
+    }
+
+    buffer.set(encodedString, pos);
+    index[i] = pos;
+    pos += encodedString.length;
+  }
+  index[array.length] = pos;
+
+  return {index, buffer};
+}
+
+function convertSymsMapToExpectedSymFormat(syms, approximateSymLength) {
+  const addresses = Array.from(syms.keys());
+  addresses.sort((a, b) => a - b);
+
+  const symsArray = addresses.map(addr => syms.get(addr));
+  const {index, buffer} =
+    convertStringArrayToUint8BufferWithIndex(symsArray, approximateSymLength);
+
+  return [new Uint32Array(addresses), index, buffer];
+}
+
+var ParseSymbols = {
+  convertSymsMapToExpectedSymFormat,
+};
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -175,16 +175,24 @@ extensions.registerModules({
     url: "chrome://browser/content/ext-pageAction.js",
     schema: "chrome://browser/content/schemas/page_action.json",
     scopes: ["addon_parent"],
     manifest: ["page_action"],
     paths: [
       ["pageAction"],
     ],
   },
+  geckoProfiler: {
+    url: "chrome://browser/content/ext-geckoProfiler.js",
+    schema: "chrome://browser/content/schemas/geckoProfiler.json",
+    scopes: ["addon_parent"],
+    paths: [
+      ["geckoProfiler"],
+    ],
+  },
   sessions: {
     url: "chrome://browser/content/ext-sessions.js",
     schema: "chrome://browser/content/schemas/sessions.json",
     scopes: ["addon_parent"],
     paths: [
       ["sessions"],
     ],
   },
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/ext-geckoProfiler.js
@@ -0,0 +1,363 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.importGlobalProperties(["fetch", "TextEncoder"]);
+
+XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ParseSymbols", "resource:///modules/ParseSymbols.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
+
+const PREF_ASYNC_STACK = "javascript.options.asyncstack";
+const PREF_SYMBOLS_URL = "extensions.geckoProfiler.symbols.url";
+const PREF_GET_SYMBOL_RULES = "extensions.geckoProfiler.getSymbolRules";
+
+const ASYNC_STACKS_ENABLED = Services.prefs.getBoolPref(PREF_ASYNC_STACK, false);
+
+function parseSym(data) {
+  const worker = new ChromeWorker("resource://app/modules/ParseSymbols-worker.js");
+  const promise = new Promise((resolve, reject) => {
+    worker.onmessage = (e) => {
+      if (e.data.error) {
+        reject(e.data.error);
+      } else {
+        resolve(e.data.result);
+      }
+    };
+  });
+  worker.postMessage(data);
+  return promise;
+}
+
+class NMParser {
+  constructor() {
+    this._addrToSymMap = new Map();
+    this._approximateLength = 0;
+  }
+
+  consume(data) {
+    const lineRegex = /.*\n?/g;
+    const buffer = this._currentLine + data;
+
+    let match;
+    while ((match = lineRegex.exec(buffer))) {
+      let [line] = match;
+      if (line[line.length - 1] === "\n") {
+        this._processLine(line);
+      } else {
+        this._currentLine = line;
+        break;
+      }
+    }
+  }
+
+  finish() {
+    this._processLine(this._currentLine);
+    return {syms: this._addrToSymMap, approximateLength: this._approximateLength};
+  }
+
+  _processLine(line) {
+    // Example lines:
+    // 00000000028c9888 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
+    // 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
+    // 00000000028c9874 t GrFragmentProcessor::MulOutputByInputUnpremulColor(sk_sp<GrFragmentProcessor>)::PremulFragmentProcessor::onCreateGLSLInstance() const::GLFP::~GLFP()
+    // 0000000003a33730 r mozilla::OggDemuxer::~OggDemuxer()::{lambda()#1}::operator()() const::__func__
+    // 0000000003a33930 r mozilla::VPXDecoder::Drain()::{lambda()#1}::operator()() const::__func__
+    //
+    // Some lines have the form
+    // <address> ' ' <letter> ' ' <symbol>
+    // and some have the form
+    // <address> ' ' <symbol>
+    // The letter has a meaning, but we ignore it.
+
+    const regex = /([^ ]+) (?:. )?(.*)/;
+    let match = regex.exec(line);
+    if (match) {
+      const [, address, symbol] = match;
+      this._addrToSymMap.set(parseInt(address, 16), symbol);
+      this._approximateLength += symbol.length;
+    }
+  }
+}
+
+class CppFiltParser {
+  constructor(length) {
+    this._index = 0;
+    this._results = new Array(length);
+  }
+
+  consume(data) {
+    const lineRegex = /.*\n?/g;
+    const buffer = this._currentLine + data;
+
+    let match;
+    while ((match = lineRegex.exec(buffer))) {
+      let [line] = match;
+      if (line[line.length - 1] === "\n") {
+        this._processLine(line);
+      } else {
+        this._currentLine = line;
+        break;
+      }
+    }
+  }
+
+  finish() {
+    this._processLine(this._currentLine);
+    return this._results;
+  }
+
+  _processLine(line) {
+    this._results[this._index++] = line.trimRight();
+  }
+}
+
+async function readAllData(pipe, processData) {
+  let data;
+  while ((data = await pipe.readString())) {
+    processData(data);
+  }
+}
+
+async function spawnProcess(name, cmdArgs, processData, stdin = null) {
+  const opts = {
+    command: await Subprocess.pathSearch(name),
+    arguments: cmdArgs,
+  };
+  const proc = await Subprocess.call(opts);
+
+  if (stdin) {
+    const encoder = new TextEncoder("utf-8");
+    proc.stdin.write(encoder.encode(stdin));
+    proc.stdin.close();
+  }
+
+  await readAllData(proc.stdout, processData);
+}
+
+async function getSymbolsFromNM(path) {
+  const parser = new NMParser();
+
+  const args = [path];
+  if (Services.appinfo.OS !== "Darwin") {
+    // Mac's `nm` doesn't support the demangle option, so we have to
+    // post-process the symbols with c++filt.
+    args.unshift("--demangle");
+  }
+
+  await spawnProcess("nm", args, data => parser.consume(data));
+  await spawnProcess("nm", ["-D", ...args], data => parser.consume(data));
+  let {syms, approximateLength} = parser.finish();
+
+  if (Services.appinfo.OS === "Darwin") {
+    const keys = Array.from(syms.keys());
+    const values = keys.map(k => syms.get(k));
+    const demangler = new CppFiltParser(keys.length);
+    await spawnProcess("c++filt", [], data => demangler.consume(data), values.join("\n"));
+    const newSymbols = demangler.finish();
+    approximateLength = 0;
+    for (let [i, symbol] of newSymbols.entries()) {
+      approximateLength += symbol.length;
+      syms.set(keys[i], symbol);
+    }
+  }
+
+  return ParseSymbols.convertSymsMapToExpectedSymFormat(syms, approximateLength);
+}
+
+function pathComponentsForSymbolFile(debugName, breakpadId) {
+  const symName = debugName.replace(/(\.pdb)?$/, ".sym");
+  return [debugName, breakpadId, symName];
+}
+
+function urlForSymFile(debugName, breakpadId) {
+  const profilerSymbolsURL = Services.prefs.getCharPref(PREF_SYMBOLS_URL,
+                                                        "http://symbols.mozilla.org/");
+  return profilerSymbolsURL + pathComponentsForSymbolFile(debugName, breakpadId).join("/");
+}
+
+function getContainingObjdirDist(path) {
+  let curPath = path;
+  let curPathBasename = OS.Path.basename(curPath);
+  while (curPathBasename) {
+    if (curPathBasename === "dist") {
+      return curPath;
+    }
+    const parentDirPath = OS.Path.dirname(curPath);
+    if (curPathBasename === "bin") {
+      return parentDirPath;
+    }
+    curPath = parentDirPath;
+    curPathBasename = OS.Path.basename(curPath);
+  }
+  return null;
+}
+
+function filePathForSymFileInObjDir(binaryPath, debugName, breakpadId) {
+  // `mach buildsymbols` generates symbol files located
+  // at /path/to/objdir/dist/crashreporter-symbols/.
+  const objDirDist = getContainingObjdirDist(binaryPath);
+  if (!objDirDist) {
+    return null;
+  }
+  return OS.Path.join(objDirDist,
+                      "crashreporter-symbols",
+                      ...pathComponentsForSymbolFile(debugName, breakpadId));
+}
+
+const symbolCache = new Map();
+
+function primeSymbolStore(libs) {
+  for (const {debugName, breakpadId, path} of libs) {
+    symbolCache.set(urlForSymFile(debugName, breakpadId), path);
+  }
+}
+
+const isRunningObserver = {
+  _observers: new Set(),
+
+  observe(subject, topic, data) {
+    switch (topic) {
+      case "profiler-started":
+      case "profiler-stopped":
+        // Call observer(false) or observer(true), but do it through a promise
+        // so that it's asynchronous.
+        // We don't want it to be synchronous because of the observer call in
+        // addObserver, which is asynchronous, and we want to get the ordering
+        // right.
+        const isRunningPromise = Promise.resolve(topic === "profiler-started");
+        for (let observer of this._observers) {
+          isRunningPromise.then(observer);
+        }
+        break;
+    }
+  },
+
+  _startListening() {
+    Services.obs.addObserver(this, "profiler-started");
+    Services.obs.addObserver(this, "profiler-stopped");
+  },
+
+  _stopListening() {
+    Services.obs.removeObserver(this, "profiler-started");
+    Services.obs.removeObserver(this, "profiler-stopped");
+  },
+
+  addObserver(observer) {
+    if (this._observers.size === 0) {
+      this._startListening();
+    }
+
+    this._observers.add(observer);
+    observer(Services.profiler.IsActive());
+  },
+
+  removeObserver(observer) {
+    if (this._observers.delete(observer) && this._observers.size === 0) {
+      this._stopListening();
+    }
+  },
+};
+
+this.geckoProfiler = class extends ExtensionAPI {
+  getAPI(context) {
+    return {
+      geckoProfiler: {
+        async start(options) {
+          const {bufferSize, interval, features, threads} = options;
+
+          Services.prefs.setBoolPref(PREF_ASYNC_STACK, false);
+          if (threads) {
+            Services.profiler.StartProfiler(bufferSize, interval, features, features.length, threads, threads.length);
+          } else {
+            Services.profiler.StartProfiler(bufferSize, interval, features, features.length);
+          }
+        },
+
+        async stop() {
+          if (ASYNC_STACKS_ENABLED !== null) {
+            Services.prefs.setBoolPref(PREF_ASYNC_STACK, ASYNC_STACKS_ENABLED);
+          }
+
+          Services.profiler.StopProfiler();
+        },
+
+        async pause() {
+          Services.profiler.PauseSampling();
+        },
+
+        async resume() {
+          Services.profiler.ResumeSampling();
+        },
+
+        async getProfile() {
+          if (!Services.profiler.IsActive()) {
+            throw new Error("The profiler is stopped. " +
+              "You need to start the profiler before you can capture a profile.");
+          }
+
+          return Services.profiler.getProfileDataAsync();
+        },
+
+        async getSymbols(debugName, breakpadId) {
+          if (symbolCache.size === 0) {
+            primeSymbolStore(Services.profiler.sharedLibraries);
+          }
+
+          const path = symbolCache.get(urlForSymFile(debugName, breakpadId));
+
+          const symbolRules = Services.prefs.getCharPref(PREF_GET_SYMBOL_RULES, "localBreakpad,remoteBreakpad");
+          const haveAbsolutePath = path && OS.Path.split(path).absolute;
+
+          // We have multiple options for obtaining symbol information for the given
+          // binary.
+          //  "localBreakpad"  - Use existing symbol dumps stored in the object directory of a local
+          //      Firefox build, generated using `mach buildsymbols` [requires path]
+          //  "remoteBreakpad" - Use symbol dumps from the Mozilla symbol server [only requires
+          //      debugName + breakpadId]
+          //  "nm"             - Use the command line tool `nm` [linux/mac only, requires path]
+          for (const rule of symbolRules.split(",")) {
+            try {
+              switch (rule) {
+                case "localBreakpad":
+                  if (haveAbsolutePath) {
+                    const filepath = filePathForSymFileInObjDir(path, debugName, breakpadId);
+                    if (filepath) {
+                      // NOTE: here and below, "return await" is used to ensure we catch any
+                      // errors in the promise. A simple return would give the error to the
+                      // caller.
+                      return await parseSym({filepath});
+                    }
+                  }
+                  break;
+                case "remoteBreakpad":
+                  const url = urlForSymFile(debugName, breakpadId);
+                  return await parseSym({url});
+                case "nm":
+                  return await getSymbolsFromNM(path);
+              }
+            } catch (e) {
+              // Each of our options can go wrong for a variety of reasons, so on failure
+              // we will try the next one.
+              // "localBreakpad" will fail if this is not a local build that's running from the object
+              // directory or if the user hasn't run `mach buildsymbols` on it.
+              // "remoteBreakpad" will fail if this is not an official mozilla build (e.g. Nightly) or a
+              // known system library.
+              // "nm" will fail if `nm` is not available.
+            }
+          }
+
+          throw new Error(`Ran out of options to get symbols from library ${debugName} ${breakpadId}.`);
+        },
+
+        onRunning: new SingletonEventManager(context, "geckoProfiler.onRunning", fire => {
+          isRunningObserver.addObserver(fire.async);
+          return () => {
+            isRunningObserver.removeObserver(fire.async);
+          };
+        }).api(),
+      },
+    };
+  }
+};
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/ext-sessions.js
@@ -40,21 +40,21 @@ function getRecentlyClosed(maxResults, e
 function createSession(restored, extension, sessionId) {
   if (!restored) {
     return Promise.reject({message: `Could not restore object using sessionId ${sessionId}.`});
   }
   let sessionObj = {lastModified: Date.now()};
   if (restored instanceof Ci.nsIDOMChromeWindow) {
     return promiseObserved("sessionstore-single-window-restored", subject => subject == restored).then(() => {
       sessionObj.window = extension.windowManager.convert(restored, {populate: true});
-      return Promise.resolve([sessionObj]);
+      return Promise.resolve(sessionObj);
     });
   }
   sessionObj.tab = extension.tabManager.convert(restored);
-  return Promise.resolve([sessionObj]);
+  return Promise.resolve(sessionObj);
 }
 
 this.sessions = class extends ExtensionAPI {
   getAPI(context) {
     let {extension} = context;
     return {
       sessions: {
         getRecentlyClosed: function(filter) {
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -18,16 +18,17 @@ browser.jar:
     content/browser/ext-browsingData.js
     content/browser/ext-chrome-settings-overrides.js
     content/browser/ext-commands.js
     content/browser/ext-contextMenus.js
     content/browser/ext-devtools.js
     content/browser/ext-devtools-inspectedWindow.js
     content/browser/ext-devtools-network.js
     content/browser/ext-devtools-panels.js
+    content/browser/ext-geckoProfiler.js
     content/browser/ext-history.js
     content/browser/ext-omnibox.js
     content/browser/ext-pageAction.js
     content/browser/ext-sessions.js
     content/browser/ext-sidebarAction.js
     content/browser/ext-tabs.js
     content/browser/ext-url-overrides.js
     content/browser/ext-utils.js
--- a/browser/components/extensions/moz.build
+++ b/browser/components/extensions/moz.build
@@ -10,16 +10,18 @@ with Files("**"):
 JAR_MANIFESTS += ['jar.mn']
 
 EXTRA_COMPONENTS += [
     'extensions-browser.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'ExtensionPopups.jsm',
+    'ParseSymbols-worker.js',
+    'ParseSymbols.jsm',
 ]
 
 DIRS += ['schemas']
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser-remote.ini',
     'test/browser/browser.ini',
 ]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/schemas/geckoProfiler.json
@@ -0,0 +1,129 @@
+[
+  {
+    "namespace": "manifest",
+    "types": [
+      {
+        "$extend": "Permission",
+        "choices": [{
+          "type": "string",
+          "enum": [
+            "geckoProfiler"
+          ]
+        }]
+      }
+    ]
+  },
+  {
+    "namespace": "geckoProfiler",
+    "description": "Exposes the browser's profiler.",
+
+    "permissions": ["geckoProfiler"],
+    "functions": [
+      {
+       "name": "start",
+       "type": "function",
+       "description": "Starts the profiler with the specified settings.",
+       "async": true,
+       "parameters": [
+          {
+            "name": "settings",
+            "type": "object",
+            "properties": {
+              "bufferSize": {
+                "type": "integer",
+                "minimum": 0,
+                "description": "The size in bytes of the buffer used to store profiling data. A larger value allows capturing a profile that covers a greater amount of time."
+              },
+              "interval": {
+                "type": "number",
+                "description": "Interval in milliseconds between samples of profiling data. A smaller value will increase the detail of the profiles captured."
+              },
+              "features": {
+                "type": "array",
+                "description": "A list of active features for the profiler.",
+                "items": {
+                  "type": "string",
+                  "enum": [
+                    "js",
+                    "stackwalk",
+                    "tasktracer",
+                    "leaf",
+                    "threads"
+                  ]
+                }
+              },
+              "threads": {
+                "type": "array",
+                "description": "A list of thread names for which to capture profiles.",
+                "optional": true,
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        ]
+      },
+      {
+        "name": "stop",
+        "type": "function",
+        "description": "Stops the profiler and discards any captured profile data.",
+        "async": true,
+        "parameters": []
+      },
+      {
+        "name": "pause",
+        "type": "function",
+        "description": "Pauses the profiler, keeping any profile data that is already written.",
+        "async": true,
+        "parameters": []
+      },
+      {
+        "name": "resume",
+        "type": "function",
+        "description": "Resumes the profiler with the settings that were initially used to start it.",
+        "async": true,
+        "parameters": []
+      },
+      {
+        "name": "getProfile",
+        "type": "function",
+        "description": "Gathers the profile data from the current profiling session.",
+        "async": true,
+        "parameters": []
+      },
+      {
+        "name": "getSymbols",
+        "type": "function",
+        "description": "Gets the debug symbols for a particular library.",
+        "async": true,
+        "parameters": [
+          {
+            "type": "string",
+            "name": "debugName",
+            "description": "The name of the library's debug file. For example, 'xul.pdb"
+          },
+          {
+            "type": "string",
+            "name": "breakpadId",
+            "description": "The Breakpad ID of the library"
+          }
+        ]
+      }
+    ],
+    "events": [
+      {
+        "name": "onRunning",
+        "type": "function",
+        "description": "Fires when the profiler starts/stops running.",
+        "parameters": [
+          {
+            "name": "isRunning",
+            "type": "boolean",
+            "description": "Whether the profiler is running or not. Pausing the profiler will not affect this value."
+          }
+        ]
+      }
+    ]
+  }
+]
--- a/browser/components/extensions/schemas/jar.mn
+++ b/browser/components/extensions/schemas/jar.mn
@@ -9,16 +9,17 @@ browser.jar:
     content/browser/schemas/chrome_settings_overrides.json
     content/browser/schemas/commands.json
     content/browser/schemas/context_menus.json
     content/browser/schemas/context_menus_internal.json
     content/browser/schemas/devtools.json
     content/browser/schemas/devtools_inspected_window.json
     content/browser/schemas/devtools_network.json
     content/browser/schemas/devtools_panels.json
+    content/browser/schemas/geckoProfiler.json
     content/browser/schemas/history.json
     content/browser/schemas/omnibox.json
     content/browser/schemas/page_action.json
     content/browser/schemas/sessions.json
     content/browser/schemas/sidebar_action.json
     content/browser/schemas/tabs.json
     content/browser/schemas/url_overrides.json
     content/browser/schemas/windows.json
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 support-files =
   head.js
   head_pageAction.js
   head_sessions.js
+  profilerSymbols.sjs
   context.html
   context_frame.html
   ctxmenu-image.png
   context_tabs_onUpdated_page.html
   context_tabs_onUpdated_iframe.html
   file_clearplugindata.html
   file_popup_api_injection_a.html
   file_popup_api_injection_b.html
@@ -57,16 +58,17 @@ support-files =
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_network.js]
 [browser_ext_devtools_page.js]
 [browser_ext_devtools_panel.js]
+[browser_ext_geckoProfiler_symbolicate.js]
 [browser_ext_getViews.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_omnibox.js]
 skip-if = debug || asan # Bug 1354681
 [browser_ext_optionsPage_browser_style.js]
 [browser_ext_optionsPage_privileges.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_geckoProfiler_symbolicate.js
@@ -0,0 +1,57 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let getExtension = () => {
+  return ExtensionTestUtils.loadExtension({
+    background: async function() {
+      const [addresses, indices, strings] = await browser.geckoProfiler.getSymbols("test.pdb",
+                                                                                   "ASDFQWERTY");
+
+      function getSymbolAtAddress(address) {
+        const index = addresses.indexOf(address);
+        if (index == -1) {
+          return null;
+        }
+
+        const nameBuffer = strings.subarray(indices[index], indices[index + 1]);
+        const decoder = new TextDecoder("utf-8");
+
+        return decoder.decode(nameBuffer);
+      }
+
+      browser.test.assertEq(getSymbolAtAddress(0x3fc74), "test_public_symbol", "Contains public symbol");
+      browser.test.assertEq(getSymbolAtAddress(0x40330), "test_func_symbol", "Contains func symbol");
+      browser.test.sendMessage("symbolicated");
+    },
+
+    manifest: {
+      "permissions": ["geckoProfiler"],
+      "applications": {
+        "gecko": {
+          "id": "profilertest@mozilla.com",
+        },
+      },
+    },
+  });
+};
+
+add_task(function* testProfilerControl() {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      [
+        "extensions.geckoProfiler.symbols.url",
+        "http://mochi.test:8888/browser/browser/components/extensions/test/browser/profilerSymbols.sjs?path=",
+      ],
+      [
+        "extensions.geckoProfiler.acceptedExtensionIds",
+        "profilertest@mozilla.com",
+      ],
+    ],
+  });
+
+  let extension = getExtension();
+  yield extension.startup();
+  yield extension.awaitMessage("symbolicated");
+  yield extension.unload();
+});
--- a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js
+++ b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js
@@ -33,23 +33,23 @@ add_task(async function test_sessions_ge
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       permissions: ["sessions", "tabs"],
     },
     background,
   });
 
   let win = await BrowserTestUtils.openNewBrowserWindow();
-  await BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:addons");
+  await BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:mozilla");
   await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
   let expectedTabs = [];
   let tab = win.gBrowser.selectedTab;
   expectedTabs.push(expectedTabInfo(tab, win));
 
-  for (let url of ["about:robots", "about:mozilla"]) {
+  for (let url of ["about:robots", "about:buildconfig"]) {
     tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
     expectedTabs.push(expectedTabInfo(tab, win));
   }
 
   await extension.startup();
 
   // Test with a closed tab.
   await BrowserTestUtils.removeTab(tab);
--- a/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
+++ b/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
@@ -80,76 +80,72 @@ add_task(function* test_sessions_restore
   // Check that our expected window is the most recently closed.
   is(recentlyClosed[0].window.tabs.length, 3, "most recently closed window has the expected number of tabs");
 
   // Restore the window.
   extension.sendMessage("restore");
   yield assertNotificationCount(2);
   let restored = yield extension.awaitMessage("restored");
 
-  is(restored.length, 1, "restore returned the expected number of sessions");
-  is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
-  checkLocalTab(restored[0].window.tabs[0], "about:config");
-  checkLocalTab(restored[0].window.tabs[1], "about:robots");
-  checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
+  is(restored.window.tabs.length, 3, "restore returned a window with the expected number of tabs");
+  checkLocalTab(restored.window.tabs[0], "about:config");
+  checkLocalTab(restored.window.tabs[1], "about:robots");
+  checkLocalTab(restored.window.tabs[2], "about:mozilla");
 
   // Close the window again.
-  let window = windowTracker.getWindow(restored[0].window.id);
+  let window = windowTracker.getWindow(restored.window.id);
   yield BrowserTestUtils.closeWindow(window);
   yield assertNotificationCount(3);
 
   // Restore the window using the sessionId.
   extension.sendMessage("check-sessions");
   recentlyClosed = yield extension.awaitMessage("recentlyClosed");
   extension.sendMessage("restore", recentlyClosed[0].window.sessionId);
   yield assertNotificationCount(4);
   restored = yield extension.awaitMessage("restored");
 
-  is(restored.length, 1, "restore returned the expected number of sessions");
-  is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
-  checkLocalTab(restored[0].window.tabs[0], "about:config");
-  checkLocalTab(restored[0].window.tabs[1], "about:robots");
-  checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
+  is(restored.window.tabs.length, 3, "restore returned a window with the expected number of tabs");
+  checkLocalTab(restored.window.tabs[0], "about:config");
+  checkLocalTab(restored.window.tabs[1], "about:robots");
+  checkLocalTab(restored.window.tabs[2], "about:mozilla");
 
   // Close the window again.
-  window = windowTracker.getWindow(restored[0].window.id);
+  window = windowTracker.getWindow(restored.window.id);
   yield BrowserTestUtils.closeWindow(window);
   // notificationCount = yield extension.awaitMessage("notificationCount");
   yield assertNotificationCount(5);
 
   // Open and close a tab.
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
   yield TabStateFlusher.flush(tab.linkedBrowser);
   yield BrowserTestUtils.removeTab(tab);
   yield assertNotificationCount(6);
 
   // Restore the most recently closed item.
   extension.sendMessage("restore");
   yield assertNotificationCount(7);
   restored = yield extension.awaitMessage("restored");
 
-  is(restored.length, 1, "restore returned the expected number of sessions");
-  tab = restored[0].tab;
+  tab = restored.tab;
   ok(tab, "restore returned a tab");
   checkLocalTab(tab, "about:robots");
 
   // Close the tab again.
   let realTab = tabTracker.getTab(tab.id);
   yield BrowserTestUtils.removeTab(realTab);
   yield assertNotificationCount(8);
 
   // Restore the tab using the sessionId.
   extension.sendMessage("check-sessions");
   recentlyClosed = yield extension.awaitMessage("recentlyClosed");
   extension.sendMessage("restore", recentlyClosed[0].tab.sessionId);
   yield assertNotificationCount(9);
   restored = yield extension.awaitMessage("restored");
 
-  is(restored.length, 1, "restore returned the expected number of sessions");
-  tab = restored[0].tab;
+  tab = restored.tab;
   ok(tab, "restore returned a tab");
   checkLocalTab(tab, "about:robots");
 
   // Close the tab again.
   realTab = tabTracker.getTab(tab.id);
   yield BrowserTestUtils.removeTab(realTab);
   yield assertNotificationCount(10);
 
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/profilerSymbols.sjs
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const SYMBOLS_FILE =
+`MODULE windows x86_64 A712ED458B2542C18785C19D17D64D842 test.pdb
+
+INFO CODE_ID 58EE0F7F3EDD000 test.dll
+
+PUBLIC 3fc74 0 test_public_symbol
+
+FUNC 40330 8e 0 test_func_symbol
+
+40330 42 22 2229
+
+40372 3a 23 2229
+
+403ac 12 23 2229
+`;
+
+function handleRequest(req, resp) {
+  let match = /path=([^\/]+)\/([^\/]+)\/([^\/]+)/.exec(req.queryString);
+  if (match && match[1] == "test.pdb") {
+    resp.write(SYMBOLS_FILE);
+  } else {
+    resp.setStatusLine(null, 404, 'Not Found');
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/xpcshell/test_ext_geckoProfiler_control.js
@@ -0,0 +1,104 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let getExtension = () => {
+  return ExtensionTestUtils.loadExtension({
+    background: async function() {
+      const runningListener = isRunning => {
+        if (isRunning) {
+          browser.test.sendMessage("started");
+        } else {
+          browser.test.sendMessage("stopped");
+        }
+      };
+
+      browser.test.onMessage.addListener(async message => {
+        let result;
+        switch (message) {
+          case "start":
+            result = await browser.geckoProfiler.start({
+              bufferSize: 10000,
+              interval: 0.5,
+              features: ["js"],
+              threads: ["GeckoMain"],
+            });
+            browser.test.assertEq(undefined, result, "start returns nothing.");
+            break;
+          case "stop":
+            result = await browser.geckoProfiler.stop();
+            browser.test.assertEq(undefined, result, "stop returns nothing.");
+            break;
+          case "pause":
+            result = await browser.geckoProfiler.pause();
+            browser.test.assertEq(undefined, result, "pause returns nothing.");
+            browser.test.sendMessage("paused");
+            break;
+          case "resume":
+            result = await browser.geckoProfiler.resume();
+            browser.test.assertEq(undefined, result, "resume returns nothing.");
+            browser.test.sendMessage("resumed");
+            break;
+          case "test profile":
+            result = await browser.geckoProfiler.getProfile();
+            browser.test.assertTrue("libs" in result, "The profile contains libs.");
+            browser.test.assertTrue("meta" in result, "The profile contains meta.");
+            browser.test.assertTrue("threads" in result, "The profile contains threads.");
+            browser.test.assertTrue(result.threads.some(t => t.name == "GeckoMain"),
+                                    "The profile contains a GeckoMain thread.");
+            browser.test.sendMessage("tested profile");
+            break;
+          case "remove runningListener":
+            browser.geckoProfiler.onRunning.removeListener(runningListener);
+            browser.test.sendMessage("removed runningListener");
+            break;
+        }
+      });
+
+      browser.test.sendMessage("ready");
+
+      browser.geckoProfiler.onRunning.addListener(runningListener);
+    },
+
+    manifest: {
+      "permissions": ["geckoProfiler"],
+      "applications": {
+        "gecko": {
+          "id": "profilertest@mozilla.com",
+        },
+      },
+    },
+  });
+};
+
+add_task(async function testProfilerControl() {
+  const acceptedExtensionIdsPref = "extensions.geckoProfiler.acceptedExtensionIds";
+  Services.prefs.setCharPref(acceptedExtensionIdsPref, "profilertest@mozilla.com");
+
+  let extension = getExtension();
+  await extension.startup();
+  await extension.awaitMessage("ready");
+  await extension.awaitMessage("stopped");
+
+  extension.sendMessage("start");
+  await extension.awaitMessage("started");
+
+  extension.sendMessage("test profile");
+  await extension.awaitMessage("tested profile");
+
+  extension.sendMessage("pause");
+  await extension.awaitMessage("paused");
+
+  extension.sendMessage("resume");
+  await extension.awaitMessage("resumed");
+
+  extension.sendMessage("stop");
+  await extension.awaitMessage("stopped");
+
+  extension.sendMessage("remove runningListener");
+  await extension.awaitMessage("removed runningListener");
+
+  await extension.unload();
+
+  Services.prefs.clearUserPref(acceptedExtensionIdsPref);
+});
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell.ini
@@ -8,8 +8,9 @@ tags = webextensions
 [test_ext_browsingData_cookies_cache.js]
 [test_ext_browsingData_downloads.js]
 [test_ext_browsingData_passwords.js]
 [test_ext_browsingData_settings.js]
 [test_ext_history.js]
 [test_ext_manifest_commands.js]
 [test_ext_manifest_omnibox.js]
 [test_ext_manifest_permissions.js]
+[test_ext_geckoProfiler_control.js]
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -522,19 +522,16 @@ BrowserGlue.prototype = {
     Feeds.init();
     ContentPrefServiceParent.init();
 
     LoginManagerParent.init();
     ReaderParent.init();
 
     SelfSupportBackend.init();
 
-    // Ensure we keep track of places/pw-mananager undo by init'ing this early.
-    Cu.import("resource:///modules/AutoMigrate.jsm");
-
     if (AppConstants.INSTALL_COMPACT_THEMES) {
       let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");
 
       LightweightThemeManager.addBuiltInTheme({
         id: "firefox-compact-light@mozilla.org",
         name: gBrowserBundle.GetStringFromName("compactLightTheme.name"),
         description: gBrowserBundle.GetStringFromName("compactLightTheme.description"),
         headerURL: "resource:///chrome/browser/content/browser/defaultthemes/compact.header.png",
copy from browser/config/mozconfigs/linux32/nightly
copy to browser/config/mozconfigs/linux32/devedition
--- a/browser/config/mozconfigs/linux32/nightly
+++ b/browser/config/mozconfigs/linux32/devedition
@@ -1,14 +1,16 @@
 . "$topsrcdir/browser/config/mozconfigs/linux32/common-opt"
 
-ac_add_options --enable-profiling
+# Add-on signing is not required for DevEdition
+MOZ_REQUIRE_SIGNING=0
+
 ac_add_options --enable-verify-mar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
-ac_add_options --with-branding=browser/branding/nightly
+ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
copy from browser/config/mozconfigs/linux64/nightly
copy to browser/config/mozconfigs/linux64/devedition
--- a/browser/config/mozconfigs/linux64/nightly
+++ b/browser/config/mozconfigs/linux64/devedition
@@ -1,14 +1,16 @@
 . "$topsrcdir/browser/config/mozconfigs/linux64/common-opt"
 
-ac_add_options --enable-profiling
+# Add-on signing is not required for DevEdition
+MOZ_REQUIRE_SIGNING=0
+
 ac_add_options --enable-verify-mar
 
 # This will overwrite the default of stripping everything and keep the symbol table.
 # This is useful for profiling and debugging and only increases the package size
 # by 2 MBs.
 STRIP_FLAGS="--strip-debug"
 
-ac_add_options --with-branding=browser/branding/nightly
+ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
copy from browser/config/mozconfigs/macosx64/nightly
copy to browser/config/mozconfigs/macosx64/devedition
--- a/browser/config/mozconfigs/macosx64/nightly
+++ b/browser/config/mozconfigs/macosx64/devedition
@@ -1,20 +1,23 @@
 . "$topsrcdir/browser/config/mozconfigs/macosx64/common-opt"
 
+# Add-on signing is not required for DevEdition
+MOZ_REQUIRE_SIGNING=0
+
 ac_add_options --disable-install-strip
 ac_add_options --enable-verify-mar
-ac_add_options --enable-profiling
+
 ac_add_options --enable-instruments
 
 # Cross-compiled builds fail when dtrace is enabled
 if test `uname -s` != Linux; then
   ac_add_options --enable-dtrace
 fi
 
 if test "${MOZ_UPDATE_CHANNEL}" = "nightly"; then
 ac_add_options --with-macbundlename-prefix=Firefox
 fi
 
-ac_add_options --with-branding=browser/branding/nightly
+ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
copy from browser/config/mozconfigs/win32/nightly
copy to browser/config/mozconfigs/win32/devedition
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/devedition
@@ -1,10 +1,12 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win32/common-opt"
 
-ac_add_options --enable-profiling
+# Add-on signing is not required for DevEdition
+MOZ_REQUIRE_SIGNING=0
+
 ac_add_options --enable-verify-mar
 
-ac_add_options --with-branding=browser/branding/nightly
+ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
copy from browser/config/mozconfigs/win64/nightly
copy to browser/config/mozconfigs/win64/devedition
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/devedition
@@ -1,11 +1,13 @@
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-opt"
 
-ac_add_options --enable-profiling
+# Add-on signing is not required for DevEdition
+MOZ_REQUIRE_SIGNING=0
+
 ac_add_options --enable-verify-mar
 
-ac_add_options --with-branding=browser/branding/nightly
+ac_add_options --with-branding=browser/branding/aurora
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.cache"
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -11,19 +11,19 @@
     "size": 11189216,
     "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
     "algorithm": "sha512",
     "filename": "gtk3.tar.xz",
     "setup": "setup.sh",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/linux64/asan.manifest
+++ b/browser/config/tooltool-manifests/linux64/asan.manifest
@@ -11,19 +11,19 @@
     "version": "clang 3.9.0, libgcc 4.9.4",
     "size": 166261192,
     "digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
     "algorithm": "sha512",
     "filename": "clang.tar.xz",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "size": 12072532,
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -11,19 +11,19 @@
     "size": 12072532,
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "algorithm": "sha512",
     "filename": "gtk3.tar.xz",
     "setup": "setup.sh",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
@@ -11,19 +11,19 @@
     "size": 12072532,
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "algorithm": "sha512",
     "filename": "gtk3.tar.xz",
     "setup": "setup.sh",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
     "algorithm": "sha512",
     "filename": "sccache2.tar.xz",
--- a/browser/config/tooltool-manifests/linux64/hazard.manifest
+++ b/browser/config/tooltool-manifests/linux64/hazard.manifest
@@ -19,19 +19,19 @@
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "unpack": true,
     "setup": "setup.sh",
     "algorithm": "sha512",
     "filename": "gtk3.tar.xz",
     "size": 12072532
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/linux64/msan.manifest
+++ b/browser/config/tooltool-manifests/linux64/msan.manifest
@@ -11,19 +11,19 @@
     "version": "clang 3.9.0, libgcc 4.9.4",
     "size": 166261192,
     "digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
     "algorithm": "sha512",
     "filename": "clang.tar.xz",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "size": 12072532,
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -11,19 +11,19 @@
     "size": 12072532,
     "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
     "algorithm": "sha512",
     "filename": "gtk3.tar.xz",
     "setup": "setup.sh",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 112075416,
-    "digest": "134538dda3512363c8b103fc59128aba830bd994053612f8ad8721e1a0033ad9836cd11595ed8056d885aec7dc78b7f4c4169c0a12bd0f9c31549f2d81939054",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 112206132,
+    "digest": "9ec214f971676369be07b13f5638f9ae1fa12400488d898193fb7a6adbb73ecc120337b569b200bc07630ef9d661a75c2710537422b57eebd9053e60bdc771c5",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -3,19 +3,19 @@
     "version": "clang 3.9.0",
     "size": 184678304,
     "digest": "cfde9a0f7f59823200f94422b4adb9a2fb5d4d07f240bbd1142c792434f6a1cbb4096d25c9853d77008fc40db0d827daa7003e78016f51241f621d6040ccc635",
     "algorithm": "sha512",
     "filename": "clang.tar.bz2",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 124444249,
-    "digest": "11239565e6dde851a5229a0380da129b129c560c3fa765eb4998549e688385be314523074456afec9ebe4ddb6af71e44c64d7c669e7e904ed253f6e7b0f6317b",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 80381089,
+    "digest": "01bc5c4d25ffd16f51643d36449ca469eccc362b8315945edd9b7e174462659a3d5d4bd5d2738f449557254ba930ca0d70d22f8717b51ba36af68193bd3c39f7",
     "algorithm": "sha512",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -37,19 +37,19 @@
     "size": 57060,
     "visibility": "public",
     "digest": "0c96f0d3ace71c4110abec6f7ead013600b0a73c89465d840276090d849279e555d977fb2aa6bbabb7891d7191fc8cc8a4e8242be888114be52346b77a512fcc",
     "algorithm": "sha512",
     "unpack": true,
     "filename": "dmg.tar.xz"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 142669732,
-    "digest": "ab74516718ac2b848f0d28df314c0588559dd77b203e8e0a20510a44e7d8b9b757b35c5eebd72524d9ce29bfa10a6e72e44fa54cb9cfe808bb42b5fc603b14bc",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 107393796,
+    "digest": "90dca0af2ddd14b8dc4414c8ece52498f8f96e98bb0e4c228e2af27d4f8bd7689dda5160a6994e514e516cb4851f26d94b1bf50456c4dd5c485eaa8b02ba9f95",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "size": 281576,
     "visibility": "public",
     "digest": "71616564533d138fb12f08e761c2638d054814fdf9c9439638ec57b201e100445c364d73d8d7a4f0e3b784898d5fe6264e8242863fc5ac40163f1791468bbc46",
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -3,19 +3,19 @@
     "version": "clang 3.9.0",
     "size": 184678304,
     "digest": "cfde9a0f7f59823200f94422b4adb9a2fb5d4d07f240bbd1142c792434f6a1cbb4096d25c9853d77008fc40db0d827daa7003e78016f51241f621d6040ccc635",
     "algorithm": "sha512",
     "filename": "clang.tar.bz2",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 124444249,
-    "digest": "11239565e6dde851a5229a0380da129b129c560c3fa765eb4998549e688385be314523074456afec9ebe4ddb6af71e44c64d7c669e7e904ed253f6e7b0f6317b",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 80381089,
+    "digest": "01bc5c4d25ffd16f51643d36449ca469eccc362b8315945edd9b7e174462659a3d5d4bd5d2738f449557254ba930ca0d70d22f8717b51ba36af68193bd3c39f7",
     "algorithm": "sha512",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "cctools port from commit hash 8e9c3f2506b51",
     "size": 2233376,
     "digest": "d632ef587f0253f016aa5323999a3d9576284c04e66b5243a5780af9a55f474ac91ad8dee5bd86a6ee4e2593e2b345e2fd0aa4e8838b3686f84c5c5ac5c9c418",
--- a/browser/config/tooltool-manifests/win32/build-clang-cl.manifest
+++ b/browser/config/tooltool-manifests/win32/build-clang-cl.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 68287611,
-    "digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 68401632,
+    "digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
     "algorithm": "sha512",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/win32/clang.manifest
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 68287611,
-    "digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 68401632,
+    "digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
     "algorithm": "sha512",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 68287611,
-    "digest": "702b162f53970de096d1c7777f37eddd22251a910d523df4edcbead399d40bfb85b059b5745ec8d6e835149a8c7e7888aed36f7ca77ddfd57466a72505c2e86f",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 68401632,
+    "digest": "d55a50d61f3dbd34e68daf985c95773f6f66fa9b88364eefde03d2b009f2f4e9df7e7e72006347bb4ba0f65ff5190b4b6367fde01e79827e10534cd03fe720df",
     "algorithm": "sha512",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 73762095,
-    "digest": "9241134ee89a0beca228d9867b7526621faf05560fe802bb6471cc19d5c42135933883e1abd934c8d6657a06c975e167a3712e80e01bf9aa443db6ebb03a8096",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 73942095,
+    "digest": "22be87ae8b6aaf1016dc4a24858caadda47dabc6f44b5730b15489727c900ff7c12e01df8d12aa1775f4b83a4fae73a1c12f69fd15b2ab7fe2603a08637f187d",
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,19 +1,19 @@
 [
   {
     "size": 266240,
     "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
     "algorithm": "sha512",
     "filename": "mozmake.exe"
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 73762095,
-    "digest": "9241134ee89a0beca228d9867b7526621faf05560fe802bb6471cc19d5c42135933883e1abd934c8d6657a06c975e167a3712e80e01bf9aa443db6ebb03a8096",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 73942095,
+    "digest": "22be87ae8b6aaf1016dc4a24858caadda47dabc6f44b5730b15489727c900ff7c12e01df8d12aa1775f4b83a4fae73a1c12f69fd15b2ab7fe2603a08637f187d",
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "rustc.tar.bz2",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -12,16 +12,19 @@ this.EXPORTED_SYMBOLS = ["FormAutofillHe
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 /**
  * Returns the autocomplete information of fields according to heuristics.
  */
 this.FormAutofillHeuristics = {
   VALID_FIELDS: [
+    "given-name",
+    "additional-name",
+    "family-name",
     "organization",
     "street-address",
     "address-level2",
     "address-level1",
     "postal-code",
     "country",
     "tel",
     "email",
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/FormAutofillNameUtils.jsm
@@ -0,0 +1,280 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+// Cu.import loads jsm files based on ISO-Latin-1 for now (see bug 530257).
+// However, the references about name parts include multi-byte characters.
+// Thus, we use |loadSubScript| to load the references instead.
+const NAME_REFERENCES = "chrome://formautofill/content/nameReferences.js";
+
+this.EXPORTED_SYMBOLS = ["FormAutofillNameUtils"];
+
+// FormAutofillNameUtils is initially translated from
+// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
+var FormAutofillNameUtils = {
+  // Will be loaded from NAME_REFERENCES.
+  NAME_PREFIXES: [],
+  NAME_SUFFIXES: [],
+  FAMILY_NAME_PREFIXES: [],
+  COMMON_CJK_MULTI_CHAR_SURNAMES: [],
+  KOREAN_MULTI_CHAR_SURNAMES: [],
+
+  // The whitespace definition based on
+  // https://cs.chromium.org/chromium/src/base/strings/string_util_constants.cc?l=9&rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
+  WHITESPACE: [
+    "\u0009", // CHARACTER TABULATION
+    "\u000A", // LINE FEED (LF)
+    "\u000B", // LINE TABULATION
+    "\u000C", // FORM FEED (FF)
+    "\u000D", // CARRIAGE RETURN (CR)
+    "\u0020", // SPACE
+    "\u0085", // NEXT LINE (NEL)
+    "\u00A0", // NO-BREAK SPACE
+    "\u1680", // OGHAM SPACE MARK
+    "\u2000", // EN QUAD
+    "\u2001", // EM QUAD
+    "\u2002", // EN SPACE
+    "\u2003", // EM SPACE
+    "\u2004", // THREE-PER-EM SPACE
+    "\u2005", // FOUR-PER-EM SPACE
+    "\u2006", // SIX-PER-EM SPACE
+    "\u2007", // FIGURE SPACE
+    "\u2008", // PUNCTUATION SPACE
+    "\u2009", // THIN SPACE
+    "\u200A", // HAIR SPACE
+    "\u2028", // LINE SEPARATOR
+    "\u2029", // PARAGRAPH SEPARATOR
+    "\u202F", // NARROW NO-BREAK SPACE
+    "\u205F", // MEDIUM MATHEMATICAL SPACE
+    "\u3000", // IDEOGRAPHIC SPACE
+  ],
+
+  // The middle dot is used as a separator for foreign names in Japanese.
+  MIDDLE_DOT: [
+    "\u30FB", // KATAKANA MIDDLE DOT
+    "\u00B7", // A (common?) typo for "KATAKANA MIDDLE DOT"
+  ],
+
+  // The Unicode range is based on Wiki:
+  // https://en.wikipedia.org/wiki/CJK_Unified_Ideographs
+  // https://en.wikipedia.org/wiki/Hangul
+  // https://en.wikipedia.org/wiki/Japanese_writing_system
+  CJK_RANGE: [
+    "\u1100-\u11FF", // Hangul Jamo
+    "\u3040-\u309F", // Hiragana
+    "\u30A0-\u30FF", // Katakana
+    "\u3105-\u312C", // Bopomofo
+    "\u3130-\u318F", // Hangul Compatibility Jamo
+    "\u31F0-\u31FF", // Katakana Phonetic Extensions
+    "\u3200-\u32FF", // Enclosed CJK Letters and Months
+    "\u3400-\u4DBF", // CJK unified ideographs Extension A
+    "\u4E00-\u9FFF", // CJK Unified Ideographs
+    "\uA960-\uA97F", // Hangul Jamo Extended-A
+    "\uAC00-\uD7AF", // Hangul Syllables
+    "\uD7B0-\uD7FF", // Hangul Jamo Extended-B
+    "\uFF00-\uFFEF", // Halfwidth and Fullwidth Forms
+  ],
+
+  HANGUL_RANGE: [
+    "\u1100-\u11FF", // Hangul Jamo
+    "\u3130-\u318F", // Hangul Compatibility Jamo
+    "\uA960-\uA97F", // Hangul Jamo Extended-A
+    "\uAC00-\uD7AF", // Hangul Syllables
+    "\uD7B0-\uD7FF", // Hangul Jamo Extended-B
+  ],
+
+  _dataLoaded: false,
+
+  // Returns true if |set| contains |token|, modulo a final period.
+  _containsString(set, token) {
+    let target = token.replace(/\.$/, "").toLowerCase();
+    return set.includes(target);
+  },
+
+  // Removes common name prefixes from |name_tokens|.
+  _stripPrefixes(nameTokens) {
+    for (let i in nameTokens) {
+      if (!this._containsString(this.NAME_PREFIXES, nameTokens[i])) {
+        return nameTokens.slice(i);
+      }
+    }
+    return [];
+  },
+
+  // Removes common name suffixes from |name_tokens|.
+  _stripSuffixes(nameTokens) {
+    for (let i = nameTokens.length - 1; i >= 0; i--) {
+      if (!this._containsString(this.NAME_SUFFIXES, nameTokens[i])) {
+        return nameTokens.slice(0, i + 1);
+      }
+    }
+    return [];
+  },
+
+  _isCJKName(name) {
+    // The name is considered to be a CJK name if it is only CJK characters,
+    // spaces, and "middle dot" separators, with at least one CJK character, and
+    // no more than 2 words.
+    //
+    // Chinese and Japanese names are usually spelled out using the Han
+    // characters (logographs), which constitute the "CJK Unified Ideographs"
+    // block in Unicode, also referred to as Unihan. Korean names are usually
+    // spelled out in the Korean alphabet (Hangul), although they do have a Han
+    // equivalent as well.
+
+    let previousWasCJK = false;
+    let wordCount = 0;
+
+    for (let c of name) {
+      let isMiddleDot = this.MIDDLE_DOT.includes(c);
+      let isCJK = !isMiddleDot && this.reCJK.test(c);
+      if (!isCJK && !isMiddleDot && !this.WHITESPACE.includes(c)) {
+        return false;
+      }
+      if (isCJK && !previousWasCJK) {
+        wordCount++;
+      }
+      previousWasCJK = isCJK;
+    }
+
+    return wordCount > 0 && wordCount < 3;
+  },
+
+  // Tries to split a Chinese, Japanese, or Korean name into its given name &
+  // surname parts. If splitting did not work for whatever reason, returns null.
+  _splitCJKName(nameTokens) {
+    // The convention for CJK languages is to put the surname (last name) first,
+    // and the given name (first name) second. In a continuous text, there is
+    // normally no space between the two parts of the name. When entering their
+    // name into a field, though, some people add a space to disambiguate. CJK
+    // names (almost) never have a middle name.
+
+    let reHangulName = new RegExp(
+      "^[" + this.HANGUL_RANGE.join("") + this.WHITESPACE.join("") + "]+$", "u");
+    let nameParts = {
+      given: "",
+      middle: "",
+      family: "",
+    };
+
+    if (nameTokens.length == 1) {
+      // There is no space between the surname and given name. Try to infer
+      // where to separate between the two. Most Chinese and Korean surnames
+      // have only one character, but there are a few that have 2. If the name
+      // does not start with a surname from a known list, default to one
+      // character.
+      let name = nameTokens[0];
+      let isKorean = reHangulName.test(name);
+      let surnameLength = 0;
+
+      // 4-character Korean names are more likely to be 2/2 than 1/3, so use
+      // the full list of Korean 2-char surnames. (instead of only the common
+      // ones)
+      let multiCharSurnames = (isKorean && name.length > 3) ?
+        this.KOREAN_MULTI_CHAR_SURNAMES :
+        this.COMMON_CJK_MULTI_CHAR_SURNAMES;
+
+      // Default to 1 character if the surname is not in the list.
+      surnameLength =
+        multiCharSurnames.some(surname => name.startsWith(surname)) ? 2 : 1;
+
+      nameParts.family = name.substr(0, surnameLength);
+      nameParts.given = name.substr(surnameLength);
+    } else if (nameTokens.length == 2) {
+      // The user entered a space between the two name parts. This makes our job
+      // easier. Family name first, given name second.
+      nameParts.family = nameTokens[0];
+      nameParts.given = nameTokens[1];
+    } else {
+      return null;
+    }
+
+    return nameParts;
+  },
+
+  init() {
+    if (this._dataLoaded) {
+      return;
+    }
+    let sandbox = {};
+    let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+                         .getService(Ci.mozIJSSubScriptLoader);
+    scriptLoader.loadSubScript(NAME_REFERENCES, sandbox, "utf-8");
+    Object.assign(this, sandbox.nameReferences);
+    this._dataLoaded = true;
+
+    this.reCJK = new RegExp("[" + this.CJK_RANGE.join("") + "]", "u");
+  },
+
+  splitName(name) {
+    let nameTokens = name.trim().split(/[ ,\u3000\u30FB\u00B7]+/);
+    let nameParts = {
+      given: "",
+      middle: "",
+      family: "",
+    };
+
+    nameTokens = this._stripPrefixes(nameTokens);
+
+    if (this._isCJKName(name)) {
+      let parts = this._splitCJKName(nameTokens);
+      if (parts) {
+        return parts;
+      }
+    }
+
+    // Don't assume "Ma" is a suffix in John Ma.
+    if (nameTokens.length > 2) {
+      nameTokens = this._stripSuffixes(nameTokens);
+    }
+
+    if (!nameTokens.length) {
+      // Bad things have happened; just assume the whole thing is a given name.
+      nameParts.given = name;
+      return nameParts;
+    }
+
+    // Only one token, assume given name.
+    if (nameTokens.length == 1) {
+      nameParts.given = nameTokens[0];
+      return nameParts;
+    }
+
+    // 2 or more tokens. Grab the family, which is the last word plus any
+    // recognizable family prefixes.
+    let familyTokens = [nameTokens.pop()];
+    while (nameTokens.length) {
+      let lastToken = nameTokens[nameTokens.length - 1];
+      if (!this._containsString(this.FAMILY_NAME_PREFIXES, lastToken)) {
+        break;
+      }
+      familyTokens.unshift(lastToken);
+      nameTokens.pop();
+    }
+    nameParts.family = familyTokens.join(" ");
+
+    // Take the last remaining token as the middle name (if there are at least 2
+    // tokens).
+    if (nameTokens.length >= 2) {
+      nameParts.middle = nameTokens.pop();
+    }
+
+    // Remainder is given name.
+    nameParts.given = nameTokens.join(" ");
+
+    return nameParts;
+  },
+
+  joinNameParts({given, middle, family}) {
+    if (this._isCJKName(given) && this._isCJKName(family) && middle == "") {
+      return family + given;
+    }
+    return [given, middle, family].filter(part => part && part.length).join(" ");
+  },
+};
+
+FormAutofillNameUtils.init();
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -85,42 +85,62 @@ ProfileAutoCompleteResult.prototype = {
    * @param   {Array<Object>} allFieldNames The field names in the same section
    * @param   {object} profile The profile providing the labels to show.
    * @returns {string} The secondary label
    */
   _getSecondaryLabel(focusedFieldName, allFieldNames, profile) {
     /* TODO: Since "name" is a special case here, so the secondary "name" label
        will be refined when the handling rule for "name" is ready.
     */
-    const possibleNameFields = ["given-name", "additional-name", "family-name"];
+    const possibleNameFields = [
+      "name",
+      "given-name",
+      "additional-name",
+      "family-name",
+    ];
+
     focusedFieldName = possibleNameFields.includes(focusedFieldName) ?
                        "name" : focusedFieldName;
-    if (!profile.name) {
-      profile.name = FormAutofillUtils.generateFullName(profile["given-name"],
-                                                        profile["family-name"],
-                                                        profile["additional-name"]);
+
+    // Clones the profile to avoid exposing our modification.
+    let clonedProfile = Object.assign({}, profile);
+    if (!clonedProfile.name) {
+      clonedProfile.name =
+        FormAutofillUtils.generateFullName(clonedProfile["given-name"],
+                                           clonedProfile["family-name"],
+                                           clonedProfile["additional-name"]);
     }
 
     const secondaryLabelOrder = [
       "street-address",  // Street address
-      "name",            // Full name if needed
+      "name",            // Full name
       "address-level2",  // City/Town
       "organization",    // Company or organization name
       "address-level1",  // Province/State (Standardized code if possible)
       "country",         // Country
       "postal-code",     // Postal code
       "tel",             // Phone number
       "email",           // Email address
     ];
 
     for (const currentFieldName of secondaryLabelOrder) {
-      if (focusedFieldName != currentFieldName &&
-          allFieldNames.includes(currentFieldName) &&
-          profile[currentFieldName]) {
-        return profile[currentFieldName];
+      if (focusedFieldName == currentFieldName ||
+          !clonedProfile[currentFieldName]) {
+        continue;
+      }
+
+      let matching;
+      if (currentFieldName == "name") {
+        matching = allFieldNames.some(fieldName => possibleNameFields.includes(fieldName));
+      } else {
+        matching = allFieldNames.includes(currentFieldName);
+      }
+
+      if (matching) {
+        return clonedProfile[currentFieldName];
       }
     }
 
     return ""; // Nothing matched.
   },
 
   _generateLabels(focusedFieldName, allFieldNames, profiles) {
     // Skip results without a primary label.
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -10,16 +10,19 @@
  *
  * {
  *   version: 1,
  *   profiles: [
  *     {
  *       guid,             // 12 character...
  *
  *       // profile
+ *       given-name,
+ *       additional-name,
+ *       family-name,
  *       organization,     // Company
  *       street-address,    // (Multiline)
  *       address-level2,    // City/Town
  *       address-level1,    // Province (Standardized code if possible)
  *       postal-code,
  *       country,          // ISO 3166
  *       tel,
  *       email,
@@ -46,28 +49,33 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
                                   "resource://gre/modules/JSONFile.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillNameUtils",
+                                  "resource://formautofill/FormAutofillNameUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 const SCHEMA_VERSION = 1;
 
 // Name-related fields will be handled in follow-up bugs due to the complexity.
 const VALID_FIELDS = [
+  "given-name",
+  "additional-name",
+  "family-name",
   "organization",
   "street-address",
   "address-level2",
   "address-level1",
   "postal-code",
   "country",
   "tel",
   "email",
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/content/nameReferences.js
@@ -0,0 +1,144 @@
+/* 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/. */
+
+/* exported nameReferences */
+
+"use strict";
+
+// The data below is initially copied from
+// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util.cc?rcl=b861deff77abecff11ae6a9f6946e9cc844b9817
+var nameReferences = {
+  NAME_PREFIXES: [
+    "1lt",
+    "1st",
+    "2lt",
+    "2nd",
+    "3rd",
+    "admiral",
+    "capt",
+    "captain",
+    "col",
+    "cpt",
+    "dr",
+    "gen",
+    "general",
+    "lcdr",
+    "lt",
+    "ltc",
+    "ltg",
+    "ltjg",
+    "maj",
+    "major",
+    "mg",
+    "mr",
+    "mrs",
+    "ms",
+    "pastor",
+    "prof",
+    "rep",
+    "reverend",
+    "rev",
+    "sen",
+    "st",
+  ],
+
+  NAME_SUFFIXES: [
+    "b.a",
+    "ba",
+    "d.d.s",
+    "dds",
+    "i",
+    "ii",
+    "iii",
+    "iv",
+    "ix",
+    "jr",
+    "m.a",
+    "m.d",
+    "ma",
+    "md",
+    "ms",
+    "ph.d",
+    "phd",
+    "sr",
+    "v",
+    "vi",
+    "vii",
+    "viii",
+    "x",
+  ],
+
+  FAMILY_NAME_PREFIXES: [
+    "d'",
+    "de",
+    "del",
+    "der",
+    "di",
+    "la",
+    "le",
+    "mc",
+    "san",
+    "st",
+    "ter",
+    "van",
+    "von",
+  ],
+
+  // The common and non-ambiguous CJK surnames (last names) that have more than
+  // one character.
+  COMMON_CJK_MULTI_CHAR_SURNAMES: [
+    // Korean, taken from the list of surnames:
+    // https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD%EC%9D%98_%EC%84%B1%EC%94%A8_%EB%AA%A9%EB%A1%9D
+    "남궁",
+    "사공",
+    "서문",
+    "선우",
+    "제갈",
+    "황보",
+    "독고",
+    "망절",
+
+    // Chinese, taken from the top 10 Chinese 2-character surnames:
+    // https://zh.wikipedia.org/wiki/%E8%A4%87%E5%A7%93#.E5.B8.B8.E8.A6.8B.E7.9A.84.E8.A4.87.E5.A7.93
+    // Simplified Chinese (mostly mainland China)
+    "欧阳",
+    "令狐",
+    "皇甫",
+    "上官",
+    "司徒",
+    "诸葛",
+    "司马",
+    "宇文",
+    "呼延",
+    "端木",
+    // Traditional Chinese (mostly Taiwan)
+    "張簡",
+    "歐陽",
+    "諸葛",
+    "申屠",
+    "尉遲",
+    "司馬",
+    "軒轅",
+    "夏侯",
+  ],
+
+  // All Korean surnames that have more than one character, even the
+  // rare/ambiguous ones.
+  KOREAN_MULTI_CHAR_SURNAMES: [
+    "강전",
+    "남궁",
+    "독고",
+    "동방",
+    "망절",
+    "사공",
+    "서문",
+    "선우",
+    "소봉",
+    "어금",
+    "장곡",
+    "제갈",
+    "황목",
+    "황보",
+  ],
+};
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -20,16 +20,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="given-name">
                <input id="family-name" autocomplete="family-name">
                <input id="street-addr" autocomplete="street-address">
                <input id="city" autocomplete="address-level2">
                <input id="country" autocomplete="country">
                <input id="email" autocomplete="email">
                <input id="tel" autocomplete="tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
     ],
   },
   {
@@ -37,16 +39,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
     ],
   },
   {
@@ -54,16 +58,18 @@ const TESTCASES = [
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
     fieldDetails: [
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
+      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel"},
     ],
   },
 ];
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_isCJKName.js
@@ -0,0 +1,76 @@
+/**
+ * Tests the "isCJKName" function of FormAutofillNameUtils object.
+ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://formautofill/FormAutofillNameUtils.jsm");
+
+// Test cases is initially copied from
+// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
+const TESTCASES = [
+  {
+    // Non-CJK language with only ASCII characters.
+    fullName: "Homer Jay Simpson",
+    expectedResult: false,
+  },
+  {
+    // Non-CJK language with some ASCII characters.
+    fullName: "Éloïse Paré",
+    expectedResult: false,
+  },
+  {
+    // Non-CJK language with no ASCII characters.
+    fullName: "Σωκράτης",
+    expectedResult: false,
+  },
+  {
+    // (Simplified) Chinese name, Unihan.
+    fullName: "刘翔",
+    expectedResult: true,
+  },
+  {
+    // (Simplified) Chinese name, Unihan, with an ASCII space.
+    fullName: "成 龙",
+    expectedResult: true,
+  },
+  {
+    // Korean name, Hangul.
+    fullName: "송지효",
+    expectedResult: true,
+  },
+  {
+    // Korean name, Hangul, with an 'IDEOGRAPHIC SPACE' (U+3000).
+    fullName: "김 종국",
+    expectedResult: true,
+  },
+  {
+    // Japanese name, Unihan.
+    fullName: "山田貴洋",
+    expectedResult: true,
+  },
+  {
+    // Japanese name, Katakana, with a 'KATAKANA MIDDLE DOT' (U+30FB).
+    fullName: "ビル・ゲイツ",
+    expectedResult: true,
+  },
+  {
+    // Japanese name, Katakana, with a 'MIDDLE DOT' (U+00B7) (likely a typo).
+    fullName: "ビル·ゲイツ",
+    expectedResult: true,
+  },
+  {
+    // CJK names don't have a middle name, so a 3-part name is bogus to us.
+    fullName: "반 기 문",
+    expectedResult: false,
+  },
+];
+
+add_task(function* test_isCJKName() {
+  TESTCASES.forEach(testcase => {
+    do_print("Starting testcase: " + testcase.fullName);
+    let result = FormAutofillNameUtils._isCJKName(testcase.fullName);
+    do_check_eq(result, testcase.expectedResult);
+  });
+});
--- a/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
+++ b/browser/extensions/formautofill/test/unit/test_markAsAutofillField.js
@@ -1,25 +1,31 @@
 "use strict";
 
 Cu.import("resource://formautofill/FormAutofillContent.jsm");
 
 const TESTCASES = [
   {
-    description: "Form containing 5 fields with autocomplete attribute.",
+    description: "Form containing 8 fields with autocomplete attribute.",
     document: `<form>
+                 <input id="given-name" autocomplete="given-name">
+                 <input id="additional-name" autocomplete="additional-name">
+                 <input id="family-name" autocomplete="family-name">
                  <input id="street-addr" autocomplete="street-address">
                  <input id="city" autocomplete="address-level2">
                  <input id="country" autocomplete="country">
                  <input id="email" autocomplete="email">
                  <input id="tel" autocomplete="tel">
                  <input id="without-autocomplete-1">
                  <input id="without-autocomplete-2">
                </form>`,
     expectedResult: [
+      "given-name",
+      "additional-name",
+      "family-name",
       "street-addr",
       "city",
       "country",
       "email",
       "tel",
     ],
   },
   {
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_nameUtils.js
@@ -0,0 +1,285 @@
+/**
+ * Tests FormAutofillNameUtils object.
+ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://formautofill/FormAutofillNameUtils.jsm");
+
+// Test cases initially copied from
+// https://cs.chromium.org/chromium/src/components/autofill/core/browser/autofill_data_util_unittest.cc
+const TESTCASES = [
+  {
+    description: "Full name including given, middle and family names",
+    fullName: "Homer Jay Simpson",
+    nameParts: {
+      given: "Homer",
+      middle: "Jay",
+      family: "Simpson",
+    },
+  },
+  {
+    description: "No middle name",
+    fullName: "Moe Szyslak",
+    nameParts: {
+      given: "Moe",
+      middle: "",
+      family: "Szyslak",
+    },
+  },
+  {
+    description: "Common name prefixes removed",
+    fullName: "Reverend Timothy Lovejoy",
+    nameParts: {
+      given: "Timothy",
+      middle: "",
+      family: "Lovejoy",
+    },
+    expectedFullName: "Timothy Lovejoy",
+  },
+  {
+    description: "Common name suffixes removed",
+    fullName: "John Frink Phd",
+    nameParts: {
+      given: "John",
+      middle: "",
+      family: "Frink",
+    },
+    expectedFullName: "John Frink",
+  },
+  {
+    description: "Exception to the name suffix removal",
+    fullName: "John Ma",
+    nameParts: {
+      given: "John",
+      middle: "",
+      family: "Ma",
+    },
+  },
+  {
+    description: "Common family name prefixes not considered a middle name",
+    fullName: "Milhouse Van Houten",
+    nameParts: {
+      given: "Milhouse",
+      middle: "",
+      family: "Van Houten",
+    },
+  },
+
+  // CJK names have reverse order (surname goes first, given name goes second).
+  {
+    description: "Chinese name, Unihan",
+    fullName: "孫 德明",
+    nameParts: {
+      given: "德明",
+      middle: "",
+      family: "孫",
+    },
+    expectedFullName: "孫德明",
+  },
+  {
+    description: "Chinese name, Unihan, \"IDEOGRAPHIC SPACE\"",
+    fullName: "孫 德明",
+    nameParts: {
+      given: "德明",
+      middle: "",
+      family: "孫",
+    },
+    expectedFullName: "孫德明",
+  },
+  {
+    description: "Korean name, Hangul",
+    fullName: "홍 길동",
+    nameParts: {
+      given: "길동",
+      middle: "",
+      family: "홍",
+    },
+    expectedFullName: "홍길동",
+  },
+  {
+    description: "Japanese name, Unihan",
+    fullName: "山田 貴洋",
+    nameParts: {
+      given: "貴洋",
+      middle: "",
+      family: "山田",
+    },
+    expectedFullName: "山田貴洋",
+  },
+
+  // In Japanese, foreign names use 'KATAKANA MIDDLE DOT' (U+30FB) as a
+  // separator. There is no consensus for the ordering. For now, we use the same
+  // ordering as regular Japanese names ("last・first").
+  {
+    description: "Foreign name in Japanese, Katakana",
+    fullName: "ゲイツ・ビル",
+    nameParts: {
+      given: "ビル",
+      middle: "",
+      family: "ゲイツ",
+    },
+    expectedFullName: "ゲイツビル",
+  },
+
+  // 'KATAKANA MIDDLE DOT' is occasionally typoed as 'MIDDLE DOT' (U+00B7).
+  {
+    description: "Foreign name in Japanese, Katakana",
+    fullName: "ゲイツ·ビル",
+    nameParts: {
+      given: "ビル",
+      middle: "",
+      family: "ゲイツ",
+    },
+    expectedFullName: "ゲイツビル",
+  },
+
+  // CJK names don't usually have a space in the middle, but most of the time,
+  // the surname is only one character (in Chinese & Korean).
+  {
+    description: "Korean name, Hangul",
+    fullName: "최성훈",
+    nameParts: {
+      given: "성훈",
+      middle: "",
+      family: "최",
+    },
+  },
+  {
+    description: "(Simplified) Chinese name, Unihan",
+    fullName: "刘翔",
+    nameParts: {
+      given: "翔",
+      middle: "",
+      family: "刘",
+    },
+  },
+  {
+    description: "(Traditional) Chinese name, Unihan",
+    fullName: "劉翔",
+    nameParts: {
+      given: "翔",
+      middle: "",
+      family: "劉",
+    },
+  },
+
+  // There are a few exceptions. Occasionally, the surname has two characters.
+  {
+    description: "Korean name, Hangul",
+    fullName: "남궁도",
+    nameParts: {
+      given: "도",
+      middle: "",
+      family: "남궁",
+    },
+  },
+  {
+    description: "Korean name, Hangul",
+    fullName: "황보혜정",
+    nameParts: {
+      given: "혜정",
+      middle: "",
+      family: "황보",
+    },
+  },
+  {
+    description: "(Traditional) Chinese name, Unihan",
+    fullName: "歐陽靖",
+    nameParts: {
+      given: "靖",
+      middle: "",
+      family: "歐陽",
+    },
+  },
+
+  // In Korean, some 2-character surnames are rare/ambiguous, like "강전": "강"
+  // is a common surname, and "전" can be part of a given name. In those cases,
+  // we assume it's 1/2 for 3-character names, or 2/2 for 4-character names.
+  {
+    description: "Korean name, Hangul",
+    fullName: "강전희",
+    nameParts: {
+      given: "전희",
+      middle: "",
+      family: "강",
+    },
+  },
+  {
+    description: "Korean name, Hangul",
+    fullName: "황목치승",
+    nameParts: {
+      given: "치승",
+      middle: "",
+      family: "황목",
+    },
+  },
+
+  // It occasionally happens that a full name is 2 characters, 1/1.
+  {
+    description: "Korean name, Hangul",
+    fullName: "이도",
+    nameParts: {
+      given: "도",
+      middle: "",
+      family: "이",
+    },
+  },
+  {
+    description: "Korean name, Hangul",
+    fullName: "孫文",
+    nameParts: {
+      given: "文",
+      middle: "",
+      family: "孫",
+    },
+  },
+
+  // These are no CJK names for us, they're just bogus.
+  {
+    description: "Bogus",
+    fullName: "Homer シンプソン",
+    nameParts: {
+      given: "Homer",
+      middle: "",
+      family: "シンプソン",
+    },
+  },
+  {
+    description: "Bogus",
+    fullName: "ホーマー Simpson",
+    nameParts: {
+      given: "ホーマー",
+      middle: "",
+      family: "Simpson",
+    },
+  },
+  {
+    description: "CJK has a middle-name, too unusual",
+    fullName: "반 기 문",
+    nameParts: {
+      given: "반",
+      middle: "기",
+      family: "문",
+    },
+  },
+];
+
+add_task(function* test_splitName() {
+  TESTCASES.forEach(testcase => {
+    if (testcase.fullName) {
+      do_print("Starting testcase: " + testcase.description);
+      let nameParts = FormAutofillNameUtils.splitName(testcase.fullName);
+      Assert.deepEqual(nameParts, testcase.nameParts);
+    }
+  });
+});
+
+add_task(function* test_joinName() {
+  TESTCASES.forEach(testcase => {
+    do_print("Starting testcase: " + testcase.description);
+    let name = FormAutofillNameUtils.joinNameParts(testcase.nameParts);
+    do_check_eq(name, testcase.expectedFullName || testcase.fullName);
+  });
+});
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -1,30 +1,34 @@
 "use strict";
 
 Cu.import("resource://formautofill/ProfileAutoCompleteResult.jsm");
 
 let matchingProfiles = [{
   guid: "test-guid-1",
+  "given-name": "Timothy",
+  "family-name": "Berners-Lee",
   organization: "Sesame Street",
   "street-address": "123 Sesame Street.",
   tel: "1-345-345-3456.",
 }, {
   guid: "test-guid-2",
+  "given-name": "John",
+  "family-name": "Doe",
   organization: "Mozilla",
   "street-address": "331 E. Evelyn Avenue",
   tel: "1-650-903-0800",
 }, {
   guid: "test-guid-3",
   organization: "",
   "street-address": "321, No Name St.",
   tel: "1-000-000-0000",
 }];
 
-let allFieldNames = ["street-address", "organization", "tel"];
+let allFieldNames = ["given-name", "family-name", "street-address", "organization", "tel"];
 
 let testCases = [{
   description: "Focus on an `organization` field",
   options: {},
   matchingProfiles,
   allFieldNames,
   searchString: "",
   fieldName: "organization",
@@ -101,26 +105,26 @@ let testCases = [{
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
     items: [{
       value: "123 Sesame Street.",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[0]),
       label: JSON.stringify({
         primary: "123 Sesame Street.",
-        secondary: "Sesame Street",
+        secondary: "Timothy Berners-Lee",
       }),
       image: "",
     }, {
       value: "331 E. Evelyn Avenue",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[1]),
       label: JSON.stringify({
         primary: "331 E. Evelyn Avenue",
-        secondary: "Mozilla",
+        secondary: "John Doe",
       }),
       image: "",
     }, {
       value: "321, No Name St.",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[2]),
       label: JSON.stringify({
         primary: "321, No Name St.",
--- a/browser/extensions/formautofill/test/unit/test_profileStorage.js
+++ b/browser/extensions/formautofill/test/unit/test_profileStorage.js
@@ -5,16 +5,19 @@
 "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 = {
+  "given-name": "Timothy",
+  "additional-name": "John",
+  "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
   "street-address": "32 Vassar Street\nMIT Room 32-G524",
   "address-level2": "Cambridge",
   "address-level1": "MA",
   "postal-code": "02139",
   country: "US",
   tel: "+1 617 253 5702",
   email: "timbl@w3.org",
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -3,14 +3,15 @@ firefox-appdir = browser
 head = head.js
 support-files =
 
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_enabledStatus.js]
 [test_findLabelElements.js]
 [test_getFormInputDetails.js]
+[test_isCJKName.js]
 [test_markAsAutofillField.js]
+[test_nameUtils.js]
 [test_onFormSubmitted.js]
 [test_profileAutocompleteResult.js]
 [test_profileStorage.js]
 [test_savedFieldNames.js]
-
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -102,39 +102,42 @@ set_config('MOZ_RUST', rust_compiler)
 @template
 def rust_triple_alias(host_or_target):
     """Template defining the alias used for rustc's --target flag.
     `host_or_target` is either `host` or `target` (the @depends functions
     from init.configure).
     """
     assert host_or_target in (host, target)
 
-    @depends(rustc, host_or_target, when=rust_compiler)
+    @depends(rustc, host_or_target, building_with_gcc, when=rust_compiler)
     @imports('os')
     @imports('subprocess')
     @imports(_from='mozbuild.configure.util', _import='LineIO')
     @imports(_from='mozbuild.shellutil', _import='quote')
     @imports(_from='tempfile', _import='mkstemp')
     @imports(_from='textwrap', _import='dedent')
-    def rust_target(rustc, host_or_target):
+    def rust_target(rustc, host_or_target, building_with_gcc):
         # Rust's --target options are similar to, but not exactly the same
         # as, the autoconf-derived targets we use.  An example would be that
         # Rust uses distinct target triples for targetting the GNU C++ ABI
         # and the MSVC C++ ABI on Win32, whereas autoconf has a single
         # triple and relies on the user to ensure that everything is
         # compiled for the appropriate ABI.  We need to perform appropriate
         # munging to get the correct option to rustc.
         #
         # The canonical list of targets supported can be derived from:
         #
         # https://github.com/rust-lang/rust/blob/master/src/librustc_back/target/mod.rs
 
         # Avoid having to write out os+kernel for all the platforms where
         # they don't differ.
         os_or_kernel = host_or_target.kernel if host_or_target.kernel == 'Linux' and host_or_target.os != 'Android' else host_or_target.os
+        # If we are targetting Windows but the compiler is gcc, we need to
+        # use a different rustc target
+        os_or_kernel = 'WINNT-MINGW' if building_with_gcc and host_or_target.kernel == 'WINNT' else os_or_kernel
         rustc_target = {
             # DragonFly
             ('x86_64', 'DragonFly'): 'x86_64-unknown-dragonfly',
             # FreeBSD
             ('aarch64', 'FreeBSD'): 'aarch64-unknown-freebsd',
             ('x86', 'FreeBSD'): 'i686-unknown-freebsd',
             ('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd',
             # NetBSD
@@ -158,20 +161,20 @@ def rust_triple_alias(host_or_target):
             ('arm', 'iOS'): 'armv7s-apple-ios',
             ('x86', 'iOS'): 'i386-apple-ios',
             ('x86_64', 'iOS'): 'x86_64-apple-ios',
             # Android
             ('aarch64', 'Android'): 'aarch64-linux-android',
             ('arm', 'Android'): 'armv7-linux-androideabi',
             ('x86', 'Android'): 'i686-linux-android',
             # Windows
-            # XXX better detection of CXX needed here, to figure out whether
-            # we need i686-pc-windows-gnu instead, since mingw32 builds work.
             ('x86', 'WINNT'): 'i686-pc-windows-msvc',
             ('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc',
+            ('x86', 'WINNT-MINGW'): 'i686-pc-windows-gnu',
+            ('x86_64', 'WINNT-MINGW'): 'x86_64-pc-windows-gnu',
         }.get((host_or_target.cpu, os_or_kernel), None)
 
         if rustc_target is None:
             die("Don't know how to translate {} for rustc".format(host_or_target.alias))
 
         # Check to see whether our rustc has a reasonably functional stdlib
         # for our chosen target.
         target_arg = '--target=' + rustc_target
--- a/config/expandlibs_config.py
+++ b/config/expandlibs_config.py
@@ -12,16 +12,18 @@ def normalize_suffix(suffix):
         value = '.' + value
     return value
 
 # Variables from the build system
 AR = substs['AR']
 AR_EXTRACT = substs['AR_EXTRACT'].replace('$(AR)', AR)
 DLL_PREFIX = substs['DLL_PREFIX']
 LIB_PREFIX = substs['LIB_PREFIX']
+RUST_LIB_PREFIX = substs['RUST_LIB_PREFIX']
 OBJ_SUFFIX = normalize_suffix(substs['OBJ_SUFFIX'])
 LIB_SUFFIX = normalize_suffix(substs['LIB_SUFFIX'])
+RUST_LIB_SUFFIX = normalize_suffix(substs['RUST_LIB_SUFFIX'])
 DLL_SUFFIX = normalize_suffix(substs['DLL_SUFFIX'])
 IMPORT_LIB_SUFFIX = normalize_suffix(substs['IMPORT_LIB_SUFFIX'])
 LIBS_DESC_SUFFIX = normalize_suffix(substs['LIBS_DESC_SUFFIX'])
 EXPAND_LIBS_LIST_STYLE = substs['EXPAND_LIBS_LIST_STYLE']
 EXPAND_LIBS_ORDER_STYLE = substs['EXPAND_LIBS_ORDER_STYLE']
 LD_PRINT_ICF_SECTIONS = substs['LD_PRINT_ICF_SECTIONS']
--- a/config/makefiles/debugmake.mk
+++ b/config/makefiles/debugmake.mk
@@ -89,16 +89,17 @@ showbuild:
 		OS_LIBS \
 		EXTRA_LIBS \
 		BIN_FLAGS \
 		INCLUDES \
 		DEFINES \
 		ACDEFINES \
 		BIN_SUFFIX \
 		LIB_SUFFIX \
+		RUST_LIB_SUFFIX \
 		DLL_SUFFIX \
 		IMPORT_LIB_SUFFIX \
 		INSTALL \
 		VPATH \
 	)
 
 showhost:
 	$(call print_vars,\
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -28,17 +28,17 @@ idl_deps_dir := .deps
 
 dist_idl_dir := $(DIST)/idl
 dist_include_dir := $(DIST)/include
 process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
 
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
-	@echo "$(@F)"
+	$(REPORT_BUILD)
 	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
 		$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
 		$(dist_idl_dir) $(dist_include_dir) $(@D) $(libxul_sdk_includes) \
 		$(basename $(notdir $@)) $($(basename $(notdir $@))_deps)
 # When some IDL is added or removed, if the actual IDL file was already, or
 # still is, in the tree, simple dependencies can't detect that the XPT needs
 # to be rebuilt.
 # Add the current value of $($(xpidl_module)_deps) in the depend file, such that
--- a/devtools/client/netmonitor/src/components/headers-panel.js
+++ b/devtools/client/netmonitor/src/components/headers-panel.js
@@ -6,17 +6,20 @@
 
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { NetMonitorController } = require("../netmonitor-controller");
-const { getFormattedSize } = require("../utils/format-utils");
+const {
+  getFormattedIPAndPort,
+  getFormattedSize,
+} = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const {
   getHeadersURL,
   getHTTPStatusCodeURL,
 } = require("../utils/mdn-utils");
 const { writeHeaderText } = require("../utils/request-utils");
 
 // Components
@@ -157,17 +160,17 @@ const HeadersPanel = createClass({
     let summaryUrl = urlDetails.unicodeUrl ?
       this.renderSummary(SUMMARY_URL, urlDetails.unicodeUrl) : null;
 
     let summaryMethod = method ?
       this.renderSummary(SUMMARY_METHOD, method) : null;
 
     let summaryAddress = remoteAddress ?
       this.renderSummary(SUMMARY_ADDRESS,
-        remotePort ? `${remoteAddress}:${remotePort}` : remoteAddress) : null;
+        getFormattedIPAndPort(remoteAddress, remotePort)) : null;
 
     let summaryStatus;
 
     if (status) {
       let code;
       if (fromCache) {
         code = "cached";
       } else if (fromServiceWorker) {
--- a/devtools/client/netmonitor/src/components/request-list-column-domain.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-domain.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
+const { getFormattedIPAndPort } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { propertiesEqual } = require("../utils/request-utils");
 
 const { div } = DOM;
 
 const UPDATED_DOMAIN_PROPS = [
   "remoteAddress",
   "securityState",
@@ -29,20 +30,22 @@ const RequestListColumnDomain = createCl
   },
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
   },
 
   render() {
     let { item, onSecurityIconClick } = this.props;
-    let { remoteAddress, securityState, urlDetails: { host, isLocal } } = item;
+    let { remoteAddress, remotePort, securityState,
+      urlDetails: { host, isLocal } } = item;
     let iconClassList = ["requests-security-state-icon"];
     let iconTitle;
-    let title = host + (remoteAddress ? ` (${remoteAddress})` : "");
+    let title = host + (remoteAddress ?
+      ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
 
     if (isLocal) {
       iconClassList.push("security-state-local");
       iconTitle = L10N.getStr("netmonitor.security.state.secure");
     } else if (securityState) {
       iconClassList.push(`security-state-${securityState}`);
       iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
     }
--- a/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
@@ -4,33 +4,35 @@
 
 "use strict";
 
 const {
   createClass,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
+const { getFormattedIPAndPort } = require("../utils/format-utils");
 
 const { div } = DOM;
 
 const RequestListColumnRemoteIP = createClass({
   displayName: "RequestListColumnRemoteIP",
 
   propTypes: {
     item: PropTypes.object.isRequired,
   },
 
   shouldComponentUpdate(nextProps) {
     return this.props.item.remoteAddress !== nextProps.item.remoteAddress;
   },
 
   render() {
     let { remoteAddress, remotePort } = this.props.item;
-    let remoteIP = remoteAddress ? `${remoteAddress}:${remotePort}` : "unknown";
+    let remoteIP = remoteAddress ?
+      getFormattedIPAndPort(remoteAddress, remotePort) : "unknown";
 
     return (
       div({ className: "requests-list-column requests-list-remoteip", title: remoteIP },
         remoteIP
       )
     );
   }
 });
--- a/devtools/client/netmonitor/src/utils/filter-text-utils.js
+++ b/devtools/client/netmonitor/src/utils/filter-text-utils.js
@@ -26,16 +26,17 @@
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 "use strict";
 
 const { HEADERS } = require("../constants");
+const { getFormattedIPAndPort } = require("./format-utils");
 const HEADER_FILTERS = HEADERS
   .filter(h => h.canFilter)
   .map(h => h.filterKey || h.name);
 
 const FILTER_FLAGS = [
   ...HEADER_FILTERS,
   "scheme",
   "mime-type",
@@ -127,17 +128,18 @@ function isFlagFilterMatch(item, { type,
       let protocol = item.httpVersion;
       match = typeof protocol === "string" ?
                 protocol.toLowerCase().includes(value) : false;
       break;
     case "domain":
       match = item.urlDetails.host.toLowerCase().includes(value);
       break;
     case "remote-ip":
-      match = `${item.remoteAddress}:${item.remotePort}`.toLowerCase().includes(value);
+      match = getFormattedIPAndPort(item.remoteAddress, item.remotePort)
+        .toLowerCase().includes(value);
       break;
     case "has-response-header":
       if (typeof item.responseHeaders === "object") {
         let { headers } = item.responseHeaders;
         match = headers.findIndex(h => h.name.toLowerCase() === value) > -1;
       } else {
         match = false;
       }
--- a/devtools/client/netmonitor/src/utils/format-utils.js
+++ b/devtools/client/netmonitor/src/utils/format-utils.js
@@ -72,14 +72,29 @@ function getFormattedTime(ms) {
   if (ms < MAX_SECOND) {
     const sec = ms / MAX_MILLISECOND;
     return L10N.getFormatStr("networkMenu.second", getTimeWithDecimals(sec));
   }
   const min = ms / MAX_SECOND;
   return L10N.getFormatStr("networkMenu.minute", getTimeWithDecimals(min));
 }
 
+/**
+ * Formats IP (v4 and v6) and port
+ *
+ * @param {string} ip - IP address
+ * @param {string} port
+ * @return {string} the formatted IP + port
+ */
+function getFormattedIPAndPort(ip, port) {
+  if (!port) {
+    return ip;
+  }
+  return ip.match(/:+/) ? `[${ip}]:${port}` : `${ip}:${port}`;
+}
+
 module.exports = {
+  getFormattedIPAndPort,
   getFormattedSize,
   getFormattedTime,
   getSizeWithDecimals,
   getTimeWithDecimals,
 };
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -10,16 +10,19 @@
 
 // shared-head.js handles imports, constants, and utility functions
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 const { EVENTS } = require("devtools/client/netmonitor/src/constants");
 const {
+  getFormattedIPAndPort
+} = require("devtools/client/netmonitor/src/utils/format-utils");
+const {
   decodeUnicodeUrl,
   getUrlBaseName,
   getUrlQuery,
   getUrlHost,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 
 /* eslint-disable no-unused-vars, max-len */
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
@@ -371,19 +374,20 @@ function verifyRequestItemTarget(documen
 
   let { fuzzyUrl, status, statusText, cause, type, fullMimeType,
         transferred, size, time, displayedStatus } = data;
 
   let target = document.querySelectorAll(".request-list-item")[visibleIndex];
   let unicodeUrl = decodeUnicodeUrl(url);
   let name = getUrlBaseName(url);
   let query = getUrlQuery(url);
-  let hostPort = getUrlHost(url);
+  let host = getUrlHost(url);
   let { httpVersion = "", remoteAddress, remotePort } = requestItem;
-  let remoteIP = remoteAddress ? `${remoteAddress}:${remotePort}` : "unknown";
+  let formattedIPPort = getFormattedIPAndPort(remoteAddress, remotePort);
+  let remoteIP = remoteAddress ? `${formattedIPPort}` : "unknown";
 
   if (fuzzyUrl) {
     ok(requestItem.method.startsWith(method), "The attached method is correct.");
     ok(requestItem.url.startsWith(url), "The attached url is correct.");
   } else {
     is(requestItem.method, method, "The attached method is correct.");
     is(requestItem.url, url, "The attached url is correct.");
   }
@@ -406,19 +410,19 @@ function verifyRequestItemTarget(documen
 
   is(target.querySelector(".requests-list-protocol").textContent,
     httpVersion, "The displayed protocol is correct.");
 
   is(target.querySelector(".requests-list-protocol").getAttribute("title"),
     httpVersion, "The tooltip protocol is correct.");
 
   is(target.querySelector(".requests-list-domain").textContent,
-    hostPort, "The displayed domain is correct.");
+    host, "The displayed domain is correct.");
 
-  let domainTooltip = hostPort + (remoteAddress ? " (" + remoteAddress + ")" : "");
+  let domainTooltip = host + (remoteAddress ? " (" + formattedIPPort + ")" : "");
   is(target.querySelector(".requests-list-domain").getAttribute("title"),
     domainTooltip, "The tooltip domain is correct.");
 
   is(target.querySelector(".requests-list-remoteip").textContent,
     remoteIP, "The displayed remote IP is correct.");
 
   is(target.querySelector(".requests-list-remoteip").getAttribute("title"),
     remoteIP, "The tooltip remote IP is correct.");
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -949,30 +949,16 @@ EffectCompositor::SetPerformanceWarning(
 }
 
 bool
 EffectCompositor::PreTraverse()
 {
   return PreTraverseInSubtree(nullptr);
 }
 
-static bool
-IsFlattenedTreeDescendantOf(nsINode* aPossibleDescendant,
-                            nsINode* aPossibleAncestor)
-{
-  do {
-    if (aPossibleDescendant == aPossibleAncestor) {
-      return true;
-    }
-    aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
-  } while (aPossibleDescendant);
-
-  return false;
-}
-
 bool
 EffectCompositor::PreTraverseInSubtree(Element* aRoot)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mPresContext->RestyleManager()->IsServo());
 
   bool foundElementsNeedingRestyle = false;
   for (size_t i = 0; i < kCascadeLevelCount; ++i) {
@@ -984,17 +970,19 @@ EffectCompositor::PreTraverseInSubtree(E
       if (!postedRestyle) {
         continue;
       }
 
       NonOwningAnimationTarget target = iter.Key();
 
       // Ignore restyles that aren't in the flattened tree subtree rooted at
       // aRoot.
-      if (aRoot && !IsFlattenedTreeDescendantOf(target.mElement, aRoot)) {
+      if (aRoot &&
+          !nsContentUtils::ContentIsFlattenedTreeDescendantOf(target.mElement,
+                                                              aRoot)) {
         continue;
       }
 
       // We need to post restyle hints even if the target is not in EffectSet to
       // ensure the final restyling for removed animations.
       // We can't call PostRestyleEvent directly here since we are still in the
       // middle of the servo traversal.
       mPresContext->RestyleManager()->AsServo()->
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/AnimValuesStyleRule.h"
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/FloatingPoint.h" // For IsFinite
 #include "mozilla/LookAndFeel.h" // For LookAndFeel::GetInt
 #include "mozilla/KeyframeUtils.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TypeTraits.h"
 #include "Layers.h" // For Layer
 #include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetStyleContext
 #include "nsContentUtils.h"  // nsContentUtils::ReportToConsole
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 #include "nsCSSPseudoElements.h" // For CSSPseudoElementType
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -38,16 +38,17 @@ class nsIFrame;
 class nsIPresShell;
 
 namespace mozilla {
 
 class AnimValuesStyleRule;
 enum class CSSPseudoElementType : uint8_t;
 class ErrorResult;
 struct AnimationRule;
+struct ServoComputedValuesWithParent;
 struct TimingParams;
 
 namespace dom {
 class ElementOrCSSPseudoElement;
 class GlobalObject;
 class OwningElementOrCSSPseudoElement;
 class UnrestrictedDoubleOrKeyframeAnimationOptions;
 class UnrestrictedDoubleOrKeyframeEffectOptions;
@@ -99,23 +100,16 @@ struct AnimationProperty
            mSegments == aOther.mSegments;
   }
   bool operator!=(const AnimationProperty& aOther) const
   {
     return !(*this == aOther);
   }
 };
 
-struct ServoComputedValuesWithParent
-{
-  const ServoComputedValues* mCurrentStyle;
-  const ServoComputedValues* mParentStyle;
-  explicit operator bool() const { return true; }
-};
-
 struct ElementPropertyTransition;
 
 namespace dom {
 
 class Animation;
 
 class KeyframeEffectReadOnly : public AnimationEffectReadOnly
 {
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/KeyframeUtils.h"
 
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Move.h"
 #include "mozilla/RangedArray.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoBindingTypes.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimingParams.h"
 #include "mozilla/dom/BaseKeyframeTypesBinding.h" // For FastBaseKeyframe etc.
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h" // For PropertyValuesPair etc.
 #include "jsapi.h" // For ForOfIterator etc.
 #include "nsClassHashtable.h"
new file mode 100644
--- /dev/null
+++ b/dom/animation/ServoComputedValuesWithParent.h
@@ -0,0 +1,27 @@
+/* -*- 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 mozilla_ServoComputedValuesWithParent_h
+#define mozilla_ServoComputedValuesWithParent_h
+
+#include "mozilla/ServoBindingTypes.h"
+
+namespace mozilla {
+
+/**
+ * A tuple of a set of computed values along with the computed values for the
+ * corresponding element's parent.
+ */
+struct ServoComputedValuesWithParent
+{
+  const ServoComputedValues* mCurrentStyle;
+  const ServoComputedValues* mParentStyle;
+  explicit operator bool() const { return true; }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ServoComputedValuesWithParent_h
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -33,16 +33,17 @@ EXPORTS.mozilla += [
     'ComputedTimingFunction.h',
     'EffectCompositor.h',
     'EffectSet.h',
     'Keyframe.h',
     'KeyframeEffectParams.h',
     'KeyframeUtils.h',
     'PendingAnimationTracker.h',
     'PseudoElementHashEntry.h',
+    'ServoComputedValuesWithParent.h',
     'TimingParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffectReadOnly.cpp',
     'AnimationEffectTiming.cpp',
     'AnimationEffectTimingReadOnly.cpp',
--- a/dom/animation/test/crashtests/1330190-2.html
+++ b/dom/animation/test/crashtests/1330190-2.html
@@ -2,16 +2,17 @@
 <html class="reftest-wait">
 <head>
 <style>
 @keyframes anim {
 }
 
 #o_0:before {
   animation: anim 10s;
+  content: "";
 }
 </style>
 <meta charset="UTF-8">
 <script>
 function boom(){
   function getPseudoElement() {
     var anim = document.getAnimations()[0];
     anim.cancel();
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -489,62 +489,16 @@ AllChildrenIterator::GetPreviousChild()
       return beforeContent;
     }
   }
 
   mPhase = eAtBegin;
   return nullptr;
 }
 
-static bool
-IsNativeAnonymousImplementationOfPseudoElement(nsIContent* aContent)
-{
-  // First, we need a frame. This leads to the tricky issue of what we can
-  // infer if the frame is null.
-  //
-  // Unlike regular nodes, native anonymous content (NAC) gets created during
-  // frame construction, which happens after the main style traversal. This
-  // means that we have to manually resolve style for those nodes shortly after
-  // they're created, either by (a) invoking ResolvePseudoElementStyle (for PE
-  // NAC), or (b) handing the subtree off to Servo for a mini-traversal (for
-  // non-PE NAC). We have assertions in nsCSSFrameConstructor that we don't do
-  // both.
-  //
-  // Once that happens, the NAC has a frame. So if we have no frame here,
-  // we're either not NAC, or in the process of doing (b). Either way, this
-  // isn't a PE.
-  nsIFrame* f = aContent->GetPrimaryFrame();
-  if (!f) {
-    return false;
-  }
-
-  // Get the pseudo type.
-  CSSPseudoElementType pseudoType = f->StyleContext()->GetPseudoType();
-
-  // In general nodes never get anonymous box style. However, there are a few
-  // special cases:
-  //
-  // * We somewhat-confusingly give text nodes a style context tagged with
-  //   ":-moz-text", so we need to check for the anonymous box case here.
-  // * The primary frame for table elements is an anonymous box that inherits
-  //   from the table's style.
-  if (pseudoType == CSSPseudoElementType::InheritingAnonBox) {
-    MOZ_ASSERT(f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::mozText ||
-               f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::tableWrapper);
-    return false;
-  }
-
-  MOZ_ASSERT(pseudoType != CSSPseudoElementType::NonInheritingAnonBox);
-
-  // Finally check the actual pseudo type.
-  bool isImpl = pseudoType != CSSPseudoElementType::NotPseudo;
-  MOZ_ASSERT_IF(isImpl, aContent->IsRootOfNativeAnonymousSubtree());
-  return isImpl;
-}
-
 /* static */ bool
 StyleChildrenIterator::IsNeeded(const Element* aElement)
 {
   // If the node is in an anonymous subtree, we conservatively return true to
   // handle insertion points.
   if (aElement->IsInAnonymousSubtree()) {
     return true;
   }
@@ -553,37 +507,36 @@ StyleChildrenIterator::IsNeeded(const El
   if (aElement->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
     nsBindingManager* manager = aElement->OwnerDoc()->BindingManager();
     nsXBLBinding* binding = manager->GetBindingWithContent(aElement);
     if (binding && binding->GetAnonymousContent()) {
       return true;
     }
   }
 
+  // If the node has a ::before or ::after pseudo, return true, because we want
+  // to visit those.
+  //
+  // TODO(emilio): Make this fast adding a bit? or, perhaps just using
+  // ProbePseudoElementStyle? It should be quite fast in Stylo.
+  if (aElement->GetProperty(nsGkAtoms::beforePseudoProperty) ||
+      aElement->GetProperty(nsGkAtoms::afterPseudoProperty)) {
+    return true;
+  }
+
   // If the node has native anonymous content, return true.
   nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
   if (ac) {
     return true;
   }
 
   return false;
 }
 
 
 nsIContent*
 StyleChildrenIterator::GetNextChild()
 {
-  while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
-    if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
-      // Skip any native-anonymous children that are used to implement pseudo-
-      // elements. These match pseudo-element selectors instead of being
-      // considered a child of their host, and thus the style system needs to
-      // handle them separately.
-    } else {
-      return child;
-    }
-  }
-
-  return nullptr;
+  return AllChildrenIterator::GetNextChild();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -250,22 +250,24 @@ private:
   // XXX we should really assert there are no frame tree changes as well, but
   // there's no easy way to do that.
   nsMutationGuard mMutationGuard;
 #endif
 };
 
 /**
  * StyleChildrenIterator traverses the children of the element from the
- * perspective of the style system, particularly the children we need to traverse
- * during restyle. This is identical to AllChildrenIterator with
- * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent), _except_ that we
- * detect and skip any native anonymous children that are used to implement
- * pseudo-elements (since the style system needs to cascade those using
- * different algorithms).
+ * perspective of the style system, particularly the children we need to
+ * traverse during restyle.
+ *
+ * At present, this is identical to AllChildrenIterator with
+ * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent). We used to have
+ * detect and skip any native anonymous children that are used to implement some
+ * special magic in here that went away, but we keep the separate class so
+ * we can reintroduce special magic back if needed.
  *
  * Note: it assumes that no mutation of the DOM or frame tree takes place during
  * iteration, and will break horribly if that is not true.
  */
 class StyleChildrenIterator : private AllChildrenIterator
 {
 public:
   explicit StyleChildrenIterator(const nsIContent* aContent)
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -161,16 +161,23 @@ nsIContent::GetFlattenedTreeParentNodeIn
     MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
     return parentNode;
   }
   nsIContent* parent = parentNode->AsContent();
 
   if (aType == eForStyle &&
       IsRootOfNativeAnonymousSubtree() &&
       OwnerDoc()->GetRootElement() == parent) {
+    // First, check if this is generated ::before/::after content for the root.
+    // If it is, we know what to do.
+    if (IsGeneratedContentContainerForBefore() ||
+        IsGeneratedContentContainerForAfter()) {
+      return parent;
+    }
+
     // When getting the flattened tree parent for style, we return null
     // for any "document level" native anonymous content subtree root.
     // This is NAC generated by an ancestor frame of the document element's
     // primary frame, and includes scrollbar elements created by the root
     // scroll frame, and the "custom content container" and accessible caret
     // generated by the nsCanvasFrame.  We distinguish document level NAC
     // from NAC generated by the root element's primary frame below.
     nsIFrame* parentFrame = parent->GetPrimaryFrame();
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2394,16 +2394,34 @@ nsContentUtils::ContentIsCrossDocDescend
       return true;
 
     aPossibleDescendant = GetCrossDocParentNode(aPossibleDescendant);
   } while (aPossibleDescendant);
 
   return false;
 }
 
+// static
+bool
+nsContentUtils::ContentIsFlattenedTreeDescendantOf(
+  const nsINode* aPossibleDescendant,
+  const nsINode* aPossibleAncestor)
+{
+  NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
+  NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
+
+  do {
+    if (aPossibleDescendant == aPossibleAncestor) {
+      return true;
+    }
+    aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
+  } while (aPossibleDescendant);
+
+  return false;
+}
 
 // static
 nsresult
 nsContentUtils::GetAncestors(nsINode* aNode,
                              nsTArray<nsINode*>& aArray)
 {
   while (aNode) {
     aArray.AppendElement(aNode);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -320,16 +320,26 @@ public:
   /**
    * Similar to ContentIsDescendantOf except it crosses document boundaries,
    * this function uses ancestor/descendant relations in the composed document
    * (see shadow DOM spec).
    */
   static bool ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
                                               nsINode* aPossibleAncestor);
 
+  /**
+   * As with ContentIsCrossDocDescendantOf but crosses shadow boundaries but not
+   * cross document boundaries.
+   *
+   * @see nsINode::GetFlattenedTreeParentNode()
+   */
+  static bool
+  ContentIsFlattenedTreeDescendantOf(const nsINode* aPossibleDescendant,
+                                     const nsINode* aPossibleAncestor);
+
   /*
    * This method fills the |aArray| with all ancestor nodes of |aNode|
    * including |aNode| at the zero index.
    */
   static nsresult GetAncestors(nsINode* aNode,
                                nsTArray<nsINode*>& aArray);
 
   /*
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5752,21 +5752,16 @@ nsGlobalWindow::GetOuterSize(CallerType 
   }
 
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
   if (!treeOwnerAsWin) {
     aError.Throw(NS_ERROR_FAILURE);
     return nsIntSize(0, 0);
   }
 
-  nsGlobalWindow* rootWindow = nsGlobalWindow::Cast(GetPrivateRoot());
-  if (rootWindow) {
-    rootWindow->FlushPendingNotifications(FlushType::Layout);
-  }
-
   nsIntSize sizeDevPixels;
   aError = treeOwnerAsWin->GetSize(&sizeDevPixels.width, &sizeDevPixels.height);
   if (aError.Failed()) {
     return nsIntSize();
   }
 
   return DevToCSSIntPixels(sizeDevPixels);
 }
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -863,20 +863,16 @@ ImageBitmap::CreateInternal(nsIGlobalObj
 
   // If the HTMLCanvasElement's rendering context is WebGL, then the snapshot
   // we got from the HTMLCanvasElement is a DataSourceSurface which is a copy
   // of the rendering context. We handle cropping in this case.
   bool needToReportMemoryAllocation = false;
   if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
        aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
       aCropRect.isSome()) {
-    // The _surface_ must be a DataSourceSurface.
-    MOZ_ASSERT(surface->IsDataSourceSurface(),
-               "The snapshot SourceSurface from WebGL rendering contest is not \
-               DataSourceSurface.");
     RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
     croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
     cropRect.MoveTo(0, 0);
     needToReportMemoryAllocation = true;
   }
   else {
     croppedSurface = surface;
   }
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -2649,17 +2649,17 @@ ContentEventHandler::OnQueryDOMWidgetHit
 
   nsIDocument* doc = mPresShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   nsIFrame* docFrame = mPresShell->GetRootFrame();
   NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
 
   LayoutDeviceIntPoint eventLoc =
     aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
-  nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels
+  CSSIntRect docFrameRect = docFrame->GetScreenRect();
   CSSIntPoint eventLocCSS(
     mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x,
     mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y);
 
   Element* contentUnderMouse =
     doc->ElementFromPointHelper(eventLocCSS.x, eventLocCSS.y, false, false);
   if (contentUnderMouse) {
     nsIWidget* targetWidget = nullptr;
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -229,18 +229,20 @@ WheelTransaction::OnEvent(WidgetEvent* a
       }
       return;
     case eMouseMove:
     case eDragOver: {
       WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
       if (mouseEvent->IsReal()) {
         // If the cursor is moving to be outside the frame,
         // terminate the scrollwheel transaction.
-        nsIntPoint pt = GetScreenPoint(mouseEvent);
-        nsIntRect r = sTargetFrame->GetScreenRect();
+        LayoutDeviceIntPoint pt = GetScreenPoint(mouseEvent);
+        auto r = LayoutDeviceIntRect::FromAppUnitsToNearest(
+          sTargetFrame->GetScreenRectInAppUnits(),
+          sTargetFrame->PresContext()->AppUnitsPerDevPixel());
         if (!r.Contains(pt)) {
           EndTransaction();
           return;
         }
 
         // If the cursor is moving inside the frame, and it is less than
         // ignoremovedelay milliseconds since the last scroll operation, ignore
         // the mouse move; otherwise, record the current mouse move time to be
@@ -331,23 +333,22 @@ WheelTransaction::SetTimeout()
   sTimer->Cancel();
   DebugOnly<nsresult> rv =
     sTimer->InitWithFuncCallback(OnTimeout, nullptr, GetTimeoutTime(),
                                  nsITimer::TYPE_ONE_SHOT);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "nsITimer::InitWithFuncCallback failed");
 }
 
-/* static */ nsIntPoint
+/* static */ LayoutDeviceIntPoint
 WheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent)
 {
   NS_ASSERTION(aEvent, "aEvent is null");
   NS_ASSERTION(aEvent->mWidget, "aEvent-mWidget is null");
-  return (aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset())
-      .ToUnknownPoint();
+  return aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
 }
 
 /* static */ uint32_t
 WheelTransaction::GetTimeoutTime()
 {
   return Preferences::GetUint("mousewheel.transaction.timeout", 1500);
 }
 
--- a/dom/events/WheelHandlingHelper.h
+++ b/dom/events/WheelHandlingHelper.h
@@ -153,17 +153,17 @@ public:
 protected:
   static void BeginTransaction(nsIFrame* aTargetFrame,
                                WidgetWheelEvent* aEvent);
   // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
   // frame might be destroyed in the event handler.
   static bool UpdateTransaction(WidgetWheelEvent* aEvent);
   static void MayEndTransaction();
 
-  static nsIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
+  static LayoutDeviceIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
   static void OnFailToScrollTarget();
   static void OnTimeout(nsITimer* aTimer, void* aClosure);
   static void SetTimeout();
   static uint32_t GetIgnoreMoveDelayTime();
   static int32_t GetAccelerationStart();
   static int32_t GetAccelerationFactor();
   static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
   static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
--- a/dom/events/test/window_wheel_default_action.html
+++ b/dom/events/test/window_wheel_default_action.html
@@ -80,25 +80,27 @@ function hitEventLoop(aFunc, aTimes)
 
   if (--aTimes) {
     setTimeout(hitEventLoop, 0, aFunc, aTimes);
   } else {
     setTimeout(aFunc, 20);
   }
 }
 
-const zoomResetTopic = "browser-fullZoom:zoomReset";
-SpecialPowers.registerObservers(zoomResetTopic);
-
 function onZoomReset(aCallback) {
-  var specialPowersTopic = "specialpowers-" + zoomResetTopic;
-  SpecialPowers.addObserver(function observe() {
-    SpecialPowers.removeObserver(observe, specialPowersTopic);
+  var fullZoom = SpecialPowers.getFullZoom(window);
+  if (fullZoom == 1) {
     SimpleTest.executeSoon(aCallback);
-  }, specialPowersTopic);
+    return;
+  }
+  SpecialPowers.addChromeEventListener("FullZoomChange", function onFullZoomChange(event) {
+    is(SpecialPowers.getFullZoom(window), 1, "Zoom should be reset to 1");
+    SpecialPowers.removeChromeEventListener("FullZoomChange", onFullZoomChange);
+    SimpleTest.executeSoon(aCallback);
+  });
 }
 
 function setDeltaMultiplierSettings(aSettings, aCallback)
 {
   SpecialPowers.pushPrefEnv({"set": [
     ["mousewheel.default.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
     ["mousewheel.default.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
     ["mousewheel.default.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
--- a/dom/gamepad/GamepadPose.cpp
+++ b/dom/gamepad/GamepadPose.cpp
@@ -50,63 +50,69 @@ GamepadPose::HasPosition() const
   return bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation);
 }
 
 void
 GamepadPose::GetPosition(JSContext* aJSContext,
                          JS::MutableHandle<JSObject*> aRetval,
                          ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mPosition, mPoseState.position, 3,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
+  SetFloat32Array(aJSContext, aRetval, mPosition,
+                  mPoseState.isPositionValid ? mPoseState.position : nullptr, 3,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
 }
 
 void
 GamepadPose::GetLinearVelocity(JSContext* aJSContext,
                                JS::MutableHandle<JSObject*> aRetval,
                                ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mLinearVelocity, mPoseState.linearVelocity, 3,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
+  SetFloat32Array(aJSContext, aRetval, mLinearVelocity,
+                  mPoseState.isPositionValid ? mPoseState.linearVelocity : nullptr, 3,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Position), aRv);
 }
 
 void
 GamepadPose::GetLinearAcceleration(JSContext* aJSContext,
                                    JS::MutableHandle<JSObject*> aRetval,
                                    ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mLinearAcceleration, mPoseState.linearAcceleration, 3,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_LinearAcceleration), aRv);
+  SetFloat32Array(aJSContext, aRetval, mLinearAcceleration,
+                  mPoseState.isPositionValid ? mPoseState.linearAcceleration : nullptr, 3,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_LinearAcceleration), aRv);
 }
 
 void
 GamepadPose::GetOrientation(JSContext* aJSContext,
                             JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mOrientation, mPoseState.orientation, 4,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
+  SetFloat32Array(aJSContext, aRetval, mOrientation,
+                  mPoseState.isOrientationValid ? mPoseState.orientation : nullptr, 4,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
 }
 
 void
 GamepadPose::GetAngularVelocity(JSContext* aJSContext,
                                 JS::MutableHandle<JSObject*> aRetval,
                                 ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mAngularVelocity, mPoseState.angularVelocity, 3,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
+  SetFloat32Array(aJSContext, aRetval, mAngularVelocity,
+                  mPoseState.isOrientationValid ? mPoseState.angularVelocity : nullptr, 3,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_Orientation), aRv);
 }
 
 void
 GamepadPose::GetAngularAcceleration(JSContext* aJSContext,
                                     JS::MutableHandle<JSObject*> aRetval,
                                     ErrorResult& aRv)
 {
-  SetFloat32Array(aJSContext, aRetval, mAngularAcceleration, mPoseState.angularAcceleration, 3,
-    bool(mPoseState.flags & GamepadCapabilityFlags::Cap_AngularAcceleration), aRv);
+  SetFloat32Array(aJSContext, aRetval, mAngularAcceleration,
+                  mPoseState.isOrientationValid ? mPoseState.angularAcceleration : nullptr, 3,
+                  bool(mPoseState.flags & GamepadCapabilityFlags::Cap_AngularAcceleration), aRv);
 }
 
 void
 GamepadPose::SetPoseState(const GamepadPoseState& aPose)
 {
   mPoseState = aPose;
 }
 
--- a/dom/gamepad/GamepadPoseState.h
+++ b/dom/gamepad/GamepadPoseState.h
@@ -37,16 +37,18 @@ struct GamepadPoseState
 {
   GamepadCapabilityFlags flags;
   float orientation[4];
   float position[3];
   float angularVelocity[3];
   float angularAcceleration[3];
   float linearVelocity[3];
   float linearAcceleration[3];
+  bool isPositionValid;
+  bool isOrientationValid;
 
   GamepadPoseState()
   {
     Clear();
   }
 
   bool operator==(const GamepadPoseState& aPose) const
   {
@@ -64,17 +66,19 @@ struct GamepadPoseState
            && angularAcceleration[0] == aPose.angularAcceleration[0]
            && angularAcceleration[1] == aPose.angularAcceleration[1]
            && angularAcceleration[2] == aPose.angularAcceleration[2]
            && linearVelocity[0] == aPose.linearVelocity[0]
            && linearVelocity[1] == aPose.linearVelocity[1]
            && linearVelocity[2] == aPose.linearVelocity[2]
            && linearAcceleration[0] == aPose.linearAcceleration[0]
            && linearAcceleration[1] == aPose.linearAcceleration[1]
-           && linearAcceleration[2] == aPose.linearAcceleration[2];
+           && linearAcceleration[2] == aPose.linearAcceleration[2]
+           && isPositionValid == aPose.isPositionValid
+           && isOrientationValid == aPose.isOrientationValid;
   }
 
   bool operator!=(const GamepadPoseState& aPose) const
   {
     return !(*this == aPose);
   }
 
   void Clear() {
--- a/dom/gamepad/GamepadServiceTest.cpp
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -255,24 +255,26 @@ GamepadServiceTest::NewPoseMove(uint32_t
   if (!aOrient.IsNull()) {
     const Float32Array& value = aOrient.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 4);
     poseState.orientation[0] = value.Data()[0];
     poseState.orientation[1] = value.Data()[1];
     poseState.orientation[2] = value.Data()[2];
     poseState.orientation[3] = value.Data()[3];
+    poseState.isOrientationValid = true;
   }
   if (!aPos.IsNull()) {
     const Float32Array& value = aPos.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
     poseState.position[0] = value.Data()[0];
     poseState.position[1] = value.Data()[1];
     poseState.position[2] = value.Data()[2];
+    poseState.isPositionValid = true;
   }
   if (!aAngVelocity.IsNull()) {
     const Float32Array& value = aAngVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
     poseState.angularVelocity[0] = value.Data()[0];
     poseState.angularVelocity[1] = value.Data()[1];
     poseState.angularVelocity[2] = value.Data()[2];
--- a/dom/gamepad/ipc/GamepadMessageUtils.h
+++ b/dom/gamepad/ipc/GamepadMessageUtils.h
@@ -57,16 +57,18 @@ struct ParamTraits<mozilla::dom::Gamepad
     WriteParam(aMsg, aParam.angularAcceleration[1]);
     WriteParam(aMsg, aParam.angularAcceleration[2]);
     WriteParam(aMsg, aParam.linearVelocity[0]);
     WriteParam(aMsg, aParam.linearVelocity[1]);
     WriteParam(aMsg, aParam.linearVelocity[2]);
     WriteParam(aMsg, aParam.linearAcceleration[0]);
     WriteParam(aMsg, aParam.linearAcceleration[1]);
     WriteParam(aMsg, aParam.linearAcceleration[2]);
+    WriteParam(aMsg, aParam.isPositionValid);
+    WriteParam(aMsg, aParam.isOrientationValid);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->flags)) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[2])) ||
@@ -80,17 +82,19 @@ struct ParamTraits<mozilla::dom::Gamepad
         !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) ||
-        !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) {
+        !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->isPositionValid)) ||
+        !ReadParam(aMsg, aIter, &(aResult->isOrientationValid))) {
       return false;
     }
     return true;
   }
 };
 
 } // namespace IPC
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -518,16 +518,17 @@ NS_INTERFACE_MAP_END
 
 
 mozilla::ipc::IPCResult
 ContentChild::RecvSetXPCOMProcessAttributes(const XPCOMInitData& aXPCOMInit,
                                             const StructuredCloneData& aInitialData,
                                             nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
 {
   mLookAndFeelCache = aLookAndFeelIntCache;
+  gfx::gfxVars::GotInitialVarUpdates(aXPCOMInit.gfxNonDefaultVarUpdates());
   InitXPCOM(aXPCOMInit, aInitialData);
   InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
 
 #ifdef NS_PRINTING
   // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
   // PrintingParent, is always available for printing initiated from the parent.
   // Create nsPrintingProxy instance later than the SystemGroup initialization.
   RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
@@ -952,18 +953,16 @@ ContentChild::AppendProcessId(nsACString
   }
   unsigned pid = getpid();
   aName.Append(nsPrintfCString("(pid %u)", pid));
 }
 
 void
 ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData)
 {
-  // Initialize the graphics platform. This may contact the parent process
-  // to read device preferences.
   gfxPlatform::InitChild(aData);
 }
 
 void
 ContentChild::InitXPCOM(const XPCOMInitData& aXPCOMInit,
                         const mozilla::dom::ipc::StructuredCloneData& aInitialData)
 {
   SET_PREF_PHASE(pref_initPhase::BEGIN_ALL_PREFS);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2222,17 +2222,22 @@ ContentParent::InitInternal(ProcessPrior
   // send the file URL instead.
   StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
   if (ucs) {
     SerializeURI(ucs->GetSheetURI(), xpcomInit.userContentSheetURL());
   } else {
     SerializeURI(nullptr, xpcomInit.userContentSheetURL());
   }
 
+  // 1. Build ContentDeviceData first, as it may affect some gfxVars.
   gfxPlatform::GetPlatform()->BuildContentDeviceData(&xpcomInit.contentDeviceData());
+  // 2. Gather non-default gfxVars.
+  xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
+  // 3. Start listening for gfxVars updates, to notify content process later on.
+  gfxVars::AddReceiver(this);
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
       int32_t status = 0;
       nsAutoCString failureId;
       gfxInfo->GetFeatureStatus(i, failureId, &status);
       dom::GfxInfoFeatureStatus gfxFeatureStatus;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -264,16 +264,17 @@ struct XPCOMInitData
     bool haveBidiKeyboards;
     nsString[] dictionaries;
     ClipboardCapabilities clipboardCaps;
     DomainPolicyClone domainPolicy;
     /* used on MacOSX only */
     FontFamilyListEntry[] fontFamilies;
     OptionalURIParams userContentSheetURL;
     PrefSetting[] prefs;
+    GfxVarUpdate[] gfxNonDefaultVarUpdates;
     ContentDeviceData contentDeviceData;
     GfxInfoFeatureStatus[] gfxFeatureStatus;
     DataStorageEntry[] dataStorage;
     nsCString[] appLocales;
     nsCString[] requestedLocales;
 };
 
 /**
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -93,18 +93,16 @@ public:
   virtual MediaEventSource<void>* WaitingForKeyEvent()
   {
     return nullptr;
   }
 
   // Return an abstract thread on which to run main thread runnables.
   virtual AbstractThread* AbstractMainThread() const = 0;
 
-protected:
-  virtual void UpdateEstimatedMediaDuration(int64_t aDuration) { };
 public:
   virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
   virtual mozilla::layers::ImageContainer* GetImageContainer() = 0;
 
   // Returns the owner of this decoder or null when the decoder is shutting
   // down. The owner should only be used on the main thread.
   virtual MediaDecoderOwner* GetOwner() const = 0;
 
--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -139,20 +139,23 @@ Benchmark::Init()
   MOZ_ASSERT(NS_IsMainThread());
   gfxVars::Initialize();
   gfxPrefs::GetSingleton();
   MediaPrefs::GetSingleton();
 }
 
 BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState,
                                      MediaDataDemuxer* aDemuxer)
-  : QueueObject(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
+  : QueueObject(new TaskQueue(
+      GetMediaThreadPool(MediaThreadType::PLAYBACK),
+      "BenchmarkPlayback::QueueObject"))
   , mMainThreadState(aMainThreadState)
-  , mDecoderTaskQueue(new TaskQueue(GetMediaThreadPool(
-                        MediaThreadType::PLATFORM_DECODER)))
+  , mDecoderTaskQueue(new TaskQueue(
+      GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+      "BenchmarkPlayback::mDecoderTaskQueue"))
   , mDemuxer(aDemuxer)
   , mSampleIndex(0)
   , mFrameCount(0)
   , mFinished(false)
   , mDrained(false)
 {
   MOZ_ASSERT(static_cast<Benchmark*>(mMainThreadState)->OnThread());
 }
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -39,22 +39,16 @@
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::media;
 
 namespace mozilla {
 
-// The amount of instability we tollerate in calls to
-// MediaDecoder::UpdateEstimatedMediaDuration(); changes of duration
-// less than this are ignored, as they're assumed to be the result of
-// instability in the duration estimation.
-static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
-
 // avoid redefined macro in unified build
 #undef LOG
 #undef DUMP
 
 LazyLogModule gMediaDecoderLog("MediaDecoder");
 #define LOG(x, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("Decoder=%p " x, this, ##__VA_ARGS__))
 
@@ -402,17 +396,16 @@ MediaDecoder::MediaDecoder(MediaDecoderO
   , INIT_MIRROR(mBuffered, TimeIntervals())
   , INIT_MIRROR(mNextFrameStatus, MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE)
   , INIT_MIRROR(mCurrentPosition, TimeUnit::Zero())
   , INIT_MIRROR(mStateMachineDuration, NullableTimeUnit())
   , INIT_MIRROR(mPlaybackPosition, 0)
   , INIT_MIRROR(mIsAudioDataAudible, false)
   , INIT_CANONICAL(mVolume, 0.0)
   , INIT_CANONICAL(mPreservesPitch, true)
-  , INIT_CANONICAL(mEstimatedDuration, NullableTimeUnit())
   , INIT_CANONICAL(mExplicitDuration, Maybe<double>())
   , INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING)
   , INIT_CANONICAL(mNextState, PLAY_STATE_PAUSED)
   , INIT_CANONICAL(mLogicallySeeking, false)
   , INIT_CANONICAL(mSameOriginMedia, false)
   , INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
   , INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
   , INIT_CANONICAL(mPlaybackRateReliable, true)
@@ -1178,18 +1171,17 @@ MediaDecoder::ChangeState(PlayState aSta
 }
 
 void
 MediaDecoder::UpdateLogicalPositionInternal()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
 
-  double currentPosition =
-    static_cast<double>(CurrentPosition()) / static_cast<double>(USECS_PER_S);
+  double currentPosition = CurrentPosition().ToSeconds();
   if (mPlayState == PLAY_STATE_ENDED) {
     currentPosition = std::max(currentPosition, mDuration);
   }
   bool logicalPositionChanged = mLogicalPosition != currentPosition;
   mLogicalPosition = currentPosition;
 
   // Invalidate the frame so any video data is displayed.
   // Do this before the timeupdate event so that if that
@@ -1229,17 +1221,17 @@ MediaDecoder::DurationChanged()
   // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
   // of whether we should fire durationchange on explicit infinity.
   if (mFiredMetadataLoaded
       && (!mozilla::IsInfinite<double>(mDuration)
           || mExplicitDuration.Ref().isSome())) {
     GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   }
 
-  if (CurrentPosition() > TimeUnit::FromSeconds(mDuration).ToMicroseconds()) {
+  if (CurrentPosition() > TimeUnit::FromSeconds(mDuration)) {
     Seek(mDuration, SeekTarget::Accurate);
   }
 }
 
 void
 MediaDecoder::NotifyCompositor()
 {
   MediaDecoderOwner* owner = GetOwner();
@@ -1329,39 +1321,16 @@ MediaDecoder::UpdateVideoDecodeMode()
 
 bool
 MediaDecoder::HasSuspendTaint() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mHasSuspendTaint;
 }
 
-void
-MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mPlayState <= PLAY_STATE_LOADING) {
-    return;
-  }
-
-  // The duration is only changed if its significantly different than the
-  // the current estimate, as the incoming duration is an estimate and so
-  // often is unstable as more data is read and the estimate is updated.
-  // Can result in a durationchangeevent. aDuration is in microseconds.
-  if (mEstimatedDuration.Ref().isSome()
-      && mozilla::Abs(mEstimatedDuration.Ref().ref().ToMicroseconds()
-                      - aDuration)
-         < ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
-    return;
-  }
-
-  mEstimatedDuration = Some(TimeUnit::FromMicroseconds(aDuration));
-}
-
 bool
 MediaDecoder::IsTransportSeekable()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GetResource()->IsTransportSeekable();
 }
 
 bool
@@ -1767,17 +1736,17 @@ MediaDecoder::RemoveMediaTracks()
 }
 
 MediaDecoderOwner::NextFrameStatus
 MediaDecoder::NextFrameBufferedStatus()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Next frame hasn't been decoded yet.
   // Use the buffered range to consider if we have the next frame available.
-  TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
+  auto currentPosition = CurrentPosition();
   media::TimeInterval interval(
     currentPosition,
     currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
   return GetBuffered().Contains(interval)
          ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
          : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
 }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -247,27 +247,16 @@ public:
   virtual bool IsMSE() const { return false; }
 
   // Return true if the MediaDecoderOwner's error attribute is not null.
   // Must be called before Shutdown().
   bool OwnerHasError() const;
 
   already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
 
-protected:
-  // Updates the media duration. This is called while the media is being
-  // played, calls before the media has reached loaded metadata are ignored.
-  // The duration is assumed to be an estimate, and so a degree of
-  // instability is expected; if the incoming duration is not significantly
-  // different from the existing duration, the change request is ignored.
-  // If the incoming duration is significantly different, the duration is
-  // changed, this causes a durationchanged event to fire to the media
-  // element.
-  void UpdateEstimatedMediaDuration(int64_t aDuration) override;
-
 public:
   // Returns true if this media supports random seeking. False for example with
   // chained ogg files.
   bool IsMediaSeekable();
   // Returns true if seeking is supported on a transport level (e.g. the server
   // supports range requests, we are playing a file, etc.).
   bool IsTransportSeekable();
 
@@ -563,19 +552,19 @@ protected:
   // mCurrentPosition), so that |v.currentTime = foo; v.currentTime == foo|
   // returns true without being affected by rounding errors.
   double mLogicalPosition;
 
   // The current playback position of the underlying playback infrastructure.
   // This corresponds to the "current position" in HTML5.
   // We allow omx subclasses to substitute an alternative current position for
   // usage with the audio offload player.
-  virtual int64_t CurrentPosition()
+  virtual media::TimeUnit CurrentPosition()
   {
-    return mCurrentPosition.Ref().ToMicroseconds();
+    return mCurrentPosition.Ref();
   }
 
   // Official duration of the media resource as observed by script.
   double mDuration;
 
   /******
    * The following member variables can be accessed from any thread.
    ******/
@@ -770,23 +759,16 @@ protected:
   // Volume of playback.  0.0 = muted. 1.0 = full volume.
   Canonical<double> mVolume;
 
   // PlaybackRate and pitch preservation status we should start at.
   double mPlaybackRate = 1;
 
   Canonical<bool> mPreservesPitch;
 
-  // Media duration according to the demuxer's current estimate.
-  // Note that it's quite bizarre for this to live on the main thread - it would
-  // make much more sense for this to be owned by the demuxer's task queue. But
-  // currently this is only every changed in NotifyDataArrived, which runs on
-  // the main thread. That will need to be cleaned up at some point.
-  Canonical<media::NullableTimeUnit> mEstimatedDuration;
-
   // Media duration set explicitly by JS. At present, this is only ever present
   // for MSE.
   Canonical<Maybe<double>> mExplicitDuration;
 
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
@@ -822,20 +804,16 @@ protected:
 
 public:
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
   AbstractCanonical<double>* CanonicalVolume() { return &mVolume; }
   AbstractCanonical<bool>* CanonicalPreservesPitch()
   {
     return &mPreservesPitch;
   }
-  AbstractCanonical<media::NullableTimeUnit>* CanonicalEstimatedDuration()
-  {
-    return &mEstimatedDuration;
-  }
   AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration()
   {
     return &mExplicitDuration;
   }
   AbstractCanonical<PlayState>* CanonicalPlayState() { return &mPlayState; }
   AbstractCanonical<PlayState>* CanonicalNextPlayState() { return &mNextState; }
   AbstractCanonical<bool>* CanonicalLogicallySeeking()
   {
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -64,18 +64,20 @@ public:
   }
 
   size_t mSize;
 };
 
 MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
   : mAudioCompactor(mAudioQueue)
   , mDecoder(aDecoder)
-  , mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
-                             /* aSupportsTailDispatch = */ true))
+  , mTaskQueue(new TaskQueue(
+      GetMediaThreadPool(MediaThreadType::PLAYBACK),
+      "MediaDecoderReader::mTaskQueue",
+      /* aSupportsTailDispatch = */ true))
   , mWatchManager(this, mTaskQueue)
   , mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)")
   , mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
   , mIgnoreAudioOutputFormat(false)
   , mHitAudioDecodeError(false)
   , mShutdown(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderReader);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2566,17 +2566,16 @@ ShutdownState::Enter()
   // Prevent dangling pointers by disconnecting the listeners.
   master->mAudioQueueListener.Disconnect();
   master->mVideoQueueListener.Disconnect();
   master->mMetadataManager.Disconnect();
   master->mOnMediaNotSeekable.Disconnect();
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   master->mBuffered.DisconnectIfConnected();
-  master->mEstimatedDuration.DisconnectIfConnected();
   master->mExplicitDuration.DisconnectIfConnected();
   master->mPlayState.DisconnectIfConnected();
   master->mNextPlayState.DisconnectIfConnected();
   master->mVolume.DisconnectIfConnected();
   master->mPreservesPitch.DisconnectIfConnected();
   master->mSameOriginMedia.DisconnectIfConnected();
   master->mMediaPrincipalHandle.DisconnectIfConnected();
   master->mPlaybackBytesPerSecond.DisconnectIfConnected();
@@ -2608,18 +2607,19 @@ ShutdownState::Enter()
 
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader) :
   mDecoderID(aDecoder),
   mAbstractMainThread(aDecoder->AbstractMainThread()),
   mFrameStats(&aDecoder->GetFrameStatistics()),
   mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
   mAudioChannel(aDecoder->GetAudioChannel()),
-  mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
-                           /* aSupportsTailDispatch = */ true)),
+  mTaskQueue(new TaskQueue(
+    GetMediaThreadPool(MediaThreadType::PLAYBACK),
+    "MDSM::mTaskQueue", /* aSupportsTailDispatch = */ true)),
   mWatchManager(this, mTaskQueue),
   mDispatchedStateMachine(false),
   mDelayedScheduler(mTaskQueue),
   mCurrentFrameID(0),
   INIT_WATCHABLE(mObservedDuration, TimeUnit()),
   mReader(new MediaDecoderReaderWrapper(mTaskQueue, aReader)),
   mPlaybackRate(1.0),
   mLowAudioThreshold(detail::LOW_AUDIO_THRESHOLD),
@@ -2630,17 +2630,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mSentFirstFrameLoadedEvent(false),
   mVideoDecodeSuspended(false),
   mVideoDecodeSuspendTimer(mTaskQueue),
   mOutputStreamManager(new OutputStreamManager()),
   mResource(aDecoder->GetResource()),
   mVideoDecodeMode(VideoDecodeMode::Normal),
   mIsMSE(aDecoder->IsMSE()),
   INIT_MIRROR(mBuffered, TimeIntervals()),
-  INIT_MIRROR(mEstimatedDuration, NullableTimeUnit()),
   INIT_MIRROR(mExplicitDuration, Maybe<double>()),
   INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
   INIT_MIRROR(mNextPlayState, MediaDecoder::PLAY_STATE_PAUSED),
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
   INIT_MIRROR(mSameOriginMedia, false),
   INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
   INIT_MIRROR(mPlaybackBytesPerSecond, 0.0),
@@ -2684,17 +2683,16 @@ MediaDecoderStateMachine::~MediaDecoderS
 
 void
 MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
   mBuffered.Connect(mReader->CanonicalBuffered());
-  mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
   mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(aDecoder->CanonicalPlayState());
   mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
   mVolume.Connect(aDecoder->CanonicalVolume());
   mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
   mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
   mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle());
   mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
@@ -2702,18 +2700,16 @@ MediaDecoderStateMachine::Initialization
   mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
 
   // Initialize watchers.
   mWatchManager.Watch(mBuffered,
                       &MediaDecoderStateMachine::BufferedRangeUpdated);
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mPreservesPitch,
                       &MediaDecoderStateMachine::PreservesPitchChanged);
-  mWatchManager.Watch(mEstimatedDuration,
-                      &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration,
                       &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration,
                       &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
 
   MOZ_ASSERT(!mStateObj);
   auto* s = new DecodeMetadataState(this);
@@ -2989,18 +2985,16 @@ void MediaDecoderStateMachine::Recompute
     if (IsNaN(d)) {
       // We have an explicit duration (which means that we shouldn't look at
       // any other duration sources), but the duration isn't ready yet.
       return;
     }
     // We don't fire duration changed for this case because it should have
     // already been fired on the main thread when the explicit duration was set.
     duration = TimeUnit::FromSeconds(d);
-  } else if (mEstimatedDuration.Ref().isSome()) {
-    duration = mEstimatedDuration.Ref().ref();
   } else if (mInfo.isSome() && Info().mMetadataDuration.isSome()) {
     // We need to check mInfo.isSome() because that this method might be invoked
     // while mObservedDuration is changed which might before the metadata been
     // read.
     duration = Info().mMetadataDuration.ref();
   } else {
     return;
   }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -697,19 +697,16 @@ private:
   MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
 
   const bool mIsMSE;
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
-  // The duration according to the demuxer's current estimate, mirrored from the main thread.
-  Mirror<media::NullableTimeUnit> mEstimatedDuration;
-
   // The duration explicitly set by JS, mirrored from the main thread.
   Mirror<Maybe<double>> mExplicitDuration;
 
   // The current play state and next play state, mirrored from the main thread.
   Mirror<MediaDecoder::PlayState> mPlayState;
   Mirror<MediaDecoder::PlayState> mNextPlayState;
 
   // Volume of playback. 0.0 = muted. 1.0 = full volume.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -733,17 +733,18 @@ MediaFormatReader::DecoderFactory::DoIni
 class MediaFormatReader::DemuxerProxy
 {
   using TrackType = TrackInfo::TrackType;
   class Wrapper;
 
 public:
   explicit DemuxerProxy(MediaDataDemuxer* aDemuxer)
     : mTaskQueue(new AutoTaskQueue(
-                   GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
+        GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+        "DemuxerProxy::mTaskQueue"))
     , mData(new Data(aDemuxer))
   {
     MOZ_COUNT_CTOR(DemuxerProxy);
   }
 
   ~DemuxerProxy()
   {
     MOZ_COUNT_DTOR(DemuxerProxy);
@@ -1215,20 +1216,23 @@ MediaFormatReader::InitLayersBackendType
 
 nsresult
 MediaFormatReader::InitInternal()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
 
   InitLayersBackendType();
 
-  mAudio.mTaskQueue =
-    new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
-  mVideo.mTaskQueue =
-    new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
+  mAudio.mTaskQueue = new TaskQueue(
+    GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+    "MFR::mAudio::mTaskQueue");
+
+  mVideo.mTaskQueue = new TaskQueue(
+    GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+    "MFR::mVideo::mTaskQueue");
 
   if (mDecoder) {
     // Note: GMPCrashHelper must be created on main thread, as it may use
     // weak references, which aren't threadsafe.
     mCrashHelper = mDecoder->GetCrashHelper();
   }
   return NS_OK;
 }
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -304,20 +304,20 @@ GenerateRandomPathName(nsCString& aOutSa
 
   // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
   // to replace illegal characters -- notably '/'
   aOutSalt.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
   return NS_OK;
 }
 
 already_AddRefed<TaskQueue>
-CreateMediaDecodeTaskQueue()
+CreateMediaDecodeTaskQueue(const char* aName)
 {
   RefPtr<TaskQueue> queue = new TaskQueue(
-    GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
+    GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), aName);
   return queue.forget();
 }
 
 void
 SimpleTimer::Cancel() {
   if (mTimer) {
 #ifdef DEBUG
     nsCOMPtr<nsIEventTarget> target;
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -248,17 +248,17 @@ nsresult
 GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
 
 // This version returns a string suitable for use as a file or URL
 // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
 nsresult
 GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
 
 already_AddRefed<TaskQueue>
-CreateMediaDecodeTaskQueue();
+CreateMediaDecodeTaskQueue(const char* aName);
 
 // Iteratively invokes aWork until aCondition returns true, or aWork returns false.
 // Use this rather than a while loop to avoid bogarting the task queue.
 template<class Work, class Condition>
 RefPtr<GenericPromise> InvokeUntil(Work aWork, Condition aCondition) {
   RefPtr<GenericPromise::Private> p = new GenericPromise::Private(__func__);
 
   if (aCondition()) {
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -240,18 +240,19 @@ MP4Decoder::IsVideoAccelerated(layers::K
   ErrorResult rv;
   RefPtr<dom::Promise> promise;
   promise = dom::Promise::Create(aParent, rv);
   if (rv.Failed()) {
     rv.SuppressException();
     return nullptr;
   }
 
-  RefPtr<TaskQueue> taskQueue =
-    new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
+  RefPtr<TaskQueue> taskQueue = new TaskQueue(
+    GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
+    "MP4Decoder::IsVideoAccelerated::taskQueue");
   VideoInfo config;
   RefPtr<MediaDataDecoder> decoder(CreateTestH264Decoder(aKnowsCompositor, config, taskQueue));
   if (!decoder) {
     taskQueue->BeginShutdown();
     taskQueue->AwaitShutdownAndIdle();
     promise->MaybeResolve(NS_LITERAL_STRING("No; Failed to create H264 decoder"));
     return promise.forget();
   }
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -40,28 +40,39 @@ ChromiumCDMChild::TimerExpired(void* aCo
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
   if (mCDM) {
     mCDM->TimerExpired(aContext);
   }
 }
 
-class CDMShmemBuffer : public cdm::Buffer
+class CDMShmemBuffer : public CDMBuffer
 {
 public:
   CDMShmemBuffer(ChromiumCDMChild* aProtocol, ipc::Shmem aShmem)
     : mProtocol(aProtocol)
     , mSize(aShmem.Size<uint8_t>())
     , mShmem(aShmem)
   {
     CDM_LOG("CDMShmemBuffer(size=%" PRIu32 ") created", Size());
     // Note: Chrome initializes the size of a buffer to it capacity. We do the same.
   }
 
+  CDMShmemBuffer(ChromiumCDMChild* aProtocol,
+                 ipc::Shmem aShmem,
+                 WidevineBuffer* aLocalBuffer)
+    : CDMShmemBuffer(aProtocol, aShmem)
+  {
+    MOZ_ASSERT(aLocalBuffer->Size() == Size());
+    memcpy(Data(),
+           aLocalBuffer->Data(),
+           std::min<uint32_t>(aLocalBuffer->Size(), Size()));
+  }
+
   ~CDMShmemBuffer() override
   {
     CDM_LOG("CDMShmemBuffer(size=%" PRIu32 ") destructed writable=%d",
             Size(),
             mShmem.IsWritable());
     if (mShmem.IsWritable()) {
       // The shmem wasn't extracted to send its data back up to the parent process,
       // so we can reuse the shmem.
@@ -92,16 +103,18 @@ public:
 
   ipc::Shmem ExtractShmem()
   {
     ipc::Shmem shmem = mShmem;
     mShmem = ipc::Shmem();
     return shmem;
   }
 
+  CDMShmemBuffer* AsShmemBuffer() override { return this; }
+
 private:
   RefPtr<ChromiumCDMChild> mProtocol;
   uint32_t mSize;
   mozilla::ipc::Shmem mShmem;
   CDMShmemBuffer(const CDMShmemBuffer&);
   void operator=(const CDMShmemBuffer&);
 };
 
@@ -138,19 +151,20 @@ ChromiumCDMChild::Allocate(uint32_t aCap
   for (size_t i = 0; i < mBuffers.Length(); i++) {
     if (mBuffers[i].Size<uint8_t>() >= aCapacity &&
         (best == invalid || wastedSpace(i) < wastedSpace(best))) {
       best = i;
     }
   }
   if (best == invalid) {
     // The parent process should have bestowed upon us a shmem of appropriate
-    // size, but did not!
-    MOZ_ASSERT(false);
-    return nullptr;
+    // size, but did not! Do a "dive and catch", and create an non-shared
+    // memory buffer. The parent will detect this and send us an extra shmem
+    // so future frames can be in shmems, i.e. returned on the fast path.
+    return new WidevineBuffer(aCapacity);
   }
   ipc::Shmem shmem = mBuffers[best];
   mBuffers.RemoveElementAt(best);
   return new CDMShmemBuffer(this, shmem);
 }
 
 void
 ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext)
@@ -566,18 +580,19 @@ ChromiumCDMChild::RecvDecrypt(const uint
   InitInputBuffer(aBuffer, subsamples, input);
 
   WidevineDecryptedBlock output;
   cdm::Status status = mCDM->Decrypt(input, &output);
 
   // CDM should have allocated a cdm::Buffer for output.
   CDMShmemBuffer* buffer =
     output.DecryptedBuffer()
-      ? reinterpret_cast<CDMShmemBuffer*>(output.DecryptedBuffer())
+      ? static_cast<CDMShmemBuffer*>(output.DecryptedBuffer())
       : nullptr;
+  MOZ_ASSERT(buffer->AsShmemBuffer());
   if (status != cdm::kSuccess || !buffer) {
     Unused << SendDecryptFailed(aId, status);
     return IPC_OK();
   }
 
   // Success! Return the decrypted sample to parent.
   MOZ_ASSERT(!HasShmemOfSize(outputShmemSize));
   ipc::Shmem shmem = buffer->ExtractShmem();
@@ -645,17 +660,16 @@ ChromiumCDMChild::RecvResetVideoDecoder(
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64 ")",
           aBuffer.mTimestamp());
   MOZ_ASSERT(mDecoderInitialized);
-  MOZ_ASSERT(!mBuffers.IsEmpty());
 
   RefPtr<ChromiumCDMChild> self = this;
   auto autoDeallocateShmem = MakeScopeExit([&, self] {
     self->DeallocShmem(aBuffer.mData());
   });
 
   // The output frame may not have the same timestamp as the frame we put in.
   // We may need to input a number of frames before we receive output. The
@@ -671,73 +685,81 @@ ChromiumCDMChild::RecvDecryptAndDecodeFr
   WidevineVideoFrame frame;
   cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
   GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64
           " CDM decoder rv=%d",
           aBuffer.mTimestamp(),
           rv);
 
   switch (rv) {
+    case cdm::kNeedMoreData:
+      Unused << SendDecodeFailed(rv);
+      break;
     case cdm::kNoKey:
       GMP_LOG("NoKey for sample at time=%" PRId64 "!", input.timestamp);
       // Somehow our key became unusable. Typically this would happen when
       // a stream requires output protection, and the configuration changed
       // such that output protection is no longer available. For example, a
       // non-compliant monitor was attached. The JS player should notice the
       // key status changing to "output-restricted", and is supposed to switch
       // to a stream that doesn't require OP. In order to keep the playback
       // pipeline rolling, just output a black frame. See bug 1343140.
       frame.InitToBlack(mCodedSize.width, mCodedSize.height, input.timestamp);
       MOZ_FALLTHROUGH;
     case cdm::kSuccess:
-      ReturnOutput(frame);
-      break;
-    case cdm::kNeedMoreData:
-      Unused << SendDecodeFailed(rv);
-      break;
+      if (frame.FrameBuffer()) {
+        ReturnOutput(frame);
+        break;
+      }
+      // CDM didn't set a frame buffer on the sample, report it as an error.
+      MOZ_FALLTHROUGH;
     default:
       Unused << SendDecodeFailed(rv);
       break;
   }
 
   return IPC_OK();
 }
 
 void
 ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
+  MOZ_ASSERT(aFrame.FrameBuffer());
   gmp::CDMVideoFrame output;
   output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
   output.mImageWidth() = aFrame.Size().width;
   output.mImageHeight() = aFrame.Size().height;
-  CDMShmemBuffer* cdmSharedBuffer =
-    reinterpret_cast<CDMShmemBuffer*>(aFrame.FrameBuffer());
-  output.mData() = cdmSharedBuffer->ExtractShmem();
   output.mYPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kYPlane),
                        aFrame.Stride(cdm::VideoFrame::kYPlane) };
   output.mUPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kUPlane),
                        aFrame.Stride(cdm::VideoFrame::kUPlane) };
   output.mVPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kVPlane),
                        aFrame.Stride(cdm::VideoFrame::kVPlane) };
   output.mTimestamp() = aFrame.Timestamp();
 
   uint64_t duration = 0;
   if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
     output.mDuration() = duration;
   }
 
-  Unused << SendDecoded(output);
+  CDMBuffer* base = reinterpret_cast<CDMBuffer*>(aFrame.FrameBuffer());
+  if (base->AsShmemBuffer()) {
+    ipc::Shmem shmem = base->AsShmemBuffer()->ExtractShmem();
+    Unused << SendDecodedShmem(output, shmem);
+  } else {
+    MOZ_ASSERT(base->AsArrayBuffer());
+    Unused << SendDecodedData(output, base->AsArrayBuffer()->ExtractBuffer());
+  }
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDrain()
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
-  MOZ_ASSERT(!mBuffers.IsEmpty());
   WidevineVideoFrame frame;
   cdm::InputBuffer sample;
   cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
   CDM_LOG("ChromiumCDMChild::RecvDrain();  DecryptAndDecodeFrame() rv=%d", rv);
   if (rv == cdm::kSuccess) {
     MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
     ReturnOutput(frame);
   } else {
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -17,16 +17,17 @@
 
 namespace mozilla {
 namespace gmp {
 
 ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
                                      uint32_t aPluginId)
   : mPluginId(aPluginId)
   , mContentParent(aContentParent)
+  , mVideoShmemCount(MediaPrefs::EMEChromiumAPIVideoShmemCount())
 {
   GMP_LOG(
     "ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
     this,
     aContentParent,
     aPluginId);
 }
 
@@ -595,45 +596,92 @@ ChromiumCDMParent::RecvDecrypted(const u
       mDecrypts.RemoveElementAt(i);
       break;
     }
   }
   return IPC_OK();
 }
 
 ipc::IPCResult
-ChromiumCDMParent::RecvDecoded(const CDMVideoFrame& aFrame)
+ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
+                                   nsTArray<uint8_t>&& aData)
 {
-  // On failure we need to deallocate the shmem used to store the decrypted
-  // sample. On success we return it to the CDM to be reused.
+  GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p) "
+          "mVideoShmemCount=%" PRIu32,
+          this,
+          mVideoShmemCount);
+  // We'd expect CDMs to not have video frames larger than 1280x720 (due to
+  // DRM robustness requirements), which is about 1.5MB per frame. So put an
+  // upper limit on the number of shmems we tolerate the CDM asking for. In
+  // practice, we expect the CDM to need less than 5, but some encodings
+  // require more.
+  Shmem shmem;
+  if (mVideoShmemCount >= 50 || !AllocShmem(mVideoFrameBufferSize,
+                                            Shmem::SharedMemory::TYPE_BASIC,
+                                            &shmem)) {
+    GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p) "
+            "failed to allocate shmem for CDM.",
+            this);
+    mVideoDecoderInitialized = false;
+    mDecodePromise.RejectIfExists(
+      MediaResult(
+        NS_ERROR_DOM_MEDIA_FATAL_ERR,
+        RESULT_DETAIL("Failled to send shmems to CDM after decode init.")),
+      __func__);
+    return IPC_OK();
+  }
+  mVideoShmemCount++;
+
+  ProcessDecoded(aFrame, aData, Move(shmem));
+
+  return IPC_OK();
+}
+
+ipc::IPCResult
+ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
+                                    ipc::Shmem&& aShmem)
+{
+  ProcessDecoded(
+    aFrame,
+    MakeSpan<uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()),
+    Move(aShmem));
+  return IPC_OK();
+}
+
+void
+ChromiumCDMParent::ProcessDecoded(const CDMVideoFrame& aFrame,
+                                  Span<uint8_t> aData,
+                                  ipc::Shmem&& aGiftShmem)
+{
+  // On failure we need to deallocate the shmem we're to return to the
+  // CDM. On success we return it to the CDM to be reused.
   auto autoDeallocateShmem =
-    MakeScopeExit([&, this] { this->DeallocShmem(aFrame.mData()); });
+    MakeScopeExit([&, this] { this->DeallocShmem(aGiftShmem); });
 
   if (mIsShutdown || mDecodePromise.IsEmpty()) {
-    return IPC_OK();
+    return;
   }
   VideoData::YCbCrBuffer b;
-  uint8_t* data = aFrame.mData().get<uint8_t>();
-  MOZ_ASSERT(aFrame.mData().Size<uint8_t>() > 0);
+  MOZ_ASSERT(aData.Length() > 0);
 
-  b.mPlanes[0].mData = data;
+  b.mPlanes[0].mData = aData.Elements();
   b.mPlanes[0].mWidth = aFrame.mImageWidth();
   b.mPlanes[0].mHeight = aFrame.mImageHeight();
   b.mPlanes[0].mStride = aFrame.mYPlane().mStride();
   b.mPlanes[0].mOffset = aFrame.mYPlane().mPlaneOffset();
   b.mPlanes[0].mSkip = 0;
 
-  b.mPlanes[1].mData = data;
+  b.mPlanes[1].mData = aData.Elements();
   b.mPlanes[1].mWidth = (aFrame.mImageWidth() + 1) / 2;
   b.mPlanes[1].mHeight = (aFrame.mImageHeight() + 1) / 2;
   b.mPlanes[1].mStride = aFrame.mUPlane().mStride();
   b.mPlanes[1].mOffset = aFrame.mUPlane().mPlaneOffset();
   b.mPlanes[1].mSkip = 0;
 
-  b.mPlanes[2].mData = data;
+  b.mPlanes[2].mData = aData.Elements();
   b.mPlanes[2].mWidth = (aFrame.mImageWidth() + 1) / 2;
   b.mPlanes[2].mHeight = (aFrame.mImageHeight() + 1) / 2;
   b.mPlanes[2].mStride = aFrame.mVPlane().mStride();
   b.mPlanes[2].mOffset = aFrame.mVPlane().mPlaneOffset();
   b.mPlanes[2].mSkip = 0;
 
   gfx::IntRect pictureRegion(0, 0, aFrame.mImageWidth(), aFrame.mImageHeight());
   RefPtr<VideoData> v = VideoData::CreateAndCopyData(
@@ -644,37 +692,35 @@ ChromiumCDMParent::RecvDecoded(const CDM
     media::TimeUnit::FromMicroseconds(aFrame.mDuration()),
     b,
     false,
     media::TimeUnit::FromMicroseconds(-1),
     pictureRegion);
 
   // Return the shmem to the CDM so the shmem can be reused to send us
   // another frame.
-  if (!SendGiveBuffer(aFrame.mData())) {
+  if (!SendGiveBuffer(aGiftShmem)) {
     mDecodePromise.RejectIfExists(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("Can't return shmem to CDM process")),
       __func__);
-    return IPC_OK();
+    return;
   }
   // Don't need to deallocate the shmem since the CDM process is responsible
   // for it again.
   autoDeallocateShmem.release();
 
   if (v) {
     mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
   } else {
     mDecodePromise.RejectIfExists(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("CallBack::CreateAndCopyData")),
       __func__);
   }
-
-  return IPC_OK();
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvDecodeFailed(const uint32_t& aStatus)
 {
   if (mIsShutdown) {
     MOZ_ASSERT(mDecodePromise.IsEmpty());
     return IPC_OK();
@@ -793,22 +839,29 @@ ChromiumCDMParent::RecvOnDecoderInitDone
     // GPU surface for video frames), and send the shmem back to the CDM
     // process so it can reuse it.
     //
     // We predict the size of buffer the CDM will allocate, and prepopulate
     // the CDM's list of shmems with shmems of at least that size, plus a bit
     // of padding for safety.
     //
     // Normally the CDM won't allocate more than one buffer at once, but
-    // we've seen cases where it allocates two buffers, returns one and holds
-    // onto the other. So the minimum number of shmems we give to the CDM
-    // must be at least two, and the default is three for safety.
-    const uint32_t count =
-      std::max<uint32_t>(2u, MediaPrefs::EMEChromiumAPIVideoShmemCount());
-    for (uint32_t i = 0; i < count; i++) {
+    // we've seen cases where it allocates multiple buffers, returns one and
+    // holds onto the rest. So we need to ensure we have a minimum number of
+    // shmems pre-allocated for the CDM. This minimum is set by the pref
+    // media.eme.chromium-api.video-shmems.
+    //
+    // We also have a failure recovery mechanism; if the CDM asks for more
+    // buffers than we have shmem's available, ChromiumCDMChild gives the
+    // CDM a non-shared memory buffer, and returns the frame to the parent
+    // in an nsTArray<uint8_t> instead of a shmem. Every time this happens,
+    // the parent sends an extra shmem to the CDM process for it to add to the
+    // set of shmems with which to return output. Via this mechanism we should
+    // recover from incorrectly predicting how many shmems to pre-allocate.
+    for (uint32_t i = 0; i < mVideoShmemCount; i++) {
       if (!SendBufferToCDM(mVideoFrameBufferSize)) {
         mVideoDecoderInitialized = false;
         mInitVideoDecoderPromise.RejectIfExists(
           MediaResult(
             NS_ERROR_DOM_MEDIA_FATAL_ERR,
             RESULT_DETAIL("Failled to send shmems to CDM after decode init.")),
           __func__);
         return IPC_OK();
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -10,16 +10,17 @@
 #include "GMPCrashHelper.h"
 #include "GMPCrashHelperHolder.h"
 #include "GMPMessageUtils.h"
 #include "mozilla/gmp/PChromiumCDMParent.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
 #include "PlatformDecoderModule.h"
 #include "ImageContainer.h"
+#include "mozilla/Span.h"
 
 namespace mozilla {
 
 class MediaRawData;
 class ChromiumCDMProxy;
 
 namespace gmp {
 
@@ -111,17 +112,25 @@ protected:
                                           const uint32_t& aSystemCode,
                                           const nsCString& aMessage) override;
   ipc::IPCResult RecvDecrypted(const uint32_t& aId,
                                const uint32_t& aStatus,
                                ipc::Shmem&& aData) override;
   ipc::IPCResult RecvDecryptFailed(const uint32_t& aId,
                                    const uint32_t& aStatus) override;
   ipc::IPCResult RecvOnDecoderInitDone(const uint32_t& aStatus) override;
-  ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
+  ipc::IPCResult RecvDecodedShmem(const CDMVideoFrame& aFrame,
+                                  ipc::Shmem&& aShmem) override;
+  ipc::IPCResult RecvDecodedData(const CDMVideoFrame& aFrame,
+                                 nsTArray<uint8_t>&& aData) override;
+
+  void ProcessDecoded(const CDMVideoFrame& aFrame,
+                      Span<uint8_t> aData,
+                      ipc::Shmem&& aGiftShmem);
+
   ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
   ipc::IPCResult RecvShutdown() override;
   ipc::IPCResult RecvResetVideoDecoderComplete() override;
   ipc::IPCResult RecvDrainComplete() override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   bool SendBufferToCDM(uint32_t aSizeInBytes);
 
   void RejectPromise(uint32_t aPromiseId,
@@ -147,16 +156,20 @@ protected:
   RefPtr<layers::ImageContainer> mImageContainer;
   VideoInfo mVideoInfo;
   uint64_t mLastStreamOffset = 0;
 
   MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushDecoderPromise;
 
   int32_t mVideoFrameBufferSize = 0;
 
+  // Count of the number of shmems in the set used to return decoded video
+  // frames from the CDM to Gecko.
+  uint32_t mVideoShmemCount;
+
   bool mIsShutdown = false;
   bool mVideoDecoderInitialized = false;
   bool mActorDestroyed = false;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
--- a/dom/media/gmp/GMPTypes.ipdlh
+++ b/dom/media/gmp/GMPTypes.ipdlh
@@ -90,17 +90,16 @@ struct CDMVideoPlane {
   uint32_t mPlaneOffset;
   uint32_t mStride;
 };
 
 struct CDMVideoFrame {
   uint32_t mFormat;
   int32_t mImageWidth;
   int32_t mImageHeight;
-  Shmem mData;
   CDMVideoPlane mYPlane;
   CDMVideoPlane mUPlane;
   CDMVideoPlane mVPlane;
   int64_t mTimestamp;
   int64_t mDuration;
 };
 
 }
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -90,17 +90,18 @@ parent:
 
   // Return values of cdm::ContentDecryptionModule8::Decrypt
   async Decrypted(uint32_t aId, uint32_t aStatus, Shmem aDecryptedData);
   async DecryptFailed(uint32_t aId, uint32_t aStatus);
 
   async OnDecoderInitDone(uint32_t aStatus);
 
   // Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
-  async Decoded(CDMVideoFrame aFrame);
+  async DecodedShmem(CDMVideoFrame aFrame, Shmem aData);
+  async DecodedData(CDMVideoFrame aFrame, uint8_t[] aData);
   async DecodeFailed(uint32_t aStatus);
 
   async ResetVideoDecoderComplete();
 
   async DrainComplete();
 
   async Shutdown();
 };
--- a/dom/media/gmp/widevine-adapter/WidevineUtils.h
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.h
@@ -56,31 +56,47 @@ private:
 
 void InitInputBuffer(const GMPEncryptedBufferMetadata* aCrypto,
                      int64_t aTimestamp,
                      const uint8_t* aData,
                      size_t aDataSize,
                      cdm::InputBuffer &aInputBuffer,
                      nsTArray<cdm::SubsampleEntry> &aSubsamples);
 
-class WidevineBuffer : public cdm::Buffer
+namespace gmp {
+class CDMShmemBuffer;
+}
+class WidevineBuffer;
+
+// Base class for our cdm::Buffer implementations, so we can tell at runtime
+// whether the buffer is a Shmem or non-Shmem buffer.
+class CDMBuffer : public cdm::Buffer
+{
+public:
+  virtual WidevineBuffer* AsArrayBuffer() { return nullptr; }
+  virtual gmp::CDMShmemBuffer* AsShmemBuffer() { return nullptr; }
+};
+
+class WidevineBuffer : public CDMBuffer
 {
 public:
   explicit WidevineBuffer(size_t aSize);
   ~WidevineBuffer() override;
   void Destroy() override;
   uint32_t Capacity() const override;
   uint8_t* Data() override;
   void SetSize(uint32_t aSize) override;
   uint32_t Size() const override;
 
   // Moves contents of buffer out into temporary.
   // Note: This empties the buffer.
   nsTArray<uint8_t> ExtractBuffer();
 
+  WidevineBuffer* AsArrayBuffer() override { return this; }
+
 private:
   nsTArray<uint8_t> mBuffer;
   WidevineBuffer(const WidevineBuffer&);
   void operator=(const WidevineBuffer&);
 };
 
 class WidevineDecryptedBlock : public cdm::DecryptedBlock
 {
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -86,17 +86,18 @@ VideoDecoderManagerParent::StartupThread
     HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
     MOZ_ASSERT(hr == S_OK);
   }), NS_DISPATCH_NORMAL);
 #endif
   sVideoDecoderManagerThread->Dispatch(NS_NewRunnableFunction([]() {
     layers::VideoBridgeChild::Startup();
   }), NS_DISPATCH_NORMAL);
 
-  sManagerTaskQueue = new TaskQueue(managerThread.forget());
+  sManagerTaskQueue = new TaskQueue(
+    managerThread.forget(), "VideoDecoderManagerParent::sManagerTaskQueue");
 
   auto* obs = new ManagerThreadShutdownObserver();
   observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 }
 
 void
 VideoDecoderManagerParent::ShutdownThreads()
 {
@@ -154,19 +155,23 @@ VideoDecoderManagerParent::~VideoDecoder
   MOZ_COUNT_DTOR(VideoDecoderManagerParent);
 }
 
 PVideoDecoderParent*
 VideoDecoderManagerParent::AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
                                                     const layers::TextureFactoryIdentifier& aIdentifier,
                                                     bool* aSuccess)
 {
-  return new VideoDecoderParent(this, aVideoInfo, aIdentifier, sManagerTaskQueue,
-                                new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4)),
-                                aSuccess);
+  RefPtr<TaskQueue> decodeTaskQueue = new TaskQueue(
+    SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4),
+    "VideoDecoderParent::mDecodeTaskQueue");
+
+  return new VideoDecoderParent(
+    this, aVideoInfo, aIdentifier,
+    sManagerTaskQueue, decodeTaskQueue, aSuccess);
 }
 
 bool
 VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor)
 {
   VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
   parent->Destroy();
   return true;
--- a/dom/media/mediasource/AutoTaskQueue.h
+++ b/dom/media/mediasource/AutoTaskQueue.h
@@ -18,16 +18,23 @@ class AutoTaskQueue : public AbstractThr
 {
 public:
   explicit AutoTaskQueue(already_AddRefed<SharedThreadPool> aPool,
                          bool aSupportsTailDispatch = false)
   : AbstractThread(aSupportsTailDispatch)
   , mTaskQueue(new TaskQueue(Move(aPool), aSupportsTailDispatch))
   {}
 
+  AutoTaskQueue(already_AddRefed<SharedThreadPool> aPool,
+                const char* aName,
+                bool aSupportsTailDispatch = false)
+  : AbstractThread(aSupportsTailDispatch)
+  , mTaskQueue(new TaskQueue(Move(aPool), aName, aSupportsTailDispatch))
+  {}
+
   TaskDispatcher& TailDispatcher() override
   {
     return mTaskQueue->TailDispatcher();
   }
 
   void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                 DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
                 DispatchReason aReason = NormalDispatch) override
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -276,17 +276,17 @@ MediaSourceDecoder::NextFrameBufferedSta
 
   if (!mMediaSource
       || mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
   }
 
   // Next frame hasn't been decoded yet.
   // Use the buffered range to consider if we have the next frame available.
-  TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
+  auto currentPosition = CurrentPosition();
   TimeIntervals buffered = GetBuffered();
   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
   TimeInterval interval(
     currentPosition,
     currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
   return buffered.ContainsStrict(ClampIntervalToEnd(interval))
          ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
          : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
@@ -301,17 +301,17 @@ MediaSourceDecoder::CanPlayThrough()
     return false;
   }
 
   if (IsNaN(mMediaSource->Duration())) {
     // Don't have any data yet.
     return false;
   }
   TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration());
-  TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
+  auto currentPosition = CurrentPosition();
   if (duration.IsInfinite()) {
     // We can't make an informed decision and just assume that it's a live
     // stream
     return true;
   } else if (duration <= currentPosition) {
     return true;
   }
   // If we have data up to the mediasource's duration or 30s ahead, we can
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -18,17 +18,17 @@
 namespace mozilla {
 
 typedef TrackInfo::TrackType TrackType;
 using media::TimeUnit;
 using media::TimeIntervals;
 
 MediaSourceDemuxer::MediaSourceDemuxer(AbstractThread* aAbstractMainThread)
   : mTaskQueue(new AutoTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
-                                 /* aSupportsTailDispatch = */ false))
+                                 "MediaSourceDemuxer::mTaskQueue"))
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 constexpr TimeUnit MediaSourceDemuxer::EOS_FUZZ;
 
 RefPtr<MediaSourceDemuxer::InitPromise>
--- a/dom/media/platforms/omx/OmxDataDecoder.cpp
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -94,17 +94,17 @@ protected:
   AudioCompactor mAudioCompactor;
 
   // video output
   RefPtr<layers::ImageContainer> mImageContainer;
 };
 
 OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
                                layers::ImageContainer* aImageContainer)
-  : mOmxTaskQueue(CreateMediaDecodeTaskQueue())
+  : mOmxTaskQueue(CreateMediaDecodeTaskQueue("OmxDataDecoder::mOmxTaskQueue"))
   , mImageContainer(aImageContainer)
   , mWatchManager(this, mOmxTaskQueue)
   , mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
   , mTrackInfo(aTrackInfo.Clone())
   , mFlushing(false)
   , mShuttingDown(false)
   , mCheckingInputExhausted(false)
   , mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -315,22 +315,28 @@ function CreateAndSetMediaKeys(v, test, 
  * Collect the init data from 'encrypted' events.
  * Return a promise which will be resolved with the init data when collection
  * is completed (specified by test.sessionCount).
  */
 function LoadInitData(v, test, token) {
   let p = new EMEPromise;
   let initDataQueue = [];
 
+  // Call SimpleTest._originalSetTimeout() to bypass the flaky timeout checker.
+  let timer = SimpleTest._originalSetTimeout.call(window, () => {
+    p.reject(`${token} Timed out in waiting for the init data.`);
+  }, 60000);
+
   function onencrypted(ev) {
     initDataQueue.push(ev);
     Log(token, `got encrypted(${ev.initDataType}, ` +
         `${StringToHex(ArrayBufferToString(ev.initData))}) event.`);
     if (test.sessionCount == initDataQueue.length) {
       p.resolve(initDataQueue);
+      clearTimeout(timer);
     }
   }
 
   v.addEventListener("encrypted", onencrypted);
   return p.promise;
 }
 
 /*
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -2,16 +2,19 @@
 /* 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 "Performance.h"
 
 #include "GeckoProfiler.h"
+#ifdef MOZ_GECKO_PROFILER
+#include "ProfilerMarkers.h"
+#endif
 #include "PerformanceEntry.h"
 #include "PerformanceMainThread.h"
 #include "PerformanceMark.h"
 #include "PerformanceMeasure.h"
 #include "PerformanceObserver.h"
 #include "PerformanceResourceTiming.h"
 #include "PerformanceService.h"
 #include "PerformanceWorker.h"
@@ -265,19 +268,23 @@ Performance::Mark(const nsAString& aName
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   RefPtr<PerformanceMark> performanceMark =
     new PerformanceMark(GetAsISupports(), aName, Now());
   InsertUserEntry(performanceMark);
 
+#ifdef MOZ_GECKO_PROFILER
   if (profiler_is_active()) {
-    PROFILER_MARKER(NS_ConvertUTF16toUTF8(aName).get());
+    PROFILER_MARKER_PAYLOAD("UserTiming",
+                            new UserTimingMarkerPayload(aName,
+                                                        TimeStamp::Now()));
   }
+#endif
 }
 
 void
 Performance::ClearMarks(const Optional<nsAString>& aName)
 {
   ClearUserEntries(aName, NS_LITERAL_STRING("mark"));
 }
 
@@ -349,16 +356,28 @@ Performance::Measure(const nsAString& aN
     }
   } else {
     endTime = Now();
   }
 
   RefPtr<PerformanceMeasure> performanceMeasure =
     new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime);
   InsertUserEntry(performanceMeasure);
+
+#ifdef MOZ_GECKO_PROFILER
+  if (profiler_is_active()) {
+    TimeStamp startTimeStamp = CreationTimeStamp() +
+                               TimeDuration::FromMilliseconds(startTime);
+    TimeStamp endTimeStamp = CreationTimeStamp() +
+                             TimeDuration::FromMilliseconds(endTime);
+    PROFILER_MARKER_PAYLOAD("UserTiming",
+                            new UserTimingMarkerPayload(aName, startTimeStamp,
+                                                        endTimeStamp));
+  }
+#endif
 }
 
 void
 Performance::ClearMeasures(const Optional<nsAString>& aName)
 {
   ClearUserEntries(aName, NS_LITERAL_STRING("measure"));
 }
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -100,20 +100,16 @@ using namespace mozilla::dom;
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
-static inline nsPoint AsNsPoint(const nsIntPoint &p) {
-  return nsPoint(p.x, p.y);
-}
-
 // special class for handeling DOM context menu events because for
 // some reason it starves other mouse events if implemented on the
 // same class
 class nsPluginDOMContextMenuListener : public nsIDOMEventListener
 {
   virtual ~nsPluginDOMContextMenuListener();
 
 public:
@@ -1061,81 +1057,88 @@ NPBool nsPluginInstanceOwner::ConvertPoi
     }
     if (destY) {
       *destY = sourceY;
     }
     return true;
   }
 
   nsPresContext* presContext = pluginFrame->PresContext();
-  double scaleFactor = double(nsPresContext::AppUnitsPerCSSPixel())/
-    presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom();
+  CSSToLayoutDeviceScale scaleFactor(
+    double(nsPresContext::AppUnitsPerCSSPixel()) /
+    presContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 
   PuppetWidget *puppetWidget = static_cast<PuppetWidget*>(widget);
   PuppetWidget *rootWidget = static_cast<PuppetWidget*>(widget->GetTopLevelWidget());
   if (!rootWidget) {
     return false;
   }
-  nsPoint chromeSize = AsNsPoint(rootWidget->GetChromeDimensions()) / scaleFactor;
+  CSSIntPoint chromeSize = CSSIntPoint::Truncate(
+    LayoutDeviceIntPoint::FromUnknownPoint(rootWidget->GetChromeDimensions()) /
+    scaleFactor);
   nsIntSize intScreenDims = rootWidget->GetScreenDimensions();
-  nsSize screenDims = nsSize(intScreenDims.width / scaleFactor,
-                             intScreenDims.height / scaleFactor);
+  CSSIntSize screenDims = CSSIntSize::Truncate(
+    LayoutDeviceIntSize::FromUnknownSize(intScreenDims) / scaleFactor);
   int32_t screenH = screenDims.height;
-  nsPoint windowPosition = AsNsPoint(rootWidget->GetWindowPosition()) / scaleFactor;
+  CSSIntPoint windowPosition = CSSIntPoint::Truncate(
+    LayoutDeviceIntPoint::FromUnknownPoint(rootWidget->GetWindowPosition()) /
+    scaleFactor);
 
   // Window size is tab size + chrome size.
   LayoutDeviceIntRect tabContentBounds = puppetWidget->GetBounds();
-  tabContentBounds.ScaleInverseRoundOut(scaleFactor);
+  tabContentBounds.ScaleInverseRoundOut(scaleFactor.scale);
   int32_t windowH = tabContentBounds.height + int(chromeSize.y);
 
-  nsPoint pluginPosition = AsNsPoint(pluginFrame->GetScreenRect().TopLeft());
+  CSSIntPoint pluginPosition = pluginFrame->GetScreenRect().TopLeft();
 
   // Convert (sourceX, sourceY) to 'real' (not PuppetWidget) screen space.
   // In OSX, the Y-axis increases upward, which is the reverse of ours.
   // We want OSX coordinates for window and screen so those equations are swapped.
-  nsPoint sourcePoint(sourceX, sourceY);
-  nsPoint screenPoint;
+  CSSIntPoint sourcePoint = CSSIntPoint::Truncate(sourceX, sourceY);
+  CSSIntPoint screenPoint;
   switch (sourceSpace) {
     case NPCoordinateSpacePlugin:
       screenPoint = sourcePoint + pluginPosition +
-        pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+        CSSIntPoint::Truncate(CSSPoint::FromAppUnits(
+          pluginFrame->GetContentRectRelativeToSelf().TopLeft()));
       break;
     case NPCoordinateSpaceWindow:
-      screenPoint = nsPoint(sourcePoint.x, windowH-sourcePoint.y) +
+      screenPoint = CSSIntPoint(sourcePoint.x, windowH-sourcePoint.y) +
         windowPosition;
       break;
     case NPCoordinateSpaceFlippedWindow:
       screenPoint = sourcePoint + windowPosition;
       break;
     case NPCoordinateSpaceScreen:
-      screenPoint = nsPoint(sourcePoint.x, screenH-sourcePoint.y);
+      screenPoint = CSSIntPoint(sourcePoint.x, screenH-sourcePoint.y);
       break;
     case NPCoordinateSpaceFlippedScreen:
       screenPoint = sourcePoint;
       break;
     default:
       return false;
   }
 
   // Convert from screen to dest space.
-  nsPoint destPoint;
+  CSSIntPoint destPoint;
   switch (destSpace) {
     case NPCoordinateSpacePlugin:
       destPoint = screenPoint - pluginPosition -
-        pluginFrame->GetContentRectRelativeToSelf().TopLeft() / nsPresContext::AppUnitsPerCSSPixel();
+        CSSIntPoint::Truncate(CSSPoint::FromAppUnits(
+          pluginFrame->GetContentRectRelativeToSelf().TopLeft()));
       break;
     case NPCoordinateSpaceWindow:
       destPoint = screenPoint - windowPosition;
       destPoint.y = windowH - destPoint.y;
       break;
     case NPCoordinateSpaceFlippedWindow:
       destPoint = screenPoint - windowPosition;
       break;
     case NPCoordinateSpaceScreen:
-      destPoint = nsPoint(screenPoint.x, screenH-screenPoint.y);
+      destPoint = CSSIntPoint(screenPoint.x, screenH-screenPoint.y);
       break;
     case NPCoordinateSpaceFlippedScreen:
       destPoint = screenPoint;
       break;
     default:
       return false;
   }
 
@@ -1184,17 +1187,17 @@ NPBool nsPluginInstanceOwner::ConvertPoi
   screenHeight /= scaleFactor;
 
   LayoutDeviceIntRect windowScreenBounds = widget->GetScreenBounds();
   windowScreenBounds.ScaleInverseRoundOut(scaleFactor);
   int32_t windowX = windowScreenBounds.x;
   int32_t windowY = windowScreenBounds.y;
   int32_t windowHeight = windowScreenBounds.height;
 
-  nsIntRect pluginScreenRect = pluginFrame->GetScreenRect();
+  CSSIntRect pluginScreenRect = pluginFrame->GetScreenRect();
 
   double screenXGecko, screenYGecko;
   switch (sourceSpace) {
     case NPCoordinateSpacePlugin:
       screenXGecko = pluginScreenRect.x + sourceX;
       screenYGecko = pluginScreenRect.y + sourceY;
       break;
     case NPCoordinateSpaceWindow:
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -6,16 +6,18 @@
 
 #include "nsSMILAnimationController.h"
 
 #include <algorithm>
 
 #include "mozilla/AutoRestore.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/SVGAnimationElement.h"
+#include "mozilla/RestyleManagerInlines.h"
+#include "nsContentUtils.h"
 #include "nsCSSProps.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIPresShellInlines.h"
 #include "nsITimer.h"
 #include "nsSMILCompositor.h"
 #include "nsSMILCSSProperty.h"
 #include "nsSMILTimedElement.h"
@@ -321,21 +323,16 @@ nsSMILAnimationController::DoSample(bool
   if (mRunningSample) {
     NS_ERROR("Shouldn't be recursively sampling");
     return;
   }
 
   bool isStyleFlushNeeded = mResampleNeeded;
   mResampleNeeded = false;
 
-  if (mDocument->IsStyledByServo()) {
-    NS_WARNING("stylo: SMIL animations not supported yet");
-    return;
-  }
-
   nsCOMPtr<nsIDocument> document(mDocument);  // keeps 'this' alive too
 
   // Set running sample flag -- do this before flushing styles so that when we
   // flush styles we don't end up requesting extra samples
   AutoRestore<bool> autoRestoreRunningSample(mRunningSample);
   mRunningSample = true;
 
   // STEP 1: Bring model up to date
@@ -718,16 +715,77 @@ nsSMILAnimationController::AddStyleUpdat
     aTracker.AddPendingRestyle(key.mElement,
                                eRestyle_StyleAttribute_Animations,
                                nsChangeHint(0));
   }
 
   mMightHavePendingStyleUpdates = false;
 }
 
+bool
+nsSMILAnimationController::PreTraverse()
+{
+  return PreTraverseInSubtree(nullptr);
+}
+
+bool
+nsSMILAnimationController::PreTraverseInSubtree(Element* aRoot)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mMightHavePendingStyleUpdates) {
+    return false;
+  }
+
+  nsIPresShell* shell = mDocument->GetShell();
+  if (!shell) {
+    return false;
+  }
+
+  nsPresContext* context = shell->GetPresContext();
+  if (!context) {
+    return false;
+  }
+  MOZ_ASSERT(context->RestyleManager()->IsServo(),
+             "PreTraverse should only be called for the servo style system");
+
+  bool foundElementsNeedingRestyle = false;
+  for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) {
+    SVGAnimationElement* animElement = iter.Get()->GetKey();
+
+    nsSMILTargetIdentifier key;
+    if (!GetTargetIdentifierForAnimation(animElement, key)) {
+      // Something's wrong/missing about animation's target; skip this animation
+      continue;
+    }
+
+    // Ignore restyles that aren't in the flattened tree subtree rooted at
+    // aRoot.
+    if (aRoot &&
+        !nsContentUtils::ContentIsFlattenedTreeDescendantOf(key.mElement,
+                                                            aRoot)) {
+      continue;
+    }
+
+    context->RestyleManager()->AsServo()->
+      PostRestyleEventForAnimations(key.mElement,
+                                    eRestyle_StyleAttribute_Animations);
+
+    foundElementsNeedingRestyle = true;
+  }
+
+  // Only clear the mMightHavePendingStyleUpdates flag if we definitely posted
+  // all restyles.
+  if (!aRoot) {
+    mMightHavePendingStyleUpdates = false;
+  }
+
+  return foundElementsNeedingRestyle;
+}
+
 //----------------------------------------------------------------------
 // Add/remove child time containers
 
 nsresult
 nsSMILAnimationController::AddChild(nsSMILTimeContainer& aChild)
 {
   TimeContainerPtrKey* key = mChildContainerTable.PutEntry(&aChild);
   NS_ENSURE_TRUE(key, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/smil/nsSMILAnimationController.h
+++ b/dom/smil/nsSMILAnimationController.h
@@ -20,16 +20,17 @@
 #include "nsRefreshDriver.h"
 
 struct nsSMILTargetIdentifier;
 class nsIDocument;
 
 namespace mozilla {
 class RestyleTracker;
 namespace dom {
+class Element;
 class SVGAnimationElement;
 } // namespace dom
 } // namespace mozilla
 
 //----------------------------------------------------------------------
 // nsSMILAnimationController
 //
 // The animation controller maintains the animation timer and determines the
@@ -108,16 +109,19 @@ public:
   }
 
   void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
   bool MightHavePendingStyleUpdates() const
   {
     return mMightHavePendingStyleUpdates;
   }
 
+  bool PreTraverse();
+  bool PreTraverseInSubtree(mozilla::dom::Element* aRoot);
+
 protected:
   ~nsSMILAnimationController();
 
   // Typedefs
   typedef nsPtrHashKey<nsSMILTimeContainer> TimeContainerPtrKey;
   typedef nsTHashtable<TimeContainerPtrKey> TimeContainerHashtable;
   typedef nsPtrHashKey<mozilla::dom::SVGAnimationElement> AnimationElementPtrKey;
   typedef nsTHashtable<AnimationElementPtrKey> AnimationElementHashtable;
--- a/dom/smil/nsSMILCSSProperty.cpp
+++ b/dom/smil/nsSMILCSSProperty.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* representation of a SMIL-animatable CSS property on an element */
 
 #include "nsSMILCSSProperty.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/Move.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "nsICSSDeclaration.h"
 #include "nsSMILCSSValueType.h"
 #include "nsSMILValue.h"
 #include "nsCSSProps.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -59,20 +60,29 @@ nsSMILCSSProperty::GetBaseValue() const
     //
     // In any case, just return a dummy value (initialized with the right
     // type, so as not to indicate failure).
     nsSMILValue tmpVal(&nsSMILCSSValueType::sSingleton);
     Swap(baseValue, tmpVal);
     return baseValue;
   }
 
-  StyleAnimationValue computedValue;
-  if (!StyleAnimationValue::ExtractComputedValue(mPropID,
-                                                 mBaseStyleContext,
-                                                 computedValue)) {
+  AnimationValue computedValue;
+  if (mElement->IsStyledByServo()) {
+    const ServoComputedValues* currentStyle =
+      mBaseStyleContext->StyleSource().AsServoComputedValues();
+    computedValue.mServo =
+      Servo_ComputedValues_ExtractAnimationValue(currentStyle, mPropID)
+      .Consume();
+    if (!computedValue.mServo) {
+      return baseValue;
+    }
+  } else if (!StyleAnimationValue::ExtractComputedValue(mPropID,
+                                                        mBaseStyleContext,
+                                                        computedValue.mGecko)) {
     return baseValue;
   }
 
   baseValue =
     nsSMILCSSValueType::ValueFromAnimationValue(mPropID, mElement,
                                                 computedValue);
   return baseValue;
 }
@@ -103,20 +113,17 @@ nsSMILCSSProperty::ValueFromString(const
 
 nsresult
 nsSMILCSSProperty::SetAnimValue(const nsSMILValue& aValue)
 {
   NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE);
 
   // Convert nsSMILValue to string
   nsAutoString valStr;
-  if (!nsSMILCSSValueType::ValueToString(aValue, valStr)) {
-    NS_WARNING("Failed to convert nsSMILValue for CSS property into a string");
-    return NS_ERROR_FAILURE;
-  }
+  nsSMILCSSValueType::ValueToString(aValue, valStr);
 
   // Use string value to style the target element
   nsICSSDeclaration* overrideDecl = mElement->GetSMILOverrideStyle();
   if (overrideDecl) {
     nsAutoString oldValStr;
     overrideDecl->GetPropertyValue(mPropID, oldValStr);
     if (valStr.Equals(oldValStr)) {
       return NS_OK;
--- a/dom/smil/nsSMILCSSValueType.cpp
+++ b/dom/smil/nsSMILCSSValueType.cpp
@@ -7,51 +7,61 @@
 /* representation of a value for a SMIL-animated CSS property */
 
 #include "nsSMILCSSValueType.h"
 
 #include "nsComputedDOMStyle.h"
 #include "nsString.h"
 #include "nsSMILParserUtils.h"
 #include "nsSMILValue.h"
+#include "nsCSSProps.h"
 #include "nsCSSValue.h"
 #include "nsColor.h"
 #include "nsPresContext.h"
-#include "mozilla/StyleAnimationValue.h"
+#include "mozilla/Keyframe.h" // For PropertyValuePair
+#include "mozilla/ServoBindings.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
+#include "mozilla/StyleAnimationValue.h" // For AnimationValue
+#include "mozilla/StyleSetHandleInlines.h"
 #include "mozilla/dom/Element.h"
 #include "nsDebug.h"
 #include "nsStyleUtil.h"
 #include "nsIDocument.h"
 
 using namespace mozilla::dom;
 using mozilla::StyleAnimationValue;
 
 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
 
 struct ValueWrapper {
-  ValueWrapper(nsCSSPropertyID aPropID, const StyleAnimationValue& aValue) :
-    mPropID(aPropID), mCSSValue(aValue) {}
+  ValueWrapper(nsCSSPropertyID aPropID, const AnimationValue& aValue)
+    : mPropID(aPropID), mCSSValue(aValue) {}
+  ValueWrapper(nsCSSPropertyID aPropID, const StyleAnimationValue& aValue)
+    : mPropID(aPropID), mCSSValue(aValue) {}
+  ValueWrapper(nsCSSPropertyID aPropID,
+               const RefPtr<RawServoAnimationValue>& aValue)
+    : mPropID(aPropID), mCSSValue(aValue) {}
 
   nsCSSPropertyID mPropID;
-  StyleAnimationValue mCSSValue;
+  AnimationValue mCSSValue;
 };
 
 // Helper Methods
 // --------------
-static const StyleAnimationValue*
+static const AnimationValue*
 GetZeroValueForUnit(StyleAnimationValue::Unit aUnit)
 {
-  static const StyleAnimationValue
-    sZeroCoord(0, StyleAnimationValue::CoordConstructor);
-  static const StyleAnimationValue
-    sZeroPercent(0.0f, StyleAnimationValue::PercentConstructor);
-  static const StyleAnimationValue
-    sZeroFloat(0.0f,  StyleAnimationValue::FloatConstructor);
-  static const StyleAnimationValue
-    sZeroColor(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor);
+  static const AnimationValue sZeroCoord(
+    StyleAnimationValue(0, StyleAnimationValue::CoordConstructor));
+  static const AnimationValue sZeroPercent(
+    StyleAnimationValue(0.0f, StyleAnimationValue::PercentConstructor));
+  static const AnimationValue sZeroFloat(
+    StyleAnimationValue(0.0f,  StyleAnimationValue::FloatConstructor));
+  static const AnimationValue sZeroColor(
+    StyleAnimationValue(NS_RGB(0,0,0), StyleAnimationValue::ColorConstructor));
 
   MOZ_ASSERT(aUnit != StyleAnimationValue::eUnit_Null,
              "Need non-null unit for a zero value");
   switch (aUnit) {
     case StyleAnimationValue::eUnit_Coord:
       return &sZeroCoord;
     case StyleAnimationValue::eUnit_Percent:
       return &sZeroPercent;
@@ -70,44 +80,58 @@ GetZeroValueForUnit(StyleAnimationValue:
 // for the other argument's Unit (if applicable; otherwise, we return false).
 //
 // If neither argument is null, this method generally does nothing, though it
 // may apply a workaround for the special case where a 0 length-value is mixed
 // with a eUnit_Float value.  (See comment below.)
 //
 // Returns true on success, or false.
 static bool
-FinalizeStyleAnimationValues(const StyleAnimationValue*& aValue1,
-                             const StyleAnimationValue*& aValue2)
+FinalizeStyleAnimationValues(const AnimationValue*& aValue1,
+                             const AnimationValue*& aValue2)
 {
   MOZ_ASSERT(aValue1 || aValue2,
              "expecting at least one non-null value");
+  MOZ_ASSERT(!aValue1 || !aValue2 || !aValue1->mServo == !aValue2->mServo,
+             "If both values are specified, they should be for the same"
+             " style system");
+
+  bool isServo = aValue1 ? aValue1->mServo : aValue2->mServo;
+
+  if (isServo) {
+    // Bug 1355349: Implement additive animation for Stylo
+    if (!aValue1 || !aValue2) {
+      NS_WARNING("stylo: Missing values are not yet supported (bug 1355349)");
+      return false;
+    }
+    return true;
+  }
 
   // Are we missing either val? (If so, it's an implied 0 in other val's units)
   if (!aValue1) {
-    aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
+    aValue1 = GetZeroValueForUnit(aValue2->mGecko.GetUnit());
     return !!aValue1; // Fail if we have no zero value for this unit.
   }
   if (!aValue2) {
-    aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
+    aValue2 = GetZeroValueForUnit(aValue1->mGecko.GetUnit());
     return !!aValue2; // Fail if we have no zero value for this unit.
   }
 
   // Ok, both values were specified.
   // Need to handle a special-case, though: unitless nonzero length (parsed as
   // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord).  These
   // won't interoperate in StyleAnimationValue, since their Units don't match.
   // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
-  const StyleAnimationValue& zeroCoord =
+  const AnimationValue& zeroCoord =
     *GetZeroValueForUnit(StyleAnimationValue::eUnit_Coord);
   if (*aValue1 == zeroCoord &&
-      aValue2->GetUnit() == StyleAnimationValue::eUnit_Float) {
+      aValue2->mGecko.GetUnit() == StyleAnimationValue::eUnit_Float) {
     aValue1 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
   } else if (*aValue2 == zeroCoord &&
-             aValue1->GetUnit() == StyleAnimationValue::eUnit_Float) {
+             aValue1->mGecko.GetUnit() == StyleAnimationValue::eUnit_Float) {
     aValue2 = GetZeroValueForUnit(StyleAnimationValue::eUnit_Float);
   }
 
   return true;
 }
 
 static void
 InvertSign(StyleAnimationValue& aValue)
@@ -229,65 +253,83 @@ nsSMILCSSValueType::Add(nsSMILValue& aDe
                             destWrapper->mPropID);
   // Special case: font-size-adjust and stroke-dasharray are explicitly
   // non-additive (even though StyleAnimationValue *could* support adding them)
   if (property == eCSSProperty_font_size_adjust ||
       property == eCSSProperty_stroke_dasharray) {
     return NS_ERROR_FAILURE;
   }
 
-  const StyleAnimationValue* valueToAdd = valueToAddWrapper ?
-    &valueToAddWrapper->mCSSValue : nullptr;
-  const StyleAnimationValue* destValue = destWrapper ?
-    &destWrapper->mCSSValue : nullptr;
+  const AnimationValue* valueToAdd = valueToAddWrapper
+                                     ? &valueToAddWrapper->mCSSValue
+                                     : nullptr;
+  const AnimationValue* destValue = destWrapper
+                                    ? &destWrapper->mCSSValue
+                                    : nullptr;
   if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
     return NS_ERROR_FAILURE;
   }
   // Did FinalizeStyleAnimationValues change destValue?
   // If so, update outparam to use the new value.
   if (destWrapper && &destWrapper->mCSSValue != destValue) {
     destWrapper->mCSSValue = *destValue;
   }
 
   // Handle barely-initialized "zero" destination.
   if (!destWrapper) {
-    aDest.mU.mPtr = destWrapper =
-      new ValueWrapper(property, *destValue);
+    aDest.mU.mPtr = destWrapper = new ValueWrapper(property, *destValue);
+  }
+
+  // Bug 1355349: Implement additive animation for Stylo
+  if (destWrapper->mCSSValue.mServo) {
+    NS_WARNING("stylo: Additive animation not supported yet (bug 1355349)");
+    return NS_ERROR_FAILURE;
   }
 
   return StyleAnimationValue::Add(property,
-                                  destWrapper->mCSSValue, *valueToAdd, aCount) ?
-    NS_OK : NS_ERROR_FAILURE;
+                                  destWrapper->mCSSValue.mGecko,
+                                  valueToAdd->mGecko, aCount)
+         ? NS_OK
+         : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
                                     const nsSMILValue& aTo,
                                     double& aDistance) const
 {
   MOZ_ASSERT(aFrom.mType == aTo.mType,
              "Trying to compare different types");
   MOZ_ASSERT(aFrom.mType == this, "Unexpected source type");
 
   const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
   const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
   MOZ_ASSERT(toWrapper, "expecting non-null endpoint");
 
-  const StyleAnimationValue* fromCSSValue = fromWrapper ?
-    &fromWrapper->mCSSValue : nullptr;
-  const StyleAnimationValue* toCSSValue = &toWrapper->mCSSValue;
+  const AnimationValue* fromCSSValue = fromWrapper
+                                       ? &fromWrapper->mCSSValue
+                                       : nullptr;
+  const AnimationValue* toCSSValue = &toWrapper->mCSSValue;
   if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
     return NS_ERROR_FAILURE;
   }
 
+  if (toCSSValue->mServo) {
+    aDistance = Servo_AnimationValues_ComputeDistance(fromCSSValue->mServo,
+                                                      toCSSValue->mServo);
+    return NS_OK;
+  }
+
   return StyleAnimationValue::ComputeDistance(toWrapper->mPropID,
-                                              *fromCSSValue, *toCSSValue,
+                                              fromCSSValue->mGecko,
+                                              toCSSValue->mGecko,
                                               nullptr,
-                                              aDistance) ?
-    NS_OK : NS_ERROR_FAILURE;
+                                              aDistance)
+         ? NS_OK
+         : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
                                 const nsSMILValue& aEndVal,
                                 double aUnitDistance,
                                 nsSMILValue& aResult) const
 {
@@ -299,26 +341,44 @@ nsSMILCSSValueType::Interpolate(const ns
   MOZ_ASSERT(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
              "unit distance value out of bounds");
   MOZ_ASSERT(!aResult.mU.mPtr, "expecting barely-initialized outparam");
 
   const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
   const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
   MOZ_ASSERT(endWrapper, "expecting non-null endpoint");
 
-  const StyleAnimationValue* startCSSValue = startWrapper ?
-    &startWrapper->mCSSValue : nullptr;
-  const StyleAnimationValue* endCSSValue = &endWrapper->mCSSValue;
+  const AnimationValue* startCSSValue = startWrapper
+                                        ? &startWrapper->mCSSValue
+                                        : nullptr;
+  const AnimationValue* endCSSValue = &endWrapper->mCSSValue;
   if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
     return NS_ERROR_FAILURE;
   }
 
+  MOZ_ASSERT(!startCSSValue ||
+             !startCSSValue->mServo == !endCSSValue->mServo,
+             "Start and end values should use the same style system");
+
+  if (endCSSValue->mServo) {
+    RefPtr<RawServoAnimationValue> resultValue =
+      Servo_AnimationValues_Interpolate(startCSSValue->mServo,
+                                        endCSSValue->mServo,
+                                        aUnitDistance).Consume();
+    if (!resultValue) {
+      return NS_ERROR_FAILURE;
+    }
+    aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue);
+    return NS_OK;
+  }
+
   StyleAnimationValue resultValue;
   if (StyleAnimationValue::Interpolate(endWrapper->mPropID,
-                                       *startCSSValue, *endCSSValue,
+                                       startCSSValue->mGecko,
+                                       endCSSValue->mGecko,
                                        aUnitDistance, resultValue)) {
     aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue);
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 }
 
 // Helper function to extract presContext
@@ -331,50 +391,56 @@ GetPresContextForElement(Element* aElem)
     // and remove anonymous animated content from the document as a result.
     // See bug 534975.
     return nullptr;
   }
   nsIPresShell* shell = doc->GetShell();
   return shell ? shell->GetPresContext() : nullptr;
 }
 
-// Helper function to parse a string into a StyleAnimationValue
-static bool
-ValueFromStringHelper(nsCSSPropertyID aPropID,
-                      Element* aTargetElement,
-                      nsPresContext* aPresContext,
-                      const nsAString& aString,
-                      StyleAnimationValue& aStyleAnimValue,
-                      bool* aIsContextSensitive)
+static const nsDependentSubstring
+GetNonNegativePropValue(const nsAString& aString, nsCSSPropertyID aPropID,
+                        bool& aIsNegative)
 {
   // If value is negative, we'll strip off the "-" so the CSS parser won't
   // barf, and then manually make the parsed value negative.
   // (This is a partial solution to let us accept some otherwise out-of-bounds
   // CSS values. Bug 501188 will provide a more complete fix.)
-  bool isNegative = false;
+  aIsNegative = false;
   uint32_t subStringBegin = 0;
 
   // NOTE: We need to opt-out 'stroke-dasharray' from the negative-number
   // check.  Its values might look negative (e.g. by starting with "-1"), but
   // they're more complicated than our simple negation logic here can handle.
   if (aPropID != eCSSProperty_stroke_dasharray) {
     int32_t absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
     if (absValuePos > 0) {
-      isNegative = true;
+      aIsNegative = true;
       subStringBegin = (uint32_t)absValuePos; // Start parsing after '-' sign
     }
   }
-  RefPtr<nsStyleContext> styleContext =
-    nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr,
-                                        aPresContext->PresShell());
-  if (!styleContext) {
-    return false;
-  }
-  nsDependentSubstring subString(aString, subStringBegin);
-  if (!StyleAnimationValue::ComputeValue(aPropID, aTargetElement, styleContext,
+
+  return Substring(aString, subStringBegin);
+}
+
+// Helper function to parse a string into a StyleAnimationValue
+static bool
+ValueFromStringHelper(nsCSSPropertyID aPropID,
+                      Element* aTargetElement,
+                      nsPresContext* aPresContext,
+                      nsStyleContext* aStyleContext,
+                      const nsAString& aString,
+                      StyleAnimationValue& aStyleAnimValue,
+                      bool* aIsContextSensitive)
+{
+  bool isNegative = false;
+  const nsDependentSubstring subString =
+    GetNonNegativePropValue(aString, aPropID, isNegative);
+
+  if (!StyleAnimationValue::ComputeValue(aPropID, aTargetElement, aStyleContext,
                                          subString, true, aStyleAnimValue,
                                          aIsContextSensitive)) {
     return false;
   }
   if (isNegative) {
     InvertSign(aStyleAnimValue);
   }
 
@@ -383,16 +449,105 @@ ValueFromStringHelper(nsCSSPropertyID aP
     MOZ_ASSERT(aStyleAnimValue.GetUnit() == StyleAnimationValue::eUnit_Coord,
                "'font-size' value with unexpected style unit");
     aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() /
                                   aPresContext->EffectiveTextZoom());
   }
   return true;
 }
 
+static already_AddRefed<RawServoAnimationValue>
+ValueFromStringHelper(nsCSSPropertyID aPropID,
+                      Element* aTargetElement,
+                      nsPresContext* aPresContext,
+                      nsStyleContext* aStyleContext,
+                      const nsAString& aString)
+{
+  // FIXME (bug 1358966): Support shorthand properties
+  if (nsCSSProps::IsShorthand(aPropID)) {
+    return nullptr;
+  }
+
+  // Get a suitable style context for Servo
+  const ServoComputedValues* currentStyle =
+    aStyleContext->StyleSource().AsServoComputedValues();
+  // Bug 1349004: Remove GetParentAllowServo
+  const ServoComputedValues* parentStyle =
+    aStyleContext->GetParentAllowServo()
+    ? aStyleContext->GetParentAllowServo()->StyleSource()
+      .AsServoComputedValues()
+    : nullptr;
+  const ServoComputedValuesWithParent servoStyles =
+    { currentStyle, parentStyle };
+
+  // FIXME (bug 1357295): Handle negative values properly
+#ifdef DEBUG
+  {
+    bool isNegative = false;
+    Unused << GetNonNegativePropValue(aString, aPropID, isNegative);
+    if (isNegative) {
+      NS_WARNING("stylo: Special negative value handling not yet supported"
+                 " (bug 1357295)");
+    }
+  }
+#endif // DEBUG
+
+  // Parse property
+  nsIDocument* doc = aTargetElement->GetUncomposedDoc();
+  if (!doc) {
+    return nullptr;
+  }
+  // FIXME this is using the wrong base uri (bug 1343919)
+  RefPtr<URLExtraData> data = new URLExtraData(doc->GetDocumentURI(),
+                                               doc->GetDocumentURI(),
+                                               doc->NodePrincipal());
+  NS_ConvertUTF16toUTF8 value(aString);
+  RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
+    Servo_ParseProperty(aPropID, &value, data).Consume();
+  if (!servoDeclarationBlock) {
+    return nullptr;
+  }
+
+  // Compute value
+  PropertyValuePair propValuePair;
+  propValuePair.mProperty = aPropID;
+  propValuePair.mServoDeclarationBlock = servoDeclarationBlock;
+  AutoTArray<Keyframe, 1> keyframes;
+  keyframes.AppendElement()->mPropertyValues.AppendElement(Move(propValuePair));
+  nsTArray<ComputedKeyframeValues> computedValues =
+    aPresContext->StyleSet()->AsServo()
+      ->GetComputedKeyframeValuesFor(keyframes, aTargetElement, servoStyles);
+
+  // Pull out the appropriate value
+  if (computedValues.IsEmpty() || computedValues[0].IsEmpty()) {
+    return nullptr;
+  }
+  // So long as we don't support shorthands (bug 1358966) the following
+  // assertion should hold.
+  MOZ_ASSERT(computedValues.Length() == 1 &&
+             computedValues[0].Length() == 1,
+             "Should only have a single property with a single value");
+  AnimationValue computedValue = computedValues[0][0].mValue;
+  if (!computedValue.mServo) {
+    return nullptr;
+  }
+
+  if (aPropID == eCSSProperty_font_size) {
+    // FIXME (bug 1357296): Divide out text-zoom, since SVG is supposed to
+    // ignore it.
+    if (aPresContext->EffectiveTextZoom() != 1.0) {
+      NS_WARNING("stylo: Dividing out text-zoom not yet supported"
+                 " (bug 1357296)");
+    }
+  }
+
+  // Result should be already add-refed
+  return computedValue.mServo.forget();
+}
+
 // static
 void
 nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID,
                                     Element* aTargetElement,
                                     const nsAString& aString,
                                     nsSMILValue& aValue,
                                     bool* aIsContextSensitive)
 {
@@ -406,29 +561,53 @@ nsSMILCSSValueType::ValueFromString(nsCS
   nsIDocument* doc = aTargetElement->GetUncomposedDoc();
   if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr,
                                                 doc->NodePrincipal(),
                                                 doc->GetDocumentURI(),
                                                 0, aString, nullptr)) {
     return;
   }
 
+  RefPtr<nsStyleContext> styleContext =
+    nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr,
+                                        presContext->PresShell());
+  if (!styleContext) {
+    return;
+  }
+
+  if (aTargetElement->IsStyledByServo()) {
+    RefPtr<RawServoAnimationValue> parsedValue =
+      ValueFromStringHelper(aPropID, aTargetElement, presContext,
+                            styleContext, aString);
+    if (aIsContextSensitive) {
+      // FIXME: Bug 1358955 - detect context-sensitive values and set this value
+      // appropriately.
+      *aIsContextSensitive = false;
+    }
+
+    if (parsedValue) {
+      sSingleton.Init(aValue);
+      aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
+    }
+    return;
+  }
+
   StyleAnimationValue parsedValue;
-  if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
+  if (ValueFromStringHelper(aPropID, aTargetElement, presContext, styleContext,
                             aString, parsedValue, aIsContextSensitive)) {
     sSingleton.Init(aValue);
     aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
   }
 }
 
 // static
 nsSMILValue
 nsSMILCSSValueType::ValueFromAnimationValue(nsCSSPropertyID aPropID,
                                             Element* aTargetElement,
-                                            const StyleAnimationValue& aValue)
+                                            const AnimationValue& aValue)
 {
   nsSMILValue result;
 
   nsIDocument* doc = aTargetElement->GetUncomposedDoc();
   // We'd like to avoid serializing |aValue| if possible, and since the
   // string passed to CSPAllowsInlineStyle is only used for reporting violations
   // and an intermediate CSS value is not likely to be particularly useful
   // in that case, we just use a generic placeholder string instead.
@@ -443,26 +622,28 @@ nsSMILCSSValueType::ValueFromAnimationVa
 
   sSingleton.Init(result);
   result.mU.mPtr = new ValueWrapper(aPropID, aValue);
 
   return result;
 }
 
 // static
-bool
+void
 nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
                                   nsAString& aString)
 {
   MOZ_ASSERT(aValue.mType == &nsSMILCSSValueType::sSingleton,
              "Unexpected SMIL value type");
   const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
-  return !wrapper ||
-    StyleAnimationValue::UncomputeValue(wrapper->mPropID,
-                                        wrapper->mCSSValue, aString);
+  if (!wrapper) {
+    return;
+  }
+
+  wrapper->mCSSValue.SerializeSpecifiedValue(wrapper->mPropID, aString);
 }
 
 // static
 nsCSSPropertyID
 nsSMILCSSValueType::PropertyFromValue(const nsSMILValue& aValue)
 {
   if (aValue.mType != &nsSMILCSSValueType::sSingleton) {
     return eCSSProperty_UNKNOWN;
--- a/dom/smil/nsSMILCSSValueType.h
+++ b/dom/smil/nsSMILCSSValueType.h
@@ -11,30 +11,30 @@
 
 #include "nsISMILType.h"
 #include "nsCSSPropertyID.h"
 #include "mozilla/Attributes.h"
 
 class nsAString;
 
 namespace mozilla {
-class StyleAnimationValue;
+struct AnimationValue;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 /*
  * nsSMILCSSValueType: Represents a SMIL-animated CSS value.
  */
 class nsSMILCSSValueType : public nsISMILType
 {
 public:
   typedef mozilla::dom::Element Element;
-  typedef mozilla::StyleAnimationValue StyleAnimationValue;
+  typedef mozilla::AnimationValue AnimationValue;
 
   // Singleton for nsSMILValue objects to hold onto.
   static nsSMILCSSValueType sSingleton;
 
 protected:
   // nsISMILType Methods
   // -------------------
   virtual void     Init(nsSMILValue& aValue) const override;
@@ -94,31 +94,29 @@ public:
    *                        applies.
    * @param aValue          The animation value to use.
    * @return                A new nsSMILValue. On failure, returns an
    *                        nsSMILValue with the null type (i.e. rv.IsNull()
    *                        returns true).
    */
   static nsSMILValue ValueFromAnimationValue(nsCSSPropertyID aPropID,
                                              Element* aTargetElement,
-                                             const StyleAnimationValue& aValue);
+                                             const AnimationValue& aValue);
 
   /**
    * Creates a string representation of the given nsSMILValue.
    *
    * Note: aValue is expected to be of this type (that is, it's expected to
    * have been initialized by nsSMILCSSValueType::sSingleton).  If aValue is a
-   * freshly-initialized value, this method will succeed, though the resulting
-   * string will be empty.
+   * freshly-initialized value the resulting string will be empty.
    *
    * @param       aValue   The nsSMILValue to be converted into a string.
    * @param [out] aString  The string to be populated with the given value.
-   * @return               true on success, false on failure.
    */
-  static bool ValueToString(const nsSMILValue& aValue, nsAString& aString);
+  static void ValueToString(const nsSMILValue& aValue, nsAString& aString);
 
   /**
    * Return the CSS property animated by the specified value.
    *
    * @param   aValue   The nsSMILValue to examine.
    * @return           The nsCSSPropertyID enum value of the property animated
    *                   by |aValue|, or eCSSProperty_UNKNOWN if the type of
    *                   |aValue| is not nsSMILCSSValueType.
--- a/dom/vr/VRServiceTest.cpp
+++ b/dom/vr/VRServiceTest.cpp
@@ -207,24 +207,26 @@ VRMockController::NewPoseMove(const Null
   if (!aOrientation.IsNull()) {
     const Float32Array& value = aOrientation.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 4);
     poseState.orientation[0] = value.Data()[0];
     poseState.orientation[1] = value.Data()[1];
     poseState.orientation[2] = value.Data()[2];
     poseState.orientation[3] = value.Data()[3];
+    poseState.isOrientationValid = true;
   }
   if (!aPosition.IsNull()) {
     const Float32Array& value = aPosition.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
     poseState.position[0] = value.Data()[0];
     poseState.position[1] = value.Data()[1];
     poseState.position[2] = value.Data()[2];
+    poseState.isPositionValid = true;
   }
   if (!aAngularVelocity.IsNull()) {
     const Float32Array& value = aAngularVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
     poseState.angularVelocity[0] = value.Data()[0];
     poseState.angularVelocity[1] = value.Data()[1];
     poseState.angularVelocity[2] = value.Data()[2];
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -1061,19 +1061,17 @@ TextEditRules::WillOutputText(Selection*
   if (!aOutString || !aOutputFormat || !aCancel || !aHandled) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // initialize out param
   *aCancel = false;
   *aHandled = false;
 
-  nsAutoString outputFormat(*aOutputFormat);
-  ToLowerCase(outputFormat);
-  if (outputFormat.EqualsLiteral("text/plain")) {
+  if (aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
     // Only use these rules for plain text output.
     if (IsPasswordEditor()) {
       *aOutString = mPasswordText;
       *aHandled = true;
     } else if (mBogusNode) {
       // This means there's no content, so output null string.
       aOutString->Truncate();
       *aHandled = true;
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1186,22 +1186,23 @@ TextEditor::GetAndInitDocEncoder(const n
       return nullptr;
     }
     mCachedDocumentEncoder = docEncoder;
     mCachedDocumentEncoderType = aFormatType;
   } else {
     docEncoder = mCachedDocumentEncoder;
   }
 
-  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryReferent(mDocWeak);
-  NS_ASSERTION(domDoc, "Need a document");
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+  NS_ASSERTION(doc, "Need a document");
 
   nsresult rv =
-    docEncoder->Init(domDoc, aFormatType,
-                     aFlags | nsIDocumentEncoder::RequiresReinitAfterOutput);
+    docEncoder->NativeInit(
+                  doc, aFormatType,
+                  aFlags | nsIDocumentEncoder::RequiresReinitAfterOutput);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) {
     docEncoder->SetCharset(aCharset);
   }
 
--- a/gfx/config/gfxVars.cpp
+++ b/gfx/config/gfxVars.cpp
@@ -9,37 +9,55 @@
 #include "mozilla/dom/ContentChild.h"
 
 namespace mozilla {
 namespace gfx {
 
 StaticAutoPtr<gfxVars> gfxVars::sInstance;
 StaticAutoPtr<nsTArray<gfxVars::VarBase*>> gfxVars::sVarList;
 
+StaticAutoPtr<nsTArray<GfxVarUpdate>> gGfxVarInitUpdates;
+
+void
+gfxVars::GotInitialVarUpdates(const nsTArray<GfxVarUpdate>& aInitUpdates)
+{
+  // We expect aInitUpdates to be provided before any other gfxVars operation.
+  MOZ_RELEASE_ASSERT(!sInstance, "Initial updates should not be provided after any gfxVars operation");
+
+  gGfxVarInitUpdates = new nsTArray<GfxVarUpdate>(aInitUpdates);
+}
+
 void
 gfxVars::Initialize()
 {
   if (sInstance) {
+    // We expect aInitUpdates to be provided before any other gfxVars operation.
+    MOZ_RELEASE_ASSERT(!gGfxVarInitUpdates, "Initial updates should not be present after any gfxVars operation");
     return;
   }
 
   // sVarList must be initialized first since it's used in the constructor for
   // sInstance.
   sVarList = new nsTArray<gfxVars::VarBase*>();
   sInstance = new gfxVars;
 
-  // Like Preferences, we want content to synchronously get initial data on
-  // init. Note the GPU process is not handled here - it cannot send sync
+  // Note the GPU process is not handled here - it cannot send sync
   // messages, so instead the initial data is pushed down.
   if (XRE_IsContentProcess()) {
-    InfallibleTArray<GfxVarUpdate> vars;
-    dom::ContentChild::GetSingleton()->SendGetGfxVars(&vars);
-    for (const auto& var : vars) {
-      ApplyUpdate(var);
+    MOZ_RELEASE_ASSERT(gGfxVarInitUpdates, "Initial updates must be provided in content process");
+    if (!gGfxVarInitUpdates) {
+      // No provided initial updates, sync-request them from parent.
+      InfallibleTArray<GfxVarUpdate> initUpdates;
+      dom::ContentChild::GetSingleton()->SendGetGfxVars(&initUpdates);
+      gGfxVarInitUpdates = new nsTArray<GfxVarUpdate>(Move(initUpdates));
     }
+    for (const auto& varUpdate : *gGfxVarInitUpdates) {
+      ApplyUpdate(varUpdate);
+    }
+    gGfxVarInitUpdates = nullptr;
   }
 }
 
 gfxVars::gfxVars()
 {
 }
 
 void
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -52,16 +52,17 @@ class gfxVarReceiver;
 //    void SetCxxName(const DataType& aValue);
 //
 // Note that the setter may only be called in the UI process; a gfxVar must be
 // a variable that is determined in the UI process and pushed to child
 // processes.
 class gfxVars final
 {
 public:
+  static void GotInitialVarUpdates(const nsTArray<GfxVarUpdate>& aInitUpdates);
   static void Initialize();
   static void Shutdown();
 
   static void ApplyUpdate(const GfxVarUpdate& aUpdate);
   static void AddReceiver(gfxVarReceiver* aReceiver);
   static void RemoveReceiver(gfxVarReceiver* aReceiver);
 
   // Return a list of updates for all variables with non-default values.
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -704,19 +704,17 @@ gfxPlatform::Init()
 #elif defined(MOZ_WIDGET_GTK)
     gPlatform = new gfxPlatformGtk;
 #elif defined(ANDROID)
     gPlatform = new gfxAndroidPlatform;
 #else
     #error "No gfxPlatform implementation available"
 #endif
     gPlatform->InitAcceleration();
-    if (XRE_IsParentProcess()) {
-      gPlatform->InitWebRenderConfig();
-    }
+    gPlatform->InitWebRenderConfig();
 
     if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
       GPUProcessManager* gpu = GPUProcessManager::Get();
       gpu->LaunchGPUProcess();
     }
 
 #ifdef USE_SKIA
     SkGraphics::Init();
@@ -2331,30 +2329,40 @@ gfxPlatform::InitCompositorAccelerationP
     feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by safe-mode",
                          NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
   }
 }
 
 void
 gfxPlatform::InitWebRenderConfig()
 {
+  bool prefEnabled = Preferences::GetBool("gfx.webrender.enabled", false);
+
+  ScopedGfxFeatureReporter reporter("WR", prefEnabled);
+  if (!XRE_IsParentProcess()) {
+    // The parent process runs through all the real decision-making code
+    // later in this function. For other processes we still want to report
+    // the state of the feature for crash reports.
+    if (gfxVars::UseWebRender()) {
+      reporter.SetSuccessful();
+    }
+    return;
+  }
+
   FeatureState& featureWebRender = gfxConfig::GetFeature(Feature::WEBRENDER);
 
   featureWebRender.DisableByDefault(
       FeatureStatus::OptIn,
       "WebRender is an opt-in feature",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_DEFAULT_OFF"));
 
-  bool prefEnabled = Preferences::GetBool("gfx.webrender.enabled", false);
   if (prefEnabled) {
     featureWebRender.UserEnable("Enabled by pref");
   }
 
-  ScopedGfxFeatureReporter reporter("WR", prefEnabled);
-
   // WebRender relies on the GPU process when on Windows
 #ifdef XP_WIN
   if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
     featureWebRender.ForceDisable(
       FeatureStatus::Unavailable,
       "GPU Process is disabled",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_GPU_PROCESS_DISABLED"));
   }
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -1259,16 +1259,17 @@ VRSystemManagerOculus::HandleInput()
       poseState.angularVelocity[0] = pose.AngularVelocity.x;
       poseState.angularVelocity[1] = pose.AngularVelocity.y;
       poseState.angularVelocity[2] = pose.AngularVelocity.z;
 
       poseState.flags |= GamepadCapabilityFlags::Cap_AngularAcceleration;
       poseState.angularAcceleration[0] = pose.AngularAcceleration.x;
       poseState.angularAcceleration[1] = pose.AngularAcceleration.y;
       poseState.angularAcceleration[2] = pose.AngularAcceleration.z;
+      poseState.isOrientationValid = true;
     }
     if (state.HandStatusFlags[handIdx] & ovrStatus_PositionTracked) {
       poseState.flags |= GamepadCapabilityFlags::Cap_Position;
       poseState.position[0] = pose.ThePose.Position.x;
       poseState.position[1] = pose.ThePose.Position.y;
       poseState.position[2] = pose.ThePose.Position.z;
       poseState.linearVelocity[0] = pose.LinearVelocity.x;
       poseState.linearVelocity[1] = pose.LinearVelocity.y;
@@ -1276,16 +1277,17 @@ VRSystemManagerOculus::HandleInput()
 
       poseState.flags |= GamepadCapabilityFlags::Cap_LinearAcceleration;
       poseState.linearAcceleration[0] = pose.LinearAcceleration.x;
       poseState.linearAcceleration[1] = pose.LinearAcceleration.y;
       poseState.linearAcceleration[2] = pose.LinearAcceleration.z;
 
       float eyeHeight = ovr_GetFloat(mSession, OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
       poseState.position[1] -= eyeHeight;
+      poseState.isPositionValid = true;
     }
     HandlePoseTracking(i, poseState, controller);
   }
 }
 
 void
 VRSystemManagerOculus::HandleButtonPress(uint32_t aControllerIdx,
                                          uint32_t aButton,
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -723,51 +723,56 @@ VRSystemManagerOpenVR::HandleInput()
       }
       MOZ_ASSERT(buttonIdx ==
                  controller->GetControllerInfo().GetNumButtons());
       controller->SetButtonPressed(state.ulButtonPressed);
       controller->SetButtonTouched(state.ulButtonTouched);
 
       // Start to process pose
       const ::vr::TrackedDevicePose_t& pose = poses[trackedIndex];
+      GamepadPoseState poseState;
 
-      if (pose.bDeviceIsConnected && pose.bPoseIsValid &&
-        pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
+      if (pose.bDeviceIsConnected) {
+        poseState.flags |= (GamepadCapabilityFlags::Cap_Orientation |
+                            GamepadCapabilityFlags::Cap_Position);
+      }
+
+      if (pose.bPoseIsValid &&
+          pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
         gfx::Matrix4x4 m;
 
         // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
         // because of its arrangement, we can copy the 12 elements in and
         // then transpose them to the right place.  We do this so we can
         // pull out a Quaternion.
         memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(float) * 12);
         m.Transpose();
 
         gfx::Quaternion rot;
         rot.SetFromRotationMatrix(m);
         rot.Invert();
 
-        GamepadPoseState poseState;
-        poseState.flags |= GamepadCapabilityFlags::Cap_Orientation;
         poseState.orientation[0] = rot.x;
         poseState.orientation[1] = rot.y;
         poseState.orientation[2] = rot.z;
         poseState.orientation[3] = rot.w;
         poseState.angularVelocity[0] = pose.vAngularVelocity.v[0];
         poseState.angularVelocity[1] = pose.vAngularVelocity.v[1];
         poseState.angularVelocity[2] = pose.vAngularVelocity.v[2];
+        poseState.isOrientationValid = true;
 
-        poseState.flags |= GamepadCapabilityFlags::Cap_Position;
         poseState.position[0] = m._41;
         poseState.position[1] = m._42;
         poseState.position[2] = m._43;
         poseState.linearVelocity[0] = pose.vVelocity.v[0];
         poseState.linearVelocity[1] = pose.vVelocity.v[1];
         poseState.linearVelocity[2] = pose.vVelocity.v[2];
-        HandlePoseTracking(i, poseState, controller);
+        poseState.isPositionValid = true;
       }
+      HandlePoseTracking(i, poseState, controller);
     }
   }
 }
 
 void
 VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
                                          uint32_t aButton,
                                          uint64_t aButtonMask,
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -473,23 +473,17 @@ VRSystemManagerPuppet::GetControllers(ns
 
 void
 VRSystemManagerPuppet::ScanForControllers()
 {
   // We make VRSystemManagerPuppet has two controllers always.
   const uint32_t newControllerCount = 2;
 
   if (newControllerCount != mControllerCount) {
-    // controller count is changed, removing the existing gamepads first.
-    for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
-      RemoveGamepad(i);
-    }
-
-    mControllerCount = 0;
-    mPuppetController.Clear();
+    RemoveControllers();
 
     // Re-adding controllers to VRControllerManager.
     for (uint32_t i = 0; i < newControllerCount; ++i) {
       dom::GamepadHand hand = (i % 2) ? dom::GamepadHand::Right :
                                         dom::GamepadHand::Left;
       RefPtr<VRControllerPuppet> puppetController = new VRControllerPuppet(hand);
       mPuppetController.AppendElement(puppetController);
 
@@ -498,11 +492,15 @@ VRSystemManagerPuppet::ScanForController
       ++mControllerCount;
     }
   }
 }
 
 void
 VRSystemManagerPuppet::RemoveControllers()
 {
+  // controller count is changed, removing the existing gamepads first.
+  for (uint32_t i = 0; i < mPuppetController.Length(); ++i) {
+    RemoveGamepad(i);
+  }
   mPuppetController.Clear();
   mControllerCount = 0;
 }
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -374,16 +374,20 @@ function ignoreContents(entry)
         // being initialized here.
         "Gecko_AnimationAppendKeyframe",
         "Gecko_NewStyleQuoteValues",
         "Gecko_NewCSSValueSharedList",
         /nsCSSValue::SetCalcValue/,
         /CSSValueSerializeCalcOps::Append/,
         "Gecko_CSSValue_SetFunction",
         "Gecko_CSSValue_SetArray",
+        "Gecko_EnsureMozBorderColors",
+        "Gecko_ClearMozBorderColors",
+        "Gecko_AppendMozBorderColors",
+        "Gecko_CopyMozBorderColors",
 
         // Needs main thread assertions or other fixes.
         /UndisplayedMap::GetEntryFor/,
         /nsStyleContext::CalcStyleDifferenceInternal/,
         /EffectCompositor::GetServoAnimationRule/,
         /LookAndFeel::GetColor/,
         "Gecko_CopyStyleContentsFrom",
         "Gecko_CSSValue_SetAbsoluteLength",
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -403,19 +403,21 @@ dnl ====================================
 dnl set the defaults first
 dnl ========================================================
 AS_BIN=$AS
 AR_EXTRACT='$(AR) x'
 AS='$(CC)'
 AS_DASH_C_FLAG='-c'
 DLL_PREFIX=lib
 LIB_PREFIX=lib
+RUST_LIB_PREFIX=lib
 DLL_SUFFIX=.so
 OBJ_SUFFIX=o
 LIB_SUFFIX=a
+RUST_LIB_SUFFIX=a
 IMPORT_LIB_SUFFIX=
 DIRENT_INO=d_ino
 MOZ_USER_DIR=".mozilla"
 
 MOZ_FIX_LINK_PATHS="-Wl,-rpath-link,${DIST}/bin -Wl,-rpath-link,${prefix}/lib"
 
 dnl Configure platform-specific CPU architecture compiler options.
 dnl ==============================================================
@@ -668,18 +670,20 @@ case "$target" in
         AR='lib'
         AR_FLAGS='-NOLOGO -OUT:$@'
         AR_EXTRACT=
         RANLIB='echo not_ranlib'
         STRIP='echo not_strip'
         PKG_SKIP_STRIP=1
         OBJ_SUFFIX=obj
         LIB_SUFFIX=lib
+        RUST_LIB_SUFFIX=lib
         DLL_PREFIX=
         LIB_PREFIX=
+        RUST_LIB_PREFIX=
         IMPORT_LIB_SUFFIX=lib
         MKSHLIB='$(LINK) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LINK) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         WIN32_SUBSYSTEM_VERSION=6.01
         WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
         WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
         DSO_LDOPTS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
         _USE_CPP_INCLUDE_FLAG=1
@@ -2090,20 +2094,22 @@ AC_SUBST(TARGET_XPCOM_ABI)
 
 AC_SUBST(WRAP_LDFLAGS)
 AC_SUBST(MKSHLIB)
 AC_SUBST(MKCSHLIB)
 AC_SUBST(DSO_CFLAGS)
 AC_SUBST(DSO_PIC_CFLAGS)
 AC_SUBST(DSO_LDOPTS)
 AC_SUBST(LIB_PREFIX)
+AC_SUBST(RUST_LIB_PREFIX)
 AC_SUBST(DLL_PREFIX)
 AC_SUBST(DLL_SUFFIX)
 AC_DEFINE_UNQUOTED(MOZ_DLL_SUFFIX, "$DLL_SUFFIX")
 AC_SUBST(LIB_SUFFIX)
+AC_SUBST(RUST_LIB_SUFFIX)
 AC_SUBST(OBJ_SUFFIX)
 AC_SUBST(BIN_SUFFIX)
 AC_SUBST(IMPORT_LIB_SUFFIX)
 AC_SUBST(USE_N32)
 AC_SUBST(CC_VERSION)
 AC_SUBST(MOZ_LINKER)
 AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
 AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Logging.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/StyleBackendType.h"
 #include <algorithm>
 
 #ifdef XP_WIN
 #include "winuser.h"
@@ -561,16 +562,19 @@ public:
 private:
   nsCOMPtr<nsIDocument> mDocument;
 };
 
 bool PresShell::sDisableNonTestMouseEvents = false;
 
 mozilla::LazyLogModule PresShell::gLog("PresShell");
 
+mozilla::TimeStamp PresShell::sLastInputCreated;
+mozilla::TimeStamp PresShell::sLastInputProcessed;
+
 #ifdef DEBUG
 static void
 VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
 {
   if (nsFrame::GetVerifyStyleTreeEnable()) {
     if (aPresContext->RestyleManager()->IsServo()) {
       NS_ERROR("stylo: cannot verify style tree with a ServoRestyleManager");
       return;
@@ -4554,29 +4558,16 @@ nsIPresShell::RestyleForCSSRuleChanges()
   }
 
   mDocument->RebuildUserFontSet();
 
   if (mPresContext) {
     mPresContext->RebuildCounterStyles();
   }
 
-  // Tell Servo that the contents of style sheets have changed.
-  //
-  // NB: It's important to do so before bailing out.
-  //
-  // Even if we have no frames, we can end up styling those when creating
-  // them, and it's important for Servo to know that it needs to use the
-  // correct styles.
-  // We don't do this notification if author styles are disabled, because
-  // the ServoStyleSet has already taken care of it.
-  if (mStyleSet->IsServo() && !mStyleSet->AsServo()->GetAuthorStyleDisabled()) {
-    mStyleSet->AsServo()->NoteStyleSheetsChanged();
-  }
-
   Element* root = mDocument->GetRootElement();
   if (!mDidInitialize) {
     // Nothing to do here, since we have no frames yet
     return;
   }
 
   if (!root) {
     // No content to restyle
@@ -4603,16 +4594,21 @@ void
 PresShell::RecordStyleSheetChange(StyleSheet* aStyleSheet)
 {
   // too bad we can't check that the update is UPDATE_STYLE
   NS_ASSERTION(mUpdateCount != 0, "must be in an update");
 
   if (mStylesHaveChanged)
     return;
 
+  // Tell Servo that the contents of style sheets have changed.
+  if (ServoStyleSet* set = mStyleSet->GetAsServo()) {
+    set->NoteStyleSheetsChanged();
+  }
+
   if (aStyleSheet->IsGecko()) {
     // XXXheycam ServoStyleSheets don't support <style scoped> yet.
     Element* scopeElement = aStyleSheet->AsGecko()->GetScopeElement();
     if (scopeElement) {
       mChangedScopeStyleRoots.AppendElement(scopeElement);
       return;
     }
   } else {
@@ -8209,21 +8205,34 @@ PresShell::HandleEventInternal(WidgetEve
       break;
     }
   }
 
   if (Telemetry::CanRecordBase() &&
       !aEvent->mTimeStamp.IsNull() &&
       aEvent->mTimeStamp > mLastOSWake &&
       aEvent->AsInputEvent()) {
-    double millis = (TimeStamp::Now() - aEvent->mTimeStamp).ToMilliseconds();
+    TimeStamp now = TimeStamp::Now();
+    double millis = (now - aEvent->mTimeStamp).ToMilliseconds();
     Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_MS, millis);
     if (mDocument && mDocument->GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
       Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_MS, millis);
     }
+
+    if (!sLastInputProcessed || sLastInputProcessed < aEvent->mTimeStamp) {
+      if (sLastInputProcessed) {
+        // This input event was created after we handled the last one.
+        // Accumulate the previous events' coalesced duration.
+        double lastMillis = (sLastInputProcessed - sLastInputCreated).ToMilliseconds();
+        Telemetry::Accumulate(Telemetry::INPUT_EVENT_RESPONSE_COALESCED_MS,
+                              lastMillis);
+      }
+      sLastInputCreated = aEvent->mTimeStamp;
+    }
+    sLastInputProcessed = now;
   }
 
   return rv;
 }
 
 /* static */ void
 nsIPresShell::DispatchGotOrLostPointerCaptureEvent(
                 bool aIsGotCapture,
@@ -8444,18 +8453,19 @@ PresShell::AdjustContextMenuKeyEvent(Wid
       nsIFrame* itemFrame =
         (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
       if (!itemFrame)
         itemFrame = popupFrame;
 
       nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
       aEvent->mWidget = widget;
       LayoutDeviceIntPoint widgetPoint = widget->WidgetToScreenOffset();
-      aEvent->mRefPoint = LayoutDeviceIntPoint::FromUnknownPoint(
-        itemFrame->GetScreenRect().BottomLeft()) - widgetPoint;
+      aEvent->mRefPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
+        itemFrame->GetScreenRectInAppUnits().BottomLeft(),
+        itemFrame->PresContext()->AppUnitsPerDevPixel()) - widgetPoint;
 
       mCurrentEventContent = itemFrame->GetContent();
       mCurrentEventFrame = itemFrame;
 
       return true;
     }
   }
 #endif
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -902,13 +902,16 @@ protected:
   // Whether the widget has received a paint message yet.
   bool                      mHasReceivedPaintMessage : 1;
 
   bool                      mIsLastKeyDownCanceled : 1;
 
   static bool               sDisableNonTestMouseEvents;
 
   mozilla::TimeStamp        mLastOSWake;
+
+  static mozilla::TimeStamp sLastInputCreated;
+  static mozilla::TimeStamp sLastInputProcessed;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_PresShell_h
--- a/layout/base/RestyleTracker.h
+++ b/layout/base/RestyleTracker.h
@@ -345,17 +345,19 @@ RestyleTracker::AddPendingRestyle(Elemen
       // the descendant.
       RestyleData* curData;
       mPendingRestyles.Get(cur, &curData);
 
       // Even if cur has a ForceDescendants restyle hint, we're not guaranteed
       // to reach aElement in the case the PresShell posts a restyle event from
       // PostRecreateFramesFor, so we need to track it here.
       MOZ_ASSERT(curData, "expected to find a RestyleData for cur");
-      curData->mDescendants.AppendElement(aElement);
+      if (curData) {
+        curData->mDescendants.AppendElement(aElement);
+      }
     }
   }
 
   mHaveLaterSiblingRestyles =
     mHaveLaterSiblingRestyles || (aRestyleHint & eRestyle_LaterSiblings) != 0;
   return hadRestyleLaterSiblings;
 }
 
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -208,19 +208,27 @@ ServoRestyleManager::ProcessPostTraversa
   // elements, though that is buggy right now even in non-stylo mode, see
   // bug 1251799.
   const bool recreateContext = oldStyleContext &&
     oldStyleContext->StyleSource().AsServoComputedValues() != computedValues;
 
   RefPtr<nsStyleContext> newContext = nullptr;
   if (recreateContext) {
     MOZ_ASSERT(styleFrame || displayContentsNode);
+
+    auto pseudo = aElement->GetPseudoElementType();
+    nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
+      ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
+
     newContext =
-      aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr,
-                            CSSPseudoElementType::NotPseudo, aElement);
+      aStyleSet->GetContext(computedValues.forget(),
+                            aParentContext,
+                            pseudoTag,
+                            pseudo,
+                            aElement);
 
     newContext->EnsureSameStructsCached(oldStyleContext);
 
     // XXX This could not always work as expected: there are kinds of content
     // with the first split and the last sharing style, but others not. We
     // should handle those properly.
     // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_
     // right, but not other cases where we happen to have different styles on
@@ -233,57 +241,16 @@ ServoRestyleManager::ProcessPostTraversa
     if (MOZ_UNLIKELY(displayContentsNode)) {
       MOZ_ASSERT(!styleFrame);
       displayContentsNode->mStyle = newContext;
     }
 
     if (styleFrame) {
       styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint);
     }
-
-    // Update pseudo-elements state if appropriate.
-    const static CSSPseudoElementType pseudosToRestyle[] = {
-      CSSPseudoElementType::before,
-      CSSPseudoElementType::after,
-    };
-
-    for (CSSPseudoElementType pseudoType : pseudosToRestyle) {
-      nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
-
-      if (nsIFrame* pseudoFrame = FrameForPseudoElement(aElement, pseudoTag)) {
-        // TODO: we could maybe make this more performant via calling into
-        // Servo just once to know which pseudo-elements we've got to restyle?
-        RefPtr<nsStyleContext> pseudoContext =
-          aStyleSet->ProbePseudoElementStyle(aElement, pseudoType, newContext);
-        MOZ_ASSERT(pseudoContext, "should have taken the ReconstructFrame path above");
-        pseudoFrame->SetStyleContext(pseudoContext);
-
-        if (pseudoFrame->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES) {
-          // XXX It really would be good to pass the actual changehint for our
-          // ::before/::after here, but we never computed it!
-          pseudoFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList,
-                                                   nsChangeHint_Hints_NotHandledForDescendants);
-        }
-
-        // We only care restyling text nodes, since other type of nodes
-        // (images), are still not supported. If that eventually changes, we
-        // may have to write more code here... Or not, I don't think too
-        // many inherited properties can affect those other frames.
-        StyleChildrenIterator it(pseudoFrame->GetContent());
-        for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-          if (n->IsNodeOfType(nsINode::eTEXT)) {
-            RefPtr<nsStyleContext> childContext =
-              aStyleSet->ResolveStyleForText(n, pseudoContext);
-            MOZ_ASSERT(n->GetPrimaryFrame(),
-                       "How? This node is created at FC time!");
-            n->GetPrimaryFrame()->SetStyleContext(childContext);
-          }
-        }
-      }
-    }
   }
 
   bool descendantsNeedFrames = aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   bool traverseElementChildren =
     aElement->HasDirtyDescendantsForServo() || descendantsNeedFrames;
   bool traverseTextChildren = recreateContext || descendantsNeedFrames;
   if (traverseElementChildren || traverseTextChildren) {
     nsStyleContext* upToDateContext =
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -235,16 +235,20 @@ struct CSSPixel {
 
   static CSSIntRect FromAppUnitsRounded(const nsRect& aRect) {
     return CSSIntRect(NSAppUnitsToIntPixels(aRect.x, float(AppUnitsPerCSSPixel())),
                       NSAppUnitsToIntPixels(aRect.y, float(AppUnitsPerCSSPixel())),
                       NSAppUnitsToIntPixels(aRect.width, float(AppUnitsPerCSSPixel())),
                       NSAppUnitsToIntPixels(aRect.height, float(AppUnitsPerCSSPixel())));
   }
 
+  static CSSIntRect FromAppUnitsToNearest(const nsRect& aRect) {
+    return CSSIntRect::FromUnknownRect(aRect.ToNearestPixels(AppUnitsPerCSSPixel()));
+  }
+
   // Conversions to app units
 
   static nscoord ToAppUnits(CSSCoord aCoord) {
     return NSToCoordRoundWithClamp(aCoord * float(AppUnitsPerCSSPixel()));
   }
 
   static nsPoint ToAppUnits(const CSSPoint& aPoint) {
     return nsPoint(NSToCoordRoundWithClamp(aPoint.x * float(AppUnitsPerCSSPixel())),
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -591,17 +591,18 @@ GetMinAndMaxScaleForAnimationProperty(co
       }
 
       // We need to factor in the scale of the base style if the base style
       // will be used on the compositor.
       StyleAnimationValue baseStyle = effect->BaseStyle(prop.mProperty);
       if (!baseStyle.IsNull()) {
         // FIXME: Bug 1334036: We need to get the baseStyle for
         //        RawServoAnimationValue.
-        UpdateMinMaxScale(aFrame, { baseStyle, nullptr }, aMinScale, aMaxScale);
+        UpdateMinMaxScale(aFrame, AnimationValue(baseStyle),
+                          aMinScale, aMaxScale);
       }
 
       for (const AnimationPropertySegment& segment : prop.mSegments) {
         // In case of add or accumulate composite, StyleAnimationValue does
         // not have a valid value.
         if (segment.HasReplaceableFromValue()) {
           UpdateMinMaxScale(aFrame, segment.mFromValue, aMinScale, aMaxScale);
         }
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -51,16 +51,17 @@
 #include "TextOverflow.h"
 #include "nsIFrameInlines.h"
 #include "CounterStyleManager.h"
 #include "nsISelection.h"
 #include "mozilla/dom/HTMLDetailsElement.h"
 #include "mozilla/dom/HTMLSummaryElement.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/Telemetry.h"
 
 #include "nsBidiPresUtils.h"
 
 #include <inttypes.h>
 
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
 static const char16_t kDiscCharacter = 0x2022;
@@ -1507,16 +1508,63 @@ nsBlockFrame::Reflow(nsPresContext*     
     char buf[400];
     SprintfLiteral(buf,
                    ": %" PRId64 " elapsed (%" PRId64 " per line) (%d lines; %d new lines)",
                    delta, perLineDelta, numLines, ectc - ctc);
     printf("%s\n", buf);
   }
 #endif
 
+#ifdef EARLY_BETA_OR_EARLIER
+  // Bug 1358299 START: Remove this code after the 56 merge date.
+  static bool sIsTelemetryEnabled;
+  static bool sTelemetryPrefCached = false;
+
+  if (!sTelemetryPrefCached) {
+    sTelemetryPrefCached = true;
+    Preferences::AddBoolVarCache(&sIsTelemetryEnabled,
+                                 "toolkit.telemetry.enabled");
+  }
+
+  if (sIsTelemetryEnabled) {
+    // Collect data for the BOX_ALIGN_PROPS_IN_BLOCKS_FLAG probe.
+    auto IsStyleNormalOrAuto = [](uint16_t value)->bool {
+      return ((value == NS_STYLE_ALIGN_NORMAL) ||
+              (value == NS_STYLE_ALIGN_AUTO));
+    };
+
+    // First check this frame for non-default values of the css-align properties
+    // that apply to block containers.
+    // Note: we check here for non-default "justify-items", though technically
+    // that'd only affect rendering if some child has "justify-self:auto".
+    // (It's safe to assume that's likely, since it's the default value that
+    // a child would have.) We also pass in nullptr for the parent style context
+    // because an accurate parameter is slower and only necessary to detect a
+    // narrow edge case with the "legacy" keyword.
+    const nsStylePosition* stylePosition = reflowInput->mStylePosition;
+    if (!IsStyleNormalOrAuto(stylePosition->mJustifyContent) ||
+        !IsStyleNormalOrAuto(stylePosition->mAlignContent) ||
+        !IsStyleNormalOrAuto(stylePosition->ComputedJustifyItems(nullptr))) {
+      Telemetry::Accumulate(Telemetry::BOX_ALIGN_PROPS_IN_BLOCKS_FLAG, true);
+    } else {
+      // If not already flagged by the parent, now check justify-self of the
+      // block-level child frames.
+      for (nsBlockFrame::LineIterator line = LinesBegin();
+           line != LinesEnd(); ++line) {
+        if (line->IsBlock() &&
+            !IsStyleNormalOrAuto(line->mFirstChild->StylePosition()->mJustifySelf)) {
+          Telemetry::Accumulate(Telemetry::BOX_ALIGN_PROPS_IN_BLOCKS_FLAG, true);
+          break;
+        }
+      }
+    }
+  }
+  // Bug 1358299 END
+#endif
+
   NS_FRAME_SET_TRUNCATION(aStatus, (*reflowInput), aMetrics);
 }
 
 bool
 nsBlockFrame::CheckForCollapsedBEndMarginFromClearanceLine()
 {
   LineIterator begin = LinesBegin();
   LineIterator line = LinesEnd();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -6093,19 +6093,19 @@ nsIFrame::GetOffsetToCrossDoc(const nsIF
     // aOther.
     nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
     offset -= negOffset;
   }
 
   return offset;
 }
 
-nsIntRect nsIFrame::GetScreenRect() const
-{
-  return GetScreenRectInAppUnits().ToNearestPixels(PresContext()->AppUnitsPerCSSPixel());
+CSSIntRect nsIFrame::GetScreenRect() const
+{
+  return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
 }
 
 nsRect nsIFrame::GetScreenRectInAppUnits() const
 {
   nsPresContext* presContext = PresContext();
   nsIFrame* rootFrame =
     presContext->PresShell()->FrameManager()->GetRootFrame();
   nsPoint rootScreenPos(0, 0);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2583,20 +2583,22 @@ public:
 
   /**
    * Like GetOffsetToCrossDoc, but the caller can specify which appunits
    * to return the result in.
    */
   nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const;
 
   /**
-   * Get the screen rect of the frame in pixels.
-   * @return the pixel rect of the frame in screen coordinates.
+   * Get the rect of the frame relative to the top-left corner of the
+   * screen in CSS pixels.
+   * @return the CSS pixel rect of the frame relative to the top-left
+   *         corner of the screen.
    */
-  nsIntRect GetScreenRect() const;
+  mozilla::CSSIntRect GetScreenRect() const;
 
   /**
    * Get the screen rect of the frame in app units.
    * @return the app unit rect of the frame in screen coordinates.
    */
   nsRect GetScreenRectInAppUnits() const;
 
   /**
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -815,34 +815,38 @@ nsCSSRendering::PaintBorderWithStyleBord
     nsITheme *theme = aPresContext->GetTheme();
     if (theme &&
         theme->ThemeSupportsWidget(aPresContext, aForFrame,
                                    displayData->UsedAppearance())) {
       return DrawResult::SUCCESS; // Let the theme handle it.
     }
   }
 
-  if (aStyleBorder.IsBorderImageLoaded()) {
-    DrawResult result;
+  if (!aStyleBorder.mBorderImageSource.IsEmpty()) {
+    DrawResult result = DrawResult::SUCCESS;
 
     uint32_t irFlags = 0;
     if (aFlags & PaintBorderFlags::SYNC_DECODE_IMAGES) {
       irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
     }
 
+    // Creating the border image renderer will request a decode, and we rely on
+    // that happening.
     Maybe<nsCSSBorderImageRenderer> renderer =
       nsCSSBorderImageRenderer::CreateBorderImageRenderer(aPresContext, aForFrame, aBorderArea,
                                                           aStyleBorder, aDirtyRect, aSkipSides,
                                                           irFlags, &result);
-    if (!renderer) {
-      return result;
+    if (aStyleBorder.IsBorderImageLoaded()) {
+      if (!renderer) {
+        return result;
+      }
+
+      return renderer->DrawBorderImage(aPresContext, aRenderingContext,
+                                       aForFrame, aDirtyRect);
     }
-
-    return renderer->DrawBorderImage(aPresContext, aRenderingContext,
-                                     aForFrame, aDirtyRect);
   }
 
   DrawResult result = DrawResult::SUCCESS;
 
   // If we had a border-image, but it wasn't loaded, then we should return
   // DrawResult::NOT_READY; we'll want to try again if we do a paint with sync
   // decoding enabled.
   if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3581,18 +3581,16 @@ nsCSSBorderImageRenderer::CreateBorderIm
                                                     nsIFrame* aForFrame,
                                                     const nsRect& aBorderArea,
                                                     const nsStyleBorder& aStyleBorder,
                                                     const nsRect& aDirtyRect,
                                                     Sides aSkipSides,
                                                     uint32_t aFlags,
                                                     DrawResult* aDrawResult)
 {
-  NS_PRECONDITION(aStyleBorder.IsBorderImageLoaded(),
-                  "drawing border image that isn't successfully loaded");
   MOZ_ASSERT(aDrawResult);
 
   if (aDirtyRect.IsEmpty()) {
     *aDrawResult = DrawResult::SUCCESS;
     return Nothing();
   }
 
   nsImageRenderer imgRenderer(aForFrame, &aStyleBorder.mBorderImageSource, aFlags);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -557,17 +557,17 @@ AddAnimationForProperty(nsIFrame* aFrame
   // to the compositor.
 
   StyleAnimationValue baseStyle =
     aAnimation->GetEffect()->AsKeyframeEffect()->BaseStyle(aProperty.mProperty);
   if (!baseStyle.IsNull()) {
     // FIXME: Bug 1334036: We need to get the baseValue for
     //        RawServoAnimationValue.
     SetAnimatable(aProperty.mProperty,
-                  { baseStyle, nullptr },
+                  AnimationValue(baseStyle),
                   aFrame, refBox,
                   animation->baseStyle());
   } else {
     animation->baseStyle() = null_t();
   }
 
   for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
     const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
--- a/layout/reftests/border-image/reftest-stylo.list
+++ b/layout/reftests/border-image/reftest-stylo.list
@@ -1,51 +1,51 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
-fails == solid-image-1.html solid-image-1.html
-fails == solid-image-1a.html solid-image-1a.html
-fails == solid-image-2.html solid-image-2.html
-fails == solid-image-2a.html solid-image-2a.html
-fails == multicolor-image-1.html multicolor-image-1.html
+== solid-image-1.html solid-image-1.html
+== solid-image-1a.html solid-image-1a.html
+== solid-image-2.html solid-image-2.html
+== solid-image-2a.html solid-image-2a.html
+== multicolor-image-1.html multicolor-image-1.html
 # This is fuzzy temporarily until bug 1044702 makes it possible to use source
 # clipping on Windows. (Any other fix would have a significant perf cost.)
-fails == multicolor-image-2.html multicolor-image-2.html
-fails == multicolor-image-3.html multicolor-image-3.html
-fails == multicolor-image-4.html multicolor-image-4.html
-fails == multicolor-image-5.html multicolor-image-5.html
-fails == transparent-image-1.html transparent-image-1.html
-fails == repeat-image-1.html repeat-image-1.html
-fails == 470250-1.html 470250-1.html
-fails == 470250-2.html 470250-2.html
-fails == different-h-v-1.html different-h-v-1.html # Bug 1341703
-fails == different-h-v-2.html different-h-v-2.html
-fails == different-h-v-1.html different-h-v-1.html # Bug 1341703
-fails == center-scaling-1.html center-scaling-1.html
-fails == center-scaling-2.html center-scaling-2.html
-fails == center-scaling-3.html center-scaling-3.html
-fails == center-scaling-4t.html center-scaling-4t.html
-fails == center-scaling-4r.html center-scaling-4r.html
-fails == center-scaling-4b.html center-scaling-4b.html
-fails == center-scaling-4l.html center-scaling-4l.html
-fails == center-scaling-4tb.html center-scaling-4tb.html
-fails == center-scaling-4lr.html center-scaling-4lr.html
-fails == side-scaling-1h.html side-scaling-1h.html
-fails == side-scaling-1v.html side-scaling-1v.html
-fails == border-image-width-1a.html border-image-width-1a.html
-fails == border-image-width-1b.html border-image-width-1b.html
-fails == border-image-width-1c.html border-image-width-1c.html
-fails == border-image-width-large.html border-image-width-large.html
-fails == border-image-outset-1a.html border-image-outset-1a.html
-fails == border-image-outset-1b.html border-image-outset-1b.html
-fails == border-image-outset-1c.html border-image-outset-1c.html
-fails == border-image-nofill-1.html border-image-nofill-1.html
-fails == border-image-outset-resize-1.html border-image-outset-resize-1.html
-fails == border-image-outset-move-1.html border-image-outset-move-1.html
-fails == border-image-style-none.html border-image-style-none.html
-fails == border-image-style-none-length.html border-image-style-none-length.html
-fails == border-image-style-none-auto.html border-image-style-none-auto.html
+== multicolor-image-2.html multicolor-image-2.html
+== multicolor-image-3.html multicolor-image-3.html
+== multicolor-image-4.html multicolor-image-4.html
+== multicolor-image-5.html multicolor-image-5.html
+== transparent-image-1.html transparent-image-1.html
+== repeat-image-1.html repeat-image-1.html
+== 470250-1.html 470250-1.html
+== 470250-2.html 470250-2.html
+== different-h-v-1.html different-h-v-1.html # Bug 1341703
+== different-h-v-2.html different-h-v-2.html
+== different-h-v-1.html different-h-v-1.html # Bug 1341703
+== center-scaling-1.html center-scaling-1.html
+== center-scaling-2.html center-scaling-2.html
+== center-scaling-3.html center-scaling-3.html
+== center-scaling-4t.html center-scaling-4t.html
+== center-scaling-4r.html center-scaling-4r.html
+== center-scaling-4b.html center-scaling-4b.html
+== center-scaling-4l.html center-scaling-4l.html
+== center-scaling-4tb.html center-scaling-4tb.html
+== center-scaling-4lr.html center-scaling-4lr.html
+== side-scaling-1h.html side-scaling-1h.html
+== side-scaling-1v.html side-scaling-1v.html
+== border-image-width-1a.html border-image-width-1a.html
+== border-image-width-1b.html border-image-width-1b.html
+== border-image-width-1c.html border-image-width-1c.html
+skip-if(stylo) == border-image-width-large.html border-image-width-large.html # Bug 1357060
+== border-image-outset-1a.html border-image-outset-1a.html
+== border-image-outset-1b.html border-image-outset-1b.html
+== border-image-outset-1c.html border-image-outset-1c.html
+== border-image-nofill-1.html border-image-nofill-1.html
+== border-image-outset-resize-1.html border-image-outset-resize-1.html
+== border-image-outset-move-1.html border-image-outset-move-1.html
+== border-image-style-none.html border-image-style-none.html
+== border-image-style-none-length.html border-image-style-none-length.html
+== border-image-style-none-auto.html border-image-style-none-auto.html
 
 # border images with gradients
 == border-image-linear-gradient.html border-image-linear-gradient.html
 == border-image-linear-gradient-slice-1.html border-image-linear-gradient-slice-1.html
 == border-image-linear-gradient-slice-2.html border-image-linear-gradient-slice-2.html
 == border-image-linear-gradient-slice-fill-1.html border-image-linear-gradient-slice-fill-1.html
 == border-image-linear-gradient-slice-fill-2.html border-image-linear-gradient-slice-fill-2.html
 fuzzy(1,48) fuzzy-if(OSX,5,1676) == border-image-linear-gradient-width.html border-image-linear-gradient-width.html
@@ -80,14 +80,14 @@ fuzzy(1,602) == border-image-repeating-r
 fuzzy(3,18000) fails-if(OSX) fuzzy-if(skiaContent,4,16462) == border-image-repeating-radial-gradient-slice-width.html border-image-repeating-radial-gradient-slice-width.html
 == border-image-repeating-radial-gradient-repeat-repeat-2.html border-image-repeating-radial-gradient-repeat-repeat-2.html
 == border-image-repeating-radial-gradient-repeat-round-2.html border-image-repeating-radial-gradient-repeat-round-2.html
 
 # border-image-source (-moz-)element
 == border-image-element.html border-image-element.html
 
 # svg-as-border-image
-fails == svg-as-border-image-1a.html svg-as-border-image-1a.html
-fails == svg-as-border-image-1b.html svg-as-border-image-1b.html
-fails == svg-as-border-image-1c.html svg-as-border-image-1c.html
-fails == svg-as-border-image-2.html svg-as-border-image-2.html
-fails == svg-as-border-image-3.html svg-as-border-image-3.html
-fails == svg-as-border-image-4.html svg-as-border-image-4.html
+== svg-as-border-image-1a.html svg-as-border-image-1a.html
+== svg-as-border-image-1b.html svg-as-border-image-1b.html
+== svg-as-border-image-1c.html svg-as-border-image-1c.html
+== svg-as-border-image-2.html svg-as-border-image-2.html
+== svg-as-border-image-3.html svg-as-border-image-3.html
+== svg-as-border-image-4.html svg-as-border-image-4.html
--- a/layout/reftests/css-break/reftest-stylo.list
+++ b/layout/reftests/css-break/reftest-stylo.list
@@ -1,13 +1,13 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 default-preferences pref(layout.css.box-decoration-break.enabled,true)
 
 == box-decoration-break-1.html box-decoration-break-1.html
 fuzzy-if(stylo,1,312) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1.html
 == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1.html
-fails == box-decoration-break-border-image.html box-decoration-break-border-image.html
+== box-decoration-break-border-image.html box-decoration-break-border-image.html
 == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding.html
 == box-decoration-break-block-margin.html box-decoration-break-block-margin.html
-fails == box-decoration-break-first-letter.html box-decoration-break-first-letter.html
+skip-if(stylo) == box-decoration-break-first-letter.html box-decoration-break-first-letter.html
 == box-decoration-break-with-bidi.html box-decoration-break-with-bidi.html
 == box-decoration-break-bug-1235152.html box-decoration-break-bug-1235152.html
 == box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913.html
--- a/layout/reftests/forms/fieldset/reftest-stylo.list
+++ b/layout/reftests/forms/fieldset/reftest-stylo.list
@@ -17,13 +17,13 @@ fails == overflow-hidden.html overflow-h
 == legend-rtl.html legend-rtl.html # Bug 1340696
 fails == fieldset-grid-001.html fieldset-grid-001.html
 fails == fieldset-flexbox-001.html fieldset-flexbox-001.html
 == fieldset-min-width-1a.html fieldset-min-width-1a.html
 == fieldset-min-width-1b.html fieldset-min-width-1b.html
 == fieldset-min-width-2a.html fieldset-min-width-2a.html
 == fieldset-min-width-2b.html fieldset-min-width-2b.html
 == legend-overlapping-right-border-1.html legend-overlapping-right-border-1.html # Bug 1340696
-fails == fieldset-border-image-1a.html fieldset-border-image-1a.html
-fails == fieldset-border-image-1b.html fieldset-border-image-1b.html
-fails == fieldset-border-image-2a.html fieldset-border-image-2a.html
-fails == fieldset-border-image-2b.html fieldset-border-image-2b.html
+== fieldset-border-image-1a.html fieldset-border-image-1a.html
+== fieldset-border-image-1b.html fieldset-border-image-1b.html
+== fieldset-border-image-2a.html fieldset-border-image-2a.html
+== fieldset-border-image-2b.html fieldset-border-image-2b.html
 == dynamic-text-indent.html dynamic-text-indent.html
--- a/layout/reftests/image/reftest-stylo.list
+++ b/layout/reftests/image/reftest-stylo.list
@@ -74,18 +74,18 @@ fuzzy(1,1) == image-orientation-explicit
 == image-orientation-generated-content.html?0&flip image-orientation-generated-content.html?0&flip
 == image-orientation-generated-content.html?90&flip image-orientation-generated-content.html?90&flip
 == image-orientation-generated-content.html?180&flip image-orientation-generated-content.html?180&flip
 == image-orientation-generated-content.html?270&flip image-orientation-generated-content.html?270&flip
 
 # Tests that image-orientation does not apply to decorative images:
 == image-orientation-background.html?from-image image-orientation-background.html?from-image
 == image-orientation-background.html?90&flip image-orientation-background.html?90&flip
-fails == image-orientation-border-image.html?from-image image-orientation-border-image.html?from-image
-fails == image-orientation-border-image.html?90&flip image-orientation-border-image.html?90&flip
+== image-orientation-border-image.html?from-image image-orientation-border-image.html?from-image
+== image-orientation-border-image.html?90&flip image-orientation-border-image.html?90&flip
 == image-orientation-list-style-image.html?from-image image-orientation-list-style-image.html?from-image
 == image-orientation-list-style-image.html?90&flip image-orientation-list-style-image.html?90&flip
 
 # Sanity checks for the image-orientation tests. Ensures that the various
 # combinations of rotations and flips actually look different from each other.
 == image-orientation-ref.html?0 image-orientation-ref.html?0
 == image-orientation-ref.html?0 image-orientation-ref.html?0
 == image-orientation-ref.html?0 image-orientation-ref.html?0
--- a/layout/reftests/pixel-rounding/reftest-stylo.list
+++ b/layout/reftests/pixel-rounding/reftest-stylo.list
@@ -169,16 +169,16 @@ fuzzy-if(skiaContent,1,145) == rounded-b
 fuzzy-if(skiaContent,1,97) == rounded-background-color-height-top-5.html rounded-background-color-height-top-5.html
 fuzzy-if(skiaContent,1,113) == rounded-background-color-height-top-6.html rounded-background-color-height-top-6.html
 fuzzy-if(skiaContent,1,225) == rounded-background-color-width-left-4.html rounded-background-color-width-left-4.html
 fuzzy-if(skiaContent,1,145) == rounded-background-color-width-left-5.html rounded-background-color-width-left-5.html
 fuzzy-if(skiaContent,1,145) == rounded-background-color-width-left-6.html rounded-background-color-width-left-6.html
 
 == background-image-tiling.html background-image-tiling.html
 
-fails == border-image-width-0.html border-image-width-0.html
-fails == border-image-width-4.html border-image-width-4.html
-fails == border-image-width-9.html border-image-width-9.html
+== border-image-width-0.html border-image-width-0.html
+== border-image-width-4.html border-image-width-4.html
+== border-image-width-9.html border-image-width-9.html
 
 fails == iframe-1.html iframe-1.html # Bug 1341769
 
 fails == viewport-units-rounding-1.html viewport-units-rounding-1.html
 == viewport-units-rounding-2.html viewport-units-rounding-2.html
--- a/layout/reftests/svg/as-image/reftest-stylo.list
+++ b/layout/reftests/svg/as-image/reftest-stylo.list
@@ -26,18 +26,18 @@ include zoom/reftest-stylo.list
 == background-scale-with-viewbox-1.html background-scale-with-viewbox-1.html
 
 # Tests with -moz-image-rect()
 == background-image-rect-1svg.html background-image-rect-1svg.html
 == background-image-rect-1png.html background-image-rect-1png.html
 == background-image-rect-2.html background-image-rect-2.html
 
 # Test for border-image
-fails == border-image-simple-1.html border-image-simple-1.html
-fails == border-image-simple-2.html border-image-simple-2.html
+== border-image-simple-1.html border-image-simple-1.html
+== border-image-simple-2.html border-image-simple-2.html
 
 # Test for canvas "drawImage" method
 == canvas-drawImage-simple-1a.html canvas-drawImage-simple-1a.html
 == canvas-drawImage-simple-1b.html canvas-drawImage-simple-1b.html
 
 == canvas-drawImage-scale-1a.html canvas-drawImage-scale-1a.html
 == canvas-drawImage-scale-1b.html canvas-drawImage-scale-1b.html
 == canvas-drawImage-scale-1c.html canvas-drawImage-scale-1c.html
--- a/layout/reftests/svg/smil/container/reftest-stylo.list
+++ b/layout/reftests/svg/smil/container/reftest-stylo.list
@@ -1,19 +1,19 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 # Tests related to SVG Animation (using SMIL), focusing on animation-sorting
 # to see which animation takes precedence (out of multiple animations on the
 # same attribute)
 
 random == enveloped-tree-1.xhtml enveloped-tree-1.xhtml
-fails == promoted-tree-1.xhtml promoted-tree-1.xhtml
+== promoted-tree-1.xhtml promoted-tree-1.xhtml
 random == moved-tree-1.xhtml moved-tree-1.xhtml
 fails == deferred-anim-1.xhtml deferred-anim-1.xhtml
 fails == deferred-tree-1.xhtml deferred-tree-1.xhtml
 == deferred-tree-2a.xhtml deferred-tree-2a.xhtml
 == deferred-tree-2b.xhtml deferred-tree-2b.xhtml
-fails == deferred-tree-3a.xhtml deferred-tree-3a.xhtml
-fails == deferred-tree-3b.xhtml deferred-tree-3b.xhtml
-fails == deferred-tree-3c.xhtml deferred-tree-3c.xhtml
-fails == deferred-tree-3d.xhtml deferred-tree-3d.xhtml
+== deferred-tree-3a.xhtml deferred-tree-3a.xhtml
+== deferred-tree-3b.xhtml deferred-tree-3b.xhtml
+== deferred-tree-3c.xhtml deferred-tree-3c.xhtml
+== deferred-tree-3d.xhtml deferred-tree-3d.xhtml
 # this will occasionally fail until we correctly clear animation effects from
 # no-longer-targeted elements
 random == invalid-elem-1.xhtml invalid-elem-1.xhtml
--- a/layout/reftests/svg/smil/seek/reftest-stylo.list
+++ b/layout/reftests/svg/smil/seek/reftest-stylo.list
@@ -10,9 +10,9 @@
 == anim-x-seek-dynamic-1c.svg anim-x-seek-dynamic-1c.svg
 == anim-x-seek-dynamic-1d.svg anim-x-seek-dynamic-1d.svg
 == anim-x-seek-dynamic-1e.svg anim-x-seek-dynamic-1e.svg
 == anim-x-seek-dynamic-1f.svg anim-x-seek-dynamic-1f.svg
 == anim-x-seek-dynamic-1g.svg anim-x-seek-dynamic-1g.svg
 == anim-x-seek-dynamic-1h.svg anim-x-seek-dynamic-1h.svg
 == anim-x-seek-dynamic-1i.svg anim-x-seek-dynamic-1i.svg
 == anim-x-seek-negative-1a.svg anim-x-seek-negative-1a.svg
-fails == anim-x-seek-cross-container-1a.xhtml anim-x-seek-cross-container-1a.xhtml
+== anim-x-seek-cross-container-1a.xhtml anim-x-seek-cross-container-1a.xhtml
--- a/layout/reftests/w3c-css/submitted/background/reftest-stylo.list
+++ b/layout/reftests/w3c-css/submitted/background/reftest-stylo.list
@@ -17,23 +17,23 @@
 == background-repeat-round-1c.html background-repeat-round-1c.html
 == background-repeat-round-1d.html background-repeat-round-1d.html
 == background-repeat-round-1e.html background-repeat-round-1e.html
 == background-repeat-round-2.html background-repeat-round-2.html
 == background-repeat-round-3.html background-repeat-round-3.html
 == background-repeat-round-4.html background-repeat-round-4.html
 
 #border-image test cases
-fails == border-image-repeat-round-1.html border-image-repeat-round-1.html
-fails == border-image-repeat-round-2.html border-image-repeat-round-2.html
-fails == border-image-repeat-space-1.html border-image-repeat-space-1.html
-fails == border-image-repeat-space-2.html border-image-repeat-space-2.html
-fails == border-image-repeat-space-3.html border-image-repeat-space-3.html
-fails == border-image-repeat-space-4.html border-image-repeat-space-4.html
+== border-image-repeat-round-1.html border-image-repeat-round-1.html
+== border-image-repeat-round-2.html border-image-repeat-round-2.html
+== border-image-repeat-space-1.html border-image-repeat-space-1.html
+== border-image-repeat-space-2.html border-image-repeat-space-2.html
+== border-image-repeat-space-3.html border-image-repeat-space-3.html
+== border-image-repeat-space-4.html border-image-repeat-space-4.html
 == border-image-repeat-space-4-ref-1.html border-image-repeat-space-4-ref-1.html
-fails == border-image-repeat-space-5.html border-image-repeat-space-5.html
+== border-image-repeat-space-5.html border-image-repeat-space-5.html
 == border-image-repeat-space-5-ref-1.html border-image-repeat-space-5-ref-1.html
-fails == border-image-repeat-space-6.html border-image-repeat-space-6.html
-fails == border-image-repeat-space-7.html border-image-repeat-space-7.html
-fails == border-image-repeat-1.html border-image-repeat-1.html
+== border-image-repeat-space-6.html border-image-repeat-space-6.html
+== border-image-repeat-space-7.html border-image-repeat-space-7.html
+== border-image-repeat-1.html border-image-repeat-1.html
 
 # background-attachment test cases
 fails == background-attachment-fixed-inside-transform-1.html background-attachment-fixed-inside-transform-1.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -46,16 +46,17 @@
 #include "mozilla/EventStates.h"
 #include "mozilla/Keyframe.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/ServoElementSnapshot.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/ServoMediaList.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/dom/HTMLTableCellElement.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/URLExtraData.h"
 
 using namespace mozilla;
@@ -282,37 +283,45 @@ Gecko_SetOwnerDocumentNeedsStyleFlush(Ra
 
   if (nsIPresShell* shell = aElement->OwnerDoc()->GetShell()) {
     shell->SetNeedStyleFlush();
     shell->ObserveStyleFlushes();
   }
 }
 
 nsStyleContext*
-Gecko_GetStyleContext(RawGeckoNodeBorrowed aNode, nsIAtom* aPseudoTagOrNull)
+Gecko_GetStyleContext(RawGeckoElementBorrowed aElement,
+                      nsIAtom* aPseudoTagOrNull)
 {
-  MOZ_ASSERT(aNode->IsContent());
   nsIFrame* relevantFrame =
-    ServoRestyleManager::FrameForPseudoElement(aNode->AsContent(),
-                                               aPseudoTagOrNull);
+    ServoRestyleManager::FrameForPseudoElement(aElement, aPseudoTagOrNull);
   if (relevantFrame) {
     return relevantFrame->StyleContext();
   }
 
   if (aPseudoTagOrNull) {
     return nullptr;
   }
 
   // FIXME(emilio): Is there a shorter path?
   nsCSSFrameConstructor* fc =
-    aNode->OwnerDoc()->GetShell()->GetPresContext()->FrameConstructor();
+    aElement->OwnerDoc()->GetShell()->GetPresContext()->FrameConstructor();
 
   // NB: This is only called for CalcStyleDifference, and we handle correctly
   // the display: none case since Servo still has the older style.
-  return fc->GetDisplayContentsStyleFor(aNode->AsContent());
+  return fc->GetDisplayContentsStyleFor(aElement);
+}
+
+nsIAtom*
+Gecko_GetImplementedPseudo(RawGeckoElementBorrowed aElement)
+{
+  CSSPseudoElementType pseudo = aElement->GetPseudoElementType();
+  if (pseudo == CSSPseudoElementType::NotPseudo)
+    return nullptr;
+  return nsCSSPseudoElements::GetPseudoAtom(pseudo);
 }
 
 nsChangeHint
 Gecko_CalcStyleDifference(nsStyleContext* aOldStyleContext,
                           ServoComputedValuesBorrowed aComputedValues)
 {
   MOZ_ASSERT(aOldStyleContext);
   MOZ_ASSERT(aComputedValues);
@@ -370,16 +379,38 @@ Gecko_GetStyleAttrDeclarationBlock(RawGe
     //     document into a Servo-style-backend document.  See bug 1330051.
     NS_WARNING("stylo: requesting a Gecko declaration block?");
     return nullptr;
   }
   return decl->AsServo()->RefRawStrong();
 }
 
 RawServoDeclarationBlockStrongBorrowedOrNull
+Gecko_GetSMILOverrideDeclarationBlock(RawGeckoElementBorrowed aElement)
+{
+  // This function duplicates a lot of the code in
+  // Gecko_GetStyleAttrDeclarationBlock above because I haven't worked out a way
+  // to persuade hazard analysis that a pointer-to-lambda is ok yet.
+  MOZ_ASSERT(aElement, "Invalid GeckoElement");
+
+  DeclarationBlock* decl =
+    const_cast<dom::Element*>(aElement)->GetSMILOverrideStyleDeclaration();
+  if (!decl) {
+    return nullptr;
+  }
+  if (decl->IsGecko()) {
+    // XXX This can happen when nodes are adopted from a Gecko-style-backend
+    //     document into a Servo-style-backend document.  See bug 1330051.
+    NS_WARNING("stylo: requesting a Gecko declaration block?");
+    return nullptr;
+  }
+  return decl->AsServo()->RefRawStrong();
+}
+
+RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetHTMLPresentationAttrDeclarationBlock(RawGeckoElementBorrowed aElement)
 {
   static_assert(sizeof(RefPtr<RawServoDeclarationBlock>) ==
                 sizeof(RawServoDeclarationBlockStrong),
                 "RefPtr should just be a pointer");
   const nsMappedAttributes* attrs = aElement->GetMappedAttributes();
   if (!attrs) {
     auto* svg = nsSVGElement::FromContentOrNull(aElement);
@@ -407,79 +438,90 @@ Gecko_GetExtraContentStyleDeclarations(R
   const HTMLTableCellElement* cell = static_cast<const HTMLTableCellElement*>(aElement);
   if (nsMappedAttributes* attrs = cell->GetMappedAttributesInheritedFromTable()) {
     const RefPtr<RawServoDeclarationBlock>& servo = attrs->GetServoStyle();
     return reinterpret_cast<const RawServoDeclarationBlockStrong*>(&servo);
   }
   return nullptr;
 }
 
+static nsIAtom*
+PseudoTagAndCorrectElementForAnimation(const Element*& aElementOrPseudo) {
+  if (aElementOrPseudo->IsGeneratedContentContainerForBefore()) {
+    aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
+    return nsCSSPseudoElements::before;
+  }
+
+  if (aElementOrPseudo->IsGeneratedContentContainerForAfter()) {
+    aElementOrPseudo = aElementOrPseudo->GetParent()->AsElement();
+    return nsCSSPseudoElements::after;
+  }
+
+  return nullptr;
+}
+
 bool
 Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
-                       nsIAtom* aPseudoTag,
                        EffectCompositor::CascadeLevel aCascadeLevel,
                        RawServoAnimationValueMapBorrowed aAnimationValues)
 {
-  MOZ_ASSERT(aElement, "Invalid GeckoElement");
-  MOZ_ASSERT(!aPseudoTag ||
-             aPseudoTag == nsCSSPseudoElements::before ||
-             aPseudoTag == nsCSSPseudoElements::after);
+  MOZ_ASSERT(aElement);
 
   nsIDocument* doc = aElement->GetComposedDoc();
   if (!doc || !doc->GetShell()) {
     return false;
   }
   nsPresContext* presContext = doc->GetShell()->GetPresContext();
   if (!presContext || !presContext->IsDynamic()) {
     // For print or print preview, ignore animations.
     return false;
   }
 
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
+
   CSSPseudoElementType pseudoType =
     nsCSSPseudoElements::GetPseudoType(
-      aPseudoTag,
+      pseudoTag,
       nsCSSProps::EnabledState::eIgnoreEnabledState);
 
   return presContext->EffectCompositor()
-    ->GetServoAnimationRule(aElement, pseudoType,
+    ->GetServoAnimationRule(aElement,
+                            pseudoType,
                             aCascadeLevel,
                             aAnimationValues);
 }
 
 bool
 Gecko_StyleAnimationsEquals(RawGeckoStyleAnimationListBorrowed aA,
                             RawGeckoStyleAnimationListBorrowed aB)
 {
   return *aA == *aB;
 }
 
 void
 Gecko_UpdateAnimations(RawGeckoElementBorrowed aElement,
-                       nsIAtom* aPseudoTagOrNull,
                        ServoComputedValuesBorrowedOrNull aOldComputedValues,
                        ServoComputedValuesBorrowedOrNull aComputedValues,
                        ServoComputedValuesBorrowedOrNull aParentComputedValues,
                        UpdateAnimationsTasks aTasks)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aElement);
-  MOZ_ASSERT(!aPseudoTagOrNull ||
-             aPseudoTagOrNull == nsCSSPseudoElements::before ||
-             aPseudoTagOrNull == nsCSSPseudoElements::after);
 
   nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement);
   if (!presContext) {
     return;
   }
 
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   if (presContext->IsDynamic() && aElement->IsInComposedDoc()) {
     const ServoComputedValuesWithParent servoValues =
       { aComputedValues, aParentComputedValues };
     CSSPseudoElementType pseudoType =
-      nsCSSPseudoElements::GetPseudoType(aPseudoTagOrNull,
+      nsCSSPseudoElements::GetPseudoType(pseudoTag,
                                          CSSEnabledState::eForAllContent);
 
     if (aTasks & UpdateAnimationsTasks::CSSAnimations) {
       presContext->AnimationManager()->
         UpdateAnimations(const_cast<dom::Element*>(aElement), pseudoType,
                          servoValues);
     }
 
@@ -504,99 +546,89 @@ Gecko_UpdateAnimations(RawGeckoElementBo
     if (aTasks & UpdateAnimationsTasks::EffectProperties) {
       presContext->EffectCompositor()->UpdateEffectProperties(
         servoValues, const_cast<dom::Element*>(aElement), pseudoType);
     }
   }
 }
 
 bool
-Gecko_ElementHasAnimations(RawGeckoElementBorrowed aElement,
-                           nsIAtom* aPseudoTagOrNull)
+Gecko_ElementHasAnimations(RawGeckoElementBorrowed aElement)
 {
-  MOZ_ASSERT(!aPseudoTagOrNull ||
-             aPseudoTagOrNull == nsCSSPseudoElements::before ||
-             aPseudoTagOrNull == nsCSSPseudoElements::after);
-
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   CSSPseudoElementType pseudoType =
-    nsCSSPseudoElements::GetPseudoType(aPseudoTagOrNull,
+    nsCSSPseudoElements::GetPseudoType(pseudoTag,
                                        CSSEnabledState::eForAllContent);
 
   return !!EffectSet::GetEffectSet(aElement, pseudoType);
 }
 
 bool
-Gecko_ElementHasCSSAnimations(RawGeckoElementBorrowed aElement,
-                              nsIAtom* aPseudoTagOrNull)
+Gecko_ElementHasCSSAnimations(RawGeckoElementBorrowed aElement)
 {
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   nsAnimationManager::CSSAnimationCollection* collection =
     nsAnimationManager::CSSAnimationCollection
-                      ::GetAnimationCollection(aElement, aPseudoTagOrNull);
+                      ::GetAnimationCollection(aElement, pseudoTag);
 
   return collection && !collection->mAnimations.IsEmpty();
 }
 
 bool
-Gecko_ElementHasCSSTransitions(RawGeckoElementBorrowed aElement,
-                               nsIAtom* aPseudoTagOrNull)
+Gecko_ElementHasCSSTransitions(RawGeckoElementBorrowed aElement)
 {
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   nsTransitionManager::CSSTransitionCollection* collection =
     nsTransitionManager::CSSTransitionCollection
-                       ::GetAnimationCollection(aElement, aPseudoTagOrNull);
+                       ::GetAnimationCollection(aElement, pseudoTag);
 
   return collection && !collection->mAnimations.IsEmpty();
 }
 
 size_t
-Gecko_ElementTransitions_Length(RawGeckoElementBorrowed aElement,
-                                nsIAtom* aPseudoTagOrNull)
+Gecko_ElementTransitions_Length(RawGeckoElementBorrowed aElement)
 {
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   nsTransitionManager::CSSTransitionCollection* collection =
     nsTransitionManager::CSSTransitionCollection
-                       ::GetAnimationCollection(aElement, aPseudoTagOrNull);
+                       ::GetAnimationCollection(aElement, pseudoTag);
 
   return collection ? collection->mAnimations.Length() : 0;
 }
 
 static CSSTransition*
-GetCurrentTransitionAt(RawGeckoElementBorrowed aElement,
-                       nsIAtom* aPseudoTagOrNull,
-                       size_t aIndex)
+GetCurrentTransitionAt(RawGeckoElementBorrowed aElement, size_t aIndex)
 {
+  nsIAtom* pseudoTag = PseudoTagAndCorrectElementForAnimation(aElement);
   nsTransitionManager::CSSTransitionCollection* collection =
     nsTransitionManager::CSSTransitionCollection
-                       ::GetAnimationCollection(aElement, aPseudoTagOrNull);
+                       ::GetAnimationCollection(aElement, pseudoTag);
   if (!collection) {
     return nullptr;
   }
   nsTArray<RefPtr<CSSTransition>>& transitions = collection->mAnimations;
   return aIndex < transitions.Length()
          ? transitions[aIndex].get()
          : nullptr;
 }
 
 nsCSSPropertyID
 Gecko_ElementTransitions_PropertyAt(RawGeckoElementBorrowed aElement,
-                                    nsIAtom* aPseudoTagOrNull,
                                     size_t aIndex)
 {
-  CSSTransition* transition = GetCurrentTransitionAt(aElement,
-                                                     aPseudoTagOrNull,
-                                                     aIndex);
+  CSSTransition* transition = GetCurrentTransitionAt(aElement, aIndex);
   return transition ? transition->TransitionProperty()
                     : nsCSSPropertyID::eCSSProperty_UNKNOWN;
 }
 
 RawServoAnimationValueBorrowedOrNull
 Gecko_ElementTransitions_EndValueAt(RawGeckoElementBorrowed aElement,
-                                    nsIAtom* aPseudoTagOrNull,
                                     size_t aIndex)
 {
   CSSTransition* transition = GetCurrentTransitionAt(aElement,
-                                                     aPseudoTagOrNull,
                                                      aIndex);
   return transition ? transition->ToValue().mServo.get() : nullptr;
 }
 
 double
 Gecko_GetProgressFromComputedTiming(RawGeckoComputedTimingBorrowed aComputedTiming)
 {
   return aComputedTiming->mProgress.Value();
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -186,43 +186,37 @@ RawServoDeclarationBlockStrongBorrowedOr
 Gecko_GetStyleAttrDeclarationBlock(RawGeckoElementBorrowed element);
 RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetHTMLPresentationAttrDeclarationBlock(RawGeckoElementBorrowed element);
 RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetExtraContentStyleDeclarations(RawGeckoElementBorrowed element);
 
 // Animations
 bool
-Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
-                       nsIAtom* aPseudoTag,
+Gecko_GetAnimationRule(RawGeckoElementBorrowed aElementOrPseudo,
                        mozilla::EffectCompositor::CascadeLevel aCascadeLevel,
                        RawServoAnimationValueMapBorrowed aAnimationValues);
+RawServoDeclarationBlockStrongBorrowedOrNull
+Gecko_GetSMILOverrideDeclarationBlock(RawGeckoElementBorrowed element);
 bool Gecko_StyleAnimationsEquals(RawGeckoStyleAnimationListBorrowed,
                                  RawGeckoStyleAnimationListBorrowed);
-void Gecko_UpdateAnimations(RawGeckoElementBorrowed aElement,
-                            nsIAtom* aPseudoTagOrNull,
+void Gecko_UpdateAnimations(RawGeckoElementBorrowed aElementOrPseudo,
                             ServoComputedValuesBorrowedOrNull aOldComputedValues,
                             ServoComputedValuesBorrowedOrNull aComputedValues,
                             ServoComputedValuesBorrowedOrNull aParentComputedValues,
                             mozilla::UpdateAnimationsTasks aTasks);
-bool Gecko_ElementHasAnimations(RawGeckoElementBorrowed aElement,
-                                nsIAtom* aPseudoTagOrNull);
-bool Gecko_ElementHasCSSAnimations(RawGeckoElementBorrowed aElement,
-                                   nsIAtom* aPseudoTagOrNull);
-bool Gecko_ElementHasCSSTransitions(RawGeckoElementBorrowed aElement,
-                                    nsIAtom* aPseudoTagOrNull);
-size_t Gecko_ElementTransitions_Length(RawGeckoElementBorrowed aElement,
-                                       nsIAtom* aPseudoTagOrNull);
+bool Gecko_ElementHasAnimations(RawGeckoElementBorrowed aElementOrPseudo);
+bool Gecko_ElementHasCSSAnimations(RawGeckoElementBorrowed aElementOrPseudo);
+bool Gecko_ElementHasCSSTransitions(RawGeckoElementBorrowed aElementOrPseudo);
+size_t Gecko_ElementTransitions_Length(RawGeckoElementBorrowed aElementOrPseudo);
 nsCSSPropertyID Gecko_ElementTransitions_PropertyAt(
-  RawGeckoElementBorrowed aElement,
-  nsIAtom* aPseudoTagOrNull,
+  RawGeckoElementBorrowed aElementOrPseudo,
   size_t aIndex);
 RawServoAnimationValueBorrowedOrNull Gecko_ElementTransitions_EndValueAt(
-  RawGeckoElementBorrowed aElement,
-  nsIAtom* aPseudoTagOrNull,
+  RawGeckoElementBorrowed aElementOrPseudo,
   size_t aIndex);
 double Gecko_GetProgressFromComputedTiming(RawGeckoComputedTimingBorrowed aComputedTiming);
 double Gecko_GetPositionInSegment(
   RawGeckoAnimationPropertySegmentBorrowed aSegment,
   double aProgress,
   mozilla::ComputedTimingFunction::BeforeFlag aBeforeFlag);
 // Get servo's AnimationValue for |aProperty| from the cached base style
 // |aBaseStyles|.
@@ -310,18 +304,19 @@ void Gecko_SetContentDataArray(nsStyleCo
 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,
+nsStyleContext* Gecko_GetStyleContext(RawGeckoElementBorrowed element,
                                       nsIAtom* aPseudoTagOrNull);
+nsIAtom* Gecko_GetImplementedPseudo(RawGeckoElementBorrowed element);
 nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
                                        ServoComputedValuesBorrowed newstyle);
 nsChangeHint Gecko_HintsHandledForDescendants(nsChangeHint aHint);
 
 // Element snapshot.
 ServoElementSnapshotOwned Gecko_CreateElementSnapshot(RawGeckoElementBorrowed element);
 void Gecko_DropElementSnapshot(ServoElementSnapshotOwned snapshot);
 
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -8,24 +8,25 @@
 
 #include "gfxPlatformFontList.h"
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/dom/AnonymousContent.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
-#include "mozilla/dom/KeyframeEffectReadOnly.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsDeviceContext.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsIDocumentInlines.h"
 #include "nsPrintfCString.h"
+#include "nsSMILAnimationController.h"
 #include "nsStyleContext.h"
 #include "nsStyleSet.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 ServoStyleSet::ServoStyleSet()
   : mPresContext(nullptr)
@@ -137,20 +138,22 @@ ServoStyleSet::SetAuthorStyleDisabled(bo
   }
 
   mAuthorStyleDisabled = aStyleDisabled;
 
   // If we've just disabled, we have to note the stylesheets have changed and
   // call flush directly, since the PresShell won't.
   if (mAuthorStyleDisabled) {
     NoteStyleSheetsChanged();
-    Servo_StyleSet_FlushStyleSheets(mRawSet.get());
   }
   // If we've just enabled, then PresShell will trigger the notification and
   // later flush when the stylesheet objects are enabled in JS.
+  //
+  // TODO(emilio): Users can have JS disabled, can't they? Will that affect that
+  // notification on content documents?
 
   return NS_OK;
 }
 
 void
 ServoStyleSet::BeginUpdate()
 {
   ++mBatching;
@@ -258,20 +261,28 @@ ServoStyleSet::PreTraverseSync()
 
 void
 ServoStyleSet::PreTraverse(Element* aRoot)
 {
   PreTraverseSync();
 
   // Process animation stuff that we should avoid doing during the parallel
   // traversal.
+  nsSMILAnimationController* smilController =
+    mPresContext->Document()->GetAnimationController();
   if (aRoot) {
     mPresContext->EffectCompositor()->PreTraverseInSubtree(aRoot);
+    if (smilController) {
+      smilController->PreTraverseInSubtree(aRoot);
+    }
   } else {
     mPresContext->EffectCompositor()->PreTraverse();
+    if (smilController) {
+      smilController->PreTraverse();
+    }
   }
 }
 
 bool
 ServoStyleSet::PrepareAndTraverseSubtree(RawGeckoElementBorrowed aRoot,
                                          TraversalRootBehavior aRootBehavior,
                                          TraversalRestyleBehavior
                                            aRestyleBehavior)
@@ -290,16 +301,21 @@ ServoStyleSet::PrepareAndTraverseSubtree
   bool postTraversalRequired =
     Servo_TraverseSubtree(aRoot, mRawSet.get(), aRootBehavior, aRestyleBehavior);
   MOZ_ASSERT_IF(isInitial || forReconstruct, !postTraversalRequired);
 
   auto root = const_cast<Element*>(aRoot);
 
   // If there are still animation restyles needed, trigger a second traversal to
   // update CSS animations or transitions' styles.
+  //
+  // We don't need to do this for SMIL since SMIL only updates its animation
+  // values once at the begin of a tick. As a result, even if the previous
+  // traversal caused, for example, the font-size to change, the SMIL style
+  // won't be updated until the next tick anyway.
   EffectCompositor* compositor = mPresContext->EffectCompositor();
   if (forReconstruct ? compositor->PreTraverseInSubtree(root)
                      : compositor->PreTraverse()) {
     if (Servo_TraverseSubtree(aRoot, mRawSet.get(),
                               aRootBehavior, aRestyleBehavior)) {
       MOZ_ASSERT(!forReconstruct);
       if (isInitial) {
         // We're doing initial styling, and the additional animation
@@ -436,17 +452,16 @@ ServoStyleSet::ResolveTransientStyle(Ele
                     aPseudoType, nullptr);
 }
 
 already_AddRefed<ServoComputedValues>
 ServoStyleSet::ResolveTransientServoStyle(Element* aElement,
                                           nsIAtom* aPseudoTag)
 {
   PreTraverseSync();
-
   return ResolveStyleLazily(aElement, aPseudoTag);
 }
 
 // aFlags is an nsStyleSet flags bitfield
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveInheritingAnonymousBoxStyle(nsIAtom* aPseudoTag,
                                                   nsStyleContext* aParentContext,
                                                   uint32_t aFlags)
@@ -819,16 +834,19 @@ ServoStyleSet::StyleSubtreeForReconstruc
                               TraversalRestyleBehavior::ForReconstruct);
   MOZ_ASSERT(!postTraversalRequired);
 }
 
 void
 ServoStyleSet::NoteStyleSheetsChanged()
 {
   Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get(), mAuthorStyleDisabled);
+  if (!mBatching) {
+    Servo_StyleSet_FlushStyleSheets(mRawSet.get());
+  }
 }
 
 #ifdef DEBUG
 void
 ServoStyleSet::AssertTreeIsClean()
 {
   DocumentStyleRootIterator iter(mPresContext->Document());
   while (Element* root = iter.GetNextStyleRoot()) {
@@ -900,26 +918,56 @@ ServoStyleSet::ClearNonInheritingStyleCo
     ptr = nullptr;
   }
 }
 
 already_AddRefed<ServoComputedValues>
 ServoStyleSet::ResolveStyleLazily(Element* aElement, nsIAtom* aPseudoTag)
 {
   mPresContext->EffectCompositor()->PreTraverse(aElement, aPseudoTag);
-
   MOZ_ASSERT(!sInServoTraversal);
   sInServoTraversal = true;
+
+  /**
+   * NB: This is needed because we process animations and transitions on the
+   * pseudo-elements themselves, not on the parent's EagerPseudoStyles.
+   *
+   * That means that that style doesn't account for animations, and we can't do
+   * that easily from the traversal without doing wasted work.
+   *
+   * As such, we just lie here a bit, which is the entrypoint of
+   * getComputedStyle, the only API where this can be observed, to look at the
+   * style of the pseudo-element if it exists instead.
+   */
+  Element* elementForStyleResolution = aElement;
+  nsIAtom* pseudoTagForStyleResolution = aPseudoTag;
+  if (aPseudoTag == nsCSSPseudoElements::before) {
+    if (Element* pseudo = nsLayoutUtils::GetBeforePseudo(aElement)) {
+      elementForStyleResolution = pseudo;
+      pseudoTagForStyleResolution = nullptr;
+    }
+  } else if (aPseudoTag == nsCSSPseudoElements::after) {
+    if (Element* pseudo = nsLayoutUtils::GetAfterPseudo(aElement)) {
+      elementForStyleResolution = pseudo;
+      pseudoTagForStyleResolution = nullptr;
+    }
+  }
+
   RefPtr<ServoComputedValues> computedValues =
-    Servo_ResolveStyleLazily(aElement, aPseudoTag, mRawSet.get()).Consume();
+    Servo_ResolveStyleLazily(elementForStyleResolution,
+                             pseudoTagForStyleResolution,
+                             mRawSet.get()).Consume();
 
   if (mPresContext->EffectCompositor()->PreTraverse(aElement, aPseudoTag)) {
     computedValues =
-      Servo_ResolveStyleLazily(aElement, aPseudoTag, mRawSet.get()).Consume();
+      Servo_ResolveStyleLazily(elementForStyleResolution,
+                               pseudoTagForStyleResolution,
+                               mRawSet.get()).Consume();
   }
+
   sInServoTraversal = false;
 
   return computedValues.forget();
 }
 
 bool
 ServoStyleSet::AppendFontFaceRules(nsTArray<nsFontFaceRuleContainer>& aArray)
 {
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -565,16 +565,22 @@ private:
   }
   static bool IsStringUnit(Unit aUnit) {
     return aUnit == eUnit_UnparsedString;
   }
 };
 
 struct AnimationValue
 {
+  explicit AnimationValue(const StyleAnimationValue& aValue)
+    : mGecko(aValue) { }
+  explicit AnimationValue(const RefPtr<RawServoAnimationValue>& aValue)
+    : mServo(aValue) { }
+  AnimationValue() = default;
+
   // mGecko and mServo are mutually exclusive: only one or the other should
   // ever be set.
   // FIXME: After obsoleting StyleAnimationValue, we should remove mGecko, and
   // make AnimationValue a wrapper of RawServoAnimationValue to hide these
   // FFIs.
   StyleAnimationValue mGecko;
   RefPtr<RawServoAnimationValue> mServo;
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -6,16 +6,17 @@
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 #include "mozilla/dom/CSSAnimationBinding.h"
 
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
 
 #include "nsPresContext.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -514,16 +514,44 @@ public:
 
 private:
   GeckoRestyleManager* mRestyleManager = nullptr;
   bool mOldSkipAnimationRules = false;
   nsComputedDOMStyle::AnimationFlag mAnimationFlag;
 };
 }
 
+/**
+ * The following function checks whether we need to explicitly resolve the style
+ * again, even though we have a style context coming from the frame.
+ *
+ * This basically checks whether the style is or may be under a ::first-line or
+ * ::first-letter frame, in which case we can't return the frame style, and we
+ * need to resolve it. See bug 505515.
+ */
+static bool
+MustReresolveStyle(const nsStyleContext* aContext)
+{
+  MOZ_ASSERT(aContext);
+
+  if (aContext->HasPseudoElementData()) {
+    if (!aContext->GetPseudo() ||
+        aContext->StyleSource().IsServoComputedValues()) {
+      // TODO(emilio): When ::first-line is supported in Servo, we may want to
+      // fix this to avoid re-resolving pseudo-element styles.
+      return true;
+    }
+
+    return aContext->GetParent() &&
+           aContext->GetParent()->HasPseudoElementData();
+  }
+
+  return false;
+}
+
 already_AddRefed<nsStyleContext>
 nsComputedDOMStyle::DoGetStyleContextNoFlush(Element* aElement,
                                              nsIAtom* aPseudo,
                                              nsIPresShell* aPresShell,
                                              AnimationFlag aAnimationFlag)
 {
   MOZ_ASSERT(aElement, "NULL element");
   // If the content has a pres shell, we must use it.  Otherwise we'd
@@ -538,53 +566,59 @@ nsComputedDOMStyle::DoGetStyleContextNoF
     presShell = aPresShell;
     if (!presShell)
       return nullptr;
   }
 
   // XXX the !aElement->IsHTMLElement(nsGkAtoms::area)
   // check is needed due to bug 135040 (to avoid using
   // mPrimaryFrame). Remove it once that's fixed.
-  if (!aPseudo &&
-      inDocWithShell &&
+  if (inDocWithShell &&
       !aElement->IsHTMLElement(nsGkAtoms::area)) {
-    nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement);
+    nsIFrame* frame = nullptr;
+    if (aPseudo == nsCSSPseudoElements::before) {
+      frame = nsLayoutUtils::GetBeforeFrame(aElement);
+    } else if (aPseudo == nsCSSPseudoElements::after) {
+      frame = nsLayoutUtils::GetAfterFrame(aElement);
+    } else if (!aPseudo) {
+      frame = nsLayoutUtils::GetStyleFrame(aElement);
+    }
     if (frame) {
       nsStyleContext* result = frame->StyleContext();
       // Don't use the style context if it was influenced by
       // pseudo-elements, since then it's not the primary style
-      // for this element.
-      if (!result->HasPseudoElementData()) {
+      // for this element / pseudo.
+      if (!MustReresolveStyle(result)) {
         // The existing style context may have animation styles so check if we
         // need to remove them.
         if (aAnimationFlag == eWithoutAnimation) {
           nsPresContext* presContext = presShell->GetPresContext();
           MOZ_ASSERT(presContext, "Should have a prescontext if we have a frame");
-          MOZ_ASSERT(presContext->StyleSet()->IsGecko(),
-                     "stylo: Need ResolveStyleByRemovingAnimation for stylo");
           if (presContext && presContext->StyleSet()->IsGecko()) {
             nsStyleSet* styleSet = presContext->StyleSet()->AsGecko();
             return styleSet->ResolveStyleByRemovingAnimation(
                      aElement, result, eRestyle_AllHintsWithAnimations);
           } else {
+            NS_WARNING("stylo: Getting the unanimated style context is not yet"
+                       " supported for Servo");
             return nullptr;
           }
         }
 
         // this function returns an addrefed style context
         RefPtr<nsStyleContext> ret = result;
         return ret.forget();
       }
     }
   }
 
   // No frame has been created, or we have a pseudo, or we're looking
   // for the default style, so resolve the style ourselves.
 
-  nsPresContext *presContext = presShell->GetPresContext();
+  nsPresContext* presContext = presShell->GetPresContext();
   if (!presContext)
     return nullptr;
 
   StyleSetHandle styleSet = presShell->StyleSet();
 
   auto type = CSSPseudoElementType::NotPseudo;
   if (aPseudo) {
     type = nsCSSPseudoElements::
@@ -710,44 +744,16 @@ nsComputedDOMStyle::SetResolvedStyleCont
 
 void
 nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext)
 {
   ClearStyleContext();
   mStyleContext = aContext;
 }
 
-/**
- * The following function checks whether we need to explicitly resolve the style
- * again, even though we have a style context coming from the frame.
- *
- * This basically checks whether the style is or may be under a ::first-line or
- * ::first-letter frame, in which case we can't return the frame style, and we
- * need to resolve it. See bug 505515.
- */
-static bool
-MustReresolveStyle(const nsStyleContext* aContext)
-{
-  MOZ_ASSERT(aContext);
-
-  if (aContext->HasPseudoElementData()) {
-    if (!aContext->GetPseudo() ||
-        aContext->StyleSource().IsServoComputedValues()) {
-      // TODO(emilio): When ::first-line is supported in Servo, we may want to
-      // fix this to avoid re-resolving pseudo-element styles.
-      return true;
-    }
-
-    return aContext->GetParent() &&
-           aContext->GetParent()->HasPseudoElementData();
-  }
-
-  return false;
-}
-
 void
 nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
 {
   nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocumentWeak);
   if (!document) {
     ClearStyleContext();
     return;
   }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -474,17 +474,21 @@ nsStyleBorder::CalcDifference(const nsSt
     }
   }
 
   if (mBorderRadius != aNewData.mBorderRadius ||
       !mBorderColors != !aNewData.mBorderColors) {
     return nsChangeHint_RepaintFrame;
   }
 
-  if (IsBorderImageLoaded() || aNewData.IsBorderImageLoaded()) {
+  // Loading status of the border image can be accessed in main thread only
+  // while CalcDifference might be executed on a background thread. As a
+  // result, we have to check mBorderImage* fields even before border image was
+  // actually loaded.
+  if (!mBorderImageSource.IsEmpty() || !aNewData.mBorderImageSource.IsEmpty()) {
     if (mBorderImageSource  != aNewData.mBorderImageSource  ||
         mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
         mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
         mBorderImageSlice   != aNewData.mBorderImageSlice   ||
         mBorderImageFill    != aNewData.mBorderImageFill    ||
         mBorderImageWidth   != aNewData.mBorderImageWidth) {
       return nsChangeHint_RepaintFrame;
     }
@@ -503,19 +507,17 @@ nsStyleBorder::CalcDifference(const nsSt
 
   // mBorder is the specified border value.  Changes to this don't
   // need any change processing, since we operate on the computed
   // border values instead.
   if (mBorder != aNewData.mBorder) {
     return nsChangeHint_NeutralChange;
   }
 
-  // mBorderImage* fields are checked only when border image was
-  // actualy loaded. But we need to return neutral change even when
-  // they are not actually used.
+  // mBorderImage* fields are checked only when border-image is not 'none'.
   if (mBorderImageSource  != aNewData.mBorderImageSource  ||
       mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
       mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
       mBorderImageSlice   != aNewData.mBorderImageSlice   ||
       mBorderImageFill    != aNewData.mBorderImageFill    ||
       mBorderImageWidth   != aNewData.mBorderImageWidth) {
     return nsChangeHint_NeutralChange;
   }
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/TimeStamp.h"
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
 #include "nsRuleWalker.h"
 #include "nsCSSPropertyIDSet.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/ServoComputedValuesWithParent.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/DocumentTimeline.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsCSSProps.h"
 #include "nsCSSPseudoElements.h"
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -20,16 +20,17 @@
 class nsIGlobalObject;
 class nsStyleContext;
 class nsPresContext;
 class nsCSSPropertyIDSet;
 
 namespace mozilla {
 enum class CSSPseudoElementType : uint8_t;
 struct Keyframe;
+struct ServoComputedValuesWithParent;
 struct StyleTransition;
 } // namespace mozilla
 
 /*****************************************************************************
  * Per-Element data                                                          *
  *****************************************************************************/
 
 namespace mozilla {
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -26,42 +26,38 @@ Any line which doesn't follow the format
 
 To use this file, you need to add `--failure-pattern-file=stylo-failures.md`
 to mochitest command.
 
 ## Failures
 
 * Media query support:
   * test_media_queries.html [156]
-  * test_media_queries_dynamic.html [11]
+  * test_media_queries_dynamic.html [7]
   * test_media_queries_dynamic_xbl.html [2]
   * test_webkit_device_pixel_ratio.html: -webkit-device-pixel-ratio [3]
   * browser_bug453896.js [8]
 * Animation support:
-  * test_transitions_and_reframes.html: pseudo frames bug 1331047 [4]
-  * test_animations.html: 6 of them bug 1331047 [25]
+  * test_animations.html [19]
   * test_animations_dynamic_changes.html [1]
   * test_bug716226.html [1]
   * OMTA
     * test_animations_effect_timing_duration.html [1]
     * test_animations_effect_timing_enddelay.html [1]
     * test_animations_effect_timing_iterations.html [1]
     * test_animations_iterationstart.html [1]
     * test_animations_omta.html [1]
     * test_animations_omta_start.html [1]
     * test_animations_pausing.html [1]
     * test_animations_playbackrate.html [1]
     * test_animations_reverse.html [1]
   * SMIL Animation
     * test_restyles_in_smil_animation.html [2]
-  * CSS Timing Functions: Frames timing functions
-    * test_value_storage.html `frames` [30]
   * Property parsing and computation:
     * test_property_syntax_errors.html `animation` [20]
-    * test_value_storage.html `animation` [15]
 * CSSOM support:
   * \@import bug 1352968
     * test_bug221428.html [1]
     * test_css_eof_handling.html: also relies on \@-moz-document [1]
   * \@keyframes bug 1345697
     * test_keyframes_rules.html [1]
     * test_rules_out_of_sheets.html [1]
   * \@support bug 1355394
@@ -69,17 +65,17 @@ to mochitest command.
 * test_bug357614.html: case-insensitivity for old attrs in attr selector servo/servo#15006 [2]
 * test_bug387615.html: servo/servo#15006 [1]
 * test_bug397427.html: @import issue bug 1331291 and CSSOM support of @import [1]
 * console support bug 1352669
   * test_bug413958.html `monitorConsole` [3]
   * test_parser_diagnostics_unprintables.html [550]
 * Transition support:
   * test_compute_data_with_start_struct.html `transition` [2]
-  * test_transitions.html: pseudo elements [10]
+  * test_transitions.html: pseudo elements [12]
   * Events:
     * test_animations_event_order.html [2]
 * test_computed_style.html `gradient`: -moz- and -webkit-prefixed gradient values [35]
 * ... `mask`: mask-image isn't set properly bug 1341667 [10]
 * ... `fill`: svg paint should distinguish whether there is fallback bug 1347409 [2]
 * ... `stroke`: svg paint should distinguish whether there is fallback bug 1347409 [2]
 * character not properly escaped servo/servo#15947
   * test_parse_url.html [1]
@@ -120,20 +116,16 @@ to mochitest command.
   * test_compute_data_with_start_struct.html `grid-` [*]
   * test_inherit_computation.html `grid` [*]
   * test_inherit_storage.html `'grid` [*]
   * ... `"grid` [*]
   * test_initial_computation.html `grid` [*]
   * test_initial_storage.html `grid` [*]
   * test_property_syntax_errors.html `grid`: actually there are issues with this [*]
   * test_value_storage.html `'grid` [*]
-* url value from decl setter bug 1330503
-  * test_compute_data_with_start_struct.html `border-image-source` [2]
-  * test_inherit_computation.html `border-image` [2]
-  * test_initial_computation.html `border-image` [4]
 * Unimplemented prefixed properties:
   * -moz-force-broken-image-icon servo/servo#16001
     * test_compute_data_with_start_struct.html `-moz-force-broken-image-icon` [2]
     * test_inherit_computation.html `-moz-force-broken-image-icon` [2]
     * test_inherit_storage.html `-moz-force-broken-image-icon` [2]
     * test_initial_computation.html `-moz-force-broken-image-icon` [4]
     * test_initial_storage.html `-moz-force-broken-image-icon` [4]
     * test_value_storage.html `-moz-force-broken-image-icon` [4]
--- a/layout/style/test/test_animations_event_order.html
+++ b/layout/style/test/test_animations_event_order.html
@@ -163,16 +163,18 @@ divs.forEach((div, i) => {
   div.setAttribute('id', 'div' + i);
 });
 
 var extraStyle = document.createElement('style');
 document.head.appendChild(extraStyle);
 var sheet = extraStyle.sheet;
 sheet.insertRule('#div0::after { animation: anim 10s }', 0);
 sheet.insertRule('#div0::before { animation: anim 10s }', 1);
+sheet.insertRule('#div0::after, #div0::before { ' +
+                 ' content: " " }', 2);
 getComputedStyle(divs[0]).animationName; // build animation
 getComputedStyle(divs[1]).animationName; // build animation
 
 advance_clock(0);
 checkEventOrder([ divs[0], 'animationstart' ],
                 [ divs[0], '::before', 'animationstart' ],
                 [ divs[0], '::after', 'animationstart' ],
                 [ divs[1], 'animationstart' ],
--- a/layout/xul/BoxObject.cpp
+++ b/layout/xul/BoxObject.cpp
@@ -212,17 +212,17 @@ BoxObject::GetScreenPosition(nsIntPoint&
 {
   aPoint.x = aPoint.y = 0;
 
   if (!mContent)
     return NS_ERROR_NOT_INITIALIZED;
 
   nsIFrame* frame = GetFrame(true);
   if (frame) {
-    nsIntRect rect = frame->GetScreenRect();
+    CSSIntRect rect = frame->GetScreenRect();
     aPoint.x = rect.x;
     aPoint.y = rect.y;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -197,17 +197,17 @@ nsResizerFrame::HandleEvent(nsPresContex
                        sizeConstraints.mMaxSize.height, mouseMove.y, direction.mVertical);
 
       // Don't allow resizing a window or a popup past the edge of the screen,
       // so adjust the rectangle to fit within the available screen area.
       if (window) {
         nsCOMPtr<nsIScreen> screen;
         nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
         if (sm) {
-          nsIntRect frameRect = GetScreenRect();
+          CSSIntRect frameRect = GetScreenRect();
           // ScreenForRect requires display pixels, so scale from device pix
           double scale;
           window->GetUnscaledDevicePixelsPerCSSPixel(&scale);
           sm->ScreenForRect(NSToIntRound(frameRect.x / scale),
                             NSToIntRound(frameRect.y / scale), 1, 1,
                             getter_AddRefs(screen));
           if (screen) {
             LayoutDeviceIntRect screenRect;
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -257,21 +257,21 @@ nsXULPopupManager::Rollup(uint32_t aCoun
       item->Frame()->GetContent()->AttrValueIs(kNameSpaceID_None,
         nsGkAtoms::norolluponanchor, nsGkAtoms::_true, eCaseMatters));
 
     // When ConsumeOutsideClicks_ParentOnly is used, always consume the click
     // when the click was over the anchor. This way, clicking on a menu doesn't
     // reopen the menu.
     if ((consumeResult == ConsumeOutsideClicks_ParentOnly || noRollupOnAnchor) && pos) {
       nsMenuPopupFrame* popupFrame = item->Frame();
-      nsIntRect anchorRect;
+      CSSIntRect anchorRect;
       if (popupFrame->IsAnchored()) {
         // Check if the popup has a screen anchor rectangle. If not, get the rectangle
         // from the anchor element.
-        anchorRect = popupFrame->GetScreenAnchorRect();
+        anchorRect = CSSIntRect::FromUnknownRect(popupFrame->GetScreenAnchorRect());
         if (anchorRect.x == -1 || anchorRect.y == -1) {
           nsCOMPtr<nsIContent> anchor = popupFrame->GetAnchor();
 
           // Check if the anchor has indicated another node to use for checking
           // for roll-up. That way, we can anchor a popup on anonymous content or
           // an individual icon, while clicking elsewhere within a button or other
           // container doesn't result in us re-opening the popup.
           if (anchor) {
@@ -293,18 +293,18 @@ nsXULPopupManager::Rollup(uint32_t aCoun
         }
       }
 
       // It's possible that some other element is above the anchor at the same
       // position, but the only thing that would happen is that the mouse
       // event will get consumed, so here only a quick coordinates check is
       // done rather than a slower complete check of what is at that location.
       nsPresContext* presContext = item->Frame()->PresContext();
-      nsIntPoint posCSSPixels(presContext->DevPixelsToIntCSSPixels(pos->x),
-                              presContext->DevPixelsToIntCSSPixels(pos->y));
+      CSSIntPoint posCSSPixels(presContext->DevPixelsToIntCSSPixels(pos->x),
+                               presContext->DevPixelsToIntCSSPixels(pos->y));
       if (anchorRect.Contains(posCSSPixels)) {
         if (consumeResult == ConsumeOutsideClicks_ParentOnly) {
           consume = true;
         }
 
         if (noRollupOnAnchor) {
           rollup = false;
         }
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -406,17 +406,17 @@ status_t MPEG4Extractor::readMetaData() 
 
     off64_t offset = 0;
     status_t err = NO_INIT;
     while (!mFirstTrack) {
         err = parseChunk(&offset, 0);
         // The parseChunk function returns UNKNOWN_ERROR to skip
         // some boxes we don't want to handle. Filter that error
         // code but return others so e.g. I/O errors propagate.
-        if (err != OK && err != (status_t) UNKNOWN_ERROR) {
+        if (err != OK && err != static_cast<status_t>(UNKNOWN_ERROR)) {
           ALOGW("Error %d parsing chunck at offset %lld looking for first track",
               err, (long long)offset);
           break;
         }
     }
 
     if (mInitCheck == OK) {
         if (mHasVideo) {
@@ -622,17 +622,17 @@ status_t MPEG4Extractor::parseDrmSINF(of
             size -= (dataLen + numOfBytes + 1);
         }
     }
 
     if (size != 0) {
         return ERROR_MALFORMED;
     }
 
-    return UNKNOWN_ERROR;  // Return a dummy error.
+    return static_cast<status_t>(UNKNOWN_ERROR);  // Return a dummy error.
 }
 
 struct PathAdder {
     PathAdder(nsTArray<uint32_t> *path, uint32_t chunkType)
         : mPath(path) {
         mPath->AppendElement(chunkType);
     }
 
@@ -840,17 +840,17 @@ status_t MPEG4Extractor::parseChunk(off6
 
                 if (err != OK) {
                     return err;
                 }
             } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mInitCheck = OK;
 
                 if (!mIsDrm) {
-                    return UNKNOWN_ERROR;  // Return a dummy error.
+                    return static_cast<status_t>(UNKNOWN_ERROR);  // Return a dummy error.
                 } else {
                     return OK;
                 }
             }
             break;
         }
 
         case FOURCC('e', 'l', 's', 't'):
@@ -1974,17 +1974,17 @@ status_t MPEG4Extractor::parseChunk(off6
             *offset += chunk_size;
             break;
         }
 
         case FOURCC('s', 'i', 'd', 'x'):
         {
             parseSegmentIndex(data_offset, chunk_data_size);
             *offset += chunk_size;
-            return UNKNOWN_ERROR; // stop parsing after sidx
+            return static_cast<status_t>(UNKNOWN_ERROR); // stop parsing after sidx
         }
 
         case FOURCC('w', 'a', 'v', 'e'):
         {
             off64_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
             while (*offset < stop_offset) {
                 status_t err = parseChunk(offset, depth + 1);
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -238,17 +238,17 @@ public:
                   NS_DISPATCH_SYNC);
   }
 
   void ReleaseUse_i()
   {
     MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
     nsrefcnt count = --mUseCount;
     MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
-    if (count == 0) {
+    if (mThread && count == 0) {
       // in-use -> idle -- no one forcing it to remain instantiated
       r_log(LOG_GENERIC,LOG_DEBUG,"Shutting down wrapped SingletonThread %p",
             mThread.get());
       mThread->Shutdown();
       mThread = nullptr;
       // It'd be nice to use a timer instead...  But be careful of
       // xpcom-shutdown-threads in that case
     }
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -90,16 +90,17 @@ static DMDBridge* gDMDBridge;
 static DMDFuncs gDMDFuncs;
 
 DMDFuncs*
 DMDBridge::GetDMDFuncs()
 {
   return &gDMDFuncs;
 }
 
+MOZ_FORMAT_PRINTF(1, 2)
 inline void
 StatusMsg(const char* aFmt, ...)
 {
   va_list ap;
   va_start(ap, aFmt);
   gDMDFuncs.StatusMsg(aFmt, ap);
   va_end(ap);
 }
@@ -1580,17 +1581,17 @@ Init(const malloc_table_t* aMallocTable)
 #endif
 
   // DMD is controlled by the |DMD| environment variable.
   const char* e = getenv("DMD");
 
   if (e) {
     StatusMsg("$DMD = '%s'\n", e);
   } else {
-    StatusMsg("$DMD is undefined\n", e);
+    StatusMsg("$DMD is undefined\n");
   }
 
   // Parse $DMD env var.
   gOptions = InfallibleAllocPolicy::new_<Options>(e);
 
 #ifdef XP_MACOSX
   // On Mac OS X we need to call StackWalkInitCriticalAddress() very early
   // (prior to the creation of any mutexes, apparently) otherwise we can get
--- a/memory/replace/dmd/DMD.h
+++ b/memory/replace/dmd/DMD.h
@@ -45,17 +45,17 @@ struct DMDFuncs
   virtual void ReportOnAlloc(const void*);
 
   virtual void ClearReports();
 
   virtual void Analyze(UniquePtr<JSONWriteFunc>);
 
   virtual void SizeOf(Sizes*);
 
-  virtual void StatusMsg(const char*, va_list);
+  virtual void StatusMsg(const char*, va_list) MOZ_FORMAT_PRINTF(2, 0);
 
   virtual void ResetEverything(const char*);
 
 #ifndef REPLACE_MALLOC_IMPL
   // We deliberately don't use ReplaceMalloc::GetDMDFuncs here, because if we
   // did, the following would happen.
   // - The code footprint of each call to Get() larger as GetDMDFuncs ends
   //   up inlined.
@@ -267,16 +267,17 @@ SizeOf(Sizes* aSizes)
 {
   DMDFuncs* funcs = DMDFuncs::Get();
   if (funcs) {
     funcs->SizeOf(aSizes);
   }
 }
 
 // Prints a status message prefixed with "DMD[<pid>]". Use sparingly.
+MOZ_FORMAT_PRINTF(1, 2)
 inline void
 StatusMsg(const char* aFmt, ...)
 {
   DMDFuncs* funcs = DMDFuncs::Get();
   if (funcs) {
     va_list ap;
     va_start(ap, aFmt);
     funcs->StatusMsg(aFmt, ap);
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -72,17 +72,17 @@ public class CustomTabsActivity extends 
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         if (savedInstanceState != null) {
             startIntent = savedInstanceState.getParcelable(SAVED_START_INTENT);
         } else {
-            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
+            sendTelemetry();
             startIntent = getIntent();
             final String host = getReferrerHost();
             recordCustomTabUsage(host);
         }
 
         setThemeFromToolbarColor();
 
         doorhangerOverlay = findViewById(R.id.custom_tabs_doorhanger_overlay);
@@ -96,16 +96,34 @@ public class CustomTabsActivity extends 
         actionBarPresenter = new ActionBarPresenter(actionBar);
         actionBarPresenter.displayUrlOnly(startIntent.getDataString());
         actionBarPresenter.setBackgroundColor(IntentUtil.getToolbarColor(startIntent), getWindow());
         actionBarPresenter.setTextLongClickListener(new UrlCopyListener());
 
         Tabs.registerOnTabsChangedListener(this);
     }
 
+    private void sendTelemetry() {
+        Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
+        if (IntentUtil.hasToolbarColor(startIntent)) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasToolbarColor");
+        }
+        if (IntentUtil.hasActionButton(startIntent)) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasActionButton");
+        }
+        if (IntentUtil.isActionButtonTinted(startIntent)) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-isActionButtonTinted");
+        }
+        if (IntentUtil.hasShareItem(startIntent)) {
+            Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasShareItem");
+        }
+
+
+    }
+
     private void recordCustomTabUsage(final String host) {
         final GeckoBundle data = new GeckoBundle(1);
         if (host != null) {
             data.putString("client", host);
         } else {
             data.putString("client", "unknown");
         }
         // Pass a message to Gecko to send Telemetry data
@@ -266,41 +284,54 @@ public class CustomTabsActivity extends 
         updateMenuItemForward();
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
             case android.R.id.home:
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-home");
                 finish();
                 return true;
             case R.id.share:
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-share");
                 onShareClicked();
                 return true;
             case R.id.custom_tabs_menu_forward:
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-forward");
                 onForwardClicked();
                 return true;
             case R.id.custom_tabs_menu_control:
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-control");
                 onLoadingControlClicked();
                 return true;
             case R.id.custom_tabs_menu_open_in:
+                Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-open-in");
                 onOpenInClicked();
                 return true;
         }
 
         final PendingIntent intent = menuItemsIntent.get(item.getItemId());
         if (intent != null) {
-            performPendingIntent(intent);
+            onCustomMenuItemClicked(intent);
             return true;
         }
 
         return super.onOptionsItemSelected(item);
     }
 
+    /**
+     * Called when the menu that's been clicked is added by the client
+     */
+    private void onCustomMenuItemClicked(PendingIntent intent) {
+        Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-customized-menu");
+        performPendingIntent(intent);
+    }
+
     @Override
     protected ActionModePresenter getTextSelectPresenter() {
         return new ActionModePresenter() {
             private ActionMode mMode;
 
             @Override
             public void startActionMode(ActionMode.Callback callback) {
                 mMode = startSupportActionMode(callback);
@@ -463,16 +494,17 @@ public class CustomTabsActivity extends 
             intent.setData(Uri.parse(tab.getURL()));
             intent.setAction(Intent.ACTION_VIEW);
             startActivity(intent);
             finish();
         }
     }
 
     private void onActionButtonClicked() {
+        Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.MENU, "customtab-action-button");
         PendingIntent pendingIntent = IntentUtil.getActionButtonPendingIntent(startIntent);
         performPendingIntent(pendingIntent);
     }
 
 
     /**
      * Callback for Share menu item.
      */
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/IntentUtil.java
@@ -68,16 +68,27 @@ class IntentUtil {
      * @return bitmap icon, if any. Otherwise, null.
      */
     static Bitmap getActionButtonIcon(@NonNull Intent intent) {
         final Bundle bundle = getActionButtonBundle(intent);
         return (bundle == null) ? null : (Bitmap) bundle.getParcelable(CustomTabsIntent.KEY_ICON);
     }
 
     /**
+     * Only for telemetry to understand caller app's customization
+     * This method should only be called once during one usage.
+     *
+     * @param intent which to launch a Custom-Tabs-Activity
+     * @return true, if the caller customized the color.
+     */
+    static boolean hasToolbarColor(@NonNull Intent intent) {
+        return intent.hasExtra(CustomTabsIntent.EXTRA_TOOLBAR_COLOR);
+    }
+
+    /**
      * To extract color code from intent for top toolbar.
      * It also ensure the color is not translucent.
      *
      * @param intent which to launch a Custom-Tabs-Activity
      * @return color code in integer type.
      */
     @ColorInt
     static int getToolbarColor(@NonNull Intent intent) {
--- a/mobile/android/config/tooltool-manifests/android-gradle-dependencies/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-gradle-dependencies/releng.manifest
@@ -10,19 +10,19 @@
     "size": 573952124,
     "visibility": "internal",
     "digest": "1d495d7a7386af3f27b14982e0ff7b0963fd1a63a08040b9b1db0e94c9681fa3704c195ba8be23b5f73e15101b2b767293bc8f96e0584e17867ef13b074e5038",
     "algorithm": "sha512",
     "filename": "android-sdk-linux.tar.xz",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 106162896,
-    "digest": "cf0f188655cb7070861f5afc4c91eab9c9f7ecd4fcc87c5edf95ada7858f6958a61a8d4a40dd27953354652546c20bbb8e75abe0f22f02369b2f1915208ef827",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 106333536,
+    "digest": "a097fdaed1f6104f0e19921cc1a823ff287381f5afc71ec33e3007c61859d81b0c4142c97895b11b2116ed09b7f6c093f3a5053c9c943f360d5ea84276a2a8b3",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "version": "sccache rev 7a3847276d05deb564cb84a16b8c551b690aaa3e",
     "algorithm": "sha512",
     "visibility": "public",
--- a/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -43,19 +43,19 @@
     "version": "gcc 4.9.4 + PR64905",
     "size": 101297752,
     "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
     "algorithm": "sha512",
     "filename": "gcc.tar.xz",
     "unpack": true
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 106162896,
-    "digest": "cf0f188655cb7070861f5afc4c91eab9c9f7ecd4fcc87c5edf95ada7858f6958a61a8d4a40dd27953354652546c20bbb8e75abe0f22f02369b2f1915208ef827",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 106333536,
+    "digest": "a097fdaed1f6104f0e19921cc1a823ff287381f5afc71ec33e3007c61859d81b0c4142c97895b11b2116ed09b7f6c093f3a5053c9c943f360d5ea84276a2a8b3",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "jcentral.tar.xz",
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -68,19 +68,19 @@
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "gradle-dist.tar.xz",
     "unpack": true,
     "digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
     "size": 51753660
   },
   {
-    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack",
-    "size": 106162896,
-    "digest": "cf0f188655cb7070861f5afc4c91eab9c9f7ecd4fcc87c5edf95ada7858f6958a61a8d4a40dd27953354652546c20bbb8e75abe0f22f02369b2f1915208ef827",
+    "version": "rustc 1.16.0 (30cf806ef 2017-03-10) repack with cargo 0.19.0-nightly (8326a3683 2017-04-19)",
+    "size": 106333536,
+    "digest": "a097fdaed1f6104f0e19921cc1a823ff287381f5afc71ec33e3007c61859d81b0c4142c97895b11b2116ed09b7f6c093f3a5053c9c943f360d5ea84276a2a8b3",
     "algorithm": "sha512",
     "filename": "rustc.tar.xz",
     "unpack": true
   },
   {
     "algorithm": "sha512",
     "visibility": "public",
     "filename": "dotgradle.tar.xz",
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -361,17 +361,17 @@ pref("media.gmp.decoder.h264", 0);
 pref("media.raw.enabled", true);
 #endif
 pref("media.ogg.enabled", true);
 pref("media.opus.enabled", true);
 pref("media.wave.enabled", true);
 pref("media.webm.enabled", true);
 
 pref("media.eme.chromium-api.enabled", true);
-pref("media.eme.chromium-api.video-shmems", 3);
+pref("media.eme.chromium-api.video-shmems", 4);
 
 #ifdef MOZ_APPLEMEDIA
 #ifdef MOZ_WIDGET_UIKIT
 pref("media.mp3.enabled", true);
 #endif
 pref("media.apple.mp3.enabled", true);
 pref("media.apple.mp4.enabled", true);
 #endif
--- a/old-configure.in
+++ b/old-configure.in
@@ -501,19 +501,21 @@ dnl ====================================
 dnl set the defaults first
 dnl ========================================================
 AS_BIN=$AS
 AR_EXTRACT='$(AR) x'
 AS='$(CC)'
 AS_DASH_C_FLAG='-c'
 DLL_PREFIX=lib
 LIB_PREFIX=lib
+RUST_LIB_PREFIX=lib
 DLL_SUFFIX=.so
 OBJ_SUFFIX=o
 LIB_SUFFIX=a
+RUST_LIB_SUFFIX=a
 IMPORT_LIB_SUFFIX=
 DIRENT_INO=d_ino
 MOZ_USER_DIR=".mozilla"
 
 MOZ_FIX_LINK_PATHS="-Wl,-rpath-link,${DIST}/bin -Wl,-rpath-link,${prefix}/lib"
 
 MOZ_FS_LAYOUT=unix
 
@@ -921,16 +923,18 @@ case "$target" in
         NSPR_LDFLAGS="$NSPR_LDFLAGS -static-libgcc"
         # Use temp file for windres (bug 213281)
         RCFLAGS='-O coff --use-temp-file'
         # mingw doesn't require kernel32, user32, and advapi32 explicitly
         LIBS="$LIBS -luuid -lgdi32 -lwinmm -lwsock32 -luserenv -lsecur32"
         MOZ_FIX_LINK_PATHS=
         DLL_PREFIX=
         IMPORT_LIB_SUFFIX=a
+        RUST_LIB_PREFIX=
+        RUST_LIB_SUFFIX=lib
 
         WIN32_CONSOLE_EXE_LDFLAGS=-mconsole
         WIN32_GUI_EXE_LDFLAGS=-mwindows
 
         # GCC/binutils can't link to a function if we try to include dllexport function
         # in the same library as dllimport caller. To work around it, we build NSPR
         # and NSS with -mnop-fun-dllimport flag. The drawback of this solution is that
         # function thunks need to be generated for cross-DLL calls.
@@ -943,18 +947,20 @@ case "$target" in
         AR='lib'
         AR_FLAGS='-NOLOGO -OUT:$@'
         AR_EXTRACT=
         RANLIB='echo not_ranlib'
         STRIP='echo not_strip'
         PKG_SKIP_STRIP=1
         OBJ_SUFFIX=obj
         LIB_SUFFIX=lib
+        RUST_LIB_SUFFIX=lib
         DLL_PREFIX=
         LIB_PREFIX=
+        RUST_LIB_PREFIX=
         IMPORT_LIB_SUFFIX=lib
         MKSHLIB='$(LINK) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         MKCSHLIB='$(LINK) -NOLOGO -DLL -OUT:$@ -PDB:$(LINK_PDBFILE) $(DSO_LDOPTS)'
         WIN32_SUBSYSTEM_VERSION=6.01
         WIN32_CONSOLE_EXE_LDFLAGS=-SUBSYSTEM:CONSOLE,$WIN32_SUBSYSTEM_VERSION
         WIN32_GUI_EXE_LDFLAGS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
         DSO_LDOPTS=-SUBSYSTEM:WINDOWS,$WIN32_SUBSYSTEM_VERSION
         _USE_CPP_INCLUDE_FLAG=1
@@ -5317,20 +5323,22 @@ AC_SUBST(GCC_USE_GNU_LD)
 
 AC_SUBST(WRAP_LDFLAGS)
 AC_SUBST(MKSHLIB)
 AC_SUBST(MKCSHLIB)
 AC_SUBST(DSO_CFLAGS)
 AC_SUBST(DSO_PIC_CFLAGS)
 AC_SUBST(DSO_LDOPTS)
 AC_SUBST(LIB_PREFIX)
+AC_SUBST(RUST_LIB_PREFIX)
 AC_SUBST(DLL_PREFIX)
 AC_SUBST(DLL_SUFFIX)
 AC_DEFINE_UNQUOTED(MOZ_DLL_SUFFIX, "$DLL_SUFFIX")
 AC_SUBST(LIB_SUFFIX)
+AC_SUBST(RUST_LIB_SUFFIX)
 AC_SUBST(OBJ_SUFFIX)
 AC_SUBST(BIN_SUFFIX)
 AC_SUBST(IMPORT_LIB_SUFFIX)
 AC_SUBST(USE_N32)
 AC_SUBST(CC_VERSION)
 AC_SUBST(NS_ENABLE_TSF)
 AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
 AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -111,17 +111,17 @@ MAX_CACHED_ARTIFACTS_SIZE = 1024 * 1024 
 # easy installation.  This is most noticeable on Mac OS X: since mounting and
 # copying from DMG files is very slow, we extract the desired binaries to a
 # separate archive for fast re-installation.
 PROCESSED_SUFFIX = '.processed.jar'
 
 CANDIDATE_TREES = (
     'mozilla-central',
     'integration/mozilla-inbound',
-    'releases/mozilla-aurora'
+    'releases/mozilla-beta'
 )
 
 class ArtifactJob(object):
     # These are a subset of TEST_HARNESS_BINS in testing/mochitest/Makefile.in.
     # Each item is a pair of (pattern, (src_prefix, dest_prefix), where src_prefix
     # is the prefix of the pattern relevant to its location in the archive, and
     # dest_prefix is the prefix to be added that will yield the final path relative
     # to dist/.
--- a/python/mozbuild/mozbuild/backend/configenvironment.py
+++ b/python/mozbuild/mozbuild/backend/configenvironment.py
@@ -120,18 +120,21 @@ class ConfigEnvironment(object):
         self.source = source
         self.defines = ReadOnlyDict(defines or {})
         self.non_global_defines = non_global_defines or []
         self.substs = dict(substs or {})
         self.topsrcdir = mozpath.abspath(topsrcdir)
         self.topobjdir = mozpath.abspath(topobjdir)
         self.mozconfig = mozpath.abspath(mozconfig) if mozconfig else None
         self.lib_prefix = self.substs.get('LIB_PREFIX', '')
+        self.rust_lib_prefix = self.substs.get('RUST_LIB_PREFIX', '')
         if 'LIB_SUFFIX' in self.substs:
             self.lib_suffix = '.%s' % self.substs['LIB_SUFFIX']
+        if 'RUST_LIB_SUFFIX' in self.substs:
+            self.rust_lib_suffix = '.%s' % self.substs['RUST_LIB_SUFFIX']
         self.dll_prefix = self.substs.get('DLL_PREFIX', '')
         self.dll_suffix = self.substs.get('DLL_SUFFIX', '')
         if self.substs.get('IMPORT_LIB_SUFFIX'):
             self.import_prefix = self.lib_prefix
             self.import_suffix = '.%s' % self.substs['IMPORT_LIB_SUFFIX']
         else:
             self.import_prefix = self.dll_prefix
             self.import_suffix = self.dll_suffix
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -534,19 +534,19 @@ class RustLibrary(StaticLibrary):
         StaticLibrary.__init__(self, context, basename, **args)
         self.cargo_file = cargo_file
         self.crate_type = crate_type
         # We need to adjust our naming here because cargo replaces '-' in
         # package names defined in Cargo.toml with underscores in actual
         # filenames. But we need to keep the basename consistent because
         # many other things in the build system depend on that.
         assert self.crate_type == 'staticlib'
-        self.lib_name = '%s%s%s' % (context.config.lib_prefix,
+        self.lib_name = '%s%s%s' % (context.config.rust_lib_prefix,
                                      basename.replace('-', '_'),
-                                     context.config.lib_suffix)
+                                     context.config.rust_lib_suffix)
         self.dependencies = dependencies
         build_dir = mozpath.join(target_dir,
                                  cargo_output_directory(context, self.TARGET_SUBST_VAR))
         self.import_name = mozpath.join(build_dir, self.lib_name)
         self.deps_path = mozpath.join(build_dir, 'deps')
         self.features = features
         self.target_dir = target_dir
 
--- a/python/mozbuild/mozbuild/test/backend/common.py
+++ b/python/mozbuild/mozbuild/test/backend/common.py
@@ -43,60 +43,70 @@ CONFIGS = defaultdict(lambda: {
             'MOZ_WIDGET_TOOLKIT': 'android',
         },
     },
     'binary-components': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'LIB_PREFIX': 'lib',
+            'RUST_LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'RUST_LIB_SUFFIX': 'a',
             'COMPILE_ENVIRONMENT': '1',
         },
     },
     'rust-library': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'COMPILE_ENVIRONMENT': '1',
             'RUST_TARGET': 'x86_64-unknown-linux-gnu',
             'LIB_PREFIX': 'lib',
+            'RUST_LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'RUST_LIB_SUFFIX': 'a',
         },
     },
     'host-rust-library': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'COMPILE_ENVIRONMENT': '1',
             'RUST_HOST_TARGET': 'x86_64-unknown-linux-gnu',
             'RUST_TARGET': 'armv7-linux-androideabi',
             'LIB_PREFIX': 'lib',
+            'RUST_LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'RUST_LIB_SUFFIX': 'a',
         },
     },
     'host-rust-library-features': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'COMPILE_ENVIRONMENT': '1',
             'RUST_HOST_TARGET': 'x86_64-unknown-linux-gnu',
             'RUST_TARGET': 'armv7-linux-androideabi',
             'LIB_PREFIX': 'lib',
+            'RUST_LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'RUST_LIB_SUFFIX': 'a',
         },
     },
     'rust-library-features': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'COMPILE_ENVIRONMENT': '1',
             'RUST_TARGET': 'x86_64-unknown-linux-gnu',
             'LIB_PREFIX': 'lib',
+            'RUST_LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'RUST_LIB_SUFFIX': 'a',
         },
     },
     'rust-programs': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'COMPILE_ENVIRONMENT': '1',
             'RUST_TARGET': 'i686-pc-windows-msvc',
--- a/python/mozbuild/mozbuild/test/common.py
+++ b/python/mozbuild/mozbuild/test/common.py
@@ -37,14 +37,16 @@ class MockConfig(object):
 
         self.substs_unicode = ReadOnlyDict({k.decode('utf-8'): v.decode('utf-8',
             'replace') for k, v in self.substs.items()})
 
         self.defines = self.substs
 
         self.external_source_dir = None
         self.lib_prefix = 'lib'
+        self.rust_lib_prefix = 'lib'
         self.lib_suffix = '.a'
+        self.rust_lib_suffix = '.a'
         self.import_prefix = 'lib'
         self.import_suffix = '.so'
         self.dll_prefix = 'lib'
         self.dll_suffix = '.so'
         self.error_is_fatal = error_is_fatal
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -670,17 +670,17 @@ struct TransportSecurityPreload {
   bool mTestMode;
   bool mIsMoz;
   int32_t mId;
   const StaticFingerprints* pinset;
 };
 
 /* Sort hostnames for binary search. */
 static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
-  { "0.me.uk", true, true, false, -1, &kPinset_ncsccs },
+  { "0.me.uk", true, false, false, -1, &kPinset_ncsccs },
   { "2mdn.net", true, false, false, -1, &kPinset_google_root_pems },
   { "accounts.firefox.com", true, false, true, 4, &kPinset_mozilla_services },
   { "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "addons.mozilla.net", true, false, true, 2, &kPinset_mozilla_services },
   { "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla_services },
   { "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "android.com", true, false, false, -1, &kPinset_google_root_pems },
   { "api.accounts.firefox.com", true, false, true, 5, &kPinset_mozilla_services },
@@ -1044,17 +1044,17 @@ static const TransportSecurityPreload kP
   { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "mt.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "mtouch.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "mu.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "mw.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "mx.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "myaccount.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "myactivity.google.com", true, false, false, -1, &kPinset_google_root_pems },
-  { "ncsccs.com", true, true, false, -1, &kPinset_ncsccs },
+  { "ncsccs.com", true, false, false, -1, &kPinset_ncsccs },
   { "ni.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "nl.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "no.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "np.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "nz.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "pa.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "passwords.google.com", true, false, false, -1, &kPinset_google_root_pems },
@@ -1097,17 +1097,17 @@ static const TransportSecurityPreload kP
   { "t.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "tablet.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "telemetry.mozilla.org", true, true, true, 8, &kPinset_mozilla_services },
   { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
   { "testpilot.firefox.com", false, false, true, 9, &kPinset_mozilla_services },
   { "th.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
-  { "themathematician.uk", true, true, false, -1, &kPinset_ncsccs },
+  { "themathematician.uk", true, false, false, -1, &kPinset_ncsccs },
   { "torproject.org", false, false, false, -1, &kPinset_tor },
   { "touch.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "tr.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
   { "tv.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "tw.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
   { "twitter.com", true, false, false, -1, &kPinset_twitterCDN },
--- a/security/manager/tools/PreloadedHPKPins.json
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -29,17 +29,18 @@
     "cert_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.pins?format=TEXT",
     "json_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT",
     "substitute_pinsets": {
       // Use the larger google_root_pems pinset instead of google
       "google": "google_root_pems"
     },
     "production_pinsets": [
       "google_root_pems",
-      "facebook"
+      "facebook",
+      "ncsccs"
     ],
     "production_domains": [
       // Chrome's test domains.
       "pinningtest.appspot.com",
       "pinning-test.badssl.com",
       // Dropbox
       "dropbox.com",
       "www.dropbox.com",
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -533,20 +533,16 @@ public:
 #ifdef ANDROID
     case SYS_SOCKET:
       return Some(Error(EACCES));
 #else // #ifdef DESKTOP
     case SYS_RECV:
     case SYS_SEND:
     case SYS_SOCKET: // DANGEROUS
     case SYS_CONNECT: // DANGEROUS
-    case SYS_ACCEPT:
-    case SYS_ACCEPT4:
-    case SYS_BIND:
-    case SYS_LISTEN:
     case SYS_GETSOCKOPT:
     case SYS_SETSOCKOPT:
     case SYS_GETSOCKNAME:
     case SYS_GETPEERNAME:
     case SYS_SHUTDOWN:
       return Some(Allow());
 #endif
     default:
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -456,26 +456,26 @@ impl<'le> TElement for ServoLayoutElemen
     unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
         self.element.insert_selector_flags(flags);
     }
 
     fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
         self.element.has_selector_flags(flags)
     }
 
-    fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
-        panic!("this should be only called on gecko");
+    fn has_animations(&self) -> bool {
+        unreachable!("this should be only called on gecko");
     }
 
-    fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool {
-        panic!("this should be only called on gecko");
+    fn has_css_animations(&self) -> bool {
+        unreachable!("this should be only called on gecko");
     }
 
-    fn has_css_transitions(&self, _pseudo: Option<&PseudoElement>) -> bool {
-        panic!("this should be only called on gecko");
+    fn has_css_transitions(&self) -> bool {
+        unreachable!("this should be only called on gecko");
     }
 }
 
 impl<'le> PartialEq for ServoLayoutElement<'le> {
     fn eq(&self, other: &Self) -> bool {
         self.as_node() == other.as_node()
     }
 }
--- a/servo/components/script_plugins/unrooted_must_root.rs
+++ b/servo/components/script_plugins/unrooted_must_root.rs
@@ -90,33 +90,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> fo
                         id: ast::NodeId) {
         let item = match cx.tcx.hir.get(id) {
             ast_map::Node::NodeItem(item) => item,
             _ => cx.tcx.hir.expect_item(cx.tcx.hir.get_parent(id)),
         };
         if item.attrs.iter().all(|a| !a.check_name("must_root")) {
             for ref field in def.fields() {
                 let def_id = cx.tcx.hir.local_def_id(field.id);
-                if is_unrooted_ty(cx, cx.tcx.item_type(def_id), false) {
+                if is_unrooted_ty(cx, cx.tcx.type_of(def_id), false) {
                     cx.span_lint(UNROOTED_MUST_ROOT, field.span,
                                  "Type must be rooted, use #[must_root] on the struct definition to propagate")
                 }
             }
         }
     }
 
     /// All enums containing #[must_root] types must be #[must_root] themselves
     fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) {
         let ref map = cx.tcx.hir;
         if map.expect_item(map.get_parent(var.node.data.id())).attrs.iter().all(|a| !a.check_name("must_root")) {
             match var.node.data {
                 hir::VariantData::Tuple(ref fields, _) => {
                     for ref field in fields {
                         let def_id = cx.tcx.hir.local_def_id(field.id);
-                        if is_unrooted_ty(cx, cx.tcx.item_type(def_id), false) {
+                        if is_unrooted_ty(cx, cx.tcx.type_of(def_id), false) {
                             cx.span_lint(UNROOTED_MUST_ROOT, field.ty.span,
                                          "Type must be rooted, use #[must_root] on \
                                           the enum definition to propagate")
                         }
                     }
                 }
                 _ => () // Struct variants already caught by check_struct_def
             }
@@ -135,17 +135,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> fo
             visit::FnKind::Method(n, _, _, _) => {
                 &*n.as_str() == "new" || n.as_str().starts_with("new_")
             }
             visit::FnKind::Closure(_) => return,
         };
 
         if !in_derive_expn(span) {
             let def_id = cx.tcx.hir.local_def_id(id);
-            let ty = cx.tcx.item_type(def_id);
+            let ty = cx.tcx.type_of(def_id);
 
             for (arg, ty) in decl.inputs.iter().zip(ty.fn_args().0.iter()) {
                 if is_unrooted_ty(cx, ty, false) {
                     cx.span_lint(UNROOTED_MUST_ROOT, arg.span, "Type must be rooted")
                 }
             }
 
             if !in_new_function {
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -347,16 +347,32 @@ impl PropertyAnimation {
                             Point2D::new(p2.x as f64, p2.y as f64)).solve(time, epsilon)
             }
             TransitionTimingFunction::Steps(steps, StartEnd::Start) => {
                 (time * (steps as f64)).ceil() / (steps as f64)
             }
             TransitionTimingFunction::Steps(steps, StartEnd::End) => {
                 (time * (steps as f64)).floor() / (steps as f64)
             }
+            TransitionTimingFunction::Frames(frames) => {
+                // https://drafts.csswg.org/css-timing/#frames-timing-functions
+                let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64);
+                if out > 1.0 {
+                    // FIXME: Basically, during the animation sampling process, the input progress
+                    // should be in the range of [0, 1]. However, |time| is not accurate enough
+                    // here, which means |time| could be larger than 1.0 in the last animation
+                    // frame. (It should be equal to 1.0 exactly.) This makes the output of frames
+                    // timing function jumps to the next frame/level.
+                    // However, this solution is still not correct because |time| is possible
+                    // outside the range of [0, 1] after introducing Web Animations. We should fix
+                    // this problem when implementing web animations.
+                    out = 1.0;
+                }
+                out
+            }
         };
 
         self.property.update(style, progress);
     }
 
     #[inline]
     fn does_animate(&self) -> bool {
         self.property.does_animate() && self.duration.seconds() != 0.0
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -15,17 +15,16 @@ use dom::{OpaqueNode, TNode, TElement, S
 use error_reporting::ParseErrorReporter;
 use euclid::Size2D;
 use fnv::FnvHashMap;
 use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::structs;
 use matching::StyleSharingCandidateCache;
 use parking_lot::RwLock;
 #[cfg(feature = "gecko")] use properties::ComputedValues;
-#[cfg(feature = "gecko")] use selector_parser::PseudoElement;
 use selectors::matching::ElementSelectorFlags;
 #[cfg(feature = "servo")] use servo_config::opts;
 use shared_lock::StylesheetGuards;
 use std::collections::HashMap;
 #[cfg(not(feature = "servo"))] use std::env;
 use std::fmt;
 use std::ops::Add;
 use std::sync::{Arc, Mutex};
@@ -265,17 +264,18 @@ impl TraversalStatistics {
     /// from lots of tiny traversals.
     pub fn is_large_traversal(&self) -> bool {
         self.elements_traversed >= 50
     }
 }
 
 #[cfg(feature = "gecko")]
 bitflags! {
-    /// Represents which tasks are performed in a SequentialTask of UpdateAnimations.
+    /// Represents which tasks are performed in a SequentialTask of
+    /// UpdateAnimations.
     pub flags UpdateAnimationsTasks: u8 {
         /// Update CSS Animations.
         const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations,
         /// Update CSS Transitions.
         const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions,
         /// Update effect properties.
         const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties,
         /// Update animation cacade results for animations running on the compositor.
@@ -291,54 +291,52 @@ pub enum SequentialTask<E: TElement> {
     /// Entry to avoid an unused type parameter error on servo.
     Unused(SendElement<E>),
 
     /// Performs one of a number of possible tasks related to updating animations based on the
     /// |tasks| field. These include updating CSS animations/transitions that changed as part
     /// of the non-animation style traversal, and updating the computed effect properties.
     #[cfg(feature = "gecko")]
     UpdateAnimations {
-        /// The target element.
+        /// The target element or pseudo-element.
         el: SendElement<E>,
-        /// The target pseudo element.
-        pseudo: Option<PseudoElement>,
         /// The before-change style for transitions. We use before-change style as the initial
         /// value of its Keyframe. Required if |tasks| includes CSSTransitions.
         before_change_style: Option<Arc<ComputedValues>>,
         /// The tasks which are performed in this SequentialTask.
         tasks: UpdateAnimationsTasks
     },
 }
 
 impl<E: TElement> SequentialTask<E> {
     /// Executes this task.
     pub fn execute(self) {
         use self::SequentialTask::*;
         debug_assert!(thread_state::get() == thread_state::LAYOUT);
         match self {
             Unused(_) => unreachable!(),
             #[cfg(feature = "gecko")]
-            UpdateAnimations { el, pseudo, before_change_style, tasks } => {
-                unsafe { el.update_animations(pseudo.as_ref(), before_change_style, tasks) };
+            UpdateAnimations { el, before_change_style, tasks } => {
+                unsafe { el.update_animations(before_change_style, tasks) };
             }
         }
     }
 
     /// Creates a task to update various animation-related state on
     /// a given (pseudo-)element.
     #[cfg(feature = "gecko")]
     pub fn update_animations(el: E,
-                             pseudo: Option<PseudoElement>,
                              before_change_style: Option<Arc<ComputedValues>>,
                              tasks: UpdateAnimationsTasks) -> Self {
         use self::SequentialTask::*;
-        UpdateAnimations { el: unsafe { SendElement::new(el) },
-                           pseudo: pseudo,
-                           before_change_style: before_change_style,
-                           tasks: tasks }
+        UpdateAnimations {
+            el: unsafe { SendElement::new(el) },
+            before_change_style: before_change_style,
+            tasks: tasks,
+        }
     }
 }
 
 /// Map from Elements to ElementSelectorFlags. Used to defer applying selector
 /// flags until after the traversal.
 pub struct SelectorFlagsMap<E: TElement> {
     /// The hashmap storing the flags to apply.
     map: FnvHashMap<SendElement<E>, ElementSelectorFlags>,
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -527,16 +527,31 @@ impl ElementData {
 
     /// Sets the computed element styles.
     pub fn set_styles(&mut self, styles: ElementStyles) {
         debug_assert!(self.get_restyle().map_or(true, |r| r.snapshot.is_none()),
                       "Traversal should have expanded snapshots");
         self.styles = Some(styles);
     }
 
+    /// Sets the computed element rules, and returns whether the rules changed.
+    pub fn set_primary_rules(&mut self, rules: StrongRuleNode) -> bool {
+        if !self.has_styles() {
+            self.set_styles(ElementStyles::new(ComputedStyle::new_partial(rules)));
+            return true;
+        }
+
+        if self.styles().primary.rules == rules {
+            return false;
+        }
+
+        self.styles_mut().primary.rules = rules;
+        true
+    }
+
     /// Returns true if the Element has a RestyleData.
     pub fn has_restyle(&self) -> bool {
         self.restyle.is_some()
     }
 
     /// Drops any RestyleData.
     pub fn clear_restyle(&mut self) {
         self.restyle = None;
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -269,21 +269,30 @@ pub unsafe fn raw_note_descendants<E, B>
 /// A trait used to synthesize presentational hints for HTML element attributes.
 pub trait PresentationalHintsSynthetizer {
     /// Generate the proper applicable declarations due to presentational hints,
     /// and insert them into `hints`.
     fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
         where V: Push<ApplicableDeclarationBlock>;
 }
 
-/// The animation rules. The first one is for Animation cascade level, and the second one is for
+/// The animation rules.
+///
+/// The first one is for Animation cascade level, and the second one is for
 /// Transition cascade level.
 pub struct AnimationRules(pub Option<Arc<Locked<PropertyDeclarationBlock>>>,
                           pub Option<Arc<Locked<PropertyDeclarationBlock>>>);
 
+impl AnimationRules {
+    /// Returns whether these animation rules represents an actual rule or not.
+    pub fn is_empty(&self) -> bool {
+        self.0.is_none() && self.1.is_none()
+    }
+}
+
 /// The element trait, the main abstraction the style crate acts over.
 pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
                      ElementExt + PresentationalHintsSynthetizer {
     /// The concrete node type.
     type ConcreteNode: TNode<ConcreteElement = Self>;
 
     /// Type of the font metrics provider
     ///
@@ -314,37 +323,41 @@ pub trait TElement : Eq + PartialEq + De
         } else {
             self.parent_element()
         }
     }
 
     /// Get this element's style attribute.
     fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>>;
 
+    /// Get this element's SMIL override declarations.
+    fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
+        None
+    }
+
     /// Get this element's animation rules.
-    fn get_animation_rules(&self, _pseudo: Option<&PseudoElement>) -> AnimationRules {
+    fn get_animation_rules(&self) -> AnimationRules {
         AnimationRules(None, None)
     }
 
     /// Get this element's animation rule by the cascade level.
     fn get_animation_rule_by_cascade(&self,
-                                     _pseudo: Option<&PseudoElement>,
                                      _cascade_level: CascadeLevel)
                                      -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
         None
     }
 
     /// Get this element's animation rule.
-    fn get_animation_rule(&self, _pseudo: Option<&PseudoElement>)
+    fn get_animation_rule(&self)
                           -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
         None
     }
 
     /// Get this element's transition rule.
-    fn get_transition_rule(&self, _pseudo: Option<&PseudoElement>)
+    fn get_transition_rule(&self)
                            -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
         None
     }
 
     /// Get this element's state, for non-tree-structural pseudos.
     fn get_state(&self) -> ElementState;
 
     /// Whether this element has an attribute with a given namespace.
@@ -418,16 +431,28 @@ pub trait TElement : Eq + PartialEq + De
     /// Only safe to call with exclusive access to the element.
     unsafe fn unset_animation_only_dirty_descendants(&self) {
     }
 
     /// Returns true if this element is native anonymous (only Gecko has native
     /// anonymous content).
     fn is_native_anonymous(&self) -> bool { false }
 
+    /// Returns the pseudo-element implemented by this element, if any.
+    ///
+    /// Gecko traverses pseudo-elements during the style traversal, and we need
+    /// to know this so we can properly grab the pseudo-element style from the
+    /// parent element.
+    ///
+    /// Note that we still need to compute the pseudo-elements before-hand,
+    /// given otherwise we don't know if we need to create an element or not.
+    ///
+    /// Servo doesn't have to deal with this.
+    fn implemented_pseudo_element(&self) -> Option<PseudoElement> { None }
+
     /// Atomically stores the number of children of this node that we will
     /// need to process during bottom-up traversal.
     fn store_children_to_process(&self, n: isize);
 
     /// Atomically notes that a child has been processed during bottom-up
     /// traversal. Returns the number of children left to process.
     fn did_process_child(&self) -> isize;
 
@@ -459,76 +484,75 @@ pub trait TElement : Eq + PartialEq + De
     /// set to run after the threads join.
     unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags);
 
     /// Returns true if the element has all the specified selector flags.
     fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
 
     /// Creates a task to update various animation state on a given (pseudo-)element.
     #[cfg(feature = "gecko")]
-    fn update_animations(&self, _pseudo: Option<&PseudoElement>,
+    fn update_animations(&self,
                          before_change_style: Option<Arc<ComputedValues>>,
                          tasks: UpdateAnimationsTasks);
 
     /// Returns true if the element has relevant animations. Relevant
     /// animations are those animations that are affecting the element's style
     /// or are scheduled to do so in the future.
-    fn has_animations(&self, _pseudo: Option<&PseudoElement>) -> bool;
+    fn has_animations(&self) -> bool;
 
     /// Returns true if the element has a CSS animation.
-    fn has_css_animations(&self, _pseudo: Option<&PseudoElement>) -> bool;
+    fn has_css_animations(&self) -> bool;
 
     /// Returns true if the element has a CSS transition (including running transitions and
     /// completed transitions).
-    fn has_css_transitions(&self, _pseudo: Option<&PseudoElement>) -> bool;
+    fn has_css_transitions(&self) -> bool;
 
     /// Returns true if the element has animation restyle hints.
     fn has_animation_restyle_hints(&self) -> bool {
         let data = match self.borrow_data() {
             Some(d) => d,
             None => return false,
         };
         return data.get_restyle()
                    .map_or(false, |r| r.hint.has_animation_hint());
     }
 
     /// Gets the current existing CSS transitions, by |property, end value| pairs in a HashMap.
     #[cfg(feature = "gecko")]
-    fn get_css_transitions_info(&self,
-                                pseudo: Option<&PseudoElement>)
+    fn get_css_transitions_info(&self)
                                 -> HashMap<TransitionProperty, Arc<AnimationValue>>;
 
     /// Does a rough (and cheap) check for whether or not transitions might need to be updated that
     /// will quickly return false for the common case of no transitions specified or running. If
     /// this returns false, we definitely don't need to update transitions but if it returns true
     /// we can perform the more thoroughgoing check, needs_transitions_update, to further
     /// reduce the possibility of false positives.
     #[cfg(feature = "gecko")]
     fn might_need_transitions_update(&self,