Merge mozilla-central to mozilla-inbound
authorDorel Luca <dluca@mozilla.com>
Sat, 21 Apr 2018 02:06:35 +0300
changeset 468441 e2da4a4bac5c9cdcbae8cb9bd08c08fd407cec87
parent 468440 25a807fa07d104fbf5a7796e8ea6e29ce94e9c35 (current diff)
parent 468317 39ccabfd7d0712a45335325cb24b0e0b2ba498c7 (diff)
child 468442 29419d3ec2915214734a505fbb6de31acc8c9fe5
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
devtools/client/aboutdebugging/modules/worker.js
testing/web-platform/meta/MANIFEST.json
--- a/accessible/xul/XULFormControlAccessible.cpp
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -99,24 +99,19 @@ XULButtonAccessible::NativeState()
   // Buttons can be checked -- they simply appear pressed in rather than checked
   nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent));
   if (xulButtonElement) {
     nsAutoString type;
     xulButtonElement->GetType(type);
     if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
       state |= states::CHECKABLE;
       bool checked = false;
-      int32_t checkState = 0;
       xulButtonElement->GetChecked(&checked);
       if (checked) {
         state |= states::PRESSED;
-        xulButtonElement->GetCheckState(&checkState);
-        if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) {
-          state |= states::MIXED;
-        }
       }
     }
   }
 
   if (ContainsMenu())
     state |= states::HASPOPUP;
 
   if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
@@ -336,20 +331,16 @@ XULCheckboxAccessible::NativeState()
   // Determine Checked state
   nsCOMPtr<nsIDOMXULCheckboxElement> xulCheckboxElement =
     do_QueryInterface(mContent);
   if (xulCheckboxElement) {
     bool checked = false;
     xulCheckboxElement->GetChecked(&checked);
     if (checked) {
       state |= states::CHECKED;
-      int32_t checkState = 0;
-      xulCheckboxElement->GetCheckState(&checkState);
-      if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED)
-        state |= states::MIXED;
     }
   }
 
   return state;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULGroupboxAccessible
--- a/browser/components/migration/ESEDBReader.jsm
+++ b/browser/components/migration/ESEDBReader.jsm
@@ -113,16 +113,18 @@ function convertESEError(errorCode) {
     case -1002 /* JET_errInvalidName*/:
     case -1507 /* JET_errColumnNotFound */:
       // The DB format has changed and we haven't updated this migration code:
       return "The database format has changed, error code: " + errorCode;
     case -1032 /* JET_errFileAccessDenied */:
     case -1207 /* JET_errDatabaseLocked */:
     case -1302 /* JET_errTableLocked */:
       return "The database or table is locked, error code: " + errorCode;
+    case -1305 /* JET_errObjectNotFound */:
+      return "The table/object was not found.";
     case -1809 /* JET_errPermissionDenied*/:
     case -1907 /* JET_errAccessDenied */:
       return "Access or permission denied, error code: " + errorCode;
     case -1044 /* JET_errInvalidFilename */:
       return "Invalid file name";
     case -1811 /* JET_errFileNotFound */:
       return "File not found";
     case -550 /* JET_errDatabaseDirtyShutdown */:
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -47,38 +47,38 @@ XPCOMUtils.defineLazyGetter(this, "gEdge
 /**
  * Get rows from a table in the Edge DB as an array of JS objects.
  *
  * @param {String}            tableName the name of the table to read.
  * @param {String[]|function} columns   a list of column specifiers
  *                                      (see ESEDBReader.jsm) or a function that
  *                                      generates them based on the database
  *                                      reference once opened.
- * @param {function}          filterFn  a function that is called for each row.
+ * @param {nsIFile}           dbFile    the database file to use. Defaults to
+ *                                      the main Edge database.
+ * @param {function}          filterFn  Optional. A function that is called for each row.
  *                                      Only rows for which it returns a truthy
  *                                      value are included in the result.
- * @param {nsIFile}           dbFile    the database file to use. Defaults to
- *                                      the main Edge database.
  * @returns {Array} An array of row objects.
  */
-function readTableFromEdgeDB(tableName, columns, filterFn, dbFile = gEdgeDatabase) {
+function readTableFromEdgeDB(tableName, columns, dbFile = gEdgeDatabase, filterFn = null) {
   let database;
   let rows = [];
   try {
     let logFile = dbFile.parent;
     logFile.append("LogFiles");
     database = ESEDBReader.openDB(dbFile.parent, dbFile, logFile);
 
     if (typeof columns == "function") {
       columns = columns(database);
     }
 
     let tableReader = database.tableItems(tableName, columns);
     for (let row of tableReader) {
-      if (filterFn(row)) {
+      if (!filterFn || filterFn(row)) {
         rows.push(row);
       }
     }
   } catch (ex) {
     Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
                    dbFile.path + " due to the following error: " + ex);
     // Deliberately make this fail so we expose failure in the UI:
     throw ex;
@@ -109,17 +109,17 @@ EdgeTypedURLMigrator.prototype = {
 
   migrate(aCallback) {
     let typedURLs = this._typedURLs;
     let pageInfos = [];
     for (let [urlString, time] of typedURLs) {
       let url;
       try {
         url = new URL(urlString);
-        if (!["http", "https", "ftp"].includes(url.scheme)) {
+        if (!["http:", "https:", "ftp:"].includes(url.protocol)) {
           continue;
         }
       } catch (ex) {
         Cu.reportError(ex);
         continue;
       }
 
       pageInfos.push({
@@ -137,16 +137,96 @@ EdgeTypedURLMigrator.prototype = {
     }
 
     MigrationUtils.insertVisitsWrapper(pageInfos).then(
       () => aCallback(true),
       () => aCallback(false));
   },
 };
 
+function EdgeTypedURLDBMigrator() {
+}
+
+EdgeTypedURLDBMigrator.prototype = {
+  type: MigrationUtils.resourceTypes.HISTORY,
+
+  get db() { return gEdgeDatabase; },
+
+  get exists() {
+    return !!this.db;
+  },
+
+  migrate(callback) {
+    this._migrateTypedURLsFromDB().then(
+      () => callback(true),
+      ex => {
+        Cu.reportError(ex);
+        callback(false);
+      }
+    );
+  },
+
+  async _migrateTypedURLsFromDB() {
+    if (await ESEDBReader.dbLocked(this.db)) {
+      throw new Error("Edge seems to be running - its database is locked.");
+    }
+    let columns = [
+      {name: "URL", type: "string"},
+      {name: "AccessDateTimeUTC", type: "date"},
+    ];
+
+    let typedUrls = [];
+    try {
+      typedUrls = readTableFromEdgeDB("TypedUrls", columns, this.db);
+    } catch (ex) {
+      // Maybe the table doesn't exist (older versions of Win10).
+      // Just fall through and we'll return because there's no data.
+      // The `readTableFromEdgeDB` helper will report errors to the
+      // console anyway.
+    }
+    if (!typedUrls.length) {
+      return;
+    }
+
+    let pageInfos = [];
+    // Sometimes the values are bogus (e.g. 0 becomes some date in 1600),
+    // and places will throw *everything* away, not just the bogus ones,
+    // so deal with that by having a cutoff date. Also, there's not much
+    // point importing really old entries. The cut-off date is related to
+    // Edge's launch date.
+    const kDateCutOff = new Date("2016", 0, 1);
+    for (let typedUrlInfo of typedUrls) {
+      try {
+        let url = new URL(typedUrlInfo.URL);
+        if (!["http:", "https:", "ftp:"].includes(url.protocol)) {
+          continue;
+        }
+
+        let date = typedUrlInfo.AccessDateTimeUTC;
+        if (!date) {
+          date = kDateCutOff;
+        } else if (date < kDateCutOff) {
+          continue;
+        }
+
+        pageInfos.push({
+          url,
+          visits: [{
+            transition: PlacesUtils.history.TRANSITIONS.TYPED,
+            date,
+          }],
+        });
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+    await MigrationUtils.insertVisitsWrapper(pageInfos);
+  },
+};
+
 function EdgeReadingListMigrator(dbOverride) {
   this.dbOverride = dbOverride;
 }
 
 EdgeReadingListMigrator.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   get db() { return this.dbOverride || gEdgeDatabase; },
@@ -183,17 +263,17 @@ EdgeReadingListMigrator.prototype = {
       }
       return columns;
     };
 
     let filterFn = row => {
       return !row.IsDeleted;
     };
 
-    let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, filterFn, this.db);
+    let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, this.db, filterFn);
     if (!readingListItems.length) {
       return;
     }
 
     let destFolderGuid = await this._ensureReadingListFolder(parentGuid);
     let bookmarks = [];
     for (let item of readingListItems) {
       let dateAdded = item.AddedDate || new Date();
@@ -282,17 +362,17 @@ EdgeBookmarksMigrator.prototype = {
       if (row.IsDeleted) {
         return false;
       }
       if (row.IsFolder) {
         folderMap.set(row.ItemId, row);
       }
       return true;
     };
-    let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn, this.db);
+    let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, this.db, filterFn);
     let toplevelBMs = [], toolbarBMs = [];
     for (let bookmark of bookmarks) {
       let bmToInsert;
       // Ignore invalid URLs:
       if (!bookmark.IsFolder) {
         try {
           new URL(bookmark.URL);
         } catch (ex) {
@@ -352,16 +432,17 @@ EdgeProfileMigrator.prototype.getReading
   return new EdgeReadingListMigrator(dbOverride);
 };
 
 EdgeProfileMigrator.prototype.getResources = function() {
   let resources = [
     new EdgeBookmarksMigrator(),
     MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
     new EdgeTypedURLMigrator(),
+    new EdgeTypedURLDBMigrator(),
     new EdgeReadingListMigrator(),
   ];
   let windowsVaultFormPasswordsMigrator =
     MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
   windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
   resources.push(windowsVaultFormPasswordsMigrator);
   return resources.filter(r => r.exists);
 };
--- a/browser/themes/osx/customizableui/panelUI.css
+++ b/browser/themes/osx/customizableui/panelUI.css
@@ -11,17 +11,17 @@
 .subviewbutton {
   padding-inline-start: 18px;
 }
 
 .subviewbutton.download {
   padding-inline-start: 14px;
 }
 
-panelmultiview .toolbaritem-combined-buttons > spacer {
+panelmultiview .toolbaritem-combined-buttons > spacer.before-label {
   width: 42px; /* 18px toolbarbutton padding + 16px icon + 8px label padding start */
 }
 
 #appMenu-addon-banners > .addon-banner-item,
 #appMenu-mainView > .panel-subview-body > .panel-banner-item {
   padding-inline-start: 18px;
 }
 
--- a/devtools/client/aboutdebugging/components/workers/Panel.js
+++ b/devtools/client/aboutdebugging/components/workers/Panel.js
@@ -2,21 +2,19 @@
  * 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/. */
 /* globals window */
 "use strict";
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-const { Ci } = require("chrome");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { getWorkerForms } = require("../../modules/worker");
 const Services = require("Services");
 
 const PanelHeader = createFactory(require("../PanelHeader"));
 const TargetList = createFactory(require("../TargetList"));
 const WorkerTarget = createFactory(require("./Target"));
 const MultiE10SWarning = createFactory(require("./MultiE10sWarning"));
 const ServiceWorkerTarget = createFactory(require("./ServiceWorkerTarget"));
 
@@ -43,17 +41,16 @@ class WorkersPanel extends Component {
     };
   }
 
   constructor(props) {
     super(props);
 
     this.updateMultiE10S = this.updateMultiE10S.bind(this);
     this.updateWorkers = this.updateWorkers.bind(this);
-    this.getRegistrationForWorker = this.getRegistrationForWorker.bind(this);
     this.isE10S = this.isE10S.bind(this);
     this.renderServiceWorkersError = this.renderServiceWorkersError.bind(this);
 
     this.state = this.initialState;
   }
 
   componentDidMount() {
     let client = this.props.client;
@@ -108,83 +105,29 @@ class WorkersPanel extends Component {
     // nsIXULRuntime.maxWebProcessCount.
     let processCount = Services.appinfo.maxWebProcessCount;
     this.setState({ processCount });
   }
 
   updateWorkers() {
     let workers = this.initialState.workers;
 
-    getWorkerForms(this.props.client).then(forms => {
-      forms.registrations.forEach(form => {
-        workers.service.push({
-          icon: WorkerIcon,
-          name: form.url,
-          url: form.url,
-          scope: form.scope,
-          fetch: form.fetch,
-          registrationActor: form.actor,
-          active: form.active
-        });
-      });
-
-      forms.workers.forEach(form => {
-        let worker = {
-          icon: WorkerIcon,
-          name: form.url,
-          url: form.url,
-          workerActor: form.actor
-        };
-        switch (form.type) {
-          case Ci.nsIWorkerDebugger.TYPE_SERVICE:
-            let registration = this.getRegistrationForWorker(form, workers.service);
-            if (registration) {
-              // XXX: Race, sometimes a ServiceWorkerRegistrationInfo doesn't
-              // have a scriptSpec, but its associated WorkerDebugger does.
-              if (!registration.url) {
-                registration.name = registration.url = form.url;
-              }
-              registration.workerActor = form.actor;
-            } else {
-              worker.fetch = form.fetch;
-
-              // If a service worker registration could not be found, this means we are in
-              // e10s, and registrations are not forwarded to other processes until they
-              // reach the activated state. Augment the worker as a registration worker to
-              // display it in aboutdebugging.
-              worker.scope = form.scope;
-              worker.active = false;
-              workers.service.push(worker);
-            }
-            break;
-          case Ci.nsIWorkerDebugger.TYPE_SHARED:
-            workers.shared.push(worker);
-            break;
-          default:
-            workers.other.push(worker);
-        }
-      });
+    this.props.client.mainRoot.listAllWorkers().then(({service, other, shared}) => {
+      workers.service = service.map(f => Object.assign({ icon: WorkerIcon }, f));
+      workers.other = other.map(f => Object.assign({ icon: WorkerIcon }, f));
+      workers.shared = shared.map(f => Object.assign({ icon: WorkerIcon }, f));
 
       // XXX: Filter out the service worker registrations for which we couldn't
       // find the scriptSpec.
       workers.service = workers.service.filter(reg => !!reg.url);
 
       this.setState({ workers });
     });
   }
 
-  getRegistrationForWorker(form, registrations) {
-    for (let registration of registrations) {
-      if (registration.scope === form.scope) {
-        return registration;
-      }
-    }
-    return null;
-  }
-
   isE10S() {
     return Services.appinfo.browserTabsRemoteAutostart;
   }
 
   renderServiceWorkersError() {
     let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
     let isPrivateBrowsingMode = PrivateBrowsingUtils.permanentPrivateBrowsing;
     let isServiceWorkerDisabled = !Services.prefs
--- a/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
+++ b/devtools/client/aboutdebugging/components/workers/ServiceWorkerTarget.js
@@ -4,21 +4,22 @@
 
 /* eslint-env browser */
 
 "use strict";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { debugWorker } = require("../../modules/worker");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/debugger-client", true);
+loader.lazyRequireGetter(this, "gDevToolsBrowser",
+  "devtools/client/framework/devtools-browser", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 class ServiceWorkerTarget extends Component {
   static get propTypes() {
     return {
       client: PropTypes.instanceOf(DebuggerClient).isRequired,
@@ -80,17 +81,17 @@ class ServiceWorkerTarget extends Compon
 
   debug() {
     if (!this.isRunning()) {
       // If the worker is not running, we can't debug it.
       return;
     }
 
     let { client, target } = this.props;
-    debugWorker(client, target.workerActor);
+    gDevToolsBrowser.openWorkerToolbox(client, target.workerActor);
   }
 
   push() {
     if (!this.isActive() || !this.isRunning()) {
       // If the worker is not running, we can't push to it.
       // If the worker is not active, the registration might be unavailable and the
       // push will not succeed.
       return;
--- a/devtools/client/aboutdebugging/components/workers/Target.js
+++ b/devtools/client/aboutdebugging/components/workers/Target.js
@@ -4,21 +4,22 @@
 
 /* eslint-env browser */
 
 "use strict";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { debugWorker } = require("../../modules/worker");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/debugger-client", true);
+loader.lazyRequireGetter(this, "gDevToolsBrowser",
+  "devtools/client/framework/devtools-browser", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 class WorkerTarget extends Component {
   static get propTypes() {
     return {
       client: PropTypes.instanceOf(DebuggerClient).isRequired,
@@ -33,17 +34,17 @@ class WorkerTarget extends Component {
 
   constructor(props) {
     super(props);
     this.debug = this.debug.bind(this);
   }
 
   debug() {
     let { client, target } = this.props;
-    debugWorker(client, target.workerActor);
+    gDevToolsBrowser.openWorkerToolbox(client, target.workerActor);
   }
 
   render() {
     let { target, debugDisabled } = this.props;
 
     return dom.li({ className: "target-container" },
       dom.img({
         className: "target-icon",
--- a/devtools/client/aboutdebugging/modules/moz.build
+++ b/devtools/client/aboutdebugging/modules/moz.build
@@ -1,9 +1,8 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'addon.js',
     'connect.js',
-    'worker.js',
 )
deleted file mode 100644
--- a/devtools/client/aboutdebugging/modules/worker.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/* 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";
-
-loader.lazyRequireGetter(this, "gDevTools",
-  "devtools/client/framework/devtools", true);
-loader.lazyRequireGetter(this, "TargetFactory",
-  "devtools/client/framework/target", true);
-loader.lazyRequireGetter(this, "Toolbox",
-  "devtools/client/framework/toolbox", true);
-
-/**
- * Open a window-hosted toolbox to debug the worker associated to the provided
- * worker actor.
- *
- * @param {DebuggerClient} client
- * @param {Object} workerActor
- *        worker actor form to debug
- */
-exports.debugWorker = function(client, workerActor) {
-  client.attachWorker(workerActor, (response, workerClient) => {
-    let workerTarget = TargetFactory.forWorker(workerClient);
-    gDevTools.showToolbox(workerTarget, "jsdebugger", Toolbox.HostType.WINDOW)
-      .then(toolbox => {
-        toolbox.once("destroy", () => workerClient.detach());
-      });
-  });
-};
-
-/**
- * Retrieve all service worker registrations as well as workers from the parent
- * and child processes.
- *
- * @param {DebuggerClient} client
- * @return {Object}
- *         - {Array} registrations
- *           Array of ServiceWorkerRegistrationActor forms
- *         - {Array} workers
- *           Array of WorkerActor forms
- */
-exports.getWorkerForms = async function(client) {
-  let registrations = [];
-  let workers = [];
-
-  try {
-    // List service worker registrations
-    ({ registrations } =
-      await client.mainRoot.listServiceWorkerRegistrations());
-
-    // List workers from the Parent process
-    ({ workers } = await client.mainRoot.listWorkers());
-
-    // And then from the Child processes
-    let { processes } = await client.mainRoot.listProcesses();
-    for (let process of processes) {
-      // Ignore parent process
-      if (process.parent) {
-        continue;
-      }
-      let { form } = await client.getProcess(process.id);
-      let processActor = form.actor;
-      let response = await client.request({
-        to: processActor,
-        type: "listWorkers"
-      });
-      workers = workers.concat(response.workers);
-    }
-  } catch (e) {
-    // Something went wrong, maybe our client is disconnected?
-  }
-
-  return { registrations, workers };
-};
--- a/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
@@ -25,16 +25,21 @@ add_task(async function() {
 
   let swTab = await addTab(TAB_URL, { background: true });
 
   info("Wait for service worker to appear in the list");
   // Check that the service worker appears in the UI
   let serviceWorkerContainer =
     await waitUntilServiceWorkerContainer(SERVICE_WORKER, document);
 
+  info("Wait until the service worker is running and the Debug button appears");
+  await waitUntil(() => {
+    return !!getDebugButton(serviceWorkerContainer);
+  }, 100);
+
   info("Check that service worker buttons are disabled.");
   let debugButton = getDebugButton(serviceWorkerContainer);
   ok(debugButton.disabled, "Start/Debug button is disabled");
 
   info("Update the preference to 1");
   let onWarningCleared = waitUntil(() => {
     let hasWarning = document.querySelector(".service-worker-multi-process");
     return !hasWarning && !debugButton.disabled;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/application.css
@@ -0,0 +1,23 @@
+/* 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/. */
+
+@import "resource://devtools/client/application/src/components/App.css";
+@import "resource://devtools/client/application/src/components/Worker.css";
+@import "resource://devtools/client/application/src/components/WorkerList.css";
+
+* {
+  box-sizing: border-box;
+}
+
+html,
+body,
+#mount {
+  height: 100%;
+}
+
+ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/index.html
@@ -0,0 +1,14 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html dir="">
+  <head>
+    <link rel="stylesheet" type="text/css" href="resource://devtools/client/application/application.css" />
+  </head>
+  <body class="theme-body" role="application">
+    <div id="mount"></div>
+    <script src="chrome://devtools/content/shared/theme-switching.js"></script>
+    <script src="initializer.js"></script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/initializer.js
@@ -0,0 +1,74 @@
+/* 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 { BrowserLoader } = ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
+const require = BrowserLoader({
+  baseURI: "resource://devtools/client/application/",
+  window,
+}).require;
+
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
+const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
+
+const { configureStore } = require("./src/create-store");
+const actions = require("./src/actions/index");
+
+const App = createFactory(require("./src/components/App"));
+
+/**
+ * Global Application object in this panel. This object is expected by panel.js and is
+ * called to start the UI for the panel.
+ */
+window.Application = {
+  async bootstrap({ toolbox, panel }) {
+    this.updateWorkers = this.updateWorkers.bind(this);
+
+    this.mount = document.querySelector("#mount");
+    this.toolbox = toolbox;
+    this.client = toolbox.target.client;
+
+    this.store = configureStore();
+    this.actions = bindActionCreators(actions, this.store.dispatch);
+
+    const serviceContainer = {
+      openAboutDebugging() {
+        let win = toolbox.doc.defaultView.top;
+        win.openUILinkIn("about:debugging#workers", "tab", { relatedToCurrent: true });
+      }
+    };
+
+    // Render the root Application component.
+    const app = App({ client: this.client, serviceContainer });
+    render(Provider({ store: this.store }, app), this.mount);
+
+    this.client.addListener("workerListChanged", this.updateWorkers);
+    this.client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+    this.client.addListener("registration-changed", this.updateWorkers);
+    this.client.addListener("processListChanged", this.updateWorkers);
+
+    await this.updateWorkers();
+  },
+
+  async updateWorkers() {
+    let { service } = await this.client.mainRoot.listAllWorkers();
+    this.actions.updateWorkers(service);
+  },
+
+  destroy() {
+    this.client.removeListener("workerListChanged", this.updateWorkers);
+    this.client.removeListener("serviceWorkerRegistrationListChanged",
+      this.updateWorkers);
+    this.client.removeListener("registration-changed", this.updateWorkers);
+    this.client.removeListener("processListChanged", this.updateWorkers);
+
+    unmountComponentAtNode(this.mount);
+    this.mount = null;
+    this.toolbox = null;
+    this.client = null;
+  },
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/moz.build
@@ -0,0 +1,12 @@
+# 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/.
+
+DIRS += [
+    'src',
+]
+
+DevToolsModules(
+    'application.css',
+    'panel.js'
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/panel.js
@@ -0,0 +1,49 @@
+/* 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";
+
+/**
+ * DevTools panel responsible for the application tool, which lists and allows to debug
+ * service workers.
+ */
+class ApplicationPanel {
+  /**
+   * Constructor.
+   *
+   * @param {Window} panelWin
+   *        The frame/window dedicated to this panel.
+   * @param {Toolbox} toolbox
+   *        The toolbox instance responsible for this panel.
+   */
+  constructor(panelWin, toolbox) {
+    this.panelWin = panelWin;
+    this.toolbox = toolbox;
+  }
+
+  async open() {
+    if (!this.toolbox.target.isRemote) {
+      await this.toolbox.target.makeRemote();
+    }
+
+    await this.panelWin.Application.bootstrap({
+      toolbox: this.toolbox,
+      panel: this,
+    });
+    this.emit("ready");
+    this.isReady = true;
+    return this;
+  }
+
+  destroy() {
+    this.panelWin.Application.destroy();
+    this.panelWin = null;
+    this.toolbox = null;
+    this.emit("destroyed");
+
+    return this;
+  }
+}
+
+exports.ApplicationPanel = ApplicationPanel;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/actions/index.js
@@ -0,0 +1,11 @@
+/* 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 workers = require("./workers");
+
+Object.assign(exports,
+  workers,
+);
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/actions/moz.build
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'index.js',
+    'workers.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/actions/workers.js
@@ -0,0 +1,20 @@
+/* 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 {
+  UPDATE_WORKERS,
+} = require("../constants");
+
+function updateWorkers(workers) {
+  return {
+    type: UPDATE_WORKERS,
+    workers
+  };
+}
+
+module.exports = {
+  updateWorkers,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/App.css
@@ -0,0 +1,44 @@
+/* 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/. */
+
+/*
+ * The current layout of the application panel is
+ *
+ *  +---------------------------------------------+
+ *  | (header) "Service workers"                  |
+ *  +---------------------------------------------+
+ *  | Service worker 1                            |
+ *  |   (...)                                     |
+ *  | Service worker N           (see Worker.css) |
+ *  +---------------------------------------------+
+ *  |                     Link to about:debugging |
+ *  +---------------------------------------------+
+ */
+.application {
+  height: 100%;
+  padding: 0 0 0 20px;
+  overflow: auto;
+  display: flex;
+  flex-direction: column;
+}
+
+h1 {
+  font-size: 22px;
+  font-weight: normal;
+}
+
+a,
+a:hover,
+a:visited {
+  color: var(--blue-60) !important;
+  margin: 0 10px;
+  cursor: pointer;
+}
+
+a.disabled,
+a.disabled:hover,
+a.disabled:visited {
+  color: var(--grey-30) !important;
+  cursor: default;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/App.js
@@ -0,0 +1,38 @@
+/* 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+
+const WorkerList = createFactory(require("./WorkerList"));
+
+/**
+ * This is the main component for the application panel.
+ */
+class App extends Component {
+  static get propTypes() {
+    return {
+      client: PropTypes.object.isRequired,
+      workers: PropTypes.object.isRequired,
+      serviceContainer: PropTypes.object.isRequired,
+    };
+  }
+
+  render() {
+    let { workers, client, serviceContainer } = this.props;
+
+    return div({className: "application"},
+      WorkerList({ workers, client, serviceContainer }));
+  }
+}
+
+// Exports
+
+module.exports = connect(
+  (state) => ({ workers: state.workers.list }),
+)(App);
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/Worker.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+ /*
+ * The current layout of a service worker item is
+ *
+ *  +----------------------------+----------------+
+ *  | Service worker scope       | Unregister btn |
+ *  +---+----------+-------------+----------------|
+ *  |   | "Source" | script name | debug link     |
+ *  |   |----------+-------------+----------------|
+ *  |   | "Status" | status      | start link     |
+ *  +---+----------+-------------+----------------|
+ */
+.service-worker-container {
+  margin-bottom: 20px;
+  width: 100%;
+  max-width: 600px;
+  position: relative;
+  line-height: 1.5;
+  font-size: 13px;
+}
+
+.service-worker-container > .service-worker-scope {
+  padding-inline-start: 30px;
+}
+
+.service-worker-container > :not(.service-worker-scope) {
+  padding-inline-start: 70px;
+}
+
+.service-worker-scope {
+  font-weight: bold;
+}
+
+.service-worker-meta-name {
+  color: var(--grey-50);
+  min-width: 50px;
+  margin-inline-end: 10px;
+  display: inline-block;
+}
+
+.unregister-button {
+  position: absolute;
+  right: 0;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/Worker.js
@@ -0,0 +1,157 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { a, button, div, li, span } = require("devtools/client/shared/vendor/react-dom-factories");
+const Services = require("Services");
+
+loader.lazyRequireGetter(this, "DebuggerClient",
+  "devtools/shared/client/debugger-client", true);
+loader.lazyRequireGetter(this, "gDevToolsBrowser",
+  "devtools/client/framework/devtools-browser", true);
+
+const Strings = Services.strings.createBundle(
+  "chrome://devtools/locale/aboutdebugging.properties");
+
+/**
+ * This component is dedicated to display a worker, more accurately a service worker, in
+ * the list of workers displayed in the application panel. It displays information about
+ * the worker as well as action links and buttons to interact with the worker (e.g. debug,
+ * unregister, update etc...).
+ */
+class Worker extends Component {
+  static get propTypes() {
+    return {
+      client: PropTypes.instanceOf(DebuggerClient).isRequired,
+      debugDisabled: PropTypes.bool,
+      worker: PropTypes.shape({
+        active: PropTypes.bool,
+        name: PropTypes.string.isRequired,
+        scope: PropTypes.string.isRequired,
+        // registrationActor can be missing in e10s.
+        registrationActor: PropTypes.string,
+        workerActor: PropTypes.string
+      }).isRequired
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.debug = this.debug.bind(this);
+    this.start = this.start.bind(this);
+    this.unregister = this.unregister.bind(this);
+  }
+
+  debug() {
+    if (!this.isRunning()) {
+      console.log("Service workers cannot be debugged if they are not running");
+      return;
+    }
+
+    let { client, worker } = this.props;
+    gDevToolsBrowser.openWorkerToolbox(client, worker.workerActor);
+  }
+
+  start() {
+    if (!this.isActive() || this.isRunning()) {
+      console.log("Running or inactive service workers cannot be started");
+      return;
+    }
+
+    let { client, worker } = this.props;
+    client.request({
+      to: worker.registrationActor,
+      type: "start"
+    });
+  }
+
+  unregister() {
+    let { client, worker } = this.props;
+    client.request({
+      to: worker.registrationActor,
+      type: "unregister"
+    });
+  }
+
+  isRunning() {
+    // We know the worker is running if it has a worker actor.
+    return !!this.props.worker.workerActor;
+  }
+
+  isActive() {
+    return this.props.worker.active;
+  }
+
+  getServiceWorkerStatus() {
+    if (this.isActive() && this.isRunning()) {
+      return "running";
+    } else if (this.isActive()) {
+      return "stopped";
+    }
+    // We cannot get service worker registrations unless the registration is in
+    // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
+    // display a custom state "registering" for now. See Bug 1153292.
+    return "registering";
+  }
+
+  formatScope(scope) {
+    let [, remainder] = scope.split("://");
+    return remainder || scope;
+  }
+
+  formatSource(source) {
+    let parts = source.split("/");
+    return parts[parts.length - 1];
+  }
+
+  render() {
+    let { worker } = this.props;
+    let status = this.getServiceWorkerStatus();
+
+    const unregisterButton = this.isActive() ?
+      button({
+        onClick: this.unregister,
+        className: "devtools-button unregister-button",
+        "data-standalone": true
+      },
+        Strings.GetStringFromName("unregister"))
+      : null;
+
+    const debugLinkDisabled = this.isRunning() ? "" : "disabled";
+    const debugLink = a({
+      onClick: this.isRunning() ? this.debug : null,
+      title: this.isRunning() ? null : "Only running service workers can be debugged",
+      className: `${debugLinkDisabled} debug-link`
+    },
+      Strings.GetStringFromName("debug"));
+
+    const startLink = !this.isRunning() ?
+      a({ onClick: this.start, className: "start-link" },
+        Strings.GetStringFromName("start"))
+      : null;
+
+    return li({ className: "service-worker-container" },
+      div(
+        { className: "service-worker-scope" },
+        span({ title: worker.scope }, this.formatScope(worker.scope)),
+        unregisterButton),
+      div(
+        { className: "service-worker-source" },
+        span({ className: "service-worker-meta-name" }, "Source"),
+        span({ title: worker.scope }, this.formatSource(worker.url)),
+        debugLink),
+      div(
+        { className: `service-worker-status service-worker-status-${status}` },
+        span({ className: "service-worker-meta-name" }, "Status"),
+        Strings.GetStringFromName(status).toLowerCase(),
+        startLink)
+    );
+  }
+}
+
+module.exports = Worker;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/WorkerList.css
@@ -0,0 +1,12 @@
+/* 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/. */
+
+.application-aboutdebugging-plug {
+  text-align: right;
+  padding: 5px 0;
+}
+
+.application-workers-container {
+  flex-grow: 1;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/WorkerList.js
@@ -0,0 +1,51 @@
+/* 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 PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+const { a, div, h1, ul, li } = require("devtools/client/shared/vendor/react-dom-factories");
+const Worker = createFactory(require("./Worker"));
+
+/**
+ * This component handles the list of service workers displayed in the application panel
+ * and also displays a suggestion to use about debugging for debugging other service
+ * workers.
+ */
+class WorkerList extends Component {
+  static get propTypes() {
+    return {
+      client: PropTypes.object.isRequired,
+      workers: PropTypes.object.isRequired,
+      serviceContainer: PropTypes.object.isRequired,
+    };
+  }
+
+  render() {
+    const { workers, client, serviceContainer } = this.props;
+    const { openAboutDebugging } = serviceContainer;
+
+    return [
+      ul({ className: "application-workers-container" },
+        li({},
+          h1({ className: "application-title" }, "Service Workers")
+        ),
+        workers.map(worker => Worker({
+          client,
+          debugDisabled: false,
+          worker,
+        }))
+      ),
+      div({ className: "application-aboutdebugging-plug" },
+        "See about:debugging for Service Workers from other domains",
+        a({ onClick: () => openAboutDebugging() }, "Open about:debugging")
+      )
+    ];
+  }
+}
+
+// Exports
+
+module.exports = WorkerList;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/moz.build
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'App.css',
+    'App.js',
+    'Worker.css',
+    'Worker.js',
+    'WorkerList.css',
+    'WorkerList.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/constants.js
@@ -0,0 +1,12 @@
+/* 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 actionTypes = {
+  UPDATE_WORKERS: "UPDATE_WORKERS",
+};
+
+// flatten constants
+module.exports = Object.assign({}, actionTypes);
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/create-store.js
@@ -0,0 +1,22 @@
+/* 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 { createStore } = require("devtools/client/shared/vendor/redux");
+
+// Reducers
+const rootReducer = require("./reducers/index");
+const { WorkersState } = require("./reducers/workers-state");
+
+function configureStore() {
+  // Prepare initial state.
+  const initialState = {
+    workers: new WorkersState(),
+  };
+
+  return createStore(rootReducer, initialState);
+}
+
+exports.configureStore = configureStore;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/moz.build
@@ -0,0 +1,14 @@
+# 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/.
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+]
+
+DevToolsModules(
+    'constants.js',
+    'create-store.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/reducers/index.js
@@ -0,0 +1,12 @@
+/* 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 { combineReducers } = require("devtools/client/shared/vendor/redux");
+const { workersReducer } = require("./workers-state");
+
+module.exports = combineReducers({
+  workers: workersReducer,
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/reducers/moz.build
@@ -0,0 +1,8 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'index.js',
+    'workers-state.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/reducers/workers-state.js
@@ -0,0 +1,33 @@
+/* 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 {
+  UPDATE_WORKERS,
+} = require("../constants");
+
+function WorkersState() {
+  return {
+    // Array of all service workers
+    list: [],
+  };
+}
+
+function workersReducer(state = WorkersState(), action) {
+  switch (action.type) {
+    case UPDATE_WORKERS: {
+      let { workers } = action;
+      return { list: workers };
+    }
+
+    default:
+      return state;
+  }
+}
+
+module.exports = {
+  WorkersState,
+  workersReducer,
+};
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -20,16 +20,17 @@ loader.lazyGetter(this, "WebAudioEditorP
 loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
 loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
 loader.lazyGetter(this, "NewPerformancePanel", () => require("devtools/client/performance-new/panel").PerformancePanel);
 loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
 loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
 loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
 loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);
 loader.lazyGetter(this, "AccessibilityPanel", () => require("devtools/client/accessibility/accessibility-panel").AccessibilityPanel);
+loader.lazyGetter(this, "ApplicationPanel", () => require("devtools/client/application/panel").ApplicationPanel);
 
 // Other dependencies
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "CommandState", "devtools/shared/gcli/command-state", true);
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 const {MultiLocalizationHelper} = require("devtools/shared/l10n");
@@ -439,32 +440,54 @@ Tools.accessibility = {
     return target.hasActor("accessibility");
   },
 
   build(iframeWindow, toolbox) {
     return new AccessibilityPanel(iframeWindow, toolbox);
   }
 };
 
+Tools.application = {
+  id: "application",
+  ordinal: 15,
+  visibilityswitch: "devtools.application.enabled",
+  icon: "chrome://devtools/skin/images/tool-application.svg",
+  url: "chrome://devtools/content/application/index.html",
+  label: "Application",
+  panelLabel: "Application",
+  tooltip: "Application",
+  inMenu: false,
+  hiddenInOptions: true,
+
+  isTargetSupported: function(target) {
+    return target.isLocalTab;
+  },
+
+  build: function(iframeWindow, toolbox) {
+    return new ApplicationPanel(iframeWindow, toolbox);
+  }
+};
+
 var defaultTools = [
   Tools.options,
   Tools.webConsole,
   Tools.inspector,
   Tools.jsdebugger,
   Tools.styleEditor,
   Tools.shaderEditor,
   Tools.canvasDebugger,
   Tools.webAudioEditor,
   Tools.performance,
   Tools.netMonitor,
   Tools.storage,
   Tools.scratchpad,
   Tools.memory,
   Tools.dom,
   Tools.accessibility,
+  Tools.application,
 ];
 
 exports.defaultTools = defaultTools;
 
 Tools.darkTheme = {
   id: "dark",
   label: l10n("options.darkTheme.label2"),
   ordinal: 1,
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -384,16 +384,34 @@ var gDevToolsBrowser = exports.gDevTools
     }
 
     let msg = L10N.getStr("toolbox.noContentProcessForTab.message");
     Services.prompt.alert(null, "", msg);
     return Promise.reject(msg);
   },
 
   /**
+   * Open a window-hosted toolbox to debug the worker associated to the provided
+   * worker actor.
+   *
+   * @param  {DebuggerClient} client
+   * @param  {Object} workerActor
+   *         worker actor form to debug
+   */
+  openWorkerToolbox(client, workerActor) {
+    client.attachWorker(workerActor, (response, workerClient) => {
+      let workerTarget = TargetFactory.forWorker(workerClient);
+      gDevTools.showToolbox(workerTarget, null, Toolbox.HostType.WINDOW)
+        .then(toolbox => {
+          toolbox.once("destroy", () => workerClient.detach());
+        });
+    });
+  },
+
+  /**
    * Install WebIDE widget
    */
   // Used by itself
   installWebIDEWidget() {
     if (this.isWebIDEWidgetInstalled()) {
       return;
     }
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -217,16 +217,17 @@ devtools.jar:
     skin/images/tool-storage.svg (themes/images/tool-storage.svg)
     skin/images/tool-profiler.svg (themes/images/tool-profiler.svg)
     skin/images/tool-network.svg (themes/images/tool-network.svg)
     skin/images/tool-scratchpad.svg (themes/images/tool-scratchpad.svg)
     skin/images/tool-webaudio.svg (themes/images/tool-webaudio.svg)
     skin/images/tool-memory.svg (themes/images/tool-memory.svg)
     skin/images/tool-dom.svg (themes/images/tool-dom.svg)
     skin/images/tool-accessibility.svg (themes/images/tool-accessibility.svg)
+    skin/images/tool-application.svg (themes/images/tool-application.svg)
     skin/images/close.svg (themes/images/close.svg)
     skin/images/clear.svg (themes/images/clear.svg)
     skin/images/vview-delete.png (themes/images/vview-delete.png)
     skin/images/vview-delete@2x.png (themes/images/vview-delete@2x.png)
     skin/images/vview-edit.png (themes/images/vview-edit.png)
     skin/images/vview-edit@2x.png (themes/images/vview-edit@2x.png)
     skin/images/vview-lock.png (themes/images/vview-lock.png)
     skin/images/vview-lock@2x.png (themes/images/vview-lock@2x.png)
@@ -295,14 +296,18 @@ devtools.jar:
     content/netmonitor/src/assets/styles/StatisticsPanel.css (netmonitor/src/assets/styles/StatisticsPanel.css)
     content/netmonitor/src/assets/styles/StatusBar.css (netmonitor/src/assets/styles/StatusBar.css)
     content/netmonitor/src/assets/styles/Toolbar.css (netmonitor/src/assets/styles/Toolbar.css)
     content/netmonitor/src/assets/styles/variables.css (netmonitor/src/assets/styles/variables.css)
     content/netmonitor/src/assets/icons/play.svg (netmonitor/src/assets/icons/play.svg)
     content/netmonitor/index.html (netmonitor/index.html)
     content/netmonitor/initializer.js (netmonitor/initializer.js)
 
+    # Application panel
+    content/application/index.html (application/index.html)
+    content/application/initializer.js (application/initializer.js)
+
     # Devtools-components
     skin/images/devtools-components/arrow.svg (themes/images/devtools-components/arrow.svg)
 
     # Devtools-reps
     skin/images/devtools-reps/jump-definition.svg (themes/images/devtools-reps/jump-definition.svg)
     skin/images/devtools-reps/open-inspector.svg (themes/images/devtools-reps/open-inspector.svg)
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include('../templates.mozbuild')
 
 DIRS += [
     'aboutdebugging',
     'accessibility',
     'animationinspector',
+    'application',
     'canvasdebugger',
     'commandline',
     'debugger',
     'dom',
     'framework',
     'inspector',
     'jsonview',
     'locales',
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -159,16 +159,19 @@ pref("devtools.performance.ui.experiment
 pref("devtools.cache.disabled", false);
 
 // The default service workers UI setting
 pref("devtools.serviceWorkers.testing.enabled", false);
 
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
 
+// Enable the Application panel
+pref("devtools.application.enabled", false);
+
 // The default Network Monitor UI settings
 pref("devtools.netmonitor.panes-network-details-width", 550);
 pref("devtools.netmonitor.panes-network-details-height", 450);
 pref("devtools.netmonitor.filters", "[\"all\"]");
 pref("devtools.netmonitor.visibleColumns",
   "[\"status\",\"method\",\"file\",\"domain\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
 );
 
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -397,8 +397,18 @@ html[dir="rtl"] .tree-node img.arrow {
 .object-inspector .object-delimiter {
   color: var(--theme-comment);
 }
 
 .object-inspector .tree-node img.arrow {
   display: inline-block;
   vertical-align: middle;
 }
+
+/* Focused styles */
+.tree.object-inspector .tree-node.focused * {
+  color: inherit;
+}
+
+.tree-node.focused button.jump-definition,
+.tree-node.focused button.open-inspector {
+  background-color: currentColor;
+}
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -1,18 +1,18 @@
 (function webpackUniversalModuleDefinition(root, factory) {
 	if(typeof exports === 'object' && typeof module === 'object')
-		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux"));
+		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux"));
 	else if(typeof define === 'function' && define.amd)
-		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/redux"], factory);
+		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/redux"], factory);
 	else {
-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/redux"]);
+		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/redux"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_54__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_18__, __WEBPACK_EXTERNAL_MODULE_19__) {
+})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_9__, __WEBPACK_EXTERNAL_MODULE_18__, __WEBPACK_EXTERNAL_MODULE_19__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -80,17 +80,17 @@ return /******/ (function(modules) { // 
 "use strict";
 
 
 /* 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/. */
 
 // Dependencies
-const validProtocols = /^(http|https|ftp|data|resource|chrome):/i;
+const validProtocols = /(http|https|ftp|data|resource|chrome):/i;
 const tokenSplitRegex = /(\s|\'|\"|\\)+/;
 const ELLIPSIS = "\u2026";
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Returns true if the given object is a grip (see RDP protocol)
  */
@@ -427,22 +427,22 @@ function getGripType(object, noGrip) {
  * Determines whether a grip is a string containing a URL.
  *
  * @param string grip
  *        The grip, which may contain a URL.
  * @return boolean
  *         Whether the grip is a string containing a URL.
  */
 function containsURL(grip) {
-  if (typeof grip !== "string") {
+  // An URL can't be shorter than 5 char (e.g. "ftp:").
+  if (typeof grip !== "string" || grip.length < 5) {
     return false;
   }
 
-  let tokens = grip.split(tokenSplitRegex);
-  return tokens.some(isURL);
+  return validProtocols.test(grip);
 }
 
 /**
  * Determines whether a string token is a valid URL.
  *
  * @param string token
  *        The token.
  * @return boolean
@@ -562,17 +562,17 @@ const NaNRep = __webpack_require__(31);
 const Accessor = __webpack_require__(32);
 
 // DOM types (grips)
 const Attribute = __webpack_require__(33);
 const DateTime = __webpack_require__(34);
 const Document = __webpack_require__(35);
 const DocumentType = __webpack_require__(36);
 const Event = __webpack_require__(37);
-const Func = __webpack_require__(12);
+const Func = __webpack_require__(11);
 const PromiseRep = __webpack_require__(38);
 const RegExp = __webpack_require__(39);
 const StyleSheet = __webpack_require__(40);
 const CommentNode = __webpack_require__(41);
 const ElementNode = __webpack_require__(42);
 const TextNode = __webpack_require__(43);
 const ErrorRep = __webpack_require__(13);
 const Window = __webpack_require__(44);
@@ -584,17 +584,17 @@ const GripMapEntry = __webpack_require__
 const Grip = __webpack_require__(8);
 
 // List of all registered template.
 // XXX there should be a way for extensions to register a new
 // or modify an existing rep.
 let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
 
 /**
- * Generic rep that is using for rendering native JS types or an object.
+ * Generic rep that is used for rendering native JS types or an object.
  * The right template used for rendering is picked automatically according
  * to the current value type. The value must be passed is as 'object'
  * property.
  */
 const Rep = function (props) {
   let {
     object,
     defaultRep
@@ -608,17 +608,17 @@ const Rep = function (props) {
 /**
  * Return a rep object that is responsible for rendering given
  * object.
  *
  * @param object {Object} Object to be rendered in the UI. This
  * can be generic JS object as well as a grip (handle to a remote
  * debuggee object).
  *
- * @param defaultObject {React.Component} The default template
+ * @param defaultRep {React.Component} The default template
  * that should be used to render given object if none is found.
  *
  * @param noGrip {Boolean} If true, will only check reps not made for remote objects.
  */
 function getRep(object, defaultRep = Obj, noGrip = false) {
   for (let i = 0; i < reps.length; i++) {
     let rep = reps[i];
     try {
@@ -708,17 +708,17 @@ const { a, span } = dom;
 /**
  * Renders a string. String value is enclosed within quotes.
  */
 StringRep.propTypes = {
   useQuotes: PropTypes.bool,
   escapeWhitespace: PropTypes.bool,
   style: PropTypes.object,
   cropLimit: PropTypes.number.isRequired,
-  member: PropTypes.string,
+  member: PropTypes.object,
   object: PropTypes.object.isRequired,
   openLink: PropTypes.func,
   className: PropTypes.string
 };
 
 function StringRep(props) {
   let {
     className,
@@ -729,23 +729,29 @@ function StringRep(props) {
     escapeWhitespace = true,
     member,
     openLink
   } = props;
 
   let text = object;
 
   const isLong = isLongString(object);
-  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+  const isOpen = member && member.open;
+  const shouldCrop = !isOpen && cropLimit && text.length > cropLimit;
 
   if (isLong) {
     text = maybeCropLongString({
       shouldCrop,
       cropLimit
     }, text);
+
+    const { fullText } = object;
+    if (isOpen && fullText) {
+      text = fullText;
+    }
   }
 
   text = formatText({
     useQuotes,
     escapeWhitespace
   }, text);
 
   const config = getElementConfig({
@@ -772,22 +778,21 @@ function StringRep(props) {
 
 function maybeCropLongString(opts, text) {
   const {
     shouldCrop,
     cropLimit
   } = opts;
 
   const {
-    fullText,
     initial,
     length
   } = text;
 
-  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;
+  text = shouldCrop ? initial.substring(0, cropLimit) : initial;
 
   if (text.length < length) {
     text += ELLIPSIS;
   }
 
   return text;
 }
 
@@ -1502,70 +1507,36 @@ let Grip = {
   maxLengthMap
 };
 
 // Exports from this module
 module.exports = Grip;
 
 /***/ }),
 /* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* 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/. */
-
-module.exports = {
-  ELEMENT_NODE: 1,
-  ATTRIBUTE_NODE: 2,
-  TEXT_NODE: 3,
-  CDATA_SECTION_NODE: 4,
-  ENTITY_REFERENCE_NODE: 5,
-  ENTITY_NODE: 6,
-  PROCESSING_INSTRUCTION_NODE: 7,
-  COMMENT_NODE: 8,
-  DOCUMENT_NODE: 9,
-  DOCUMENT_TYPE_NODE: 10,
-  DOCUMENT_FRAGMENT_NODE: 11,
-  NOTATION_NODE: 12,
-
-  // DocumentPosition
-  DOCUMENT_POSITION_DISCONNECTED: 0x01,
-  DOCUMENT_POSITION_PRECEDING: 0x02,
-  DOCUMENT_POSITION_FOLLOWING: 0x04,
-  DOCUMENT_POSITION_CONTAINS: 0x08,
-  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
-  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
-};
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_9__;
 
 /***/ }),
 /* 10 */
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_10__;
-
-/***/ }),
-/* 11 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
 const client = __webpack_require__(20);
 const loadProperties = __webpack_require__(21);
 const node = __webpack_require__(22);
 const { nodeIsError, nodeIsPrimitive } = node;
-const selection = __webpack_require__(55);
+const selection = __webpack_require__(54);
 
 const { MODE } = __webpack_require__(3);
 const {
   REPS: {
     Rep,
     Grip
   }
 } = __webpack_require__(4);
@@ -1595,17 +1566,17 @@ module.exports = {
   loadProperties,
   node,
   renderRep,
   selection,
   shouldRenderRootsInReps
 };
 
 /***/ }),
-/* 12 */
+/* 11 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -1767,16 +1738,50 @@ module.exports = {
   rep: wrapRender(FunctionRep),
   supportsObject,
   cleanFunctionName,
   // exported for testing purpose.
   getFunctionName
 };
 
 /***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+/* 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/. */
+
+module.exports = {
+  ELEMENT_NODE: 1,
+  ATTRIBUTE_NODE: 2,
+  TEXT_NODE: 3,
+  CDATA_SECTION_NODE: 4,
+  ENTITY_REFERENCE_NODE: 5,
+  ENTITY_NODE: 6,
+  PROCESSING_INSTRUCTION_NODE: 7,
+  COMMENT_NODE: 8,
+  DOCUMENT_NODE: 9,
+  DOCUMENT_TYPE_NODE: 10,
+  DOCUMENT_FRAGMENT_NODE: 11,
+  NOTATION_NODE: 12,
+
+  // DocumentPosition
+  DOCUMENT_POSITION_DISCONNECTED: 0x01,
+  DOCUMENT_POSITION_PRECEDING: 0x02,
+  DOCUMENT_POSITION_FOLLOWING: 0x04,
+  DOCUMENT_POSITION_CONTAINS: 0x08,
+  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+};
+
+/***/ }),
 /* 13 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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
@@ -1785,17 +1790,17 @@ module.exports = {
 // ReactJS
 const PropTypes = __webpack_require__(2);
 // Utils
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { cleanFunctionName } = __webpack_require__(12);
+const { cleanFunctionName } = __webpack_require__(11);
 const { isLongString } = __webpack_require__(5);
 const { MODE } = __webpack_require__(3);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 const IGNORED_SOURCE_URLS = ["debugger eval code"];
 
 /**
@@ -2489,17 +2494,17 @@ function GripMapEntry(props) {
     suppressQuotes: false
   }));
 }
 
 function supportsObject(grip, noGrip = false) {
   if (noGrip === true) {
     return false;
   }
-  return grip && grip.type === "mapEntry" && grip.preview;
+  return grip && (grip.type === "mapEntry" || grip.type === "storageEntry") && grip.preview;
 }
 
 function createGripMapEntry(key, value) {
   return {
     type: "mapEntry",
     preview: {
       key,
       value
@@ -2582,28 +2587,51 @@ async function enumSymbols(objectClient,
 async function getPrototype(objectClient) {
   if (typeof objectClient.getPrototype !== "function") {
     console.error("objectClient.getPrototype is not a function");
     return Promise.resolve({});
   }
   return objectClient.getPrototype();
 }
 
+async function getFullText(longStringClient, object) {
+  const { initial, length } = object;
+
+  return new Promise((resolve, reject) => {
+    longStringClient.substring(initial.length, length, response => {
+      if (response.error) {
+        console.error("LongStringClient.substring", response.error + ": " + response.message);
+        reject({});
+        return;
+      }
+
+      resolve({
+        fullText: initial + response.substring
+      });
+    });
+  });
+}
+
 function iteratorSlice(iterator, start, end) {
   start = start || 0;
   const count = end ? end - start + 1 : iterator.count;
+
+  if (count === 0) {
+    return Promise.resolve({});
+  }
   return iterator.slice(start, count);
 }
 
 module.exports = {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   enumSymbols,
-  getPrototype
+  getPrototype,
+  getFullText
 };
 
 /***/ }),
 /* 21 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -2612,36 +2640,38 @@ module.exports = {
  * 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/. */
 
 const {
   enumEntries,
   enumIndexedProperties,
   enumNonIndexedProperties,
   getPrototype,
-  enumSymbols
+  enumSymbols,
+  getFullText
 } = __webpack_require__(20);
 
 const {
   getClosestGripNode,
   getClosestNonBucketNode,
   getValue,
   nodeHasAccessors,
   nodeHasAllEntriesInPreview,
   nodeHasProperties,
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
   nodeIsMapEntry,
   nodeIsPrimitive,
   nodeIsProxy,
-  nodeNeedsNumericalBuckets
+  nodeNeedsNumericalBuckets,
+  nodeIsLongString
 } = __webpack_require__(22);
 
-function loadItemProperties(item, createObjectClient, loadedProperties) {
+function loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties) {
   const gripItem = getClosestGripNode(item);
   const value = getValue(gripItem);
 
   const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
 
   let promises = [];
   let objectClient;
   const getObjectClient = () => objectClient || createObjectClient(value);
@@ -2661,16 +2691,20 @@ function loadItemProperties(item, create
   if (shouldLoadItemPrototype(item, loadedProperties)) {
     promises.push(getPrototype(getObjectClient()));
   }
 
   if (shouldLoadItemSymbols(item, loadedProperties)) {
     promises.push(enumSymbols(getObjectClient(), start, end));
   }
 
+  if (shouldLoadItemFullText(item, loadedProperties)) {
+    promises.push(getFullText(createLongStringClient(value), value));
+  }
+
   return Promise.all(promises).then(mergeResponses);
 }
 
 function mergeResponses(responses) {
   const data = {};
 
   for (const response of responses) {
     if (response.hasOwnProperty("ownProperties")) {
@@ -2679,16 +2713,20 @@ function mergeResponses(responses) {
 
     if (response.ownSymbols && response.ownSymbols.length > 0) {
       data.ownSymbols = response.ownSymbols;
     }
 
     if (response.prototype) {
       data.prototype = response.prototype;
     }
+
+    if (response.fullText) {
+      data.fullText = response.fullText;
+    }
   }
 
   return data;
 }
 
 function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
   const gripItem = getClosestGripNode(item);
   const value = getValue(gripItem);
@@ -2712,53 +2750,58 @@ function shouldLoadItemEntries(item, loa
   const value = getValue(gripItem);
 
   return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
 }
 
 function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
   const value = getValue(item);
 
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item);
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item);
 }
 
 function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
   const value = getValue(item);
 
-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsProxy(item);
+  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsLongString(item) && !nodeIsProxy(item);
+}
+
+function shouldLoadItemFullText(item, loadedProperties = new Map()) {
+  return !loadedProperties.has(item.path) && nodeIsLongString(item);
 }
 
 module.exports = {
   loadItemProperties,
   mergeResponses,
   shouldLoadItemEntries,
   shouldLoadItemIndexedProperties,
   shouldLoadItemNonIndexedProperties,
   shouldLoadItemPrototype,
-  shouldLoadItemSymbols
+  shouldLoadItemSymbols,
+  shouldLoadItemFullText
 };
 
 /***/ }),
 /* 22 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
-const { get, has } = __webpack_require__(54);
 const { maybeEscapePropertyName } = __webpack_require__(0);
 const ArrayRep = __webpack_require__(6);
 const GripArrayRep = __webpack_require__(14);
 const GripMap = __webpack_require__(16);
 const GripMapEntryRep = __webpack_require__(17);
 const ErrorRep = __webpack_require__(13);
+const { isLongString } = __webpack_require__(5);
 
 const MAX_NUMERICAL_PROPERTIES = 100;
 
 const NODE_TYPES = {
   BUCKET: Symbol("[n…m]"),
   DEFAULT_PROPERTIES: Symbol("<default properties>"),
   ENTRIES: Symbol("<entries>"),
   GET: Symbol("<get>"),
@@ -2781,22 +2824,22 @@ if (typeof window === "object") {
   WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
 }
 
 function getType(item) {
   return item.type;
 }
 
 function getValue(item) {
-  if (has(item, "contents.value")) {
-    return get(item, "contents.value");
-  }
-
-  if (has(item, "contents.getterValue")) {
-    return get(item, "contents.getterValue", undefined);
+  if (item && item.contents && item.contents.hasOwnProperty("value")) {
+    return item.contents.value;
+  }
+
+  if (item && item.contents && item.contents.hasOwnProperty("getterValue")) {
+    return item.contents.getterValue;
   }
 
   if (nodeHasAccessors(item)) {
     return item.contents;
   }
 
   return undefined;
 }
@@ -2863,17 +2906,17 @@ function nodeIsMissingArguments(item) {
   return !nodeHasChildren(item) && value && value.missingArguments;
 }
 
 function nodeHasProperties(item) {
   return !nodeHasChildren(item) && nodeIsObject(item);
 }
 
 function nodeIsPrimitive(item) {
-  return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(item);
+  return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item) && !nodeIsBucket(item) && !nodeIsLongString(item);
 }
 
 function nodeIsDefaultProperties(item) {
   return getType(item) === NODE_TYPES.DEFAULT_PROPERTIES;
 }
 
 function isDefaultWindowProperty(name) {
   return WINDOW_PROPERTIES.includes(name);
@@ -2921,33 +2964,42 @@ function nodeIsSetter(item) {
 function nodeIsBlock(item) {
   return getType(item) === NODE_TYPES.BLOCK;
 }
 
 function nodeIsError(item) {
   return ErrorRep.supportsObject(getValue(item));
 }
 
+function nodeIsLongString(item) {
+  return isLongString(getValue(item));
+}
+
+function nodeHasFullText(item) {
+  const value = getValue(item);
+  return nodeIsLongString(item) && value.hasOwnProperty("fullText");
+}
+
 function nodeHasAccessors(item) {
   return !!getNodeGetter(item) || !!getNodeSetter(item);
 }
 
 function nodeSupportsNumericalBucketing(item) {
   // We exclude elements with entries since it's the <entries> node
   // itself that can have buckets.
   return nodeIsArrayLike(item) && !nodeHasEntries(item) || nodeIsEntries(item) || nodeIsBucket(item);
 }
 
 function nodeHasEntries(item) {
   const value = getValue(item);
   if (!value) {
     return false;
   }
 
-  return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet";
+  return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet" || value.class === "Storage";
 }
 
 function nodeHasAllEntriesInPreview(item) {
   const { preview } = getValue(item) || {};
   if (!preview) {
     return false;
   }
 
@@ -3081,21 +3133,21 @@ function makeNodesForMapEntry(item) {
     parent: item,
     name: "<value>",
     contents: { value },
     type: NODE_TYPES.MAP_ENTRY_VALUE
   })];
 }
 
 function getNodeGetter(item) {
-  return get(item, "contents.get", undefined);
+  return item && item.contents ? item.contents.get : undefined;
 }
 
 function getNodeSetter(item) {
-  return get(item, "contents.set", undefined);
+  return item && item.contents ? item.contents.set : undefined;
 }
 
 function makeNodesForAccessors(item) {
   const accessors = [];
 
   const getter = getNodeGetter(item);
   if (getter && getter.type !== "undefined") {
     accessors.push(createNode({
@@ -3255,16 +3307,28 @@ function makeNodesForProperties(objProps
   // Add the prototype if it exists and is not null
   if (prototype && prototype.type !== "null") {
     nodes.push(makeNodeForPrototype(objProps, parent));
   }
 
   return nodes;
 }
 
+function setNodeFullText(loadedProps, node) {
+  if (nodeHasFullText(node)) {
+    return node;
+  }
+
+  if (nodeIsLongString(node)) {
+    node.contents.value.fullText = loadedProps.fullText;
+  }
+
+  return node;
+}
+
 function makeNodeForPrototype(objProps, parent) {
   const {
     prototype
   } = objProps || {};
 
   // Add the prototype if it exists and is not null
   if (prototype && prototype.type !== "null") {
     return createNode({
@@ -3360,19 +3424,23 @@ function getChildren(options) {
   if (nodeIsMapEntry(item)) {
     return addToCache(makeNodesForMapEntry(item));
   }
 
   if (nodeIsProxy(item)) {
     return addToCache(makeNodesForProxyProperties(item));
   }
 
+  if (nodeIsLongString(item) && hasLoadedProps) {
+    // Set longString object's fullText to fetched one.
+    return addToCache(setNodeFullText(loadedProps, item));
+  }
+
   if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
     // Even if we have numerical buckets, we should have loaded non indexed properties,
-    // like length for example.
     const bucketNodes = makeNumericalBuckets(item);
     return addToCache(bucketNodes.concat(makeNodesForProperties(loadedProps, item)));
   }
 
   if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
     return [];
   }
 
@@ -3458,16 +3526,18 @@ module.exports = {
   nodeHasChildren,
   nodeHasEntries,
   nodeHasProperties,
   nodeIsBlock,
   nodeIsBucket,
   nodeIsDefaultProperties,
   nodeIsEntries,
   nodeIsError,
+  nodeIsLongString,
+  nodeHasFullText,
   nodeIsFunction,
   nodeIsGetter,
   nodeIsMapEntry,
   nodeIsMissingArguments,
   nodeIsObject,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPromise,
@@ -3494,17 +3564,17 @@ module.exports = {
 
 /* 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/. */
 
 const { MODE } = __webpack_require__(3);
 const { REPS, getRep } = __webpack_require__(4);
 const ObjectInspector = __webpack_require__(47);
-const ObjectInspectorUtils = __webpack_require__(11);
+const ObjectInspectorUtils = __webpack_require__(10);
 
 const {
   parseURLEncodedText,
   parseURLParams,
   maybeEscapePropertyName,
   getGripPreviewItems
 } = __webpack_require__(0);
 
@@ -4652,17 +4722,17 @@ module.exports = {
 const PropTypes = __webpack_require__(2);
 const {
   isGrip,
   cropString,
   cropMultipleLines,
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
-const nodeConstants = __webpack_require__(9);
+const nodeConstants = __webpack_require__(12);
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM comment node.
  */
 CommentNode.propTypes = {
   object: PropTypes.object.isRequired,
@@ -4719,92 +4789,118 @@ const PropTypes = __webpack_require__(2)
 
 // Utils
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 const { rep: StringRep } = __webpack_require__(5);
 const { MODE } = __webpack_require__(3);
-const nodeConstants = __webpack_require__(9);
+const nodeConstants = __webpack_require__(12);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders DOM element node.
  */
 ElementNode.propTypes = {
   object: PropTypes.object.isRequired,
+  inspectIconTitle: PropTypes.string,
   // @TODO Change this to Object.values once it's supported in Node's version of V8
   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeClick: PropTypes.func,
   onDOMNodeMouseOver: PropTypes.func,
   onDOMNodeMouseOut: PropTypes.func,
   onInspectIconClick: PropTypes.func
 };
 
 function ElementNode(props) {
   let {
     object,
+    inspectIconTitle,
     mode,
+    onDOMNodeClick,
     onDOMNodeMouseOver,
     onDOMNodeMouseOut,
     onInspectIconClick
   } = props;
   let elements = getElements(object, mode);
 
   let isInTree = object.preview && object.preview.isConnected === true;
 
   let baseConfig = {
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-node"
   };
   let inspectIcon;
   if (isInTree) {
+    if (onDOMNodeClick) {
+      Object.assign(baseConfig, {
+        onClick: _ => onDOMNodeClick(object)
+      });
+    }
+
     if (onDOMNodeMouseOver) {
       Object.assign(baseConfig, {
         onMouseOver: _ => onDOMNodeMouseOver(object)
       });
     }
 
     if (onDOMNodeMouseOut) {
       Object.assign(baseConfig, {
         onMouseOut: onDOMNodeMouseOut
       });
     }
 
     if (onInspectIconClick) {
       inspectIcon = dom.button({
         className: "open-inspector",
         // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
-        title: "Click to select the node in the inspector",
-        onClick: e => onInspectIconClick(object, e)
+        title: inspectIconTitle || "Click to select the node in the inspector",
+        onClick: e => {
+          if (onDOMNodeClick) {
+            e.stopPropagation();
+          }
+
+          onInspectIconClick(object, e);
+        }
       });
     }
   }
 
   return span(baseConfig, ...elements, inspectIcon);
 }
 
 function getElements(grip, mode) {
-  let { attributes, nodeName } = grip.preview;
+  let {
+    attributes,
+    nodeName,
+    isAfterPseudoElement,
+    isBeforePseudoElement
+  } = grip.preview;
   const nodeNameElement = span({
     className: "tag-name"
   }, nodeName);
 
+  if (isAfterPseudoElement || isBeforePseudoElement) {
+    return [span({ className: "attrName" }, `::${isAfterPseudoElement ? "after" : "before"}`)];
+  }
+
   if (mode === MODE.TINY) {
     let elements = [nodeNameElement];
     if (attributes.id) {
       elements.push(span({ className: "attrName" }, `#${attributes.id}`));
     }
     if (attributes.class) {
       elements.push(span({ className: "attrName" }, attributes.class.trim().split(/\s+/).map(cls => `.${cls}`).join("")));
     }
     return elements;
   }
+
   let attributeKeys = Object.keys(attributes);
   if (attributeKeys.includes("class")) {
     attributeKeys.splice(attributeKeys.indexOf("class"), 1);
     attributeKeys.unshift("class");
   }
   if (attributeKeys.includes("id")) {
     attributeKeys.splice(attributeKeys.indexOf("id"), 1);
     attributeKeys.unshift("id");
@@ -5159,21 +5255,21 @@ module.exports = {
 
 "use strict";
 
 
 /* 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/. */
 
-const { createElement, createFactory, PureComponent } = __webpack_require__(10);
+const { createElement, createFactory, PureComponent } = __webpack_require__(9);
 const { Provider } = __webpack_require__(18);
 const ObjectInspector = createFactory(__webpack_require__(48));
-const createStore = __webpack_require__(57);
-const Utils = __webpack_require__(11);
+const createStore = __webpack_require__(56);
+const Utils = __webpack_require__(10);
 const {
   renderRep,
   shouldRenderRootsInReps
 } = Utils;
 
 class OI extends PureComponent {
 
   constructor(props) {
@@ -5213,30 +5309,30 @@ function _interopRequireDefault(obj) { r
 
 /* 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/. */
 
 const {
   Component,
   createFactory
-} = __webpack_require__(10);
+} = __webpack_require__(9);
 const dom = __webpack_require__(1);
 const { connect } = __webpack_require__(18);
 const { bindActionCreators } = __webpack_require__(19);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(52);
 
 const classnames = __webpack_require__(53);
 const {
   MODE
 } = __webpack_require__(3);
 
-const Utils = __webpack_require__(11);
+const Utils = __webpack_require__(10);
 
 const {
   getChildren,
   getClosestGripNode,
   getParent,
   getValue,
   nodeHasAccessors,
   nodeHasProperties,
@@ -5248,17 +5344,19 @@ const {
   nodeIsMissingArguments,
   nodeIsOptimizedOut,
   nodeIsPrimitive,
   nodeIsPrototype,
   nodeIsSetter,
   nodeIsUninitializedBinding,
   nodeIsUnmappedBinding,
   nodeIsUnscopedBinding,
-  nodeIsWindow
+  nodeIsWindow,
+  nodeIsLongString,
+  nodeHasFullText
 } = Utils.node;
 
 // This implements a component that renders an interactive inspector
 // for looking at JavaScript objects. It expects descriptions of
 // objects from the protocol, and will dynamically fetch children
 // properties as objects are expanded.
 //
 // If you want to inspect a single object, pass the name and the
@@ -5295,32 +5393,54 @@ class ObjectInspector extends Component 
     self.setExpanded = this.setExpanded.bind(this);
     self.focusItem = this.focusItem.bind(this);
     self.getRoots = this.getRoots.bind(this);
   }
 
   shouldComponentUpdate(nextProps) {
     const {
       expandedPaths,
+      focusedItem,
       loadedProperties,
       roots
     } = this.props;
 
     if (roots !== nextProps.roots) {
-      // Since the roots changed, we assume the properties did as well. Thus we can clear
-      // the cachedNodes to avoid bugs and memory leaks.
+      // Since the roots changed, we assume the properties did as well, so we need to
+      // cleanup the component internal state.
+
+      // We can clear the cachedNodes to avoid bugs and memory leaks.
       this.cachedNodes.clear();
+      // The rootsChanged action will be handled in a middleware to release the actors
+      // of the old roots, as well as cleanup the state properties (expandedPaths,
+      // loadedProperties, …).
+      this.props.rootsChanged(nextProps);
+      // We don't render right away since the state is going to be changed by the
+      // rootsChanged action. The `state.forceUpdate` flag will be set to `true` so we
+      // can execute a new render cycle with the cleaned state.
+      return false;
+    }
+
+    if (nextProps.forceUpdate === true) {
       return true;
     }
 
     // We should update if:
     // - there are new loaded properties
     // - OR the expanded paths number changed, and all of them have properties loaded
     // - OR the expanded paths number did not changed, but old and new sets differ
-    return loadedProperties.size !== nextProps.loadedProperties.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key));
+    // - OR the focused node changed.
+    return loadedProperties.size !== nextProps.loadedProperties.size || expandedPaths.size !== nextProps.expandedPaths.size && [...nextProps.expandedPaths].every(path => nextProps.loadedProperties.has(path)) || expandedPaths.size === nextProps.expandedPaths.size && [...nextProps.expandedPaths].some(key => !expandedPaths.has(key)) || focusedItem !== nextProps.focusedItem;
+  }
+
+  componentDidUpdate(prevProps) {
+    if (this.props.forceUpdate) {
+      // If the component was updated, we can then reset the forceUpdate flag.
+      this.props.forceUpdated();
+    }
   }
 
   componentWillUnmount() {
     const { releaseActor } = this.props;
     if (typeof releaseActor !== "function") {
       return;
     }
 
@@ -5353,49 +5473,55 @@ class ObjectInspector extends Component 
 
   setExpanded(item, expand) {
     if (nodeIsPrimitive(item)) {
       return;
     }
 
     const {
       createObjectClient,
+      createLongStringClient,
       loadedProperties,
       nodeExpand,
       nodeCollapse,
       roots
     } = this.props;
 
     if (expand === true) {
       const gripItem = getClosestGripNode(item);
       const value = getValue(gripItem);
       const isRoot = value && roots.some(root => {
         const rootValue = getValue(root);
         return rootValue && rootValue.actor === value.actor;
       });
       const actor = isRoot || !value ? null : value.actor;
-      nodeExpand(item, actor, loadedProperties, createObjectClient);
+      nodeExpand(item, actor, loadedProperties, createObjectClient, createLongStringClient);
     } else {
       nodeCollapse(item);
     }
   }
 
   focusItem(item) {
     const {
+      focusable = true,
       focusedItem,
+      nodeFocus,
       onFocus
     } = this.props;
 
-    if (focusedItem !== item && onFocus) {
-      onFocus(item);
+    if (focusable && focusedItem !== item) {
+      nodeFocus(item);
+      if (focusedItem !== item && onFocus) {
+        onFocus(item);
+      }
     }
   }
 
   getTreeItemLabelAndValue(item, depth, expanded) {
-    let label = item.name;
+    const label = item.name;
     const isPrimitive = nodeIsPrimitive(item);
 
     if (nodeIsOptimizedOut(item)) {
       return {
         label,
         value: dom.span({ className: "unavailable" }, "(optimized away)")
       };
     }
@@ -5442,28 +5568,34 @@ class ObjectInspector extends Component 
       return {
         label: Utils.renderRep(item, {
           ...this.props,
           functionName: label
         })
       };
     }
 
-    if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
-      let repsProp = { ...this.props };
+    if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || nodeIsLongString(item) || isPrimitive) {
+      let repProps = { ...this.props };
       if (depth > 0) {
-        repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
+        repProps.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
       }
       if (expanded) {
-        repsProp.mode = MODE.TINY;
+        repProps.mode = MODE.TINY;
+      }
+
+      if (nodeIsLongString(item)) {
+        repProps.member = {
+          open: nodeHasFullText(item) && expanded
+        };
       }
 
       return {
         label,
-        value: Utils.renderRep(item, repsProp)
+        value: Utils.renderRep(item, repProps)
       };
     }
 
     return {
       label
     };
   }
 
@@ -5493,35 +5625,48 @@ class ObjectInspector extends Component 
           setExpanded: this.setExpanded
         });
       } : undefined
     }, label);
   }
 
   getTreeTopElementProps(item, depth, focused, expanded) {
     const {
+      onCmdCtrlClick,
       onDoubleClick,
       dimTopLevelWindow
     } = this.props;
 
     let parentElementProps = {
       className: classnames("node object-node", {
         focused,
         lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0),
         block: nodeIsBlock(item)
       }),
       onClick: e => {
-        e.stopPropagation();
-
-        // If the user selected text, bail out.
-        if (Utils.selection.documentHasSelection()) {
+        if (e.metaKey && onCmdCtrlClick) {
+          onCmdCtrlClick(item, {
+            depth,
+            event: e,
+            focused,
+            expanded
+          });
+          e.stopPropagation();
           return;
         }
 
-        this.setExpanded(item, !expanded);
+        // If this click happened because the user selected some text, bail out.
+        // Note that if the user selected some text before and then clicks here,
+        // the previously selected text will be first unselected, unless the user
+        // clicked on the arrow itself. Indeed because the arrow is an image, clicking on
+        // it does not remove any existing text selection. So we need to also check if
+        // teh arrow was clicked.
+        if (Utils.selection.documentHasSelection() && !(e.target && e.target.matches && e.target.matches(".arrow"))) {
+          e.stopPropagation();
+        }
       }
     };
 
     if (onDoubleClick) {
       parentElementProps.onDoubleClick = e => {
         e.stopPropagation();
         onDoubleClick(item, {
           depth,
@@ -5541,62 +5686,63 @@ class ObjectInspector extends Component 
 
     return dom.div(this.getTreeTopElementProps(item, depth, focused, expanded), arrow, labelElement, delimiter, value);
   }
 
   render() {
     const {
       autoExpandAll = true,
       autoExpandDepth = 1,
-      disabledFocus,
+      focusable = true,
       disableWrap = false,
       expandedPaths,
       focusedItem,
       inline
     } = this.props;
 
     return Tree({
       className: classnames({
         inline,
         nowrap: disableWrap,
         "object-inspector": true
       }),
       autoExpandAll,
       autoExpandDepth,
-      disabledFocus,
 
       isExpanded: item => expandedPaths && expandedPaths.has(item.path),
       isExpandable: item => nodeIsPrimitive(item) === false,
       focused: focusedItem,
 
       getRoots: this.getRoots,
       getParent,
       getChildren: this.getItemChildren,
       getKey: this.getNodeKey,
 
       onExpand: item => this.setExpanded(item, true),
       onCollapse: item => this.setExpanded(item, false),
-      onFocus: this.focusItem,
+      onFocus: focusable ? this.focusItem : null,
 
       renderItem: this.renderTreeItem
     });
   }
 }
 
 function mapStateToProps(state, props) {
   return {
     actors: state.actors,
     expandedPaths: state.expandedPaths,
-    focusedItem: state.focusedItem,
-    loadedProperties: state.loadedProperties
+    // If the root changes, we want to pass a possibly new focusedItem property
+    focusedItem: state.roots !== props.roots ? props.focusedItem : state.focusedItem,
+    loadedProperties: state.loadedProperties,
+    forceUpdate: state.forceUpdate
   };
 }
 
 function mapDispatchToProps(dispatch) {
-  return bindActionCreators(__webpack_require__(56), dispatch);
+  return bindActionCreators(__webpack_require__(55), dispatch);
 }
 
 module.exports = connect(mapStateToProps, mapDispatchToProps)(ObjectInspector);
 
 /***/ }),
 /* 49 */
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -5621,17 +5767,17 @@ module.exports = {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _react = __webpack_require__(10);
+var _react = __webpack_require__(9);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactDomFactories = __webpack_require__(1);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
 var _propTypes = __webpack_require__(2);
@@ -5955,16 +6101,19 @@ class Tree extends Component {
       //     onExpand(item: Item)
       //     onCollapse(item: Item)
       //
       // Example:
       //
       //     onExpand: item => dispatchExpandActionToRedux(item)
       onExpand: _propTypes2.default.func,
       onCollapse: _propTypes2.default.func,
+      // Optional event handler called with the current focused node when the Enter key
+      // is pressed. Can be useful to allow further keyboard actions within the tree node.
+      onActivate: _propTypes2.default.func,
       isExpandable: _propTypes2.default.func,
       // Additional classes to add to the root element.
       className: _propTypes2.default.string,
       // style object to be applied to the root element.
       style: _propTypes2.default.object
     };
   }
 
@@ -5982,42 +6131,49 @@ class Tree extends Component {
       seen: new Set()
     };
 
     this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
     this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
     this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
     this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
     this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(this);
+    this._focusFirstNode = oncePerAnimationFrame(this._focusFirstNode).bind(this);
+    this._focusLastNode = oncePerAnimationFrame(this._focusLastNode).bind(this);
 
     this._autoExpand = this._autoExpand.bind(this);
     this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
     this._dfs = this._dfs.bind(this);
     this._dfsFromRoots = this._dfsFromRoots.bind(this);
     this._focus = this._focus.bind(this);
     this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
     this._onBlur = this._onBlur.bind(this);
     this._onKeyDown = this._onKeyDown.bind(this);
     this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
+    this._activateNode = oncePerAnimationFrame(this._activateNode).bind(this);
   }
 
   componentDidMount() {
     this._autoExpand();
     if (this.props.focused) {
       this._scrollNodeIntoView(this.props.focused);
+      // Always keep the focus on the tree itself.
+      this.treeRef.focus();
     }
   }
 
   componentWillReceiveProps(nextProps) {
     this._autoExpand();
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (prevProps.focused !== this.props.focused) {
       this._scrollNodeIntoView(this.props.focused);
+      // Always keep the focus on the tree itself.
+      this.treeRef.focus();
     }
   }
 
   _autoExpand() {
     if (!this.props.autoExpandDepth) {
       return;
     }
 
@@ -6147,18 +6303,21 @@ class Tree extends Component {
    *        The item to be focused, or undefined to focus no item.
    *
    * @param {Object|undefined} options
    *        An options object which can contain:
    *          - dir: "up" or "down" to indicate if we should scroll the element to the
    *                 top or the bottom of the scrollable container when the element is
    *                 off canvas.
    */
-  _focus(item, options) {
-    this._scrollNodeIntoView(item, options);
+  _focus(item, options = {}) {
+    const { preventAutoScroll } = options;
+    if (item && !preventAutoScroll) {
+      this._scrollNodeIntoView(item, options);
+    }
     if (this.props.onFocus) {
       this.props.onFocus(item);
     }
   }
 
   /**
    * Sets the passed in item to be the focused item.
    *
@@ -6170,33 +6329,36 @@ class Tree extends Component {
    *          - dir: "up" or "down" to indicate if we should scroll the element to the
    *                 top or the bottom of the scrollable container when the element is
    *                 off canvas.
    */
   _scrollNodeIntoView(item, options = {}) {
     if (item !== undefined) {
       const treeElement = this.treeRef;
       const element = document.getElementById(this.props.getKey(item));
+
       if (element) {
         const { top, bottom } = element.getBoundingClientRect();
         const closestScrolledParent = node => {
           if (node == null) {
             return null;
           }
 
           if (node.scrollHeight > node.clientHeight) {
             return node;
           }
           return closestScrolledParent(node.parentNode);
         };
         const scrolledParent = closestScrolledParent(treeElement);
-        const isVisible = !scrolledParent || top >= 0 && bottom <= scrolledParent.clientHeight;
+        const scrolledParentRect = scrolledParent ? scrolledParent.getBoundingClientRect() : null;
+        const isVisible = !scrolledParent || top >= scrolledParentRect.top && bottom <= scrolledParentRect.bottom;
 
         if (!isVisible) {
-          let scrollToTop = !options.alignTo && top < 0 || options.alignTo === "top";
+          const { alignTo } = options;
+          let scrollToTop = alignTo ? alignTo === "top" : !scrolledParentRect || top < scrolledParentRect.top;
           element.scrollIntoView(scrollToTop);
         }
       }
     }
   }
 
   /**
    * Sets the state to have no focused item.
@@ -6240,16 +6402,28 @@ class Tree extends Component {
         return;
 
       case "ArrowRight":
         if (this._nodeIsExpandable(this.props.focused) && !this.props.isExpanded(this.props.focused)) {
           this._onExpand(this.props.focused);
         } else {
           this._focusNextNode();
         }
+        return;
+
+      case "Home":
+        this._focusFirstNode();
+        return;
+
+      case "End":
+        this._focusLastNode();
+        return;
+
+      case "Enter":
+        this._activateNode();
     }
   }
 
   /**
    * Sets the previous node relative to the currently focused item, to focused.
    */
   _focusPrevNode() {
     // Start a depth first search and keep going until we reach the currently
@@ -6316,16 +6490,33 @@ class Tree extends Component {
       if (traversal[parentIndex].item === parent) {
         break;
       }
     }
 
     this._focus(parent, { alignTo: "top" });
   }
 
+  _focusFirstNode() {
+    const traversal = this._dfsFromRoots();
+    this._focus(traversal[0].item, { alignTo: "top" });
+  }
+
+  _focusLastNode() {
+    const traversal = this._dfsFromRoots();
+    const lastIndex = traversal.length - 1;
+    this._focus(traversal[lastIndex].item, { alignTo: "bottom" });
+  }
+
+  _activateNode() {
+    if (this.props.onActivate) {
+      this.props.onActivate(this.props.focused);
+    }
+  }
+
   _nodeIsExpandable(item) {
     return this.props.isExpandable ? this.props.isExpandable(item) : !!this.props.getChildren(item).length;
   }
 
   render() {
     const traversal = this._dfsFromRoots();
     const {
       focused
@@ -6342,17 +6533,19 @@ class Tree extends Component {
         depth,
         renderItem: this.props.renderItem,
         focused: focused === item,
         expanded: this.props.isExpanded(item),
         isExpandable: this._nodeIsExpandable(item),
         onExpand: this._onExpand,
         onCollapse: this._onCollapse,
         onClick: e => {
-          this._focus(item);
+          // Since the user just clicked the node, there's no need to check if it should
+          // be scrolled into view.
+          this._focus(item, { preventAutoScroll: true });
           if (this.props.isExpanded(item)) {
             this.props.onCollapse(item);
           } else {
             this.props.onExpand(item, e.altKey);
           }
         }
       });
     });
@@ -6464,84 +6657,64 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
 	} else {
 		window.classNames = classNames;
 	}
 }());
 
 
 /***/ }),
 /* 54 */
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_54__;
-
-/***/ }),
-/* 55 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
-const { ELEMENT_NODE } = __webpack_require__(9);
-
 function documentHasSelection() {
   const selection = getSelection();
   if (!selection) {
     return false;
   }
 
-  const {
-    anchorNode,
-    focusNode
-  } = selection;
-
-  // When clicking the arrow, which is an inline svg element, the selection do have a type
-  // of "Range". We need to have an explicit case when the anchor and the focus node are
-  // the same and they have an arrow ancestor.
-  if (focusNode && focusNode === anchorNode && focusNode.nodeType == ELEMENT_NODE && focusNode.closest(".arrow")) {
-    return false;
-  }
-
   return selection.type === "Range";
 }
 
 module.exports = {
   documentHasSelection
 };
 
 /***/ }),
-/* 56 */
+/* 55 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 const {
   loadItemProperties
 } = __webpack_require__(21); /* This Source Code Form is subject to the terms of the Mozilla Public
                                          * License, v. 2.0. If a copy of the MPL was not distributed with this
                                          * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * This action is responsible for expanding a given node,
  * which also means that it will call the action responsible to fetch properties.
  */
-function nodeExpand(node, actor, loadedProperties, createObjectClient) {
+function nodeExpand(node, actor, loadedProperties, createObjectClient, createLongStringClient) {
   return async ({ dispatch }) => {
     dispatch({
       type: "NODE_EXPAND",
       data: { node }
     });
 
     if (!loadedProperties.has(node.path)) {
-      dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient));
+      dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient, createLongStringClient));
     }
   };
 }
 
 function nodeCollapse(node) {
   return {
     type: "NODE_COLLAPSE",
     data: { node }
@@ -6553,79 +6726,130 @@ function nodeFocus(node) {
     type: "NODE_FOCUS",
     data: { node }
   };
 }
 /*
  * This action checks if we need to fetch properties, entries, prototype and symbols
  * for a given node. If we do, it will call the appropriate ObjectClient functions.
  */
-function nodeLoadProperties(item, actor, loadedProperties, createObjectClient) {
+function nodeLoadProperties(item, actor, loadedProperties, createObjectClient, createLongStringClient) {
   return async ({ dispatch }) => {
     try {
-      const properties = await loadItemProperties(item, createObjectClient, loadedProperties);
+      const properties = await loadItemProperties(item, createObjectClient, createLongStringClient, loadedProperties);
       dispatch(nodePropertiesLoaded(item, actor, properties));
     } catch (e) {
       console.error(e);
     }
   };
 }
 
 function nodePropertiesLoaded(node, actor, properties) {
   return {
     type: "NODE_PROPERTIES_LOADED",
     data: { node, actor, properties }
   };
 }
 
+/*
+ * This action is dispatched when the `roots` prop, provided by a consumer of the
+ * ObjectInspector (inspector, console, …), is modified. It will clean the internal
+ * state properties (expandedPaths, loadedProperties, …) and release the actors consumed
+ * with the previous roots.
+ * It takes a props argument which reflects what is passed by the upper-level consumer.
+ */
+function rootsChanged(props) {
+  return {
+    type: "ROOTS_CHANGED",
+    data: props
+  };
+}
+
+/*
+ * This action will reset the `forceUpdate` flag in the state.
+ */
+function forceUpdated() {
+  return {
+    type: "FORCE_UPDATED"
+  };
+}
+
 module.exports = {
+  forceUpdated,
   nodeExpand,
   nodeCollapse,
   nodeFocus,
   nodeLoadProperties,
-  nodePropertiesLoaded
+  nodePropertiesLoaded,
+  rootsChanged
 };
 
 /***/ }),
-/* 57 */
+/* 56 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
 
-const { applyMiddleware, createStore } = __webpack_require__(19);
-const { thunk } = __webpack_require__(58);
-const { waitUntilService } = __webpack_require__(59);
-const reducer = __webpack_require__(60);
+const { applyMiddleware, createStore, compose } = __webpack_require__(19);
+const { thunk } = __webpack_require__(57);
+const { waitUntilService } = __webpack_require__(58);
+const reducer = __webpack_require__(59);
 
 function createInitialState(overrides) {
   return {
     actors: new Set(),
     expandedPaths: new Set(),
     focusedItem: null,
     loadedProperties: new Map(),
+    forceUpdated: false,
     ...overrides
   };
 }
 
+function enableStateReinitializer(props) {
+  return next => (innerReducer, initialState, enhancer) => {
+    function reinitializerEnhancer(state, action) {
+      if (action.type !== "ROOTS_CHANGED") {
+        return innerReducer(state, action);
+      }
+
+      if (props.releaseActor && initialState.actors) {
+        initialState.actors.forEach(props.releaseActor);
+      }
+
+      return {
+        ...action.data,
+        actors: new Set(),
+        expandedPaths: new Set(),
+        loadedProperties: new Map(),
+        // Indicates to the component that we do want to render on the next render cycle.
+        forceUpdate: true
+      };
+    }
+    return next(reinitializerEnhancer, initialState, enhancer);
+  };
+}
+
 module.exports = props => {
   const middlewares = [thunk];
+
   if (props.injectWaitService) {
     middlewares.push(waitUntilService);
   }
 
-  return createStore(reducer, createInitialState(props), applyMiddleware(...middlewares));
+  return createStore(reducer, createInitialState(props), compose(applyMiddleware(...middlewares), enableStateReinitializer(props)));
 };
 
 /***/ }),
-/* 58 */
+/* 57 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -6639,17 +6863,17 @@ module.exports = props => {
 function thunk({ dispatch, getState }) {
   return next => action => {
     return typeof action === "function" ? action({ dispatch, getState }) : next(action);
   };
 }
 exports.thunk = thunk;
 
 /***/ }),
-/* 59 */
+/* 58 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* 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/. */
@@ -6713,17 +6937,17 @@ function waitUntilService({ dispatch, ge
 }
 
 module.exports = {
   WAIT_UNTIL_TYPE,
   waitUntilService
 };
 
 /***/ }),
-/* 60 */
+/* 59 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 function reducer(state = {}, action) {
   const {
     type,
@@ -6747,21 +6971,31 @@ function reducer(state = {}, action) {
   if (type === "NODE_PROPERTIES_LOADED") {
     return cloneState({
       actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
       loadedProperties: new Map(state.loadedProperties).set(data.node.path, action.data.properties)
     });
   }
 
   if (type === "NODE_FOCUS") {
+    if (state.focusedItem === data.node) {
+      return state;
+    }
+
     return cloneState({
       focusedItem: data.node
     });
   }
 
+  if (type === "FORCE_UPDATED") {
+    return cloneState({
+      forceUpdate: false
+    });
+  }
+
   return state;
 } /* 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/. */
 
 
 module.exports = reducer;
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/tool-application.svg
@@ -0,0 +1,16 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" stroke="context-fill #0b0b0b">
+  <rect x="1.5" y="1.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="6.5" y="1.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="11.5" y="1.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+
+  <rect x="1.5" y="6.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="6.5" y="6.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="11.5" y="6.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+
+  <rect x="1.5" y="11.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="6.5" y="11.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+  <rect x="11.5" y="11.5" width="3" height="3" fill="transparent" stroke-width="1"/>
+</svg>
\ No newline at end of file
--- a/devtools/client/webconsole/utils/object-inspector.js
+++ b/devtools/client/webconsole/utils/object-inspector.js
@@ -43,17 +43,17 @@ function getObjectInspector(grip, servic
       : null;
   }
 
   const objectInspectorProps = {
     autoExpandDepth: 0,
     mode: MODE.LONG,
     // TODO: we disable focus since it's not currently working well in ObjectInspector.
     // Let's remove the property below when problem are fixed in OI.
-    disabledFocus: true,
+    focusable: false,
     roots: [{
       path: (grip && grip.actor) || JSON.stringify(grip),
       contents: {
         value: grip
       }
     }],
     createObjectClient: object =>
       new ObjectClient(serviceContainer.hudProxy.client, object),
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/animation-type-longhand.js
@@ -0,0 +1,347 @@
+/* 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";
+
+// Types of animation types of longhand properties.
+exports.ANIMATION_TYPE_FOR_LONGHANDS = [
+  ["discrete", new Set([
+    "align-content",
+    "align-items",
+    "align-self",
+    "-moz-appearance",
+    "backface-visibility",
+    "background-attachment",
+    "background-blend-mode",
+    "background-clip",
+    "background-image",
+    "background-origin",
+    "background-repeat",
+    "border-bottom-style",
+    "border-collapse",
+    "border-image-outset",
+    "border-image-repeat",
+    "border-image-slice",
+    "border-image-source",
+    "border-image-width",
+    "border-left-style",
+    "border-right-style",
+    "border-top-style",
+    "-moz-box-align",
+    "box-decoration-break",
+    "-moz-box-direction",
+    "-moz-box-ordinal-group",
+    "-moz-box-orient",
+    "-moz-box-pack",
+    "box-sizing",
+    "caption-side",
+    "clear",
+    "clip-rule",
+    "color-adjust",
+    "color-interpolation",
+    "color-interpolation-filters",
+    "column-fill",
+    "column-rule-style",
+    "column-span",
+    "contain",
+    "content",
+    "counter-increment",
+    "counter-reset",
+    "cursor",
+    "direction",
+    "dominant-baseline",
+    "empty-cells",
+    "fill-rule",
+    "flex-direction",
+    "flex-wrap",
+    "float",
+    "-moz-float-edge",
+    "font-family",
+    "font-feature-settings",
+    "font-kerning",
+    "font-language-override",
+    "font-style",
+    "font-synthesis",
+    "font-variant-alternates",
+    "font-variant-caps",
+    "font-variant-east-asian",
+    "font-variant-ligatures",
+    "font-variant-numeric",
+    "font-variant-position",
+    "-moz-force-broken-image-icon",
+    "grid-auto-columns",
+    "grid-auto-flow",
+    "grid-auto-rows",
+    "grid-column-end",
+    "grid-column-start",
+    "grid-row-end",
+    "grid-row-start",
+    "grid-template-areas",
+    "grid-template-columns",
+    "grid-template-rows",
+    "hyphens",
+    "image-orientation",
+    "image-rendering",
+    "ime-mode",
+    "initial-letter",
+    "isolation",
+    "justify-content",
+    "justify-items",
+    "justify-self",
+    "list-style-image",
+    "list-style-position",
+    "list-style-type",
+    "marker-end",
+    "marker-mid",
+    "marker-start",
+    "mask-clip",
+    "mask-composite",
+    "mask-image",
+    "mask-mode",
+    "mask-origin",
+    "mask-repeat",
+    "mask-type",
+    "mix-blend-mode",
+    "object-fit",
+    "-moz-orient",
+    "-moz-osx-font-smoothing",
+    "outline-style",
+    "overflow-clip-box-block",
+    "overflow-clip-box-inline",
+    "overflow-wrap",
+    "overflow-x",
+    "overflow-y",
+    "overscroll-behavior-x",
+    "overscroll-behavior-y",
+    "page-break-after",
+    "page-break-before",
+    "page-break-inside",
+    "paint-order",
+    "pointer-events",
+    "position",
+    "quotes",
+    "resize",
+    "ruby-align",
+    "ruby-position",
+    "scroll-behavior",
+    "scroll-snap-coordinate",
+    "scroll-snap-destination",
+    "scroll-snap-points-x",
+    "scroll-snap-points-y",
+    "scroll-snap-type-x",
+    "scroll-snap-type-y",
+    "shape-rendering",
+    "-moz-stack-sizing",
+    "stroke-linecap",
+    "stroke-linejoin",
+    "table-layout",
+    "text-align",
+    "text-align-last",
+    "text-anchor",
+    "text-combine-upright",
+    "text-decoration-line",
+    "text-decoration-style",
+    "text-emphasis-position",
+    "text-emphasis-style",
+    "text-justify",
+    "text-orientation",
+    "text-overflow",
+    "text-rendering",
+    "-moz-text-size-adjust",
+    "-webkit-text-stroke-width",
+    "text-transform",
+    "touch-action",
+    "transform-box",
+    "transform-style",
+    "unicode-bidi",
+    "-moz-user-focus",
+    "-moz-user-input",
+    "-moz-user-modify",
+    "-moz-user-select",
+    "vector-effect",
+    "visibility",
+    "white-space",
+    "will-change",
+    "-moz-window-dragging",
+    "word-break",
+    "writing-mode",
+  ])],
+  ["none", new Set([
+    "animation-delay",
+    "animation-direction",
+    "animation-duration",
+    "animation-fill-mode",
+    "animation-iteration-count",
+    "animation-name",
+    "animation-play-state",
+    "animation-timing-function",
+    "-moz-binding",
+    "block-size",
+    "border-block-end-color",
+    "border-block-end-style",
+    "border-block-end-width",
+    "border-block-start-color",
+    "border-block-start-style",
+    "border-block-start-width",
+    "border-inline-end-color",
+    "border-inline-end-style",
+    "border-inline-end-width",
+    "border-inline-start-color",
+    "border-inline-start-style",
+    "border-inline-start-width",
+    "-moz-context-properties",
+    "-moz-control-character-visibility",
+    "display",
+    "font-optical-sizing",
+    "inline-size",
+    "margin-block-end",
+    "margin-block-start",
+    "margin-inline-end",
+    "margin-inline-start",
+    "-moz-math-display",
+    "max-block-size",
+    "max-inline-size",
+    "min-block-size",
+    "-moz-min-font-size-ratio",
+    "min-inline-size",
+    "offset-block-end",
+    "offset-block-start",
+    "offset-inline-end",
+    "offset-inline-start",
+    "padding-block-end",
+    "padding-block-start",
+    "padding-inline-end",
+    "padding-inline-start",
+    "rotate",
+    "scale",
+    "-moz-script-level",
+    "-moz-top-layer",
+    "transition-delay",
+    "transition-duration",
+    "transition-property",
+    "transition-timing-function",
+    "translate",
+    "-moz-window-shadow",
+  ])],
+  ["color", new Set([
+    "background-color",
+    "border-bottom-color",
+    "border-left-color",
+    "border-right-color",
+    "border-top-color",
+    "caret-color",
+    "color",
+    "column-rule-color",
+    "flood-color",
+    "-moz-font-smoothing-background-color",
+    "lighting-color",
+    "outline-color",
+    "stop-color",
+    "text-decoration-color",
+    "text-emphasis-color",
+    "-webkit-text-fill-color",
+    "-webkit-text-stroke-color",
+  ])],
+  ["custom", new Set([
+    "background-position-x",
+    "background-position-y",
+    "background-size",
+    "border-bottom-width",
+    "border-left-width",
+    "border-right-width",
+    "border-spacing",
+    "border-top-width",
+    "clip",
+    "clip-path",
+    "column-count",
+    "column-rule-width",
+    "filter",
+    "font-stretch",
+    "font-variation-settings",
+    "font-weight",
+    "-moz-image-region",
+    "mask-position-x",
+    "mask-position-y",
+    "mask-size",
+    "object-position",
+    "order",
+    "perspective-origin",
+    "shape-outside",
+    "stroke-dasharray",
+    "transform",
+    "transform-origin",
+    "-moz-window-transform",
+    "-moz-window-transform-origin",
+  ])],
+  ["coord", new Set([
+    "border-bottom-left-radius",
+    "border-bottom-right-radius",
+    "border-top-left-radius",
+    "border-top-right-radius",
+    "bottom",
+    "column-gap",
+    "column-width",
+    "flex-basis",
+    "grid-column-gap",
+    "grid-row-gap",
+    "height",
+    "left",
+    "letter-spacing",
+    "line-height",
+    "margin-bottom",
+    "margin-left",
+    "margin-right",
+    "margin-top",
+    "max-height",
+    "max-width",
+    "min-height",
+    "min-width",
+    "-moz-outline-radius-bottomleft",
+    "-moz-outline-radius-bottomright",
+    "-moz-outline-radius-topleft",
+    "-moz-outline-radius-topright",
+    "padding-bottom",
+    "padding-left",
+    "padding-right",
+    "padding-top",
+    "perspective",
+    "right",
+    "stroke-dashoffset",
+    "stroke-width",
+    "-moz-tab-size",
+    "text-indent",
+    "top",
+    "vertical-align",
+    "width",
+    "word-spacing",
+    "z-index",
+  ])],
+  ["float", new Set([
+    "-moz-box-flex",
+    "fill-opacity",
+    "flex-grow",
+    "flex-shrink",
+    "flood-opacity",
+    "font-size-adjust",
+    "opacity",
+    "shape-image-threshold",
+    "stop-opacity",
+    "stroke-miterlimit",
+    "stroke-opacity",
+    "-moz-window-opacity",
+  ])],
+  ["shadow", new Set([
+    "box-shadow",
+    "text-shadow",
+  ])],
+  ["paintServer", new Set([
+    "fill",
+    "stroke",
+  ])],
+  ["length", new Set([
+    "font-size",
+    "outline-offset",
+    "outline-width",
+  ])],
+];
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -24,26 +24,37 @@
  * - WebAnimation WebIDL files:
  *   /dom/webidl/Animation*.webidl
  */
 
 const {Cu, Ci} = require("chrome");
 const protocol = require("devtools/shared/protocol");
 const {Actor} = protocol;
 const {animationPlayerSpec, animationsSpec} = require("devtools/shared/specs/animation");
+const {ANIMATION_TYPE_FOR_LONGHANDS} = require("./animation-type-longhand");
 
 // Types of animations.
 const ANIMATION_TYPES = {
   CSS_ANIMATION: "cssanimation",
   CSS_TRANSITION: "csstransition",
   SCRIPT_ANIMATION: "scriptanimation",
   UNKNOWN: "unknown"
 };
 exports.ANIMATION_TYPES = ANIMATION_TYPES;
 
+function getAnimationTypeForLonghand(property) {
+  for (let [type, props] of ANIMATION_TYPE_FOR_LONGHANDS) {
+    if (props.has(property)) {
+      return type;
+    }
+  }
+  throw new Error("Unknown longhand property name");
+}
+exports.getAnimationTypeForLonghand = getAnimationTypeForLonghand;
+
 /**
  * The AnimationPlayerActor provides information about a given animation: its
  * startTime, currentTime, current state, etc.
  *
  * Since the state of a player changes as the animation progresses it is often
  * useful to call getCurrentState at regular intervals to get the current state.
  *
  * This actor also allows playing, pausing and seeking the animation.
@@ -510,17 +521,17 @@ var AnimationPlayerActor = protocol.Acto
             pseudo = target.type;
             target = target.parentElement;
           }
           const value =
             DOMWindowUtils.getUnanimatedComputedStyle(target,
                                                       pseudo,
                                                       property.name,
                                                       DOMWindowUtils.FLUSH_NONE);
-          const animationType = DOMWindowUtils.getAnimationTypeForLonghand(property.name);
+          const animationType = getAnimationTypeForLonghand(property.name);
           underlyingValue = animationType === "float" ? parseFloat(value, 10) : value;
         }
         values.value = underlyingValue;
       });
     }
 
     // Calculate the distance.
     for (let property of properties) {
@@ -564,22 +575,19 @@ var AnimationPlayerActor = protocol.Acto
   },
 
   /**
    * Get the animation types for a given list of CSS property names.
    * @param {Array} propertyNames - CSS property names (e.g. background-color)
    * @return {Object} Returns animation types (e.g. {"background-color": "rgb(0, 0, 0)"}.
    */
   getAnimationTypes: function(propertyNames) {
-    const DOMWindowUtils = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDOMWindowUtils);
     const animationTypes = {};
     for (let propertyName of propertyNames) {
-      animationTypes[propertyName] =
-        DOMWindowUtils.getAnimationTypeForLonghand(propertyName);
+      animationTypes[propertyName] = getAnimationTypeForLonghand(propertyName);
     }
     return animationTypes;
   },
 
   /**
    * Returns the distance of between value1, value2.
    * @param {Object} target - dom element
    * @param {String} propertyName - e.g. transform
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -16,16 +16,17 @@ DIRS += [
 ]
 
 DevToolsModules(
     'accessibility-parent.js',
     'accessibility.js',
     'actor-registry.js',
     'addon.js',
     'addons.js',
+    'animation-type-longhand.js',
     'animation.js',
     'array-buffer.js',
     'breakpoint.js',
     'call-watcher.js',
     'canvas.js',
     'child-process.js',
     'chrome.js',
     'common.js',
--- a/devtools/server/tests/mochitest/chrome.ini
+++ b/devtools/server/tests/mochitest/chrome.ini
@@ -25,16 +25,17 @@ support-files =
   memory-helpers.js
   nonchrome_unsafeDereference.html
   small-image.gif
   setup-in-child.js
   setup-in-parent.js
   webconsole-helpers.js
   webextension-helpers.js
 [test_animation_actor-lifetime.html]
+[test_animation-type-longhand.html]
 [test_connection-manager.html]
 [test_connectToFrame.html]
 [test_css-logic.html]
 [test_css-logic-media-queries.html]
 [test_css-logic-specificity.html]
 [test_css-properties.html]
 [test_Debugger.Source.prototype.introductionScript.html]
 [test_Debugger.Source.prototype.introductionType.html]
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/mochitest/test_animation-type-longhand.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Test animation-type-longhand</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<body>
+<script>
+  "use strict";
+
+  // This test checks the content of animation type for longhands table that
+  // * every longhand property is included
+  // * nothing else is included
+  // * no property is mapped to more than one animation type
+  window.onload = function() {
+    const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+    const { ANIMATION_TYPE_FOR_LONGHANDS } =
+      require("devtools/server/actors/animation-type-longhand");
+    const InspectorUtils = SpecialPowers.InspectorUtils;
+
+    const all_longhands = InspectorUtils.getCSSPropertyNames({
+      includeShorthands: false,
+      includeExperimentals: true,
+    });
+
+    let unseen_longhands = new Set(all_longhands);
+    let seen_longhands = new Set();
+    for (let [, names] of ANIMATION_TYPE_FOR_LONGHANDS) {
+      for (let name of names) {
+        ok(!seen_longhands.has(name),
+           `${name} should have only one animation type`);
+        ok(unseen_longhands.has(name),
+           `${name} is an unseen longhand property`);
+        unseen_longhands.delete(name);
+        seen_longhands.add(name);
+      }
+    }
+    is(unseen_longhands.size, 0,
+       "All longhands should be mapped to some animation type");
+
+    SimpleTest.finish();
+  };
+</script>
+</body>
--- a/devtools/shared/client/root-client.js
+++ b/devtools/shared/client/root-client.js
@@ -86,16 +86,117 @@ RootClient.prototype = {
    * List the running processes.
    *
    * @param function onResponse
    *        Called with the response packet.
    */
   listProcesses: DebuggerClient.requester({ type: "listProcesses" }),
 
   /**
+   * Retrieve all service worker registrations as well as workers from the parent
+   * and child processes. Listing service workers involves merging information coming from
+   * registrations and workers, this method will combine this information to present a
+   * unified array of serviceWorkers. If you are only interested in other workers, use
+   * listWorkers.
+   *
+   * @return {Object}
+   *         - {Array} service
+   *           array of form-like objects for serviceworkers
+   *         - {Array} shared
+   *           Array of WorkerActor forms, containing shared workers.
+   *         - {Array} other
+   *           Array of WorkerActor forms, containing other workers.
+   */
+  listAllWorkers: async function() {
+    let result = {
+      service: [],
+      shared: [],
+      other: []
+    };
+
+    try {
+      let registrations = [];
+      let workers = [];
+
+      // List service worker registrations
+      ({ registrations } = await this.listServiceWorkerRegistrations());
+
+      // List workers from the Parent process
+      ({ workers } = await this.listWorkers());
+
+      // And then from the Child processes
+      let { processes } = await this.listProcesses();
+      for (let process of processes) {
+        // Ignore parent process
+        if (process.parent) {
+          continue;
+        }
+        let { form } = await this._client.getProcess(process.id);
+        let processActor = form.actor;
+        let response = await this._client.request({
+          to: processActor,
+          type: "listWorkers"
+        });
+        workers = workers.concat(response.workers);
+      }
+
+      registrations.forEach(form => {
+        result.service.push({
+          name: form.url,
+          url: form.url,
+          scope: form.scope,
+          fetch: form.fetch,
+          registrationActor: form.actor,
+          active: form.active
+        });
+      });
+
+      workers.forEach(form => {
+        let worker = {
+          name: form.url,
+          url: form.url,
+          workerActor: form.actor
+        };
+        switch (form.type) {
+          case Ci.nsIWorkerDebugger.TYPE_SERVICE:
+            let registration = result.service.find(r => r.scope === form.scope);
+            if (registration) {
+              // XXX: Race, sometimes a ServiceWorkerRegistrationInfo doesn't
+              // have a scriptSpec, but its associated WorkerDebugger does.
+              if (!registration.url) {
+                registration.name = registration.url = form.url;
+              }
+              registration.workerActor = form.actor;
+            } else {
+              worker.fetch = form.fetch;
+
+              // If a service worker registration could not be found, this means we are in
+              // e10s, and registrations are not forwarded to other processes until they
+              // reach the activated state. Augment the worker as a registration worker to
+              // display it in aboutdebugging.
+              worker.scope = form.scope;
+              worker.active = false;
+              result.service.push(worker);
+            }
+            break;
+          case Ci.nsIWorkerDebugger.TYPE_SHARED:
+            result.shared.push(worker);
+            break;
+          default:
+            result.other.push(worker);
+        }
+      });
+    } catch (e) {
+      // Something went wrong, maybe our client is disconnected?
+    }
+
+    return result;
+  },
+
+  /**
    * Fetch the TabActor for the currently selected tab, or for a specific
    * tab given as first parameter.
    *
    * @param [optional] object filter
    *        A dictionary object with following optional attributes:
    *         - outerWindowID: used to match tabs in parent process
    *         - tabId: used to match tabs in child processes
    *         - tab: a reference to xul:tab element
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2738,70 +2738,16 @@ nsDOMWindowUtils::ComputeAnimationDistan
 
   RefPtr<ComputedStyle> computedStyle =
     nsComputedDOMStyle::GetComputedStyle(element, nullptr);
   *aResult = v1.ComputeDistance(property, v2, computedStyle);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetAnimationTypeForLonghand(const nsAString& aProperty,
-                                              nsAString& aResult)
-{
-  nsCSSPropertyID propertyID =
-    nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
-  if (propertyID == eCSSProperty_UNKNOWN) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  if (nsCSSProps::IsShorthand(propertyID)) {
-    // The given property should be a longhand.
-    return NS_ERROR_INVALID_ARG;
-  }
-  switch (nsCSSProps::kAnimTypeTable[propertyID]) {
-    case eStyleAnimType_Custom:
-      aResult.AssignLiteral("custom");
-      break;
-    case eStyleAnimType_Coord:
-    case eStyleAnimType_Sides_Top:
-    case eStyleAnimType_Sides_Right:
-    case eStyleAnimType_Sides_Bottom:
-    case eStyleAnimType_Sides_Left:
-    case eStyleAnimType_Corner_TopLeft:
-    case eStyleAnimType_Corner_TopRight:
-    case eStyleAnimType_Corner_BottomRight:
-    case eStyleAnimType_Corner_BottomLeft:
-      aResult.AssignLiteral("coord");
-      break;
-    case eStyleAnimType_nscoord:
-      aResult.AssignLiteral("length");
-      break;
-    case eStyleAnimType_float:
-      aResult.AssignLiteral("float");
-      break;
-    case eStyleAnimType_Color:
-    case eStyleAnimType_ComplexColor:
-      aResult.AssignLiteral("color");
-      break;
-    case eStyleAnimType_PaintServer:
-      aResult.AssignLiteral("paintServer");
-      break;
-    case eStyleAnimType_Shadow:
-      aResult.AssignLiteral("shadow");
-      break;
-    case eStyleAnimType_Discrete:
-      aResult.AssignLiteral("discrete");
-      break;
-    case eStyleAnimType_None:
-      aResult.AssignLiteral("none");
-      break;
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::GetUnanimatedComputedStyle(nsIDOMElement* aElement,
                                              const nsAString& aPseudoElement,
                                              const nsAString& aProperty,
                                              int32_t aFlushType,
                                              nsAString& aResult)
 {
   nsCOMPtr<Element> element = do_QueryInterface(aElement);
   if (!element) {
--- a/dom/base/test/test_domwindowutils.html
+++ b/dom/base/test/test_domwindowutils.html
@@ -50,125 +50,28 @@ function test_sendMouseEventOptionals() 
     SimpleTest.executeSoon(next);
   }, {once: true});
 
   // Check explicit value for optional args
   utils.sendMouseEvent("mouseup", x, y, button, clickCount, modifiers,
                        false, pressure, source, false);
 }
 
-function test_getAnimationType() {
-  [
-    {
-      propertyName: "align-content",
-      expectedType: "discrete"
-    },
-    {
-      propertyName: "animation-delay",
-      expectedType: "none"
-    },
-    {
-      propertyName: "background-color",
-      expectedType: "color"
-    },
-    {
-      propertyName: "background-size",
-      expectedType: "custom"
-    },
-    {
-      propertyName: "border-bottom-left-radius",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "border-bottom-right-radius",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "border-top-left-radius",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "border-top-right-radius",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "font-size",
-      expectedType: "length"
-    },
-    {
-      propertyName: "margin-top",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "margin-right",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "margin-bottom",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "margin-left",
-      expectedType: "coord"
-    },
-    {
-      propertyName: "opacity",
-      expectedType: "float"
-    },
-    {
-      propertyName: "stroke",
-      expectedType: "paintServer"
-    },
-    {
-      propertyName: "text-shadow",
-      expectedType: "shadow"
-    },
-    {
-      propertyName: "transform",
-      expectedType: "custom"
-    },
-    {
-      propertyName: "visibility",
-      expectedType: "discrete"
-    },
-    {
-      propertyName: "width",
-      expectedType: "coord"
-    }
-  ].forEach(({ propertyName, expectedType }) => {
-    is(utils.getAnimationTypeForLonghand(propertyName), expectedType,
-       `Animation type should be ${ expectedType }`);
-  });
-
-  SimpleTest.doesThrow(
-    () => utils.getAnimationTypeForLonghand("background"),
-    "NS_ERROR_ILLEGAL_VALUE",
-    "background property should throw");
-
-  SimpleTest.doesThrow(
-    () => utils.getAnimationTypeForLonghand("invalid"),
-    "NS_ERROR_ILLEGAL_VALUE",
-    "Invalid property should throw");
-
-  next();
-}
-
 function test_getUnanimatedComputedStyle() {
   SpecialPowers.pushPrefEnv(
     { set: [["dom.animations-api.core.enabled", true]] },
     () => {
       window.open("file_domwindowutils_animation.html");
     }
   );
 }
 
 var tests = [
   test_sendMouseEventDefaults,
   test_sendMouseEventOptionals,
-  test_getAnimationType,
   test_getUnanimatedComputedStyle
 ];
 
 function next() {
   if (!tests.length) {
     SimpleTest.finish();
     return;
   }
--- a/dom/chrome-webidl/InspectorUtils.webidl
+++ b/dom/chrome-webidl/InspectorUtils.webidl
@@ -83,16 +83,18 @@ namespace InspectorUtils {
   boolean hasPseudoClassLock(Element element, DOMString pseudoClass);
   void clearPseudoClassLocks(Element element);
   [Throws] void parseStyleSheet(CSSStyleSheet sheet, DOMString input);
   void scrollElementIntoView(Element element);
 };
 
 dictionary PropertyNamesOptions {
   boolean includeAliases = false;
+  boolean includeShorthands = true;
+  boolean includeExperimentals = false;
 };
 
 dictionary InspectorRGBATuple {
   /*
    * NOTE: This tuple is in the normal 0-255-sized RGB space but can be
    * fractional and may extend outside the 0-255 range.
    *
    * a is in the range 0 - 1.
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -61,21 +61,21 @@ public:
     // another SetValue is already happening for this editor.  If it is,
     // we must wait until we unwind to re-enable oninput events.
     , mOuterTransaction(aTextEditor->IsSuppressingDispatchingInputEvent())
   {
     MOZ_ASSERT(aTextEditor);
   }
   ~ValueSetter()
   {
-    mTextEditor->SetSuppressDispatchingInputEvent(mOuterTransaction);
+    mTextEditor->SuppressDispatchingInputEvent(mOuterTransaction);
   }
   void Init()
   {
-    mTextEditor->SetSuppressDispatchingInputEvent(true);
+    mTextEditor->SuppressDispatchingInputEvent(true);
   }
 
 private:
   RefPtr<TextEditor> mTextEditor;
   bool mOuterTransaction;
 };
 
 class RestoreSelectionState : public Runnable {
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1430,23 +1430,16 @@ interface nsIDOMWindowUtils : nsISupport
    * property.
    */
   double computeAnimationDistance(in nsIDOMElement element,
                                   in AString property,
                                   in AString value1,
                                   in AString value2);
 
   /**
-   * Returns the animation type of the specified property (e.g. 'coord').
-   *
-   * @param aProperty A longhand CSS property (e.g. 'background-color').
-   */
-  AString getAnimationTypeForLonghand(in AString aProperty);
-
-  /**
    * Returns the computed style for the specified property of given pseudo type
    * on the given element after removing styles from declarative animations.
    * @param aElement - A target element
    * @param aPseudoElement - A pseudo type (e.g. '::before' or null)
    * @param aProperty - A longhand CSS property (e.g. 'background-color')
    * @param aFlushType - FLUSH_NONE if any pending styles should not happen,
    *                     FLUSH_STYLE to flush pending styles.
    */
--- a/dom/interfaces/xul/nsIDOMXULButtonElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULButtonElement.idl
@@ -1,28 +1,23 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMXULLabeledControlEl.idl"
 
-[scriptable, uuid(d56f1f8f-fc4e-4650-9a85-25108bbd1980)]
+[scriptable, uuid(6ed53cfb-9e59-424c-af8d-e74582381951)]
 interface nsIDOMXULButtonElement : nsIDOMXULLabeledControlElement {
-  const short CHECKSTATE_UNCHECKED = 0;
-  const short CHECKSTATE_CHECKED = 1;
-  const short CHECKSTATE_MIXED = 2;
-
   attribute DOMString type;
   attribute DOMString dlgType;
 
   // For buttons of type="menu" only.
   attribute boolean open;
   
   // For buttons of type="checkbox" only.
   attribute boolean checked;
-  attribute long checkState;
   attribute boolean autoCheck;
 
   // For buttons of type="radio" only.
   attribute DOMString group;
 };
 
--- a/dom/interfaces/xul/nsIDOMXULCheckboxElement.idl
+++ b/dom/interfaces/xul/nsIDOMXULCheckboxElement.idl
@@ -1,18 +1,13 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIDOMElement.idl"
 #include "nsIDOMXULLabeledControlEl.idl"
 
-[scriptable, uuid(745518ad-3163-41f0-b358-c81fb5a587bc)]
+[scriptable, uuid(674965d9-aff4-411d-a382-7cb32c0f25a1)]
 interface nsIDOMXULCheckboxElement : nsIDOMXULLabeledControlElement {
-  const short CHECKSTATE_UNCHECKED = 0;
-  const short CHECKSTATE_CHECKED = 1;
-  const short CHECKSTATE_MIXED = 2;
-
   attribute boolean checked;
-  attribute long checkState;
   attribute boolean autoCheck;
 };
new file mode 100644
--- /dev/null
+++ b/dom/media/AsyncLogger.h
@@ -0,0 +1,269 @@
+/* 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/. */
+
+/* Implementation of an asynchronous lock-free logging system. */
+
+#ifndef mozilla_dom_AsyncLogger_h
+#define mozilla_dom_AsyncLogger_h
+
+#include <atomic>
+#include <thread>
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Sprintf.h"
+
+namespace mozilla {
+
+namespace detail {
+
+ // This class implements a lock-free multiple producer single consumer queue of
+ // fixed size log messages, with the following characteristics:
+ // - Unbounded (uses a intrinsic linked list)
+ // - Allocates on Push. Push can be called on any thread.
+ // - Deallocates on Pop. Pop MUST always be called on the same thread for the
+ // life-time of the queue.
+ //
+ // In our scenario, the producer threads are real-time, they can't block. The
+ // consummer thread runs every now and then and empties the queue to a log
+ // file, on disk.
+ //
+ // Having fixed size messages and jemalloc is probably not the fastest, but
+ // allows having a simpler design, we count on the fact that jemalloc will get
+ // the memory from a thread-local source most of the time.
+template<size_t MESSAGE_LENGTH>
+class MPSCQueue
+{
+public:
+    struct Message {
+        Message()
+        {
+           mNext.store(nullptr, std::memory_order_relaxed);
+        }
+        Message(const Message& aMessage) = delete;
+        void operator=(const Message& aMessage) = delete;
+
+        char data[MESSAGE_LENGTH];
+        std::atomic<Message*> mNext;
+    };
+    // Creates a new MPSCQueue. Initially, the queue has a single sentinel node,
+    // pointed to by both mHead and mTail.
+    MPSCQueue()
+    // At construction, the initial message points to nullptr (it has no
+    // successor). It is a sentinel node, that does not contain meaningful
+    // data.
+    : mHead(new Message())
+    , mTail(mHead.load(std::memory_order_relaxed))
+    { }
+
+    ~MPSCQueue()
+    {
+        Message dummy;
+        while (this->Pop(dummy.data)) {}
+        Message* front = mHead.load(std::memory_order_relaxed);
+        delete front;
+    }
+
+    void
+    Push(MPSCQueue<MESSAGE_LENGTH>::Message* aMessage)
+    {
+        // The next two non-commented line are called A and B in this paragraph.
+        // Producer threads i, i-1, etc. are numbered in the order they reached
+        // A in time, thread i being the thread that has reached A first.
+        // Atomically, on line A the new `mHead` is set to be the node that was
+        // just allocated, with strong memory order. From now one, any thread
+        // that reaches A will see that the node just allocated is
+        // effectively the head of the list, and will make itself the new head
+        // of the list.
+        // In a bad case (when thread i executes A and then
+        // is not scheduled for a long time), it is possible that thread i-1 and
+        // subsequent threads create a seemingly disconnected set of nodes, but
+        // they all have the correct value for the next node to set as their
+        // mNext member on their respective stacks (in `prev`), and this is
+        // always correct. When the scheduler resumes, and line B is executed,
+        // the correct linkage is resumed.
+        // Before line B, since mNext for the node the was the last element of
+        // the queue still has an mNext of nullptr, Pop will not see the node
+        // added.
+        // For line A, it's critical to have strong ordering both ways (since
+        // it's going to possibly be read and write repeatidly by multiple
+        // threads)
+        // Line B can have weaker guarantees, it's only going to be written by a
+        // single thread, and we just need to ensure it's read properly by a
+        // single other one.
+        Message* prev = mHead.exchange(aMessage, std::memory_order_acq_rel);
+        prev->mNext.store(aMessage, std::memory_order_release);
+    }
+
+    // Allocates a new node, copy aInput to the new memory location, and pushes
+    // it to the end of the list.
+    void
+    Push(const char aInput[MESSAGE_LENGTH])
+    {
+        // Create a new message, and copy the messages passed on argument to the
+        // new memory location. We are not touching the queue right now. The
+        // successor for this new node is set to be nullptr.
+        Message* msg = new Message();
+        strncpy(msg->data, aInput, MESSAGE_LENGTH);
+
+        Push(msg);
+    }
+
+    // Copy the content of the first message of the queue to aOutput, and
+    // frees the message. Returns true if there was a message, in which case
+    // `aOutput` contains a valid value. If the queue was empty, returns false,
+    // in which case `aOutput` is left untouched.
+    bool
+    Pop(char aOutput[MESSAGE_LENGTH])
+    {
+        // Similarly, in this paragraph, the two following lines are called A
+        // and B, and threads are called thread i, i-1, etc. in order of
+        // execution of line A.
+        // On line A, the first element of the queue is acquired. It is simply a
+        // sentinel node.
+        // On line B, we acquire the node that has the data we want. If B is
+        // null, then only the sentinel node was present in the queue, we can
+        // safely return false.
+        // mTail can be loaded with relaxed ordering, since it's not written nor
+        // read by any other thread (this queue is single consumer).
+        // mNext can be written to by one of the producer, so it's necessary to
+        // ensure those writes are seen, hence the stricter ordering.
+        Message* tail = mTail.load(std::memory_order_relaxed);
+        Message* next = tail->mNext.load(std::memory_order_acquire);
+
+        if (next == nullptr) {
+            return false;
+        }
+
+        strncpy(aOutput, next->data, MESSAGE_LENGTH);
+
+        // Simply shift the queue one node further, so that the sentinel node is
+        // now pointing to the correct most ancient node. It contains stale data,
+        // but this data will never be read again.
+        // It's only necessary to ensure the previous load on this thread is not
+        // reordered past this line, so release ordering is sufficient here.
+        mTail.store(next, std::memory_order_release);
+
+        // This thread is now the only thing that points to `tail`, it can be
+        // safely deleted.
+        delete tail;
+
+        return true;
+    }
+
+private:
+    // An atomic pointer to the most recent message in the queue.
+    std::atomic<Message*> mHead;
+    // An atomic pointer to a sentinel node, that points to the oldest message
+    // in the queue.
+    std::atomic<Message*> mTail;
+
+    MPSCQueue(const MPSCQueue&) = delete;
+    void operator=(const MPSCQueue&) = delete;
+public:
+    // The goal here is to make it easy on the allocator. We pack a pointer in the
+    // message struct, and we still want to do power of two allocations to
+    // minimize allocator slop. The allocation size are going to be constant, so
+    // the allocation is probably going to hit the thread local cache in jemalloc,
+    // making it cheap and, more importantly, lock-free enough.
+    static const size_t MESSAGE_PADDING = sizeof(Message::mNext);
+private:
+    static_assert(IsPowerOfTwo(MESSAGE_LENGTH + MESSAGE_PADDING),
+                  "MPSCQueue internal allocations must have a size that is a"
+                  "power of two ");
+};
+} // end namespace detail
+
+// This class implements a lock-free asynchronous logger, that outputs to
+// MOZ_LOG.
+// Any thread can use this logger without external synchronization and without
+// being blocked. This log is suitable for use in real-time audio threads.
+// Log formatting is best done externally, this class implements the output
+// mechanism only.
+// This class uses a thread internally, and must be started and stopped
+// manually.
+// If logging is disabled, all the calls are no-op.
+class AsyncLogger
+{
+public:
+  static const uint32_t MAX_MESSAGE_LENGTH = 512 - detail::MPSCQueue<sizeof(void*)>::MESSAGE_PADDING;
+
+  // aLogModuleName is the name of the MOZ_LOG module.
+  explicit AsyncLogger(const char* aLogModuleName)
+  : mThread(nullptr)
+  , mLogModule(aLogModuleName)
+  , mRunning(false)
+  { }
+
+  ~AsyncLogger()
+  {
+    if (Enabled()) {
+      Stop();
+    }
+  }
+
+  void Start()
+  {
+    MOZ_ASSERT(!mRunning, "Double calls to AsyncLogger::Start");
+    if (Enabled()) {
+      mRunning = true;
+      Run();
+    }
+  }
+
+  void Stop()
+  {
+    if (Enabled()) {
+      if (mRunning) {
+        mRunning = false;
+        mThread->join();
+      }
+    } else {
+      MOZ_ASSERT(!mRunning && !mThread);
+    }
+  }
+
+  void Log(const char* format, ...) MOZ_FORMAT_PRINTF(2,3)
+  {
+    if (Enabled()) {
+      auto* msg = new detail::MPSCQueue<MAX_MESSAGE_LENGTH>::Message();
+      va_list args;
+      va_start(args, format);
+      VsprintfLiteral(msg->data, format, args);
+      va_end(args);
+      mMessageQueue.Push(msg);
+    }
+  }
+
+  bool Enabled()
+  {
+    return MOZ_LOG_TEST(mLogModule, mozilla::LogLevel::Verbose);
+  }
+
+private:
+  void Run()
+  {
+    MOZ_ASSERT(Enabled());
+    mThread.reset(new std::thread([this]() {
+      while (mRunning) {
+        char message[MAX_MESSAGE_LENGTH];
+        while (mMessageQueue.Pop(message) && mRunning) {
+          MOZ_LOG(mLogModule, mozilla::LogLevel::Verbose, ("%s", message));
+        }
+        Sleep();
+      }
+    }));
+  }
+
+  void Sleep() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
+
+  std::unique_ptr<std::thread> mThread;
+  mozilla::LazyLogModule mLogModule;
+  detail::MPSCQueue<MAX_MESSAGE_LENGTH> mMessageQueue;
+  std::atomic<bool> mRunning;
+};
+
+} // end namespace mozilla
+
+#endif // mozilla_dom_AsyncLogger_h
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -1,19 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
 #include <MediaStreamGraphImpl.h>
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Unused.h"
 #include "CubebUtils.h"
+#include "Tracing.h"
 
 #ifdef MOZ_WEBRTC
 #include "webrtc/MediaEngineWebRTC.h"
 #endif
 
 #ifdef XP_MACOSX
 #include <sys/sysctl.h>
 #endif
@@ -876,16 +878,19 @@ AudioCallbackDriver::AutoInCallback::Aut
 AudioCallbackDriver::AutoInCallback::~AutoInCallback() {
   mDriver->mInCallback = false;
 }
 
 long
 AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
                                   AudioDataValue* aOutputBuffer, long aFrames)
 {
+   TRACE_AUDIO_CALLBACK_BUDGET(aFrames, mSampleRate);
+   TRACE_AUDIO_CALLBACK();
+
   // Don't add the callback until we're inited and ready
   if (!mAddedMixer) {
     mGraphImpl->mMixer.AddCallback(this);
     mAddedMixer = true;
   }
 
 #ifdef DEBUG
   // DebugOnly<> doesn't work here... it forces an initialization that will cause
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -27,25 +27,28 @@
 #include "mozilla/media/MediaUtils.h"
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "VideoFrameContainer.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Unused.h"
 #include "mtransport/runnable_utils.h"
 #include "VideoUtils.h"
+#include "Tracing.h"
 
 #include "webaudio/blink/DenormalDisabler.h"
 #include "webaudio/blink/HRTFDatabaseLoader.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::media;
 
+mozilla::AsyncLogger gMSGTraceLogger("MSGTracing");
+
 namespace mozilla {
 
 LazyLogModule gMediaStreamGraphLog("MediaStreamGraph");
 #ifdef LOG
 #undef LOG
 #endif // LOG
 #define LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
 
@@ -63,16 +66,20 @@ enum SourceMediaStream::TrackCommands : 
 static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   MOZ_ASSERT(mStreams.IsEmpty() && mSuspendedStreams.IsEmpty(),
              "All streams should have been destroyed by messages from the main thread");
   LOG(LogLevel::Debug, ("MediaStreamGraph %p destroyed", this));
   LOG(LogLevel::Debug, ("MediaStreamGraphImpl::~MediaStreamGraphImpl"));
+
+#ifdef TRACING
+  gMSGTraceLogger.Stop();
+#endif
 }
 
 void
 MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream)
 {
   MOZ_ASSERT(OnGraphThreadOrNotRunning());
   aStream->mTracksStartTime = mProcessedTime;
 
@@ -1117,16 +1124,17 @@ MediaStreamGraphImpl::RunMessageAfterPro
   // Only one block is used for messages from the graph thread.
   MOZ_ASSERT(mFrontMessageQueue.Length() == 1);
   mFrontMessageQueue[0].mMessages.AppendElement(Move(aMessage));
 }
 
 void
 MediaStreamGraphImpl::RunMessagesInQueue()
 {
+  TRACE_AUDIO_CALLBACK();
   MOZ_ASSERT(OnGraphThread());
   // Calculate independent action times for each batch of messages (each
   // batch corresponding to an event loop task). This isolates the performance
   // of different scripts to some extent.
   for (uint32_t i = 0; i < mFrontMessageQueue.Length(); ++i) {
     nsTArray<UniquePtr<ControlMessage>>& messages = mFrontMessageQueue[i].mMessages;
 
     for (uint32_t j = 0; j < messages.Length(); ++j) {
@@ -1134,16 +1142,17 @@ MediaStreamGraphImpl::RunMessagesInQueue
     }
   }
   mFrontMessageQueue.Clear();
 }
 
 void
 MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecisions)
 {
+  TRACE_AUDIO_CALLBACK();
   MOZ_ASSERT(OnGraphThread());
   MOZ_ASSERT(aEndBlockingDecisions >= mProcessedTime);
   // The next state computed time can be the same as the previous: it
   // means the driver would have been blocking indefinitly, but the graph has
   // been woken up right after having been to sleep.
   MOZ_ASSERT(aEndBlockingDecisions >= mStateComputedTime);
 
   UpdateStreamOrder();
@@ -1229,16 +1238,17 @@ MediaStreamGraphImpl::UpdateGraph(GraphT
       aEndBlockingDecisions == mStateComputedTime) {
     EnsureNextIteration();
   }
 }
 
 void
 MediaStreamGraphImpl::Process()
 {
+  TRACE_AUDIO_CALLBACK();
   MOZ_ASSERT(OnGraphThread());
   // Play stream contents.
   bool allBlockedForever = true;
   // True when we've done ProcessInput for all processed streams.
   bool doneAllProducing = false;
   // This is the number of frame that are written to the AudioStreams, for
   // this cycle.
   StreamTime ticksPlayed = 0;
@@ -1333,16 +1343,17 @@ MediaStreamGraphImpl::UpdateMainThreadSt
 
   SwapMessageQueues();
   return true;
 }
 
 bool
 MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd)
 {
+  TRACE_AUDIO_CALLBACK();
   // Changes to LIFECYCLE_RUNNING occur before starting or reviving the graph
   // thread, and so the monitor need not be held to check mLifecycleState.
   // LIFECYCLE_THREAD_NOT_STARTED is possible when shutting down offline
   // graphs that have not started.
   MOZ_DIAGNOSTIC_ASSERT(mLifecycleState <= LIFECYCLE_RUNNING);
   MOZ_ASSERT(OnGraphThread());
   WebCore::DenormalDisabler disabler;
 
@@ -1539,16 +1550,17 @@ public:
                                                bool aSourceIsMSG)
     : Runnable("MediaStreamGraphStableStateRunnable")
     , mGraph(aGraph)
     , mSourceIsMSG(aSourceIsMSG)
   {
   }
   NS_IMETHOD Run() override
   {
+    TRACE();
     if (mGraph) {
       mGraph->RunInStableState(mSourceIsMSG);
     }
     return NS_OK;
   }
 private:
   RefPtr<MediaStreamGraphImpl> mGraph;
   bool mSourceIsMSG;
@@ -2760,16 +2772,17 @@ SourceMediaStream::SetPullEnabled(bool a
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aEnabled));
 }
 
 bool
 SourceMediaStream::PullNewData(
   StreamTime aDesiredUpToTime,
   nsTArray<RefPtr<SourceMediaStream::NotifyPullPromise>>& aPromises)
 {
+  TRACE_AUDIO_CALLBACK();
   MutexAutoLock lock(mMutex);
   if (!mPullEnabled || mFinished) {
     return false;
   }
   // Compute how much stream time we'll need assuming we don't block
   // the stream at all.
   StreamTime t = GraphTimeToStreamTime(aDesiredUpToTime);
   StreamTime current = mTracks.GetEnd();
@@ -3609,16 +3622,22 @@ MediaStreamGraphImpl::MediaStreamGraphIm
 {
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
       AudioCallbackDriver* driver = new AudioCallbackDriver(this);
       mDriver = driver;
     } else {
       mDriver = new SystemClockDriver(this);
     }
+
+#ifdef TRACING
+    // This is a noop if the logger has not been enabled.
+    gMSGTraceLogger.Start();
+    gMSGTraceLogger.Log("[");
+#endif
   } else {
     mDriver = new OfflineClockDriver(this, MEDIA_GRAPH_TARGET_PERIOD_MS);
   }
 
   mLastMainThreadUpdate = TimeStamp::Now();
 
   RegisterWeakAsyncMemoryReporter(this);
 }
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -20,16 +20,23 @@
 #include "nsIRunnable.h"
 #include "nsTArray.h"
 #include <speex/speex_resampler.h>
 
 class nsIRunnable;
 class nsIGlobalObject;
 class nsPIDOMWindowInner;
 
+namespace mozilla {
+  class AsyncLogger;
+};
+
+extern mozilla::AsyncLogger gMSGTraceLogger;
+
+
 template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
 {
   public:
   static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
 };
 
 namespace mozilla {
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -18,16 +18,17 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsDataHashtable.h"
 #include "nsIMemoryReporter.h"
 #include "nsINamed.h"
 #include "nsIRunnable.h"
 #include "nsIThread.h"
 #include "nsITimer.h"
+#include "AsyncLogger.h"
 
 namespace mozilla {
 
 namespace media {
 class ShutdownTicket;
 }
 
 template <typename T>
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -2028,18 +2028,23 @@ class RTCRtpSender {
   }
 
   setTrack(track) {
     this._pc._replaceTrackNoRenegotiation(this._transceiverImpl, track);
     this.track = track;
   }
 
   getStats() {
-    return this._pc._async(
-      async () => this._pc._getStats(this.track));
+    if (this.track) {
+      return this._pc._async(
+        async () => this._pc._getStats(this.track));
+    }
+    return this._pc._win.Promise.resolve().then(
+      () => this._pc._win.RTCStatsReport._create(this._pc._win, new Map())
+    );
   }
 
   checkWasCreatedByPc(pc) {
     if (pc != this._pc.__DOM_IMPL__) {
       throw new this._pc._win.DOMException(
           "This sender was not created by this PeerConnection",
           "InvalidAccessError");
     }
new file mode 100644
--- /dev/null
+++ b/dom/media/Tracing.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "Tracing.h"
+
+#include <inttypes.h>
+#include <cstdio>
+
+#include "AsyncLogger.h"
+#include "mozilla/TimeStamp.h"
+
+using namespace mozilla;
+
+uint64_t
+AutoTracer::NowInUs()
+{
+  static TimeStamp base = TimeStamp::Now();
+  return (TimeStamp::Now() - base).ToMicroseconds();
+}
+
+void
+AutoTracer::PrintEvent(const char* aName,
+                       const char* aCategory,
+                       const char* aComment,
+                       TracingPhase aPhase,
+                       uint64_t aTime,
+                       uint64_t aPID,
+                       uint64_t aThread)
+{
+  mLogger.Log("{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"%c\","
+              "\"ts\": %" PRIu64 ", \"pid\": %" PRIu64 ", \"tid\":"
+              " %" PRIu64 ", \"args\": { \"comment\": \"%s\"}},",
+           aName, aCategory, TRACING_PHASE_STRINGS[static_cast<int>(aPhase)],
+           aTime, aPID, aThread, aComment);
+}
+
+void
+AutoTracer::PrintBudget(const char* aName,
+                        const char* aCategory,
+                        const char* aComment,
+                        uint64_t aDuration,
+                        uint64_t aPID,
+                        uint64_t aThread,
+                        uint64_t aFrames)
+{
+  mLogger.Log("{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"X\","
+              "\"ts\": %" PRIu64 ", \"dur\": %" PRIu64 ", \"pid\": %" PRIu64 ","
+              "\"tid\": %" PRIu64 ", \"args\": { \"comment\": %" PRIu64 "}},",
+              aName, aCategory, NowInUs(), aDuration, aPID, aThread, aFrames);
+}
+
+AutoTracer::AutoTracer(AsyncLogger& aLogger,
+                       const char* aLocation,
+                       uint64_t aPID,
+                       uint64_t aTID,
+                       EventType aEventType,
+                       uint64_t aFrames,
+                       uint64_t aSampleRate)
+  : mLogger(aLogger)
+  , mLocation(aLocation)
+  , mEventType(aEventType)
+  , mPID(aPID)
+  , mTID(aTID)
+{
+  MOZ_ASSERT(aEventType == EventType::BUDGET);
+
+  if (aLogger.Enabled()) {
+    float durationUS = (static_cast<float>(aFrames) / aSampleRate) * 1e6;
+    PrintBudget(aLocation, "perf", mComment, durationUS, mPID, mTID, aFrames);
+  }
+}
+
+AutoTracer::AutoTracer(AsyncLogger& aLogger,
+                       const char* aLocation,
+                       uint64_t aPID,
+                       uint64_t aTID,
+                       EventType aEventType,
+                       const char* aComment)
+  : mLogger(aLogger)
+  , mLocation(aLocation)
+  , mComment(aComment)
+  , mEventType(aEventType)
+  , mPID(aPID)
+  , mTID(aTID)
+{
+  MOZ_ASSERT(aEventType == EventType::DURATION);
+  if (aLogger.Enabled()) {
+    PrintEvent(aLocation, "perf", mComment, TracingPhase::BEGIN, NowInUs(), aPID, aTID);
+  }
+}
+
+AutoTracer::~AutoTracer()
+{
+  if (mEventType == EventType::DURATION) {
+    if (mLogger.Enabled()) {
+      PrintEvent(mLocation, "perf", mComment, TracingPhase::END, NowInUs(), mPID, mTID);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/Tracing.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 2; 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 TRACING_H
+#define TRACING_H
+
+#include <cstdint>
+
+#include "AsyncLogger.h"
+
+#include <mozilla/Attributes.h>
+
+#if defined(_WIN32)
+#include <process.h>
+#define getpid() _getpid()
+#else
+#include <unistd.h>
+#endif
+
+#if defined(_MSC_VER)
+// MSVC
+#define FUNCTION_SIGNATURE  __FUNCSIG__
+#elif defined(__GNUC__)
+// gcc, clang
+#define FUNCTION_SIGNATURE __PRETTY_FUNCTION__
+#endif
+
+#ifdef TRACING
+  /* TRACE is for use in the real-time audio rendering thread.
+   * It would be better to always pass in the thread id. However, the thread an
+   * audio callback runs on can change when the underlying audio device change,
+   * and also it seems to be called from a thread pool in a round-robin fashion
+   * when audio remoting is activated, making the traces unreadable.
+   * The thread on which the AudioCallbackDriver::DataCallback is to always
+   * be thread 0, and the budget is set to always be thread 1. This allows
+   * displaying those elements in two separate lanes.
+   * The other thread have "normal" tid. Hashing allows being able to get a
+   * string representation that is unique and guaranteed to be portable. */
+  #define TRACE_AUDIO_CALLBACK()                                               \
+    AutoTracer trace(gMSGTraceLogger, FUNCTION_SIGNATURE, getpid(), 0);
+  #define TRACE_AUDIO_CALLBACK_BUDGET(aFrames, aSampleRate)                    \
+    AutoTracer budget(gMSGTraceLogger, "Real-time budget", getpid(), 1,        \
+                      AutoTracer::EventType::BUDGET, aFrames, aSampleRate);
+  #define TRACE()                                                              \
+    AutoTracer trace(gMSGTraceLogger, FUNCTION_SIGNATURE, getpid(),            \
+                     std::hash<std::thread::id>{}(std::this_thread::get_id()));
+  #define TRACE_COMMENT(aComment)                                              \
+    AutoTracer trace(gMSGTraceLogger, FUNCTION_SIGNATURE, getpid(),            \
+                     std::hash<std::thread::id>{}(std::this_thread::get_id()), \
+                     AutoTracer::EventType::DURATION,                          \
+                     aComment);
+#else
+  #define TRACE_AUDIO_CALLBACK()
+  #define TRACE_AUDIO_CALLBACK_BUDGET(aFrames, aSampleRate)
+  #define TRACE()
+  #define TRACE_COMMENT(aComment)
+#endif
+
+class MOZ_RAII AutoTracer
+{
+public:
+  enum class EventType
+  {
+    DURATION,
+    BUDGET
+  };
+
+  AutoTracer(mozilla::AsyncLogger& aLogger,
+             const char* aLocation,
+             uint64_t aPID,
+             uint64_t aTID,
+             EventType aEventType = EventType::DURATION,
+             const char* aComment = nullptr);
+  AutoTracer(mozilla::AsyncLogger& aLogger,
+             const char* aLocation,
+             uint64_t aPID,
+             uint64_t aTID,
+             EventType aEventType,
+             uint64_t aSampleRate,
+             uint64_t aFrames);
+  ~AutoTracer();
+private:
+  uint64_t NowInUs();
+
+  enum class TracingPhase
+  {
+    BEGIN,
+    END,
+    COMPLETE
+  };
+
+  const char TRACING_PHASE_STRINGS[3] = {
+    'B',
+    'E',
+    'X'
+  };
+
+  void PrintEvent(const char* aName,
+                  const char* aCategory,
+                  const char* aComment,
+                  TracingPhase aPhase,
+                  uint64_t aTime,
+                  uint64_t aPID,
+                  uint64_t aThread);
+
+  void PrintBudget(const char* aName,
+                   const char* aCategory,
+                   const char* aComment,
+                   uint64_t aDuration,
+                   uint64_t aPID,
+                   uint64_t aThread,
+                   uint64_t aFrames);
+
+  // The logger to use. It musdt have a lifetime longer than the block an
+  // instance of this class traces.
+  mozilla::AsyncLogger& mLogger;
+  // The location for this trace point, arbitrary string literal, often the
+  // name of the calling function, with a static lifetime.
+  const char* mLocation;
+  // A comment for this trace point, abitrary string literal with a static
+  // lifetime.
+  const char* mComment;
+  // The event type, for now either a budget or a duration.
+  const EventType mEventType;
+  // The process ID of the calling process. Traces are grouped by PID in the
+  // vizualizer.
+  const uint64_t mPID;
+  // The thread ID of the calling thread, will be displayed in a separate
+  // section in the trace visualizer.
+  const uint64_t mTID;
+};
+
+#if defined(_WIN32)
+#undef getpid
+#endif
+
+#endif /* TRACING_H */
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -63,16 +63,17 @@ TrackUnionStream::TrackUnionStream()
         EndTrack(i);
         mTrackMap.RemoveElementAt(i);
       }
     }
     ProcessedMediaStream::RemoveInput(aPort);
   }
   void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
   {
+    TRACE();
     if (IsFinishedOnGraphThread()) {
       return;
     }
     AutoTArray<bool,8> mappedTracksFinished;
     AutoTArray<bool,8> mappedTracksWithMatchingInputTracks;
     for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
       mappedTracksFinished.AppendElement(true);
       mappedTracksWithMatchingInputTracks.AppendElement(false);
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -84,16 +84,17 @@ XPIDL_SOURCES += [
     'nsIMediaManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
+    'AsyncLogger.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
     'AudioConfig.h',
     'AudioConverter.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
@@ -148,16 +149,17 @@ EXPORTS += [
     'QueueObject.h',
     'SeekJob.h',
     'SeekTarget.h',
     'SelfRef.h',
     'SharedBuffer.h',
     'StreamTracks.h',
     'ThreadPoolCOMListener.h',
     'TimeUnits.h',
+    'Tracing.h',
     'TrackID.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoLimits.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
     'XiphExtradata.h',
@@ -258,16 +260,17 @@ UNIFIED_SOURCES += [
     'ReaderProxy.cpp',
     'SeekJob.cpp',
     'StreamTracks.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
+    'Tracing.cpp',
     'TrackUnionStream.cpp',
     'VideoFrameContainer.cpp',
     'VideoPlaybackQuality.cpp',
     'VideoSegment.cpp',
     'VideoStreamTrack.cpp',
     'VideoTrack.cpp',
     'VideoTrackList.cpp',
     'VideoUtils.cpp',
@@ -321,16 +324,17 @@ LOCAL_INCLUDES += [
 
 if CONFIG['MOZ_WEBRTC']:
     LOCAL_INCLUDES += [
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/trunk',
     ]
 
 DEFINES['MOZILLA_INTERNAL_API'] = True
+DEFINES['TRACING'] = True
 
 if CONFIG['MOZ_ANDROID_HLS_SUPPORT']:
     DEFINES['MOZ_ANDROID_HLS_SUPPORT'] = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 # Suppress some GCC warnings being treated as errors:
 #  - about attributes on forward declarations for types that are already
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -332,15 +332,17 @@ skip-if = (android_version == '18') # an
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_selftest.html]
 # Bug 1227781: Crash with bogus TURN server.
 [test_peerConnection_bug1227781.html]
 [test_peerConnection_stats.html]
 skip-if = toolkit == 'android' # android(Bug 1189784, timeouts on 4.3 emulator, Bug 1373858)
 [test_peerConnection_sender_and_receiver_stats.html]
 skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_trackless_sender_stats.html]
+skip-if = (android_version == '18') # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_verifyDescriptions.html]
 skip-if = (android_version == '18')
 [test_fingerprinting_resistance.html]
 [test_getUserMedia_nonDefaultRate.html]
 [test_peerConnection_nonDefaultRate.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_forceSampleRate.html]
--- a/dom/media/tests/mochitest/test_peerConnection_sender_and_receiver_stats.html
+++ b/dom/media/tests/mochitest/test_peerConnection_sender_and_receiver_stats.html
@@ -7,19 +7,19 @@
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "1355220",
     title: "RTCRtpSender.getStats() and RTCRtpReceiver.getStats()",
     visible: true
   });
 
-  var test;
-
   var checkStats = (sndReport, rcvReport, mediaType) => {
+    ok(sndReport instanceof window.RTCStatsReport, "sender stats are a RTCStatsReport");
+    ok(rcvReport instanceof window.RTCStatsReport, "receiver stats are a RTCStatsReport");
     // Returns SSRCs and checks that the tracks are of the correct mediaType
     let getSsrcs = (report, kind) => {
       return [...report.values()]
         .filter(stat => stat.type.endsWith("bound-rtp")).map(stat =>{
           is(stat.mediaType, kind, "mediaType of " + stat.id
               + " is expected type " + kind);
           return stat.ssrc;
       }).sort().join("|");
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_trackless_sender_stats.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1452673",
+    title: "Trackless RTCRtpSender.getStats()",
+    visible: true
+  });
+
+  // Calling getstats() on a trackless RTCRtpSender should yield an empty
+  // stats report.  When track stats are added in the future, the stats
+  // for the removed tracks should continue to appear.
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    test.chain.removeAfter("PC_REMOTE_WAIT_FOR_MEDIA_FLOW");
+    test.chain.append(
+      async function PC_LOCAL_AND_REMOTE_TRACKLESS_SENDER_STATS(test) {
+        return Promise.all([test.pcLocal.waitForSyncedRtcp(),
+                  test.pcRemote.waitForSyncedRtcp()])
+          .then(async () => {
+            let senders = test.pcLocal.getSenders();
+            let receivers = test.pcRemote.getReceivers();
+            is(senders.length, 2, "Have exactly two senders.");
+            is(receivers.length, 2, "Have exactly two receivers.");
+            for(let kind of ["audio", "video"]) {
+              is(senders.filter(s => s.track.kind == kind).length, 1,
+                  "Exactly 1 sender of kind " + kind);
+              is(receivers.filter(r => r.track.kind == kind).length, 1,
+                  "Exactly 1 receiver of kind " + kind);
+            }
+            // Remove tracks from senders
+            senders.forEach(async sender => {
+              await sender.replaceTrack(null);
+              is(sender.track, null, "Sender track removed");
+              let stats = await sender.getStats();
+              ok(stats instanceof window.RTCStatsReport, "Stats is instance of RTCStatsReport");
+              // Number of stats in the report. This should be 0.
+              is(stats.size, 0, "Trackless sender stats report is empty");
+            });
+          })
+        });
+    test.setMediaConstraints([{audio: true}, {video: true}], []);
+    test.run();
+  });
+
+</script>
+</pre>
+</body>
+</html>
\ No newline at end of file
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -12,16 +12,17 @@
 #include "AudioConverter.h"
 #include "MediaManager.h"
 #include "MediaStreamGraphImpl.h"
 #include "MediaTrackConstraints.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorNames.h"
 #include "mtransport/runnable_utils.h"
 #include "nsAutoPtr.h"
+#include "Tracing.h"
 
 // scoped_ptr.h uses FF
 #ifdef FF
 #undef FF
 #endif
 #include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
 #include "webrtc/voice_engine/voice_engine_defines.h"
 #include "webrtc/modules/audio_processing/include/audio_processing.h"
@@ -1178,16 +1179,17 @@ MediaEngineWebRTCMicrophoneSource::Inser
 // Note this can be called back after ::Shutdown()
 void
 MediaEngineWebRTCMicrophoneSource::NotifyInputData(MediaStreamGraph* aGraph,
                                                    const AudioDataValue* aBuffer,
                                                    size_t aFrames,
                                                    TrackRate aRate,
                                                    uint32_t aChannels)
 {
+  TRACE_AUDIO_CALLBACK();
   // If some processing is necessary, packetize and insert in the WebRTC.org
   // code. Otherwise, directly insert the mic data in the MSG, bypassing all processing.
   if (PassThrough()) {
     InsertInGraph<AudioDataValue>(aBuffer, aFrames, aChannels);
   } else {
     PacketizeAndProcess(aGraph, aBuffer, aFrames, aRate, aChannels);
   }
 }
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -5328,35 +5328,16 @@ EditorBase::OnFocus(EventTarget* aFocusE
   InitializeSelection(aFocusEventTarget);
   mSpellCheckerDictionaryUpdated = false;
   if (mInlineSpellChecker && CanEnableSpellCheck()) {
     mInlineSpellChecker->UpdateCurrentDictionary();
     mSpellCheckerDictionaryUpdated = true;
   }
 }
 
-NS_IMETHODIMP
-EditorBase::GetSuppressDispatchingInputEvent(bool* aSuppressed)
-{
-  // NOTE: If you need to override this method, you need to make
-  //       IsSuppressingDispatchingInputEvent() virtual.
-  if (NS_WARN_IF(aSuppressed)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  *aSuppressed = IsSuppressingDispatchingInputEvent();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-EditorBase::SetSuppressDispatchingInputEvent(bool aSuppress)
-{
-  mDispatchInputEvent = !aSuppress;
-  return NS_OK;
-}
-
 int32_t
 EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
 {
   MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr");
 
   nsISelectionController* selectionController = GetSelectionController();
   if (NS_WARN_IF(!selectionController)) {
     return -1;
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -1440,16 +1440,25 @@ public:
 
   /**
    * IsInEditAction() return true while the instance is handling an edit action.
    * Otherwise, false.
    */
   bool IsInEditAction() const { return mIsInEditAction; }
 
   /**
+   * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
+   * "input" event.
+   */
+  void SuppressDispatchingInputEvent(bool aSuppress)
+  {
+    mDispatchInputEvent = !aSuppress;
+  }
+
+  /**
    * IsSuppressingDispatchingInputEvent() returns true if the editor stops
    * dispatching input event.  Otherwise, false.
    */
   bool IsSuppressingDispatchingInputEvent() const
   {
     return !mDispatchInputEvent;
   }
 
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -513,19 +513,16 @@ interface nsIEditor  : nsISupports
   void dumpContentTree();
 
   /** Dumps a text representation of the content tree to standard out */
   void debugDumpContent() ;
 
   /* Run unit tests. Noop in optimized builds */
   void debugUnitTests(out long outNumTests, out long  outNumTestsFailed);
 
-  /* Set true if you want to suppress dispatching input event. */
-  attribute boolean suppressDispatchingInputEvent;
-
   /**
    * forceCompositionEnd() force the composition end
    */
   void forceCompositionEnd();
 
   /**
    * whether this editor has active IME transaction
    */
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_layers_APZSampler_h
 #define mozilla_layers_APZSampler_h
 
 #include <unordered_map>
 
 #include "base/platform_thread.h" // for PlatformThreadId
 #include "mozilla/layers/AsyncCompositionManager.h" // for AsyncTransform
 #include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
 #include "nsTArray.h"
 #include "Units.h"
 
 namespace mozilla {
 
 class TimeStamp;
 
 namespace wr {
@@ -36,17 +37,18 @@ struct ScrollbarData;
  * This interface exposes APZ methods related to "sampling" (i.e. reading the
  * async transforms produced by APZ). These methods should all be called on
  * the sampler thread.
  */
 class APZSampler {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZSampler)
 
 public:
-  explicit APZSampler(const RefPtr<APZCTreeManager>& aApz);
+  APZSampler(const RefPtr<APZCTreeManager>& aApz,
+             bool aIsUsingWebRender);
 
   void SetWebRenderWindowId(const wr::WindowId& aWindowId);
 
   /**
    * This function is invoked from rust on the render backend thread when it
    * is created. It effectively tells the APZSampler "the current thread is
    * the sampler thread for this window id" and allows APZSampler to remember
    * which thread it is.
@@ -97,41 +99,38 @@ public:
   /**
    * Returns true if currently on the APZSampler's "sampler thread".
    */
   bool IsSamplerThread() const;
 
 protected:
   virtual ~APZSampler();
 
-  bool UsingWebRenderSamplerThread() const;
   static already_AddRefed<APZSampler> GetSampler(const wr::WrWindowId& aWindowId);
 
 private:
   RefPtr<APZCTreeManager> mApz;
+  bool mIsUsingWebRender;
 
   // Used to manage the mapping from a WR window id to APZSampler. These are only
   // used if WebRender is enabled. Both sWindowIdMap and mWindowId should only
-  // be used while holding the sWindowIdLock.
+  // be used while holding the sWindowIdLock. Note that we use a StaticAutoPtr
+  // wrapper on sWindowIdMap to avoid a static initializer for the unordered_map.
+  // This also avoids the initializer/memory allocation in cases where we're
+  // not using WebRender.
   static StaticMutex sWindowIdLock;
-  static std::unordered_map<uint64_t, APZSampler*> sWindowIdMap;
+  static StaticAutoPtr<std::unordered_map<uint64_t, APZSampler*>> sWindowIdMap;
   Maybe<wr::WrWindowId> mWindowId;
 
+  // Lock used to protected mSamplerThreadId
+  mutable Mutex mThreadIdLock;
   // If WebRender is enabled, this holds the thread id of the render backend
   // thread (which is the sampler thread) for the compositor associated with
   // this APZSampler instance.
-  // This is written to once during init and never cleared, and so reading it
-  // from multiple threads during normal operation (after initialization)
-  // without locking should be fine.
   Maybe<PlatformThreadId> mSamplerThreadId;
-#ifdef DEBUG
-  // This flag is used to ensure that we don't ever try to do sampler-thread
-  // stuff before the updater thread has been properly initialized.
-  mutable bool mSamplerThreadQueried;
-#endif
 
   Mutex mSampleTimeLock;
   // Can only be accessed or modified while holding mSampleTimeLock.
   TimeStamp mSampleTime;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -10,16 +10,17 @@
 #include <deque>
 #include <unordered_map>
 
 #include "base/platform_thread.h"   // for PlatformThreadId
 #include "LayersTypes.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/layers/WebRenderScrollData.h"
 #include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
 
 namespace mozilla {
 
 namespace wr {
 struct WrWindowId;
@@ -38,17 +39,18 @@ class WebRenderScrollData;
  * response to IPC messages. The method implementations internally redispatch
  * themselves to the updater thread in the case where the compositor thread
  * is not the updater thread.
  */
 class APZUpdater {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZUpdater)
 
 public:
-  explicit APZUpdater(const RefPtr<APZCTreeManager>& aApz);
+  APZUpdater(const RefPtr<APZCTreeManager>& aApz,
+             bool aIsUsingWebRender);
 
   bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
   void SetWebRenderWindowId(const wr::WindowId& aWindowId);
 
   /**
    * This function is invoked from rust on the scene builder thread when it
    * is created. It effectively tells the APZUpdater "the current thread is
    * the updater thread for this window id" and allows APZUpdater to remember
@@ -139,16 +141,17 @@ protected:
 
   bool UsingWebRenderUpdaterThread() const;
   static already_AddRefed<APZUpdater> GetUpdater(const wr::WrWindowId& aWindowId);
 
   void ProcessQueue();
 
 private:
   RefPtr<APZCTreeManager> mApz;
+  bool mIsUsingWebRender;
 
   // Map from layers id to WebRenderScrollData. This can only be touched on
   // the updater thread.
   std::unordered_map<LayersId,
                      WebRenderScrollData,
                      LayersId::HashFn> mScrollData;
 
   // Stores epoch state for a particular layers id. This structure is only
@@ -179,35 +182,32 @@ private:
   // Map from layers id to epoch state.
   // This data structure can only be touched on the updater thread.
   std::unordered_map<LayersId,
                      EpochState,
                      LayersId::HashFn> mEpochData;
 
   // Used to manage the mapping from a WR window id to APZUpdater. These are only
   // used if WebRender is enabled. Both sWindowIdMap and mWindowId should only
-  // be used while holding the sWindowIdLock.
+  // be used while holding the sWindowIdLock. Note that we use a StaticAutoPtr
+  // wrapper on sWindowIdMap to avoid a static initializer for the unordered_map.
+  // This also avoids the initializer/memory allocation in cases where we're
+  // not using WebRender.
   static StaticMutex sWindowIdLock;
-  static std::unordered_map<uint64_t, APZUpdater*> sWindowIdMap;
+  static StaticAutoPtr<std::unordered_map<uint64_t, APZUpdater*>> sWindowIdMap;
   Maybe<wr::WrWindowId> mWindowId;
 
+  // Lock used to protected mUpdaterThreadId;
+  mutable Mutex mThreadIdLock;
   // If WebRender and async scene building are enabled, this holds the thread id
   // of the scene builder thread (which is the updater thread) for the
   // compositor associated with this APZUpdater instance. It may be populated
   // even if async scene building is not enabled, but in that case we don't
   // care about the contents.
-  // This is written to once during init and never cleared, and so reading it
-  // from multiple threads during normal operation (after initialization)
-  // without locking should be fine.
   Maybe<PlatformThreadId> mUpdaterThreadId;
-#ifdef DEBUG
-  // This flag is used to ensure that we don't ever try to do updater-thread
-  // stuff before the updater thread has been properly initialized.
-  mutable bool mUpdaterThreadQueried;
-#endif
 
   // Helper struct that pairs each queued runnable with the layers id that it
   // is associated with. This allows us to easily implement the conceptual
   // separation of mUpdaterQueue into independent queues per layers id.
   struct QueuedTask {
     LayersId mLayersId;
     RefPtr<Runnable> mRunnable;
   };
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -14,55 +14,58 @@
 #include "mozilla/layers/SynchronousTask.h"
 #include "TreeTraversal.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 namespace layers {
 
 StaticMutex APZSampler::sWindowIdLock;
-std::unordered_map<uint64_t, APZSampler*> APZSampler::sWindowIdMap;
+StaticAutoPtr<std::unordered_map<uint64_t, APZSampler*>> APZSampler::sWindowIdMap;
 
 
-APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz)
+APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz,
+                       bool aIsUsingWebRender)
   : mApz(aApz)
-#ifdef DEBUG
-  , mSamplerThreadQueried(false)
-#endif
+  , mIsUsingWebRender(aIsUsingWebRender)
+  , mThreadIdLock("APZSampler::mThreadIdLock")
   , mSampleTimeLock("APZSampler::mSampleTimeLock")
 {
   MOZ_ASSERT(aApz);
   mApz->SetSampler(this);
 }
 
 APZSampler::~APZSampler()
 {
   mApz->SetSampler(nullptr);
 
   StaticMutexAutoLock lock(sWindowIdLock);
   if (mWindowId) {
-    sWindowIdMap.erase(wr::AsUint64(*mWindowId));
+    MOZ_ASSERT(sWindowIdMap);
+    sWindowIdMap->erase(wr::AsUint64(*mWindowId));
   }
 }
 
 void
 APZSampler::SetWebRenderWindowId(const wr::WindowId& aWindowId)
 {
   StaticMutexAutoLock lock(sWindowIdLock);
   MOZ_ASSERT(!mWindowId);
   mWindowId = Some(aWindowId);
-  sWindowIdMap[wr::AsUint64(aWindowId)] = this;
+  if (!sWindowIdMap) {
+    sWindowIdMap = new std::unordered_map<uint64_t, APZSampler*>();
+  }
+  (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this;
 }
 
 /*static*/ void
 APZSampler::SetSamplerThread(const wr::WrWindowId& aWindowId)
 {
   if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
-    // Ensure nobody tried to use the updater thread before this point.
-    MOZ_ASSERT(!sampler->mSamplerThreadQueried);
+    MutexAutoLock lock(sampler->mThreadIdLock);
     sampler->mSamplerThreadId = Some(PlatformThread::CurrentId());
   }
 }
 
 /*static*/ void
 APZSampler::SampleForWebRender(const wr::WrWindowId& aWindowId,
                                wr::Transaction* aTransaction)
 {
@@ -209,47 +212,37 @@ APZSampler::AssertOnSamplerThread() cons
   if (APZThreadUtils::GetThreadAssertionsEnabled()) {
     MOZ_ASSERT(IsSamplerThread());
   }
 }
 
 bool
 APZSampler::IsSamplerThread() const
 {
-  if (UsingWebRenderSamplerThread()) {
-    return PlatformThread::CurrentId() == *mSamplerThreadId;
+  if (mIsUsingWebRender) {
+    // If the sampler thread id isn't set yet then we cannot be running on the
+    // sampler thread (because we will have the thread id before we run any
+    // other C++ code on it, and this function is only ever invoked from C++
+    // code), so return false in that scenario.
+    MutexAutoLock lock(mThreadIdLock);
+    return mSamplerThreadId && PlatformThread::CurrentId() == *mSamplerThreadId;
   }
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
-bool
-APZSampler::UsingWebRenderSamplerThread() const
-{
-  // If mSamplerThreadId is not set at the point that this is called, then
-  // that means that either (a) WebRender is not enabled for the compositor
-  // to which this APZSampler is attached or (b) we are attempting to do
-  // something sampler-related before WebRender is up and running. In case
-  // (a) falling back to the compositor thread is correct, and in case (b)
-  // we should stop doing the sampler-related thing so early. We catch this
-  // case by setting the mSamplerThreadQueried flag and asserting on WR
-  // initialization.
-#ifdef DEBUG
-  mSamplerThreadQueried = true;
-#endif
-  return mSamplerThreadId.isSome();
-}
-
 /*static*/ already_AddRefed<APZSampler>
 APZSampler::GetSampler(const wr::WrWindowId& aWindowId)
 {
   RefPtr<APZSampler> sampler;
   StaticMutexAutoLock lock(sWindowIdLock);
-  auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
-  if (it != sWindowIdMap.end()) {
-    sampler = it->second;
+  if (sWindowIdMap) {
+    auto it = sWindowIdMap->find(wr::AsUint64(aWindowId));
+    if (it != sWindowIdMap->end()) {
+      sampler = it->second;
+    }
   }
   return sampler.forget();
 }
 
 } // namespace layers
 } // namespace mozilla
 
 void
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -14,62 +14,65 @@
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/layers/WebRenderScrollDataWrapper.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 namespace layers {
 
 StaticMutex APZUpdater::sWindowIdLock;
-std::unordered_map<uint64_t, APZUpdater*> APZUpdater::sWindowIdMap;
+StaticAutoPtr<std::unordered_map<uint64_t, APZUpdater*>> APZUpdater::sWindowIdMap;
 
 
-APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
+APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz,
+                       bool aIsUsingWebRender)
   : mApz(aApz)
-#ifdef DEBUG
-  , mUpdaterThreadQueried(false)
-#endif
+  , mIsUsingWebRender(aIsUsingWebRender)
+  , mThreadIdLock("APZUpdater::ThreadIdLock")
   , mQueueLock("APZUpdater::QueueLock")
 {
   MOZ_ASSERT(aApz);
   mApz->SetUpdater(this);
 }
 
 APZUpdater::~APZUpdater()
 {
   mApz->SetUpdater(nullptr);
 
   StaticMutexAutoLock lock(sWindowIdLock);
   if (mWindowId) {
+    MOZ_ASSERT(sWindowIdMap);
     // Ensure that ClearTree was called and the task got run
-    MOZ_ASSERT(sWindowIdMap.find(wr::AsUint64(*mWindowId)) == sWindowIdMap.end());
+    MOZ_ASSERT(sWindowIdMap->find(wr::AsUint64(*mWindowId)) == sWindowIdMap->end());
   }
 }
 
 bool
 APZUpdater::HasTreeManager(const RefPtr<APZCTreeManager>& aApz)
 {
   return aApz.get() == mApz.get();
 }
 
 void
 APZUpdater::SetWebRenderWindowId(const wr::WindowId& aWindowId)
 {
   StaticMutexAutoLock lock(sWindowIdLock);
   MOZ_ASSERT(!mWindowId);
   mWindowId = Some(aWindowId);
-  sWindowIdMap[wr::AsUint64(aWindowId)] = this;
+  if (!sWindowIdMap) {
+    sWindowIdMap = new std::unordered_map<uint64_t, APZUpdater*>();
+  }
+  (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this;
 }
 
 /*static*/ void
 APZUpdater::SetUpdaterThread(const wr::WrWindowId& aWindowId)
 {
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
-    // Ensure nobody tried to use the updater thread before this point.
-    MOZ_ASSERT(!updater->mUpdaterThreadQueried);
+    MutexAutoLock lock(updater->mThreadIdLock);
     updater->mUpdaterThreadId = Some(PlatformThread::CurrentId());
   }
 }
 
 /*static*/ void
 APZUpdater::PrepareForSceneSwap(const wr::WrWindowId& aWindowId)
 {
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
@@ -142,17 +145,18 @@ APZUpdater::ClearTree(LayersId aRootLaye
       self->mApz->ClearTree();
 
       // Once ClearTree is called on the APZCTreeManager, we are in a shutdown
       // phase. After this point it's ok if WebRender cannot get a hold of the
       // updater via the window id, and it's a good point to remove the mapping
       // and avoid leaving a dangling pointer to this object.
       StaticMutexAutoLock lock(sWindowIdLock);
       if (self->mWindowId) {
-        sWindowIdMap.erase(wr::AsUint64(*(self->mWindowId)));
+        MOZ_ASSERT(sWindowIdMap);
+        sWindowIdMap->erase(wr::AsUint64(*(self->mWindowId)));
       }
     }
   ));
 }
 
 void
 APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
                              LayersId aOriginatingLayersId,
@@ -329,16 +333,23 @@ APZUpdater::AssertOnUpdaterThread() cons
   }
 }
 
 void
 APZUpdater::RunOnUpdaterThread(LayersId aLayersId, already_AddRefed<Runnable> aTask)
 {
   RefPtr<Runnable> task = aTask;
 
+  // In the scenario where UsingWebRenderUpdaterThread() is true, this function
+  // might get called early (before mUpdaterThreadId is set). In that case
+  // IsUpdaterThread() will return false and we'll queue the task onto
+  // mUpdaterQueue. This is fine; the task is still guaranteed to run (barring
+  // catastrophic failure) because the WakeSceneBuilder call will still trigger
+  // the callback to run tasks.
+
   if (IsUpdaterThread()) {
     task->Run();
     return;
   }
 
   if (UsingWebRenderUpdaterThread()) {
     // If the updater thread is a WebRender thread, and we're not on it
     // right now, save the task in the queue. We will run tasks from the queue
@@ -370,17 +381,22 @@ APZUpdater::RunOnUpdaterThread(LayersId 
     NS_WARNING("Dropping task posted to updater thread");
   }
 }
 
 bool
 APZUpdater::IsUpdaterThread() const
 {
   if (UsingWebRenderUpdaterThread()) {
-    return PlatformThread::CurrentId() == *mUpdaterThreadId;
+    // If the updater thread id isn't set yet then we cannot be running on the
+    // updater thread (because we will have the thread id before we run any
+    // C++ code on it, and this function is only ever invoked from C++ code),
+    // so return false in that scenario.
+    MutexAutoLock lock(mThreadIdLock);
+    return mUpdaterThreadId && PlatformThread::CurrentId() == *mUpdaterThreadId;
   }
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
 void
 APZUpdater::RunOnControllerThread(LayersId aLayersId, already_AddRefed<Runnable> aTask)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
@@ -389,41 +405,29 @@ APZUpdater::RunOnControllerThread(Layers
       "APZUpdater::RunOnControllerThread",
       &APZThreadUtils::RunOnControllerThread,
       Move(aTask)));
 }
 
 bool
 APZUpdater::UsingWebRenderUpdaterThread() const
 {
-  if (!gfxPrefs::WebRenderAsyncSceneBuild()) {
-    return false;
-  }
-  // If mUpdaterThreadId is not set at the point that this is called, then
-  // that means that either (a) WebRender is not enabled for the compositor
-  // to which this APZUpdater is attached or (b) we are attempting to do
-  // something updater-related before WebRender is up and running. In case
-  // (a) falling back to the compositor thread is correct, and in case (b)
-  // we should stop doing the updater-related thing so early. We catch this
-  // case by setting the mUpdaterThreadQueried flag and asserting on WR
-  // initialization.
-#ifdef DEBUG
-  mUpdaterThreadQueried = true;
-#endif
-  return mUpdaterThreadId.isSome();
+  return (mIsUsingWebRender && gfxPrefs::WebRenderAsyncSceneBuild());
 }
 
 /*static*/ already_AddRefed<APZUpdater>
 APZUpdater::GetUpdater(const wr::WrWindowId& aWindowId)
 {
   RefPtr<APZUpdater> updater;
   StaticMutexAutoLock lock(sWindowIdLock);
-  auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
-  if (it != sWindowIdMap.end()) {
-    updater = it->second;
+  if (sWindowIdMap) {
+    auto it = sWindowIdMap->find(wr::AsUint64(aWindowId));
+    if (it != sWindowIdMap->end()) {
+      updater = it->second;
+    }
   }
   return updater.forget();
 }
 
 void
 APZUpdater::ProcessQueue()
 {
   { // scope lock to check for emptiness
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -26,18 +26,18 @@ public:
 protected:
   virtual void SetUp()
   {
     gfxPrefs::GetSingleton();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     tm = new TestAPZCTreeManager(mcc);
-    updater = new APZUpdater(tm);
-    sampler = new APZSampler(tm);
+    updater = new APZUpdater(tm, false);
+    sampler = new APZSampler(tm, false);
     apzc = new TestAsyncPanZoomController(LayersId{0}, mcc, tm, mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
     apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
   }
 
   /**
    * Get the APZC's scroll range in CSS pixels.
    */
--- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
+++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
@@ -22,18 +22,18 @@ class APZCTreeManagerTester : public APZ
 protected:
   virtual void SetUp() {
     gfxPrefs::GetSingleton();
     gfxPlatform::GetPlatform();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     manager = new TestAPZCTreeManager(mcc);
-    updater = new APZUpdater(manager);
-    sampler = new APZSampler(manager);
+    updater = new APZUpdater(manager, false);
+    sampler = new APZSampler(manager, false);
   }
 
   virtual void TearDown() {
     while (mcc->RunThroughDelayedTasks());
     manager->ClearTree();
     manager->ClearContentController();
   }
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -753,17 +753,18 @@ MoveScrollbarForLayerMargin(Layer* aRoot
                             const ScreenMargin& aFixedLayerMargins)
 {
   // See bug 1223928 comment 9 - once we can detect the RCD with just the
   // isRootContent flag on the metrics, we can probably move this code into
   // ApplyAsyncTransformToScrollbar rather than having it as a separate
   // adjustment on the layer tree.
   Layer* scrollbar = BreadthFirstSearch<ReverseIterator>(aRoot,
     [aRootScrollId](Layer* aNode) {
-      return (aNode->GetScrollbarData().mDirection.isSome() &&
+      return (aNode->GetScrollbarData().IsThumb() &&
+              aNode->GetScrollbarData().mDirection.isSome() &&
               *aNode->GetScrollbarData().mDirection == ScrollDirection::eHorizontal &&
               aNode->GetScrollbarData().mTargetViewId == aRootScrollId);
     });
   if (scrollbar) {
     // Shift the horizontal scrollbar down into the new space exposed by the
     // dynamic toolbar hiding. Technically we should also scale the vertical
     // scrollbar a bit to expand into the new space but it's not as noticeable
     // and it would add a lot more complexity, so we're going with the "it's not
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -380,18 +380,18 @@ CompositorBridgeParent::Initialize()
   MOZ_ASSERT(CompositorThread(),
              "The compositor thread must be Initialized before instanciating a CompositorBridgeParent.");
 
   if (mOptions.UseAPZ()) {
     MOZ_ASSERT(!mApzcTreeManager);
     MOZ_ASSERT(!mApzSampler);
     MOZ_ASSERT(!mApzUpdater);
     mApzcTreeManager = new APZCTreeManager(mRootLayerTreeID);
-    mApzSampler = new APZSampler(mApzcTreeManager);
-    mApzUpdater = new APZUpdater(mApzcTreeManager);
+    mApzSampler = new APZSampler(mApzcTreeManager, mOptions.UseWebRender());
+    mApzUpdater = new APZUpdater(mApzcTreeManager, mOptions.UseWebRender());
   }
 
   mCompositorBridgeID = 0;
   // FIXME: This holds on the the fact that right now the only thing that
   // can destroy this instance is initialized on the compositor thread after
   // this task has been processed.
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(NewRunnableFunction("AddCompositorRunnable",
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -130,17 +130,17 @@ CrossProcessCompositorBridgeParent::Allo
   // If the widget has shutdown its compositor, we may not have had a chance yet
   // to unmap our layers id, and we could get here without a parent compositor.
   // In this case return an empty APZCTM.
   if (!state.mParent) {
     // Note: we immediately call ClearTree since otherwise the APZCTM will
     // retain a reference to itself, through the checkerboard observer.
     LayersId dummyId{0};
     RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId);
-    RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp);
+    RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, false);
     tempUpdater->ClearTree(dummyId);
     return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
   }
 
   state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
   return state.mApzcTreeManagerParent;
 }
 bool
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1160,24 +1160,28 @@ void
 WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   Destroy();
 }
 
 void
 WebRenderBridgeParent::AdvanceAnimations()
 {
-  TimeStamp animTime = mCompositorScheduler->GetLastComposeTime();
+  Maybe<TimeStamp> testingTimeStamp;
   if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
-    animTime = cbp->GetTestingTimeStamp().valueOr(animTime);
+    testingTimeStamp = cbp->GetTestingTimeStamp();
   }
 
-  AnimationHelper::SampleAnimations(mAnimStorage,
-                                    !mPreviousFrameTimeStamp.IsNull() ?
-                                    mPreviousFrameTimeStamp : animTime);
+  TimeStamp animTime = testingTimeStamp.valueOr(
+    !mPreviousFrameTimeStamp.IsNull()
+    ? mPreviousFrameTimeStamp
+    : mCompositorScheduler->GetLastComposeTime());
+
+
+  AnimationHelper::SampleAnimations(mAnimStorage, animTime);
 
   // Reset the previous time stamp if we don't already have any running
   // animations to avoid using the time which is far behind for newly
   // started animations.
   mPreviousFrameTimeStamp =
     mAnimStorage->AnimatedValueCount() ? animTime : TimeStamp();
 }
 
--- a/intl/locale/langGroups.properties
+++ b/intl/locale/langGroups.properties
@@ -16,31 +16,35 @@
 # (arc)/Avestan script.)
 #ae=ar
 
 ab=x-cyrillic
 ach=x-western
 af=x-western
 alg=x-cans
 am=x-ethi
+an=x-western
 ar=ar
 as=x-beng
+ast=x-western
 ay=x-western
+az=x-western
 be=x-cyrillic
 bg=x-cyrillic
 bn=x-beng
 bo=x-tibt
 br=x-western
 bs=x-western
 ca=x-western
 cak=x-western
 ce=x-western
 ch=x-western
 co=x-western
 cr=x-cans
+crh=x-western
 cs=x-western
 csb=x-western
 #cu=x-cyrillic
 cv=x-cyrillic
 cy=x-western
 da=x-western
 de=x-western
 dsb=x-western
@@ -98,19 +102,22 @@ kw=x-western
 #ky=x-cyrillic
 la=x-western
 lb=x-western
 lij=x-western
 ln=x-western
 lt=x-western
 ltg=x-western
 lv=x-western
+mai=x-devanagari
+meh=x-western
 mg=x-western
 mh=x-western
 mi=x-western
+mix=x-western
 mk=x-cyrillic
 ml=x-mlym
 # Mongolian script is also used for Mongolian
 mn=x-cyrillic
 mr=x-devanagari
 ms=x-western
 mt=x-western
 na=x-western
@@ -172,16 +179,17 @@ tl=x-western
 tlh=x-western
 tn=x-western
 to=x-western
 tr=x-western
 ts=x-western
 tt=x-western
 uk=x-cyrillic
 ur=ar
+uz=x-western
 ve=x-western
 vi=x-western
 vo=x-western
 wa=x-western
 wo=x-western
 xh=x-western
 yi=he
 yo=x-western
--- a/intl/locale/language.properties
+++ b/intl/locale/language.properties
@@ -43,16 +43,17 @@ bo.accept     = true
 br.accept     = true
 bs.accept     = true
 ca.accept     = true
 cak.accept    = true
 ce.accept     = true
 ch.accept     = true
 co.accept     = true
 cr.accept     = true
+crh.accept    = true
 cs.accept     = true
 csb.accept    = true
 cu.accept     = true
 cv.accept     = true
 cy.accept     = true
 da.accept     = true
 de.accept     = true
 de-at.accept  = true
@@ -171,19 +172,22 @@ lg.accept     = true
 li.accept     = true
 lij.accept    = true
 ln.accept     = true
 lo.accept     = true
 lt.accept     = true
 ltg.accept    = true
 lu.accept     = true
 lv.accept     = true
+mai.accept    = true
+meh.accept    = true
 mg.accept     = true
 mh.accept     = true
 mi.accept     = true
+mix.accept    = true
 mk.accept     = true
 mk-mk.accept  = true
 ml.accept     = true
 mn.accept     = true
 mr.accept     = true
 ms.accept     = true
 mt.accept     = true
 my.accept     = true
@@ -226,16 +230,17 @@ sa.accept     = true
 sc.accept     = true
 sd.accept     = true
 sg.accept     = true
 si.accept     = true
 sk.accept     = true
 sl.accept     = true
 sm.accept     = true
 so.accept     = true
+son.accept    = true
 son-ml.accept = true
 sq.accept     = true
 sr.accept     = true
 ss.accept     = true
 st.accept     = true
 su.accept     = true
 sv.accept     = true
 sv-fi.accept  = true
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3517,24 +3517,30 @@ RestyleManager::DoReparentComputedStyleF
   // updated as a child).  And given how this method ends up getting called, if
   // we reach here for a table frame, we are already in the middle of
   // reparenting the table wrapper frame.  So no need to
   // UpdateStyleOfOwnedAnonBoxes() here.
 
   ReparentFrameDescendants(aFrame, providerChild, aStyleSet);
 
   // We do not need to do the equivalent of UpdateFramePseudoElementStyles,
-  // because those are hadled by our descendant walk.
+  // because those are handled by our descendant walk.
 }
 
 void
 RestyleManager::ReparentFrameDescendants(nsIFrame* aFrame,
                                          nsIFrame* aProviderChild,
                                          ServoStyleSet& aStyleSet)
 {
+  if (aFrame->GetContent()->IsElement() &&
+      !aFrame->GetContent()->AsElement()->HasServoData()) {
+    // We're getting into a display: none subtree, avoid reparenting into stuff
+    // that is going to go away anyway in seconds.
+    return;
+  }
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
     for (nsIFrame* child : lists.CurrentList()) {
       // only do frames that are in flow
       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
           child != aProviderChild) {
         DoReparentComputedStyleForFirstLine(child, aStyleSet);
       }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -11363,17 +11363,23 @@ nsIFrame::GetCompositorHitTestInfo(nsDis
       MOZ_ASSERT((result & CompositorHitTestInfo::eTouchActionMask)
                == CompositorHitTestInfo::eTouchActionMask);
     }
   }
 
   nsDisplayOwnLayerFlags flags = aBuilder->GetCurrentScrollbarFlags();
   if (flags != nsDisplayOwnLayerFlags::eNone) {
     if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
-      result |= CompositorHitTestInfo::eScrollbarThumb;
+      bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
+          layers::FrameMetrics::NULL_SCROLL_ID;
+      if (thumbGetsLayer) {
+        result |= CompositorHitTestInfo::eScrollbarThumb;
+      } else {
+        result |= CompositorHitTestInfo::eDispatchToContent;
+      }
     }
     // The only flags that get set in nsDisplayListBuilder::mCurrentScrollbarFlags
     // are the scrollbar direction flags
     if (flags == nsDisplayOwnLayerFlags::eVerticalScrollbar) {
       result |= CompositorHitTestInfo::eScrollbarVertical;
     } else {
       MOZ_ASSERT(flags == nsDisplayOwnLayerFlags::eHorizontalScrollbar);
     }
--- a/layout/inspector/InspectorUtils.cpp
+++ b/layout/inspector/InspectorUtils.cpp
@@ -363,44 +363,47 @@ InspectorUtils::IsInheritedProperty(Glob
   return Servo_Property_IsInherited(&propName);
 }
 
 /* static */ void
 InspectorUtils::GetCSSPropertyNames(GlobalObject& aGlobalObject,
                                     const PropertyNamesOptions& aOptions,
                                     nsTArray<nsString>& aResult)
 {
-#define DO_PROP(_prop)                                                      \
-  PR_BEGIN_MACRO                                                            \
-    nsCSSPropertyID cssProp = nsCSSPropertyID(_prop);                       \
-    if (nsCSSProps::IsEnabled(cssProp, CSSEnabledState::eForAllContent)) {  \
-      nsDependentCString name(kCSSRawProperties[_prop]);                    \
-      aResult.AppendElement(NS_ConvertASCIItoUTF16(name));                  \
-    }                                                                       \
-  PR_END_MACRO
+  CSSEnabledState enabledState = aOptions.mIncludeExperimentals
+    ? CSSEnabledState::eIgnoreEnabledState
+    : CSSEnabledState::eForAllContent;
+
+  auto appendProperty = [enabledState, &aResult](uint32_t prop) {
+    nsCSSPropertyID cssProp = nsCSSPropertyID(prop);
+    if (nsCSSProps::IsEnabled(cssProp, enabledState)) {
+      nsDependentCString name(kCSSRawProperties[prop]);
+      aResult.AppendElement(NS_ConvertASCIItoUTF16(name));
+    }
+  };
 
   uint32_t prop = 0;
   for ( ; prop < eCSSProperty_COUNT_no_shorthands; ++prop) {
     if (nsCSSProps::PropertyParseType(nsCSSPropertyID(prop)) !=
         CSS_PROPERTY_PARSE_INACCESSIBLE) {
-      DO_PROP(prop);
+      appendProperty(prop);
     }
   }
 
-  for ( ; prop < eCSSProperty_COUNT; ++prop) {
-    DO_PROP(prop);
+  if (aOptions.mIncludeShorthands) {
+    for ( ; prop < eCSSProperty_COUNT; ++prop) {
+      appendProperty(prop);
+    }
   }
 
   if (aOptions.mIncludeAliases) {
     for (prop = eCSSProperty_COUNT; prop < eCSSProperty_COUNT_with_aliases; ++prop) {
-      DO_PROP(prop);
+      appendProperty(prop);
     }
   }
-
-#undef DO_PROP
 }
 
 static void InsertNoDuplicates(nsTArray<nsString>& aArray,
                                const nsAString& aString)
 {
   size_t i = aArray.IndexOfFirstElementGt(aString);
   if (i > 0 && aArray[i-1].Equals(aString)) {
     return;
--- a/layout/style/ServoCSSPropList.mako.py
+++ b/layout/style/ServoCSSPropList.mako.py
@@ -32,16 +32,20 @@ def flags(prop):
     if prop.explicitly_enabled_in_chrome():
         result.append("CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME")
     elif prop.explicitly_enabled_in_ua_sheets():
         result.append("CSS_PROPERTY_ENABLED_IN_UA_SHEETS")
     if is_internal(prop):
         result.append("CSS_PROPERTY_INTERNAL")
     if prop.enabled_in == "":
         result.append("CSS_PROPERTY_PARSE_INACCESSIBLE")
+    if "GETCS_NEEDS_LAYOUT_FLUSH" in prop.flags:
+        result.append("CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH")
+    if "CAN_ANIMATE_ON_COMPOSITOR" in prop.flags:
+        result.append("CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR")
     return ", ".join('"{}"'.format(flag) for flag in result)
 
 def pref(prop):
     if prop.gecko_pref:
         return '"' + prop.gecko_pref + '"'
     return '""'
 %>
 
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1455108.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<style>
+  div::first-line {
+    color: yellow:
+  }
+  .foo span {
+    display: none;
+  }
+  .foo::first-line {
+    color: red;
+  }
+</style>
+<div><span>Yo, I'm a first-line<span> really</span></span>
+<script>
+  document.body.offsetTop;
+  document.querySelector('div').classList.add('foo');
+</script>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -268,8 +268,9 @@ test-pref(dom.animations-api.core.enable
 pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
 load 1426312.html
 load 1439793.html
 load 1409183.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1445682.html
 load 1450691.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1453206.html
 load 1454140.html
+load 1455108.html
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -90,25 +90,25 @@
 
 // A caller who wants all the properties can define the |CSS_PROP|
 // macro.
 #ifdef CSS_PROP
 
 #define USED_CSS_PROP
 // We still need this extra level so that CSS_PROP_DOMPROP_PREFIXED has
 // a chance to be expanded.
-#define CSS_PROP_(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, animtype_)
+#define CSS_PROP_(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_)
 
 #else /* !defined(CSS_PROP) */
 
 // An includer who does not define CSS_PROP can define any or all of the
 // per-struct macros that are equivalent to it, and the rest will be
 // ignored.
 
-#define CSS_PROP_(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, animtype_) /* nothing */
+#define CSS_PROP_(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_) /* nothing */
 
 #endif /* !defined(CSS_PROP) */
 
 /*************************************************************************/
 
 // For notes XXX bug 3935 below, the names being parsed do not correspond
 // to the constants used internally.  It would be nice to bring the
 // constants into line sometime.
@@ -142,264 +142,235 @@
 
 CSS_PROP_(
     align-content,
     align_content,
     AlignContent,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
-    kAutoCompletionAlignJustifyContent,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignJustifyContent)
 CSS_PROP_(
     align-items,
     align_items,
     AlignItems,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
-    kAutoCompletionAlignItems,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignItems)
 CSS_PROP_(
     align-self,
     align_self,
     AlignSelf,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
-    kAutoCompletionAlignJustifySelf,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignJustifySelf)
 CSS_PROP_SHORTHAND(
     all,
     all,
     All,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.all-shorthand.enabled")
 CSS_PROP_SHORTHAND(
     animation,
     animation,
     Animation,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     animation-delay,
     animation_delay,
     AnimationDelay,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_TIME, // used by list parsing
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     animation-direction,
     animation_direction,
     AnimationDirection,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kAnimationDirectionKTable,
-    eStyleAnimType_None)
+    kAnimationDirectionKTable)
 CSS_PROP_(
     animation-duration,
     animation_duration,
     AnimationDuration,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_TIME | VARIANT_NONNEGATIVE_DIMENSION, // used by list parsing
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     animation-fill-mode,
     animation_fill_mode,
     AnimationFillMode,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kAnimationFillModeKTable,
-    eStyleAnimType_None)
+    kAnimationFillModeKTable)
 CSS_PROP_(
     animation-iteration-count,
     animation_iteration_count,
     AnimationIterationCount,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD | VARIANT_NUMBER, // used by list parsing
-    kAnimationIterationCountKTable,
-    eStyleAnimType_None)
+    kAnimationIterationCountKTable)
 CSS_PROP_(
     animation-name,
     animation_name,
     AnimationName,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     // FIXME: The spec should say something about 'inherit' and 'initial'
     // not being allowed.
     VARIANT_NONE | VARIANT_IDENTIFIER_NO_INHERIT | VARIANT_STRING, // used by list parsing
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     animation-play-state,
     animation_play_state,
     AnimationPlayState,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kAnimationPlayStateKTable,
-    eStyleAnimType_None)
+    kAnimationPlayStateKTable)
 CSS_PROP_(
     animation-timing-function,
     animation_timing_function,
     AnimationTimingFunction,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing
-    kTransitionTimingFunctionKTable,
-    eStyleAnimType_None)
+    kTransitionTimingFunctionKTable)
 CSS_PROP_(
     -moz-appearance,
     _moz_appearance,
     CSS_PROP_DOMPROP_PREFIXED(Appearance),
     0,
     "",
     VARIANT_HK,
-    kAppearanceKTable,
-    eStyleAnimType_Discrete)
+    kAppearanceKTable)
 CSS_PROP_(
     backface-visibility,
     backface_visibility,
     BackfaceVisibility,
     0,
     "",
     VARIANT_HK,
-    kBackfaceVisibilityKTable,
-    eStyleAnimType_Discrete)
+    kBackfaceVisibilityKTable)
 CSS_PROP_SHORTHAND(
     background,
     background,
     Background,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     background-attachment,
     background_attachment,
     BackgroundAttachment,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kImageLayerAttachmentKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerAttachmentKTable)
 CSS_PROP_(
     background-blend-mode,
     background_blend_mode,
     BackgroundBlendMode,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "layout.css.background-blend-mode.enabled",
     VARIANT_KEYWORD, // used by list parsing
-    kBlendModeKTable,
-    eStyleAnimType_Discrete)
+    kBlendModeKTable)
 CSS_PROP_(
     background-clip,
     background_clip,
     BackgroundClip,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kBackgroundClipKTable,
-    eStyleAnimType_Discrete)
+    kBackgroundClipKTable)
 CSS_PROP_(
     background-color,
     background_color,
     BackgroundColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     background-image,
     background_image,
     BackgroundImage,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_IMAGE, // used by list parsing
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     background-origin,
     background_origin,
     BackgroundOrigin,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kBackgroundOriginKTable,
-    eStyleAnimType_Discrete)
+    kBackgroundOriginKTable)
 CSS_PROP_SHORTHAND(
     background-position,
     background_position,
     BackgroundPosition,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     background-position-x,
     background_position_x,
     BackgroundPositionX,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     background-position-y,
     background_position_y,
     BackgroundPositionY,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     background-repeat,
     background_repeat,
     BackgroundRepeat,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kImageLayerRepeatKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerRepeatKTable)
 CSS_PROP_(
     background-size,
     background_size,
     BackgroundSize,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerSizeKTable,
-    eStyleAnimType_Custom)
+    kImageLayerSizeKTable)
 CSS_PROP_(
     -moz-binding,
     _moz_binding,
     CSS_PROP_DOMPROP_PREFIXED(Binding),
     0,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_None) // XXX bug 3935
+    nullptr) // XXX bug 3935
 CSS_PROP_(
     block-size,
     block_size,
     BlockSize,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_SHORTHAND(
     border,
     border,
     Border,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     border-block-end,
@@ -409,129 +380,117 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     border-block-end-color,
     border_block_end_color,
     BorderBlockEndColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     border-block-end-style,
     border_block_end_style,
     BorderBlockEndStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_None)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-block-end-width,
     border_block_end_width,
     BorderBlockEndWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_None)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-block-start,
     border_block_start,
     BorderBlockStart,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     border-block-start-color,
     border_block_start_color,
     BorderBlockStartColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     border-block-start-style,
     border_block_start_style,
     BorderBlockStartStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_None)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-block-start-width,
     border_block_start_width,
     BorderBlockStartWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_None)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-bottom,
     border_bottom,
     BorderBottom,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     border-bottom-color,
     border_bottom_color,
     BorderBottomColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     border-bottom-left-radius,
     border_bottom_left_radius,
     BorderBottomLeftRadius,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_BottomLeft)
+    nullptr)
 CSS_PROP_(
     border-bottom-right-radius,
     border_bottom_right_radius,
     BorderBottomRightRadius,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_BottomRight)
+    nullptr)
 CSS_PROP_(
     border-bottom-style,
     border_bottom_style,
     BorderBottomStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_Discrete)  // on/off will need reflow
+    kBorderStyleKTable)  // on/off will need reflow
 CSS_PROP_(
     border-bottom-width,
     border_bottom_width,
     BorderBottomWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Custom)
+    kBorderWidthKTable)
 CSS_PROP_(
     border-collapse,
     border_collapse,
     BorderCollapse,
     0,
     "",
     VARIANT_HK,
-    kBorderCollapseKTable,
-    eStyleAnimType_Discrete)
+    kBorderCollapseKTable)
 CSS_PROP_SHORTHAND(
     border-color,
     border_color,
     BorderColor,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     border-image,
@@ -541,153 +500,139 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     border-image-outset,
     border_image_outset,
     BorderImageOutset,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     border-image-repeat,
     border_image_repeat,
     BorderImageRepeat,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kBorderImageRepeatKTable,
-    eStyleAnimType_Discrete)
+    kBorderImageRepeatKTable)
 CSS_PROP_(
     border-image-slice,
     border_image_slice,
     BorderImageSlice,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kBorderImageSliceKTable,
-    eStyleAnimType_Discrete)
+    kBorderImageSliceKTable)
 CSS_PROP_(
     border-image-source,
     border_image_source,
     BorderImageSource,
     0,
     "",
     VARIANT_IMAGE | VARIANT_INHERIT,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     border-image-width,
     border_image_width,
     BorderImageWidth,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     border-inline-end,
     border_inline_end,
     BorderInlineEnd,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     border-inline-end-color,
     border_inline_end_color,
     BorderInlineEndColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     border-inline-end-style,
     border_inline_end_style,
     BorderInlineEndStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_None)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-inline-end-width,
     border_inline_end_width,
     BorderInlineEndWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_None)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-inline-start,
     border_inline_start,
     BorderInlineStart,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     border-inline-start-color,
     border_inline_start_color,
     BorderInlineStartColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     border-inline-start-style,
     border_inline_start_style,
     BorderInlineStartStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_None)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-inline-start-width,
     border_inline_start_width,
     BorderInlineStartWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_None)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-left,
     border_left,
     BorderLeft,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     border-left-color,
     border_left_color,
     BorderLeftColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     border-left-style,
     border_left_style,
     BorderLeftStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_Discrete)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-left-width,
     border_left_width,
     BorderLeftWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Custom)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-radius,
     border_radius,
     BorderRadius,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     border-right,
@@ -697,45 +642,41 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     border-right-color,
     border_right_color,
     BorderRightColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     border-right-style,
     border_right_style,
     BorderRightStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_Discrete)
+    kBorderStyleKTable)
 CSS_PROP_(
     border-right-width,
     border_right_width,
     BorderRightWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Custom)
+    kBorderWidthKTable)
 CSS_PROP_(
     border-spacing,
     border_spacing,
     BorderSpacing,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_SHORTHAND(
     border-style,
     border_style,
     BorderStyle,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")  // on/off will need reflow
 CSS_PROP_SHORTHAND(
     border-top,
@@ -745,771 +686,688 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     border-top-color,
     border_top_color,
     BorderTopColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     border-top-left-radius,
     border_top_left_radius,
     BorderTopLeftRadius,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_TopLeft)
+    nullptr)
 CSS_PROP_(
     border-top-right-radius,
     border_top_right_radius,
     BorderTopRightRadius,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_TopRight)
+    nullptr)
 CSS_PROP_(
     border-top-style,
     border_top_style,
     BorderTopStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_Discrete)  // on/off will need reflow
+    kBorderStyleKTable)  // on/off will need reflow
 CSS_PROP_(
     border-top-width,
     border_top_width,
     BorderTopWidth,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Custom)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     border-width,
     border_width,
     BorderWidth,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     bottom,
     bottom,
     Bottom,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Bottom)
+    nullptr)
 CSS_PROP_(
     -moz-box-align,
     _moz_box_align,
     CSS_PROP_DOMPROP_PREFIXED(BoxAlign),
     0,
     "",
     VARIANT_HK,
-    kBoxAlignKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kBoxAlignKTable) // XXX bug 3935
 CSS_PROP_(
     box-decoration-break,
     box_decoration_break,
     BoxDecorationBreak,
     0,
     "layout.css.box-decoration-break.enabled",
     VARIANT_HK,
-    kBoxDecorationBreakKTable,
-    eStyleAnimType_Discrete)
+    kBoxDecorationBreakKTable)
 CSS_PROP_(
     -moz-box-direction,
     _moz_box_direction,
     CSS_PROP_DOMPROP_PREFIXED(BoxDirection),
     0,
     "",
     VARIANT_HK,
-    kBoxDirectionKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kBoxDirectionKTable) // XXX bug 3935
 CSS_PROP_(
     -moz-box-flex,
     _moz_box_flex,
     CSS_PROP_DOMPROP_PREFIXED(BoxFlex),
     0,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float) // XXX bug 3935
+    nullptr) // XXX bug 3935
 CSS_PROP_(
     -moz-box-ordinal-group,
     _moz_box_ordinal_group,
     CSS_PROP_DOMPROP_PREFIXED(BoxOrdinalGroup),
     0,
     "",
     VARIANT_HI,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     -moz-box-orient,
     _moz_box_orient,
     CSS_PROP_DOMPROP_PREFIXED(BoxOrient),
     0,
     "",
     VARIANT_HK,
-    kBoxOrientKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kBoxOrientKTable) // XXX bug 3935
 CSS_PROP_(
     -moz-box-pack,
     _moz_box_pack,
     CSS_PROP_DOMPROP_PREFIXED(BoxPack),
     0,
     "",
     VARIANT_HK,
-    kBoxPackKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kBoxPackKTable) // XXX bug 3935
 CSS_PROP_(
     box-shadow,
     box_shadow,
     BoxShadow,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
         // NOTE: some components must be nonnegative
     "",
     VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC | VARIANT_INHERIT | VARIANT_NONE,
-    kBoxShadowTypeKTable,
-    eStyleAnimType_Shadow)
+    kBoxShadowTypeKTable)
 CSS_PROP_(
     box-sizing,
     box_sizing,
     BoxSizing,
     0,
     "",
     VARIANT_HK,
-    kBoxSizingKTable,
-    eStyleAnimType_Discrete)
+    kBoxSizingKTable)
 CSS_PROP_(
     caption-side,
     caption_side,
     CaptionSide,
     0,
     "",
     VARIANT_HK,
-    kCaptionSideKTable,
-    eStyleAnimType_Discrete)
+    kCaptionSideKTable)
 CSS_PROP_(
     caret-color,
     caret_color,
     CaretColor,
     0,
     "",
     VARIANT_AUTO | VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     clear,
     clear,
     Clear,
     0,
     "",
     VARIANT_HK,
-    kClearKTable,
-    eStyleAnimType_Discrete)
+    kClearKTable)
 CSS_PROP_(
     clip,
     clip,
     Clip,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_AH,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     clip-path,
     clip_path,
     ClipPath,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     clip-rule,
     clip_rule,
     ClipRule,
     0,
     "",
     VARIANT_HK,
-    kFillRuleKTable,
-    eStyleAnimType_Discrete)
+    kFillRuleKTable)
 CSS_PROP_(
     color,
     color,
     Color,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_Color)
+    nullptr)
 CSS_PROP_(
     color-adjust,
     color_adjust,
     ColorAdjust,
     0,
     "layout.css.color-adjust.enabled",
     VARIANT_HK,
-    kColorAdjustKTable,
-    eStyleAnimType_Discrete)
+    kColorAdjustKTable)
 CSS_PROP_(
     color-interpolation,
     color_interpolation,
     ColorInterpolation,
     0,
     "",
     VARIANT_HK,
-    kColorInterpolationKTable,
-    eStyleAnimType_Discrete)
+    kColorInterpolationKTable)
 CSS_PROP_(
     color-interpolation-filters,
     color_interpolation_filters,
     ColorInterpolationFilters,
     0,
     "",
     VARIANT_HK,
-    kColorInterpolationKTable,
-    eStyleAnimType_Discrete)
+    kColorInterpolationKTable)
 CSS_PROP_(
     column-count,
     column_count,
     ColumnCount,
     0,
     "",
     VARIANT_AHI,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     column-fill,
     column_fill,
     ColumnFill,
     0,
     "",
     VARIANT_HK,
-    kColumnFillKTable,
-    eStyleAnimType_Discrete)
+    kColumnFillKTable)
 CSS_PROP_(
     column-gap,
     column_gap,
     ColumnGap,
     0,
     "",
     VARIANT_HLP | VARIANT_NORMAL | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_SHORTHAND(
     column-rule,
     column_rule,
     ColumnRule,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     column-rule-color,
     column_rule_color,
     ColumnRuleColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     column-rule-style,
     column_rule_style,
     ColumnRuleStyle,
     0,
     "",
     VARIANT_HK,
-    kBorderStyleKTable,
-    eStyleAnimType_Discrete)
+    kBorderStyleKTable)
 CSS_PROP_(
     column-rule-width,
     column_rule_width,
     ColumnRuleWidth,
     0,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Custom)
+    kBorderWidthKTable)
 CSS_PROP_(
     column-span,
     column_span,
     ColumnSpan,
     0,
     "layout.css.column-span.enabled",
     VARIANT_HK,
-    kColumnSpanKTable,
-    eStyleAnimType_Discrete)
+    kColumnSpanKTable)
 CSS_PROP_(
     column-width,
     column_width,
     ColumnWidth,
     0,
     "",
     VARIANT_AHL | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_SHORTHAND(
     columns,
     columns,
     Columns,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     contain,
     contain,
     Contain,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.contain.enabled",
     // Does not affect parsing, but is needed for tab completion in devtools:
     VARIANT_HK | VARIANT_NONE,
-    kContainKTable,
-    eStyleAnimType_Discrete)
+    kContainKTable)
 CSS_PROP_(
     content,
     content,
     Content,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HMK | VARIANT_NONE | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR,
-    kContentKTable,
-    eStyleAnimType_Discrete)
+    kContentKTable)
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     // Only intended to be used internally by Mozilla, so prefixed.
     -moz-context-properties,
     _moz_context_properties,
     CSS_PROP_DOMPROP_PREFIXED(ContextProperties),
     CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
         CSS_PROPERTY_INTERNAL,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     -moz-control-character-visibility,
     _moz_control_character_visibility,
     CSS_PROP_DOMPROP_PREFIXED(ControlCharacterVisibility),
     CSS_PROPERTY_INTERNAL,
     "",
     VARIANT_HK,
-    kControlCharacterVisibilityKTable,
-    eStyleAnimType_None)
+    kControlCharacterVisibilityKTable)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     counter-increment,
     counter_increment,
     CounterIncrement,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_INHERIT | VARIANT_NONE,
-    nullptr,
-    eStyleAnimType_Discrete) // XXX bug 137285
+    nullptr) // XXX bug 137285
 CSS_PROP_(
     counter-reset,
     counter_reset,
     CounterReset,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_INHERIT | VARIANT_NONE,
-    nullptr,
-    eStyleAnimType_Discrete) // XXX bug 137285
+    nullptr) // XXX bug 137285
 CSS_PROP_(
     cursor,
     cursor,
     Cursor,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kCursorKTable,
-    eStyleAnimType_Discrete)
+    kCursorKTable)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     direction,
     direction,
     Direction,
     0,
     "",
     VARIANT_HK,
-    kDirectionKTable,
-    eStyleAnimType_Discrete)
+    kDirectionKTable)
 #endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
 CSS_PROP_(
     display,
     display,
     Display,
     0,
     "",
     VARIANT_HK,
-    kDisplayKTable,
-    eStyleAnimType_None)
+    kDisplayKTable)
 CSS_PROP_(
     dominant-baseline,
     dominant_baseline,
     DominantBaseline,
     0,
     "",
     VARIANT_HK,
-    kDominantBaselineKTable,
-    eStyleAnimType_Discrete)
+    kDominantBaselineKTable)
 CSS_PROP_(
     empty-cells,
     empty_cells,
     EmptyCells,
     0,
     "",
     VARIANT_HK,
-    kEmptyCellsKTable,
-    eStyleAnimType_Discrete)
+    kEmptyCellsKTable)
 CSS_PROP_(
     fill,
     fill,
     Fill,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kContextPatternKTable,
-    eStyleAnimType_PaintServer)
+    kContextPatternKTable)
 CSS_PROP_(
     fill-opacity,
     fill_opacity,
     FillOpacity,
     0,
     "",
     VARIANT_HN | VARIANT_KEYWORD,
-    kContextOpacityKTable,
-    eStyleAnimType_float)
+    kContextOpacityKTable)
 CSS_PROP_(
     fill-rule,
     fill_rule,
     FillRule,
     0,
     "",
     VARIANT_HK,
-    kFillRuleKTable,
-    eStyleAnimType_Discrete)
+    kFillRuleKTable)
 CSS_PROP_(
     filter,
     filter,
     Filter,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_SHORTHAND(
     flex,
     flex,
     Flex,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     flex-basis,
     flex_basis,
     FlexBasis,
     0,
     "",
     // NOTE: The parsing implementation for the 'flex' shorthand property has
     // its own code to parse each subproperty. It does not depend on the
     // longhand parsing defined here.
     VARIANT_AHKLP | VARIANT_CALC,
-    kFlexBasisKTable,
-    eStyleAnimType_Coord)
+    kFlexBasisKTable)
 CSS_PROP_(
     flex-direction,
     flex_direction,
     FlexDirection,
     0,
     "",
     VARIANT_HK,
-    kFlexDirectionKTable,
-    eStyleAnimType_Discrete)
+    kFlexDirectionKTable)
 CSS_PROP_SHORTHAND(
     flex-flow,
     flex_flow,
     FlexFlow,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     flex-grow,
     flex_grow,
     FlexGrow,
     0,
     "",
     // NOTE: The parsing implementation for the 'flex' shorthand property has
     // its own code to parse each subproperty. It does not depend on the
     // longhand parsing defined here.
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     flex-shrink,
     flex_shrink,
     FlexShrink,
     0,
     "",
     // NOTE: The parsing implementation for the 'flex' shorthand property has
     // its own code to parse each subproperty. It does not depend on the
     // longhand parsing defined here.
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     flex-wrap,
     flex_wrap,
     FlexWrap,
     0,
     "",
     VARIANT_HK,
-    kFlexWrapKTable,
-    eStyleAnimType_Discrete)
+    kFlexWrapKTable)
 CSS_PROP_(
     float,
     float,
     CSS_PROP_PUBLIC_OR_PRIVATE(CssFloat, Float),
     0,
     "",
     VARIANT_HK,
-    kFloatKTable,
-    eStyleAnimType_Discrete)
+    kFloatKTable)
 CSS_PROP_(
     -moz-float-edge,
     _moz_float_edge,
     CSS_PROP_DOMPROP_PREFIXED(FloatEdge),
     0,
     "",
     VARIANT_HK,
-    kFloatEdgeKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kFloatEdgeKTable) // XXX bug 3935
 CSS_PROP_(
     flood-color,
     flood_color,
     FloodColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_Color)
+    nullptr)
 CSS_PROP_(
     flood-opacity,
     flood_opacity,
     FloodOpacity,
     0,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_SHORTHAND(
     font,
     font,
     Font,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     font-family,
     font_family,
     FontFamily,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     font-feature-settings,
     font_feature_settings,
     FontFeatureSettings,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     font-kerning,
     font_kerning,
     FontKerning,
     0,
     "",
     VARIANT_HK,
-    kFontKerningKTable,
-    eStyleAnimType_Discrete)
+    kFontKerningKTable)
 CSS_PROP_(
     font-language-override,
     font_language_override,
     FontLanguageOverride,
     0,
     "",
     VARIANT_NORMAL | VARIANT_INHERIT | VARIANT_STRING,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     font-optical-sizing,
     font_optical_sizing,
     FontOpticalSizing,
     0,
     "layout.css.font-variations.enabled",
     VARIANT_HK,
-    kFontOpticalSizingKTable,
-    eStyleAnimType_None)
+    kFontOpticalSizingKTable)
 CSS_PROP_(
     font-size,
     font_size,
     FontSize,
     0,
     "",
     VARIANT_HKLP | VARIANT_SYSFONT | VARIANT_CALC,
-    kFontSizeKTable,
-    // Note that mSize is the correct place for *reading* the computed value,
-    // but setting it requires setting mFont.size as well.
-    eStyleAnimType_nscoord)
+    kFontSizeKTable)
 CSS_PROP_(
     font-size-adjust,
     font_size_adjust,
     FontSizeAdjust,
     0,
     "",
     VARIANT_HON | VARIANT_SYSFONT,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-font-smoothing-background-color,
     _moz_font_smoothing_background_color,
     CSS_PROP_DOMPROP_PREFIXED(FontSmoothingBackgroundColor),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_Color)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     font-stretch,
     font_stretch,
     FontStretch,
     0,
     "",
     VARIANT_HK | VARIANT_SYSFONT,
-    kFontStretchKTable,
-    eStyleAnimType_Custom)
+    kFontStretchKTable)
 CSS_PROP_(
     font-style,
     font_style,
     FontStyle,
     0,
     "",
     VARIANT_HK | VARIANT_SYSFONT,
-    kFontStyleKTable,
-    eStyleAnimType_Discrete)
+    kFontStyleKTable)
 CSS_PROP_(
     font-synthesis,
     font_synthesis,
     FontSynthesis,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kFontSynthesisKTable,
-    eStyleAnimType_Discrete)
+    kFontSynthesisKTable)
 CSS_PROP_SHORTHAND(
     font-variant,
     font_variant,
     FontVariant,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     font-variant-alternates,
     font_variant_alternates,
     FontVariantAlternates,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kFontVariantAlternatesKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantAlternatesKTable)
 CSS_PROP_(
     font-variant-caps,
     font_variant_caps,
     FontVariantCaps,
     0,
     "",
     VARIANT_HMK,
-    kFontVariantCapsKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantCapsKTable)
 CSS_PROP_(
     font-variant-east-asian,
     font_variant_east_asian,
     FontVariantEastAsian,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kFontVariantEastAsianKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantEastAsianKTable)
 CSS_PROP_(
     font-variant-ligatures,
     font_variant_ligatures,
     FontVariantLigatures,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kFontVariantLigaturesKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantLigaturesKTable)
 CSS_PROP_(
     font-variant-numeric,
     font_variant_numeric,
     FontVariantNumeric,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kFontVariantNumericKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantNumericKTable)
 CSS_PROP_(
     font-variant-position,
     font_variant_position,
     FontVariantPosition,
     0,
     "",
     VARIANT_HMK,
-    kFontVariantPositionKTable,
-    eStyleAnimType_Discrete)
+    kFontVariantPositionKTable)
 CSS_PROP_(
     font-variation-settings,
     font_variation_settings,
     FontVariationSettings,
-    CSS_PROPERTY_VALUE_PARSER_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.font-variations.enabled",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     font-weight,
     font_weight,
     FontWeight,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
         // NOTE: This property has range restrictions on interpolation!
     "",
     0,
-    kFontWeightKTable,
-    eStyleAnimType_Custom)
+    kFontWeightKTable)
 CSS_PROP_(
     -moz-force-broken-image-icon,
     _moz_force_broken_image_icon,
     CSS_PROP_DOMPROP_PREFIXED(ForceBrokenImageIcon),
     0,
     "",
     VARIANT_HI,
-    nullptr,
-    eStyleAnimType_Discrete) // bug 58646
+    nullptr) // bug 58646
 CSS_PROP_SHORTHAND(
     grid,
     grid,
     Grid,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     grid-area,
@@ -1519,69 +1377,63 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     grid-auto-columns,
     grid_auto_columns,
     GridAutoColumns,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kGridTrackBreadthKTable,
-    eStyleAnimType_Discrete)
+    kGridTrackBreadthKTable)
 CSS_PROP_(
     grid-auto-flow,
     grid_auto_flow,
     GridAutoFlow,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kGridAutoFlowKTable,
-    eStyleAnimType_Discrete)
+    kGridAutoFlowKTable)
 CSS_PROP_(
     grid-auto-rows,
     grid_auto_rows,
     GridAutoRows,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kGridTrackBreadthKTable,
-    eStyleAnimType_Discrete)
+    kGridTrackBreadthKTable)
 CSS_PROP_SHORTHAND(
     grid-column,
     grid_column,
     GridColumn,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     grid-column-end,
     grid_column_end,
     GridColumnEnd,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     grid-column-gap,
     grid_column_gap,
     GridColumnGap,
     0,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     grid-column-start,
     grid_column_start,
     GridColumnStart,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     grid-gap,
     grid_gap,
     GridGap,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     grid-row,
@@ -1591,771 +1443,690 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     grid-row-end,
     grid_row_end,
     GridRowEnd,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     grid-row-gap,
     grid_row_gap,
     GridRowGap,
     0,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     grid-row-start,
     grid_row_start,
     GridRowStart,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     grid-template,
     grid_template,
     GridTemplate,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     grid-template-areas,
     grid_template_areas,
     GridTemplateAreas,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     grid-template-columns,
     grid_template_columns,
     GridTemplateColumns,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     0,
-    kGridTrackBreadthKTable,
-    eStyleAnimType_Discrete)
+    kGridTrackBreadthKTable)
 CSS_PROP_(
     grid-template-rows,
     grid_template_rows,
     GridTemplateRows,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     0,
-    kGridTrackBreadthKTable,
-    eStyleAnimType_Discrete)
+    kGridTrackBreadthKTable)
 CSS_PROP_(
     height,
     height,
     Height,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     hyphens,
     hyphens,
     Hyphens,
     0,
     "",
     VARIANT_HK,
-    kHyphensKTable,
-    eStyleAnimType_Discrete)
+    kHyphensKTable)
 CSS_PROP_(
     image-orientation,
     image_orientation,
     ImageOrientation,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.image-orientation.enabled",
     0,
-    kImageOrientationKTable,
-    eStyleAnimType_Discrete)
+    kImageOrientationKTable)
 CSS_PROP_(
     -moz-image-region,
     _moz_image_region,
     CSS_PROP_DOMPROP_PREFIXED(ImageRegion),
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     image-rendering,
     image_rendering,
     ImageRendering,
     0,
     "",
     VARIANT_HK,
-    kImageRenderingKTable,
-    eStyleAnimType_Discrete)
+    kImageRenderingKTable)
 CSS_PROP_(
     ime-mode,
     ime_mode,
     ImeMode,
     0,
     "",
     VARIANT_HK,
-    kIMEModeKTable,
-    eStyleAnimType_Discrete)
+    kIMEModeKTable)
 CSS_PROP_(
     initial-letter,
     initial_letter,
     InitialLetter,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.initial-letter.enabled",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     inline-size,
     inline_size,
     InlineSize,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_None)
+    kWidthKTable)
 CSS_PROP_(
     isolation,
     isolation,
     Isolation,
     0,
     "layout.css.isolation.enabled",
     VARIANT_HK,
-    kIsolationKTable,
-    eStyleAnimType_Discrete)
+    kIsolationKTable)
 CSS_PROP_(
     justify-content,
     justify_content,
     JustifyContent,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
-    kAutoCompletionAlignJustifyContent,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignJustifyContent)
 CSS_PROP_(
     justify-items,
     justify_items,
     JustifyItems,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
     // for auto-completion we use same values as justify-self:
-    kAutoCompletionAlignJustifySelf,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignJustifySelf)
 CSS_PROP_(
     justify-self,
     justify_self,
     JustifySelf,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HK,
-    kAutoCompletionAlignJustifySelf,
-    eStyleAnimType_Discrete)
+    kAutoCompletionAlignJustifySelf)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -x-lang,
     _x_lang,
     Lang,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     left,
     left,
     Left,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Left)
+    nullptr)
 CSS_PROP_(
     letter-spacing,
     letter_spacing,
     LetterSpacing,
     0,
     "",
     VARIANT_HL | VARIANT_NORMAL | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     lighting-color,
     lighting_color,
     LightingColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_Color)
+    nullptr)
 CSS_PROP_(
     line-height,
     line_height,
     LineHeight,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLPN | VARIANT_KEYWORD | VARIANT_NORMAL | VARIANT_SYSFONT | VARIANT_CALC,
-    kLineHeightKTable,
-    eStyleAnimType_Coord)
+    kLineHeightKTable)
 CSS_PROP_SHORTHAND(
     list-style,
     list_style,
     ListStyle,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     list-style-image,
     list_style_image,
     ListStyleImage,
     0,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     list-style-position,
     list_style_position,
     ListStylePosition,
     0,
     "",
     VARIANT_HK,
-    kListStylePositionKTable,
-    eStyleAnimType_Discrete)
+    kListStylePositionKTable)
 CSS_PROP_(
     list-style-type,
     list_style_type,
     ListStyleType,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     margin,
     margin,
     Margin,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     margin-block-end,
     margin_block_end,
     MarginBlockEnd,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     margin-block-start,
     margin_block_start,
     MarginBlockStart,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     margin-bottom,
     margin_bottom,
     MarginBottom,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Bottom)
+    nullptr)
 CSS_PROP_(
     margin-inline-end,
     margin_inline_end,
     MarginInlineEnd,
-    0,
+    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     margin-inline-start,
     margin_inline_start,
     MarginInlineStart,
-    0,
+    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     margin-left,
     margin_left,
     MarginLeft,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Left)
+    nullptr)
 CSS_PROP_(
     margin-right,
     margin_right,
     MarginRight,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Right)
+    nullptr)
 CSS_PROP_(
     margin-top,
     margin_top,
     MarginTop,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Top)
+    nullptr)
 CSS_PROP_SHORTHAND(
     marker,
     marker,
     Marker,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     marker-end,
     marker_end,
     MarkerEnd,
     0,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     marker-mid,
     marker_mid,
     MarkerMid,
     0,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     marker-start,
     marker_start,
     MarkerStart,
     0,
     "",
     VARIANT_HUO,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     mask,
     mask,
     Mask,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     mask-clip,
     mask_clip,
     MaskClip,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kMaskClipKTable,
-    eStyleAnimType_Discrete)
+    kMaskClipKTable)
 CSS_PROP_(
     mask-composite,
     mask_composite,
     MaskComposite,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kImageLayerCompositeKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerCompositeKTable)
 CSS_PROP_(
     mask-image,
     mask_image,
     MaskImage,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_IMAGE, // used by list parsing
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     mask-mode,
     mask_mode,
     MaskMode,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kImageLayerModeKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerModeKTable)
 CSS_PROP_(
     mask-origin,
     mask_origin,
     MaskOrigin,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kMaskOriginKTable,
-    eStyleAnimType_Discrete)
+    kMaskOriginKTable)
 CSS_PROP_SHORTHAND(
     mask-position,
     mask_position,
     MaskPosition,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     mask-position-x,
     mask_position_x,
     MaskPositionX,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     mask-position-y,
     mask_position_y,
     MaskPositionY,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     mask-repeat,
     mask_repeat,
     MaskRepeat,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_KEYWORD, // used by list parsing
-    kImageLayerRepeatKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerRepeatKTable)
 CSS_PROP_(
     mask-size,
     mask_size,
     MaskSize,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kImageLayerSizeKTable,
-    eStyleAnimType_Custom)
+    kImageLayerSizeKTable)
 CSS_PROP_(
     mask-type,
     mask_type,
     MaskType,
     0,
     "",
     VARIANT_HK,
-    kMaskTypeKTable,
-    eStyleAnimType_Discrete)
+    kMaskTypeKTable)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-math-display,
     _moz_math_display,
     MathDisplay,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "",
     VARIANT_HK,
-    kMathDisplayKTable,
-    eStyleAnimType_None)
+    kMathDisplayKTable)
 CSS_PROP_(
     -moz-math-variant,
     _moz_math_variant,
     MathVariant,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     VARIANT_HK,
-    kMathVariantKTable,
-    eStyleAnimType_None)
+    kMathVariantKTable)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     max-block-size,
     max_block_size,
     MaxBlockSize,
-    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    0,
     "",
     VARIANT_HLPO | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     max-height,
     max_height,
     MaxHeight,
     0,
     "",
     VARIANT_HKLPO | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     max-inline-size,
     max_inline_size,
     MaxInlineSize,
-    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    0,
     "",
     VARIANT_HKLPO | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_None)
+    kWidthKTable)
 CSS_PROP_(
     max-width,
     max_width,
     MaxWidth,
     0,
     "",
     VARIANT_HKLPO | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     min-block-size,
     min_block_size,
     MinBlockSize,
-    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    0,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-min-font-size-ratio,
     _moz_min_font_size_ratio,
     CSS_PROP_DOMPROP_PREFIXED(MinFontSizeRatio),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "",
     VARIANT_INHERIT | VARIANT_PERCENT,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     min-height,
     min_height,
     MinHeight,
     0,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     min-inline-size,
     min_inline_size,
     MinInlineSize,
-    CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    0,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_None)
+    kWidthKTable)
 CSS_PROP_(
     min-width,
     min_width,
     MinWidth,
     0,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     mix-blend-mode,
     mix_blend_mode,
     MixBlendMode,
     0,
     "layout.css.mix-blend-mode.enabled",
     VARIANT_HK,
-    kBlendModeKTable,
-    eStyleAnimType_Discrete)
+    kBlendModeKTable)
 CSS_PROP_(
     object-fit,
     object_fit,
     ObjectFit,
     0,
     "",
     VARIANT_HK,
-    kObjectFitKTable,
-    eStyleAnimType_Discrete)
+    kObjectFitKTable)
 CSS_PROP_(
     object-position,
     object_position,
     ObjectPosition,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_CALC,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     offset-block-end,
     offset_block_end,
     OffsetBlockEnd,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     offset-block-start,
     offset_block_start,
     OffsetBlockStart,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     offset-inline-end,
     offset_inline_end,
     OffsetInlineEnd,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     offset-inline-start,
     offset_inline_start,
     OffsetInlineStart,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     opacity,
     opacity,
     Opacity,
     CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     order,
     order,
     Order,
     0,
     "",
     VARIANT_HI,
-    nullptr,
-    eStyleAnimType_Custom) // <integer>
+    nullptr) // <integer>
 CSS_PROP_(
     -moz-orient,
     _moz_orient,
     CSS_PROP_DOMPROP_PREFIXED(Orient),
     0,
     "",
     VARIANT_HK,
-    kOrientKTable,
-    eStyleAnimType_Discrete)
+    kOrientKTable)
 CSS_PROP_(
     -moz-osx-font-smoothing,
     _moz_osx_font_smoothing,
     CSS_PROP_DOMPROP_PREFIXED(OsxFontSmoothing),
     0,
     "layout.css.osx-font-smoothing.enabled",
     VARIANT_HK,
-    kFontSmoothingKTable,
-    eStyleAnimType_Discrete)
+    kFontSmoothingKTable)
 CSS_PROP_SHORTHAND(
     outline,
     outline,
     Outline,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     outline-color,
     outline_color,
     OutlineColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     outline-offset,
     outline_offset,
     OutlineOffset,
     0,
     "",
     VARIANT_HL | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_nscoord)
+    nullptr)
 CSS_PROP_SHORTHAND(
     -moz-outline-radius,
     _moz_outline_radius,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadius),
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     -moz-outline-radius-bottomleft,
     _moz_outline_radius_bottomleft,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomleft),
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_BottomLeft)
+    nullptr)
 CSS_PROP_(
     -moz-outline-radius-bottomright,
     _moz_outline_radius_bottomright,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusBottomright),
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_BottomRight)
+    nullptr)
 CSS_PROP_(
     -moz-outline-radius-topleft,
     _moz_outline_radius_topleft,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopleft),
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_TopLeft)
+    nullptr)
 CSS_PROP_(
     -moz-outline-radius-topright,
     _moz_outline_radius_topright,
     CSS_PROP_DOMPROP_PREFIXED(OutlineRadiusTopright),
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Corner_TopRight)
+    nullptr)
 CSS_PROP_(
     outline-style,
     outline_style,
     OutlineStyle,
     0,
     "",
     VARIANT_HK,
-    kOutlineStyleKTable,
-    eStyleAnimType_Discrete)
+    kOutlineStyleKTable)
 CSS_PROP_(
     outline-width,
     outline_width,
     OutlineWidth,
     0,
     "",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_nscoord)
+    kBorderWidthKTable)
 CSS_PROP_SHORTHAND(
     overflow,
     overflow,
     Overflow,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     overflow-clip-box,
@@ -2366,212 +2137,191 @@ CSS_PROP_SHORTHAND(
     "layout.css.overflow-clip-box.enabled")
 CSS_PROP_(
     overflow-clip-box-block,
     overflow_clip_box_block,
     OverflowClipBoxBlock,
     CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "layout.css.overflow-clip-box.enabled",
     VARIANT_HK,
-    kOverflowClipBoxKTable,
-    eStyleAnimType_Discrete)
+    kOverflowClipBoxKTable)
 CSS_PROP_(
     overflow-clip-box-inline,
     overflow_clip_box_inline,
     OverflowClipBoxInline,
     CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "layout.css.overflow-clip-box.enabled",
     VARIANT_HK,
-    kOverflowClipBoxKTable,
-    eStyleAnimType_Discrete)
+    kOverflowClipBoxKTable)
 CSS_PROP_(
     overflow-wrap,
     overflow_wrap,
     OverflowWrap,
     0,
     "",
     VARIANT_HK,
-    kOverflowWrapKTable,
-    eStyleAnimType_Discrete)
+    kOverflowWrapKTable)
 CSS_PROP_(
     overflow-x,
     overflow_x,
     OverflowX,
     0,
     "",
     VARIANT_HK,
-    kOverflowSubKTable,
-    eStyleAnimType_Discrete)
+    kOverflowSubKTable)
 CSS_PROP_(
     overflow-y,
     overflow_y,
     OverflowY,
     0,
     "",
     VARIANT_HK,
-    kOverflowSubKTable,
-    eStyleAnimType_Discrete)
+    kOverflowSubKTable)
 CSS_PROP_SHORTHAND(
     overscroll-behavior,
     overscroll_behavior,
     OverscrollBehavior,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.overscroll-behavior.enabled")
 CSS_PROP_(
     overscroll-behavior-x,
     overscroll_behavior_x,
     OverscrollBehaviorX,
     0,
     "layout.css.overscroll-behavior.enabled",
     VARIANT_HK,
-    kOverscrollBehaviorKTable,
-    eStyleAnimType_Discrete)
+    kOverscrollBehaviorKTable)
 CSS_PROP_(
     overscroll-behavior-y,
     overscroll_behavior_y,
     OverscrollBehaviorY,
     0,
     "layout.css.overscroll-behavior.enabled",
     VARIANT_HK,
-    kOverscrollBehaviorKTable,
-    eStyleAnimType_Discrete)
+    kOverscrollBehaviorKTable)
 CSS_PROP_SHORTHAND(
     padding,
     padding,
     Padding,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     padding-block-end,
     padding_block_end,
     PaddingBlockEnd,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     padding-block-start,
     padding_block_start,
     PaddingBlockStart,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     padding-bottom,
     padding_bottom,
     PaddingBottom,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Bottom)
+    nullptr)
 CSS_PROP_(
     padding-inline-end,
     padding_inline_end,
     PaddingInlineEnd,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     padding-inline-start,
     padding_inline_start,
     PaddingInlineStart,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     padding-left,
     padding_left,
     PaddingLeft,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Left)
+    nullptr)
 CSS_PROP_(
     padding-right,
     padding_right,
     PaddingRight,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Right)
+    nullptr)
 CSS_PROP_(
     padding-top,
     padding_top,
     PaddingTop,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Top)
+    nullptr)
 CSS_PROP_(
     page-break-after,
     page_break_after,
     PageBreakAfter,
     0,
     "",
     VARIANT_HK,
-    kPageBreakKTable,
-    eStyleAnimType_Discrete) // temp fix for bug 24000
+    kPageBreakKTable) // temp fix for bug 24000
 CSS_PROP_(
     page-break-before,
     page_break_before,
     PageBreakBefore,
     0,
     "",
     VARIANT_HK,
-    kPageBreakKTable,
-    eStyleAnimType_Discrete) // temp fix for bug 24000
+    kPageBreakKTable) // temp fix for bug 24000
 CSS_PROP_(
     page-break-inside,
     page_break_inside,
     PageBreakInside,
     0,
     "",
     VARIANT_HK,
-    kPageBreakInsideKTable,
-    eStyleAnimType_Discrete)
+    kPageBreakInsideKTable)
 CSS_PROP_(
     paint-order,
     paint_order,
     PaintOrder,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     perspective,
     perspective,
     Perspective,
     0,
     "",
     VARIANT_NONE | VARIANT_INHERIT | VARIANT_LENGTH |
       VARIANT_NONNEGATIVE_DIMENSION,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     perspective-origin,
     perspective_origin,
     PerspectiveOrigin,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_CALC,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_SHORTHAND(
     place-content,
     place_content,
     PlaceContent,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SHORTHAND(
     place-items,
@@ -2587,902 +2337,805 @@ CSS_PROP_SHORTHAND(
     "")
 CSS_PROP_(
     pointer-events,
     pointer_events,
     PointerEvents,
     0,
     "",
     VARIANT_HK,
-    kPointerEventsKTable,
-    eStyleAnimType_Discrete)
+    kPointerEventsKTable)
 CSS_PROP_(
     position,
     position,
     Position,
     0,
     "",
     VARIANT_HK,
-    kPositionKTable,
-    eStyleAnimType_Discrete)
+    kPositionKTable)
 CSS_PROP_(
     quotes,
     quotes,
     Quotes,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_HOS,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     resize,
     resize,
     Resize,
     0,
     "",
     VARIANT_HK,
-    kResizeKTable,
-    eStyleAnimType_Discrete)
+    kResizeKTable)
 CSS_PROP_(
     right,
     right,
     Right,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Right)
+    nullptr)
 CSS_PROP_(
     rotate,
     rotate,
     Rotate,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.individual-transform.enabled",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     ruby-align,
     ruby_align,
     RubyAlign,
     0,
     "",
     VARIANT_HK,
-    kRubyAlignKTable,
-    eStyleAnimType_Discrete)
+    kRubyAlignKTable)
 CSS_PROP_(
     ruby-position,
     ruby_position,
     RubyPosition,
     0,
     "",
     VARIANT_HK,
-    kRubyPositionKTable,
-    eStyleAnimType_Discrete)
+    kRubyPositionKTable)
 CSS_PROP_(
     scale,
     scale,
     Scale,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.individual-transform.enabled",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-script-level,
     _moz_script_level,
     ScriptLevel,
     // We only allow 'script-level' when unsafe rules are enabled, because
     // otherwise it could interfere with rulenode optimizations if used in
     // a non-MathML-enabled document.
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "",
     // script-level can take Auto, Integer and Number values, but only Auto
     // ("increment if parent is not in displaystyle") and Integer
     // ("relative") values can be specified in a style sheet.
     VARIANT_AHI,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     -moz-script-min-size,
     _moz_script_min_size,
     ScriptMinSize,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     -moz-script-size-multiplier,
     _moz_script_size_multiplier,
     ScriptSizeMultiplier,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     scroll-behavior,
     scroll_behavior,
     ScrollBehavior,
     0,
     "layout.css.scroll-behavior.property-enabled",
     VARIANT_HK,
-    kScrollBehaviorKTable,
-    eStyleAnimType_Discrete)
+    kScrollBehaviorKTable)
 CSS_PROP_(
     scroll-snap-coordinate,
     scroll_snap_coordinate,
     ScrollSnapCoordinate,
-    CSS_PROPERTY_VALUE_PARSER_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.scroll-snap.enabled",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     scroll-snap-destination,
     scroll_snap_destination,
     ScrollSnapDestination,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.scroll-snap.enabled",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Discrete)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     scroll-snap-points-x,
     scroll_snap_points_x,
     ScrollSnapPointsX,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.scroll-snap.enabled",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     scroll-snap-points-y,
     scroll_snap_points_y,
     ScrollSnapPointsY,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.scroll-snap.enabled",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_SHORTHAND(
     scroll-snap-type,
     scroll_snap_type,
     ScrollSnapType,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.scroll-snap.enabled")
 CSS_PROP_(
     scroll-snap-type-x,
     scroll_snap_type_x,
     ScrollSnapTypeX,
     0,
     "layout.css.scroll-snap.enabled",
     VARIANT_HK,
-    kScrollSnapTypeKTable,
-    eStyleAnimType_Discrete)
+    kScrollSnapTypeKTable)
 CSS_PROP_(
     scroll-snap-type-y,
     scroll_snap_type_y,
     ScrollSnapTypeY,
     0,
     "layout.css.scroll-snap.enabled",
     VARIANT_HK,
-    kScrollSnapTypeKTable,
-    eStyleAnimType_Discrete)
+    kScrollSnapTypeKTable)
 CSS_PROP_(
     shape-image-threshold,
     shape_image_threshold,
     ShapeImageThreshold,
     0,
     "layout.css.shape-outside.enabled",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     shape-outside,
     shape_outside,
     ShapeOutside,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.shape-outside.enabled",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     shape-rendering,
     shape_rendering,
     ShapeRendering,
     0,
     "",
     VARIANT_HK,
-    kShapeRenderingKTable,
-    eStyleAnimType_Discrete)
+    kShapeRenderingKTable)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -x-span,
     _x_span,
     Span,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     -moz-stack-sizing,
     _moz_stack_sizing,
     CSS_PROP_DOMPROP_PREFIXED(StackSizing),
     0,
     "",
     VARIANT_HK,
-    kStackSizingKTable,
-    eStyleAnimType_Discrete)
+    kStackSizingKTable)
 CSS_PROP_(
     stop-color,
     stop_color,
     StopColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_Color)
+    nullptr)
 CSS_PROP_(
     stop-opacity,
     stop_opacity,
     StopOpacity,
     0,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     stroke,
     stroke,
     Stroke,
     CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    kContextPatternKTable,
-    eStyleAnimType_PaintServer)
+    kContextPatternKTable)
 CSS_PROP_(
     stroke-dasharray,
     stroke_dasharray,
     StrokeDasharray,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
         // NOTE: Internal values have range restrictions.
     "",
     0,
-    kStrokeContextValueKTable,
-    eStyleAnimType_Custom)
+    kStrokeContextValueKTable)
 CSS_PROP_(
     stroke-dashoffset,
     stroke_dashoffset,
     StrokeDashoffset,
     0,
     "",
     VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD,
-    kStrokeContextValueKTable,
-    eStyleAnimType_Coord)
+    kStrokeContextValueKTable)
 CSS_PROP_(
     stroke-linecap,
     stroke_linecap,
     StrokeLinecap,
     0,
     "",
     VARIANT_HK,
-    kStrokeLinecapKTable,
-    eStyleAnimType_Discrete)
+    kStrokeLinecapKTable)
 CSS_PROP_(
     stroke-linejoin,
     stroke_linejoin,
     StrokeLinejoin,
     0,
     "",
     VARIANT_HK,
-    kStrokeLinejoinKTable,
-    eStyleAnimType_Discrete)
+    kStrokeLinejoinKTable)
 CSS_PROP_(
     stroke-miterlimit,
     stroke_miterlimit,
     StrokeMiterlimit,
     0,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     stroke-opacity,
     stroke_opacity,
     StrokeOpacity,
     0,
     "",
     VARIANT_HN | VARIANT_KEYWORD,
-    kContextOpacityKTable,
-    eStyleAnimType_float)
+    kContextOpacityKTable)
 CSS_PROP_(
     stroke-width,
     stroke_width,
     StrokeWidth,
     0,
     "",
     VARIANT_HLPN | VARIANT_OPENTYPE_SVG_KEYWORD,
-    kStrokeContextValueKTable,
-    eStyleAnimType_Coord)
+    kStrokeContextValueKTable)
 CSS_PROP_(
     -moz-tab-size,
     _moz_tab_size,
     CSS_PROP_DOMPROP_PREFIXED(TabSize),
     0,
     "",
     VARIANT_INHERIT | VARIANT_LNCALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     table-layout,
     table_layout,
     TableLayout,
     0,
     "",
     VARIANT_HK,
-    kTableLayoutKTable,
-    eStyleAnimType_Discrete)
+    kTableLayoutKTable)
 CSS_PROP_(
     text-align,
     text_align,
     TextAlign,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     // When we support aligning on a string, we can parse text-align
     // as a string....
     VARIANT_HK /* | VARIANT_STRING */,
-    kTextAlignKTable,
-    eStyleAnimType_Discrete)
+    kTextAlignKTable)
 CSS_PROP_(
     text-align-last,
     text_align_last,
     TextAlignLast,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     VARIANT_HK,
-    kTextAlignLastKTable,
-    eStyleAnimType_Discrete)
+    kTextAlignLastKTable)
 CSS_PROP_(
     text-anchor,
     text_anchor,
     TextAnchor,
     0,
     "",
     VARIANT_HK,
-    kTextAnchorKTable,
-    eStyleAnimType_Discrete)
+    kTextAnchorKTable)
 CSS_PROP_(
     text-combine-upright,
     text_combine_upright,
     TextCombineUpright,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.text-combine-upright.enabled",
     0,
-    kTextCombineUprightKTable,
-    eStyleAnimType_Discrete)
+    kTextCombineUprightKTable)
 CSS_PROP_SHORTHAND(
     text-decoration,
     text_decoration,
     TextDecoration,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     text-decoration-color,
     text_decoration_color,
     TextDecorationColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     text-decoration-line,
     text_decoration_line,
     TextDecorationLine,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kTextDecorationLineKTable,
-    eStyleAnimType_Discrete)
+    kTextDecorationLineKTable)
 CSS_PROP_(
     text-decoration-style,
     text_decoration_style,
     TextDecorationStyle,
     0,
     "",
     VARIANT_HK,
-    kTextDecorationStyleKTable,
-    eStyleAnimType_Discrete)
+    kTextDecorationStyleKTable)
 CSS_PROP_SHORTHAND(
     text-emphasis,
     text_emphasis,
     TextEmphasis,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     text-emphasis-color,
     text_emphasis_color,
     TextEmphasisColor,
     0,
     "",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     text-emphasis-position,
     text_emphasis_position,
     TextEmphasisPosition,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kTextEmphasisPositionKTable,
-    eStyleAnimType_Discrete)
+    kTextEmphasisPositionKTable)
 CSS_PROP_(
     text-emphasis-style,
     text_emphasis_style,
     TextEmphasisStyle,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     -webkit-text-fill-color,
     _webkit_text_fill_color,
     WebkitTextFillColor,
     0,
     "layout.css.prefixes.webkit",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     text-indent,
     text_indent,
     TextIndent,
     0,
     "",
     VARIANT_HLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     text-justify,
     text_justify,
     TextJustify,
     0,
     "layout.css.text-justify.enabled",
     VARIANT_HK,
-    kTextJustifyKTable,
-    eStyleAnimType_Discrete)
+    kTextJustifyKTable)
 CSS_PROP_(
     text-orientation,
     text_orientation,
     TextOrientation,
     0,
     "",
     VARIANT_HK,
-    kTextOrientationKTable,
-    eStyleAnimType_Discrete)
+    kTextOrientationKTable)
 CSS_PROP_(
     text-overflow,
     text_overflow,
     TextOverflow,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     0,
-    kTextOverflowKTable,
-    eStyleAnimType_Discrete)
+    kTextOverflowKTable)
 CSS_PROP_(
     text-rendering,
     text_rendering,
     TextRendering,
     0,
     "",
     VARIANT_HK,
-    kTextRenderingKTable,
-    eStyleAnimType_Discrete)
+    kTextRenderingKTable)
 CSS_PROP_(
     text-shadow,
     text_shadow,
     TextShadow,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
         // NOTE: some components must be nonnegative
     "",
     VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC | VARIANT_INHERIT | VARIANT_NONE,
-    nullptr,
-    eStyleAnimType_Shadow)
+    nullptr)
 CSS_PROP_(
     -moz-text-size-adjust,
     _moz_text_size_adjust,
     CSS_PROP_DOMPROP_PREFIXED(TextSizeAdjust),
     0,
     "",
     VARIANT_HK,
-    kTextSizeAdjustKTable,
-    eStyleAnimType_Discrete)
+    kTextSizeAdjustKTable)
 CSS_PROP_SHORTHAND(
     -webkit-text-stroke,
     _webkit_text_stroke,
     WebkitTextStroke,
     CSS_PROPERTY_PARSE_FUNCTION,
     "layout.css.prefixes.webkit")
 CSS_PROP_(
     -webkit-text-stroke-color,
     _webkit_text_stroke_color,
     WebkitTextStrokeColor,
     0,
     "layout.css.prefixes.webkit",
     VARIANT_HC,
-    nullptr,
-    eStyleAnimType_ComplexColor)
+    nullptr)
 CSS_PROP_(
     -webkit-text-stroke-width,
     _webkit_text_stroke_width,
     WebkitTextStrokeWidth,
     0,
     "layout.css.prefixes.webkit",
     VARIANT_HKL | VARIANT_CALC,
-    kBorderWidthKTable,
-    eStyleAnimType_Discrete)
+    kBorderWidthKTable)
 CSS_PROP_(
     text-transform,
     text_transform,
     TextTransform,
     0,
     "",
     VARIANT_HK,
-    kTextTransformKTable,
-    eStyleAnimType_Discrete)
+    kTextTransformKTable)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -x-text-zoom,
     _x_text_zoom,
     TextZoom,
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     top,
     top,
     Top,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHLP | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Sides_Top)
+    nullptr)
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-top-layer,
     _moz_top_layer,
     CSS_PROP_DOMPROP_PREFIXED(TopLayer),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS,
     "",
     VARIANT_HK,
-    kTopLayerKTable,
-    eStyleAnimType_None)
+    kTopLayerKTable)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     touch-action,
     touch_action,
     TouchAction,
     CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "layout.css.touch_action.enabled",
     VARIANT_HK,
-    kTouchActionKTable,
-    eStyleAnimType_Discrete)
+    kTouchActionKTable)
 CSS_PROP_(
     transform,
     transform,
     Transform,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
         CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     transform-box,
     transform_box,
     TransformBox,
     0,
     "svg.transform-box.enabled",
     VARIANT_HK,
-    kTransformBoxKTable,
-    eStyleAnimType_Discrete)
+    kTransformBoxKTable)
 CSS_PROP_(
     transform-origin,
     transform_origin,
     TransformOrigin,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 CSS_PROP_(
     transform-style,
     transform_style,
     TransformStyle,
     0,
     "",
     VARIANT_HK,
-    kTransformStyleKTable,
-    eStyleAnimType_Discrete)
+    kTransformStyleKTable)
 CSS_PROP_SHORTHAND(
     transition,
     transition,
     Transition,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_(
     transition-delay,
     transition_delay,
     TransitionDelay,
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_TIME, // used by list parsing
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     transition-duration,
     transition_duration,
     TransitionDuration,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_TIME | VARIANT_NONNEGATIVE_DIMENSION, // used by list parsing
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     transition-property,
     transition_property,
     TransitionProperty,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     VARIANT_IDENTIFIER | VARIANT_NONE | VARIANT_ALL, // used only in shorthand
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 CSS_PROP_(
     transition-timing-function,
     transition_timing_function,
     TransitionTimingFunction,
-    CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    0,
     "",
     VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing
-    kTransitionTimingFunctionKTable,
-    eStyleAnimType_None)
+    kTransitionTimingFunctionKTable)
 CSS_PROP_(
     translate,
     translate,
     Translate,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "layout.css.individual-transform.enabled",
     0,
-    nullptr,
-    eStyleAnimType_None)
+    nullptr)
 #ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     unicode-bidi,
     unicode_bidi,
     UnicodeBidi,
     0,
     "",
     VARIANT_HK,
-    kUnicodeBidiKTable,
-    eStyleAnimType_Discrete)
+    kUnicodeBidiKTable)
 #endif // CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_(
     -moz-user-focus,
     _moz_user_focus,
     CSS_PROP_DOMPROP_PREFIXED(UserFocus),
     0,
     "",
     VARIANT_HK,
-    kUserFocusKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kUserFocusKTable) // XXX bug 3935
 CSS_PROP_(
     -moz-user-input,
     _moz_user_input,
     CSS_PROP_DOMPROP_PREFIXED(UserInput),
     0,
     "",
     VARIANT_HK,
-    kUserInputKTable,
-    eStyleAnimType_Discrete) // XXX ??? // XXX bug 3935
+    kUserInputKTable) // XXX ??? // XXX bug 3935
 CSS_PROP_(
     -moz-user-modify,
     _moz_user_modify,
     CSS_PROP_DOMPROP_PREFIXED(UserModify),
     0,
     "",
     VARIANT_HK,
-    kUserModifyKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kUserModifyKTable) // XXX bug 3935
 CSS_PROP_(
     -moz-user-select,
     _moz_user_select,
     CSS_PROP_DOMPROP_PREFIXED(UserSelect),
     0,
     "",
     VARIANT_HK,
-    kUserSelectKTable,
-    eStyleAnimType_Discrete) // XXX bug 3935
+    kUserSelectKTable) // XXX bug 3935
 CSS_PROP_(
     vector-effect,
     vector_effect,
     VectorEffect,
     0,
     "",
     VARIANT_HK,
-    kVectorEffectKTable,
-    eStyleAnimType_Discrete)
+    kVectorEffectKTable)
 // NOTE: vertical-align is only supposed to apply to :first-letter when
 // 'float' is 'none', but we don't worry about that since it has no
 // effect otherwise
 CSS_PROP_(
     vertical-align,
     vertical_align,
     VerticalAlign,
     0,
     "",
     VARIANT_HKLP | VARIANT_CALC,
-    kVerticalAlignKTable,
-    eStyleAnimType_Coord)
+    kVerticalAlignKTable)
 CSS_PROP_(
     visibility,
     visibility,
     Visibility,
     0,
     "",
     VARIANT_HK,
-    kVisibilityKTable,
-    eStyleAnimType_Discrete)  // reflow for collapse
+    kVisibilityKTable)  // reflow for collapse
 CSS_PROP_(
     white-space,
     white_space,
     WhiteSpace,
     0,
     "",
     VARIANT_HK,
-    kWhitespaceKTable,
-    eStyleAnimType_Discrete)
+    kWhitespaceKTable)
 CSS_PROP_(
     width,
     width,
     Width,
     CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     VARIANT_AHKLP | VARIANT_CALC,
-    kWidthKTable,
-    eStyleAnimType_Coord)
+    kWidthKTable)
 CSS_PROP_(
     will-change,
     will_change,
     WillChange,
-    CSS_PROPERTY_PARSE_FUNCTION |
-        CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
+    CSS_PROPERTY_PARSE_FUNCTION,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Discrete)
+    nullptr)
 CSS_PROP_(
     -moz-window-dragging,
     _moz_window_dragging,
     CSS_PROP_DOMPROP_PREFIXED(WindowDragging),
     0,
     "",
     VARIANT_HK,
-    kWindowDraggingKTable,
-    eStyleAnimType_Discrete)
+    kWindowDraggingKTable)
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     -moz-window-opacity,
     _moz_window_opacity,
     CSS_PROP_DOMPROP_PREFIXED(WindowOpacity),
-    CSS_PROPERTY_INTERNAL | 0,
+    CSS_PROPERTY_INTERNAL,
     "",
     VARIANT_HN,
-    nullptr,
-    eStyleAnimType_float)
+    nullptr)
 CSS_PROP_(
     -moz-window-shadow,
     _moz_window_shadow,
     CSS_PROP_DOMPROP_PREFIXED(WindowShadow),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME,
     "",
     VARIANT_HK,
-    kWindowShadowKTable,
-    eStyleAnimType_None)
+    kWindowShadowKTable)
 CSS_PROP_(
     -moz-window-transform,
     _moz_window_transform,
     CSS_PROP_DOMPROP_PREFIXED(WindowTransform),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     0,
-    nullptr,
-    eStyleAnimType_Custom)
+    nullptr)
 CSS_PROP_(
     -moz-window-transform-origin,
     _moz_window_transform_origin,
     CSS_PROP_DOMPROP_PREFIXED(WindowTransformOrigin),
     CSS_PROPERTY_INTERNAL |
         CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH,
     "",
     0,
-    kImageLayerPositionKTable,
-    eStyleAnimType_Custom)
+    kImageLayerPositionKTable)
 #endif // CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_(
     word-break,
     word_break,
     WordBreak,
     0,
     "",
     VARIANT_HK,
-    kWordBreakKTable,
-    eStyleAnimType_Discrete)
+    kWordBreakKTable)
 CSS_PROP_(
     word-spacing,
     word_spacing,
     WordSpacing,
     0,
     "",
     VARIANT_HLP | VARIANT_NORMAL | VARIANT_CALC,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 CSS_PROP_(
     writing-mode,
     writing_mode,
     WritingMode,
     0,
     "",
     VARIANT_HK,
-    kWritingModeKTable,
-    eStyleAnimType_Discrete)
+    kWritingModeKTable)
 CSS_PROP_(
     z-index,
     z_index,
     ZIndex,
     0,
     "",
     VARIANT_AHI,
-    nullptr,
-    eStyleAnimType_Coord)
+    nullptr)
 
 #undef CSS_PROP_
 
 #ifdef DEFINED_CSS_PROP_SHORTHAND
 #undef CSS_PROP_SHORTHAND
 #undef DEFINED_CSS_PROP_SHORTHAND
 #endif
 
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -180,30 +180,31 @@ CheckServoCSSPropList()
 #include "mozilla/ServoCSSPropList.h"
 #undef CSS_PROP_ALIAS
 #undef CSS_PROP_SHORTHAND
 #undef CSS_PROP_LONGHAND
   };
 
   const uint32_t kServoFlags =
     CSS_PROPERTY_ENABLED_MASK | CSS_PROPERTY_INTERNAL |
-    CSS_PROPERTY_PARSE_INACCESSIBLE;
+    CSS_PROPERTY_PARSE_INACCESSIBLE | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH |
+    CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR;
   bool mismatch = false;
   for (size_t i = 0; i < eCSSProperty_COUNT_with_aliases; i++) {
     auto& geckoData = sGeckoProps[i];
     auto& servoData = sServoProps[i];
     const char* name = nsCSSProps::GetStringValue(geckoData.mID).get();
     if (geckoData.mID != servoData.mID) {
       printf_stderr("Order mismatches: gecko: %s, servo: %s\n",
                     name, nsCSSProps::GetStringValue(servoData.mID).get());
       mismatch = true;
       continue;
     }
     if ((geckoData.mFlags & kServoFlags) != servoData.mFlags) {
-      printf_stderr("Enabled flags of %s mismatch\n", name);
+      printf_stderr("Flags of %s mismatch\n", name);
       mismatch = true;
     }
     if (strcmp(geckoData.mPref, servoData.mPref) != 0) {
       printf_stderr("Pref of %s mismatches\n", name);
       mismatch = true;
     }
   }
 
@@ -2298,17 +2299,17 @@ nsCSSProps::ValueToKeyword(int32_t aValu
   } else {
     return nsCSSKeywords::GetStringValue(keyword);
   }
 }
 
 /* static */ const KTableEntry* const
 nsCSSProps::kKeywordTableTable[eCSSProperty_COUNT_no_shorthands] = {
   #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \
-                   kwtable_, ...) kwtable_,
+                   kwtable_) kwtable_,
   #include "nsCSSPropList.h"
   #undef CSS_PROP
 };
 
 const nsCString&
 nsCSSProps::LookupPropertyValue(nsCSSPropertyID aProp, int32_t aValue)
 {
   MOZ_ASSERT(aProp >= 0 && aProp < eCSSProperty_COUNT,
@@ -2341,25 +2342,16 @@ bool nsCSSProps::GetColorName(int32_t aP
     nsCSSKeywords::AddRefTable();
     aStr = nsCSSKeywords::GetStringValue(keyword);
     nsCSSKeywords::ReleaseTable();
     rv = true;
   }
   return rv;
 }
 
-const nsStyleAnimType
-nsCSSProps::kAnimTypeTable[eCSSProperty_COUNT_no_shorthands] = {
-#define CSS_PROP(name_, id_, method_, flags_, pref_, \
-                 parsevariant_, kwtable_, animtype_) \
-  animtype_,
-#include "nsCSSPropList.h"
-#undef CSS_PROP
-};
-
 const uint32_t nsCSSProps::kFlagsTable[eCSSProperty_COUNT] = {
 #define CSS_PROP(name_, id_, method_, flags_, ...) flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP
 #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP_SHORTHAND
 };
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -119,18 +119,16 @@
 #define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT)
 #define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER)
 #define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT)
 #define VARIANT_IMAGE   (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \
                         VARIANT_IMAGE_RECT | VARIANT_ELEMENT)
 
 // Flags for the kFlagsTable bitfield (flags_ in nsCSSPropList.h)
 
-#define CSS_PROPERTY_VALUE_LIST_USES_COMMAS       (1<<1) /* otherwise spaces */
-
 // Define what mechanism the CSS parser uses for parsing the property.
 // See CSSParserImpl::ParseProperty(nsCSSPropertyID).  Don't use 0 so that
 // we can verify that every property sets one of the values.
 #define CSS_PROPERTY_PARSE_PROPERTY_MASK          (7<<9)
 #define CSS_PROPERTY_PARSE_INACCESSIBLE           (1<<9)
 #define CSS_PROPERTY_PARSE_FUNCTION               (2<<9)
 
 // See CSSParserImpl::ParseSingleValueProperty and comment above
@@ -171,66 +169,16 @@ static_assert((CSS_PROPERTY_PARSE_PROPER
 // This property can be animated on the compositor.
 #define CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR    (1<<27)
 
 // This property is an internal property that is not represented
 // in the DOM.  Properties with this flag must be defined in an #ifndef
 // CSS_PROP_LIST_EXCLUDE_INTERNAL section of nsCSSPropList.h.
 #define CSS_PROPERTY_INTERNAL                     (1<<28)
 
-/**
- * Types of animatable values.
- */
-enum nsStyleAnimType {
-  // requires a custom implementation in
-  // StyleAnimationValue::ExtractComputedValue
-  eStyleAnimType_Custom,
-
-  // nsStyleCoord with animatable values
-  eStyleAnimType_Coord,
-
-  // same as Coord, except for one side of an nsStyleSides
-  // listed in the same order as the NS_STYLE_* constants
-  eStyleAnimType_Sides_Top,
-  eStyleAnimType_Sides_Right,
-  eStyleAnimType_Sides_Bottom,
-  eStyleAnimType_Sides_Left,
-
-  // similar, but for the *pair* of coord members of an nsStyleCorners
-  // for the relevant corner
-  eStyleAnimType_Corner_TopLeft,
-  eStyleAnimType_Corner_TopRight,
-  eStyleAnimType_Corner_BottomRight,
-  eStyleAnimType_Corner_BottomLeft,
-
-  // nscoord values
-  eStyleAnimType_nscoord,
-
-  // float values
-  eStyleAnimType_float,
-
-  // nscolor values
-  eStyleAnimType_Color,
-
-  // StyleComplexColor values
-  eStyleAnimType_ComplexColor,
-
-  // nsStyleSVGPaint values
-  eStyleAnimType_PaintServer,
-
-  // RefPtr<nsCSSShadowArray> values
-  eStyleAnimType_Shadow,
-
-  // discrete values
-  eStyleAnimType_Discrete,
-
-  // property not animatable
-  eStyleAnimType_None
-};
-
 class nsCSSProps {
 public:
   typedef mozilla::CSSEnabledState EnabledState;
 
   struct KTableEntry
   {
     // KTableEntry objects can be initialized either with an int16_t value
     // or a value of an enumeration type that can fit within an int16_t.
@@ -335,17 +283,16 @@ public:
   static const nsCString& ValueToKeyword(T aValue, const KTableEntry aTable[])
   {
     static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
                   "aValue must be an enum that fits within KTableEntry::mValue");
     return ValueToKeyword(static_cast<int16_t>(aValue), aTable);
   }
 
   static const KTableEntry* const kKeywordTableTable[eCSSProperty_COUNT_no_shorthands];
-  static const nsStyleAnimType kAnimTypeTable[eCSSProperty_COUNT_no_shorthands];
 
 private:
   static const uint32_t        kFlagsTable[eCSSProperty_COUNT];
 
 public:
   static inline bool PropHasFlags(nsCSSPropertyID aProperty, uint32_t aFlags)
   {
     MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -45,16 +45,17 @@
 #include "nsThreadUtils.h"
 #include "nspr.h"
 #include "runnable_utils.h"
 #include "srtp.h"
 #include "transportflow.h"
 #include "transportlayer.h"
 #include "transportlayerdtls.h"
 #include "transportlayerice.h"
+#include "Tracing.h"
 
 #include "webrtc/base/bind.h"
 #include "webrtc/base/keep_ref_until_done.h"
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/include/i420_buffer_pool.h"
 #include "webrtc/common_video/include/video_frame_buffer.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 
@@ -1926,22 +1927,23 @@ MediaPipelineTransmit::PipelineListener:
     LOGTAG,
     "MediaPipeline::NotifyRealtimeTrackData() listener=%p, offset=%" PRId64
     ", duration=%" PRId64,
     this,
     aOffset,
     aMedia.GetDuration());
 
   if (aMedia.GetType() == MediaSegment::VIDEO) {
+    TRACE_COMMENT("Video");
     // We have to call the upstream NotifyRealtimeTrackData and
     // MediaStreamVideoSink will route them to SetCurrentFrames.
     MediaStreamVideoSink::NotifyRealtimeTrackData(aGraph, aOffset, aMedia);
     return;
   }
-
+  TRACE_COMMENT("Audio");
   NewData(aMedia, aGraph->GraphRate());
 }
 
 void
 MediaPipelineTransmit::PipelineListener::NotifyQueuedChanges(
   MediaStreamGraph* aGraph,
   StreamTime aOffset,
   const MediaSegment& aQueuedMedia)
@@ -2017,16 +2019,17 @@ MediaPipelineTransmit::PipelineListener:
 
     const AudioSegment* audio = static_cast<const AudioSegment*>(&aMedia);
     for (AudioSegment::ConstChunkIterator iter(*audio); !iter.IsEnded();
          iter.Next()) {
       mAudioProcessing->QueueAudioChunk(aRate, *iter, mEnabled);
     }
   } else {
     const VideoSegment* video = static_cast<const VideoSegment*>(&aMedia);
+
     for (VideoSegment::ConstChunkIterator iter(*video); !iter.IsEnded();
          iter.Next()) {
       mConverter->QueueVideoChunk(*iter, !mEnabled);
     }
   }
 }
 
 void
@@ -2229,16 +2232,17 @@ private:
   ~PipelineListener()
   {
     NS_ReleaseOnMainThreadSystemGroup("MediaPipeline::mConduit",
                                       mConduit.forget());
   }
 
   void NotifyPullImpl(StreamTime aDesiredTime)
   {
+    TRACE();
     uint32_t samplesPer10ms = mRate / 100;
 
     // mSource's rate is not necessarily the same as the graph rate, since there
     // are sample-rate constraints on the inbound audio: only 16, 32, 44.1 and
     // 48kHz are supported. The audio frames we get here is going to be
     // resampled when inserted into the graph.
     TrackTicks desired = mSource->TimeToTicksRoundUp(mRate, aDesiredTime);
     TrackTicks framesNeeded = desired - mPlayedTicks;
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1794,16 +1794,24 @@ nsStandardURL::SetUsername(const nsACStr
 
 nsresult
 nsStandardURL::SetPassword(const nsACString &input)
 {
     ENSURE_MUTABLE();
 
     const nsPromiseFlatCString &password = PromiseFlatCString(input);
 
+    auto clearedPassword = MakeScopeExit([&password, this]() {
+        // Check that if this method is called with the empty string then the
+        // password is definitely cleared when exiting this method.
+        if (password.IsEmpty()) {
+            MOZ_DIAGNOSTIC_ASSERT(this->Password().IsEmpty());
+        }
+    });
+
     LOG(("nsStandardURL::SetPassword [password=%s]\n", password.get()));
 
     if (mURLType == URLTYPE_NO_AUTHORITY) {
         if (password.IsEmpty())
             return NS_OK;
         NS_WARNING("cannot set password on no-auth url");
         return NS_ERROR_UNEXPECTED;
     }
--- a/python/mozbuild/mozbuild/virtualenv.py
+++ b/python/mozbuild/mozbuild/virtualenv.py
@@ -483,17 +483,17 @@ class VirtualenvManager(object):
         args = [
             'install',
             '--use-wheel',
             package,
         ]
 
         return self._run_pip(args)
 
-    def install_pip_requirements(self, path, require_hashes=True):
+    def install_pip_requirements(self, path, require_hashes=True, quiet=False):
         """Install a pip requirements.txt file.
 
         The supplied path is a text file containing pip requirement
         specifiers.
 
         If require_hashes is True, each specifier must contain the
         expected hash of the downloaded package. See:
         https://pip.pypa.io/en/stable/reference/pip_install/#hash-checking-mode
@@ -506,16 +506,19 @@ class VirtualenvManager(object):
             'install',
             '--requirement',
             path,
         ]
 
         if require_hashes:
             args.append('--require-hashes')
 
+        if quiet:
+            args.append('--quiet')
+
         return self._run_pip(args)
 
     def _run_pip(self, args):
         # It's tempting to call pip natively via pip.main(). However,
         # the current Python interpreter may not be the virtualenv python.
         # This will confuse pip and cause the package to attempt to install
         # against the executing interpreter. By creating a new process, we
         # force the virtualenv's interpreter to be used and all is well.
--- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
+++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
@@ -173,17 +173,19 @@ var Authentication = {
 
     try {
       // Required here since we don't go through the real login page
       await FxAccountsConfig.ensureConfigured();
 
       let client = new FxAccountsClient();
       let credentials = await client.signIn(account.username, account.password, true);
       await fxAccounts.setSignedInUser(credentials);
-      await this._completeVerification(account.username);
+      if (!credentials.verified) {
+        await this._completeVerification(account.username);
+      }
 
       if (Weave.Status.login !== Weave.LOGIN_SUCCEEDED) {
         Logger.logInfo("Logging into Weave.");
         await Weave.Service.login();
       }
       return true;
     } catch (error) {
       throw new Error("signIn() failed with: " + error.message);
--- a/services/sync/tps/extensions/tps/resource/modules/bookmarks.jsm
+++ b/services/sync/tps/extensions/tps/resource/modules/bookmarks.jsm
@@ -75,16 +75,17 @@ function PlacesItem(props) {
 PlacesItem.prototype = {
   // an array of possible root folders for places items
   _bookmarkFolders: {
     "places": PlacesUtils.bookmarks.rootGuid,
     "menu": PlacesUtils.bookmarks.menuGuid,
     "tags": PlacesUtils.bookmarks.tagsGuid,
     "unfiled": PlacesUtils.bookmarks.unfiledGuid,
     "toolbar": PlacesUtils.bookmarks.toolbarGuid,
+    "mobile": PlacesUtils.bookmarks.mobileGuid,
   },
 
   _typeMap: new Map([
     [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER, PlacesUtils.bookmarks.TYPE_FOLDER],
     [PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR, PlacesUtils.bookmarks.TYPE_SEPARATOR],
     [PlacesUtils.TYPE_X_MOZ_PLACE, PlacesUtils.bookmarks.TYPE_BOOKMARK],
   ]),
 
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -38,27 +38,29 @@
         alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
         spec=maybe_logical_spec(side, "style"),
         flags="APPLIES_TO_FIRST_LETTER",
         animation_value_type="discrete" if not is_logical else "none",
         logical=is_logical,
         needs_context=False,
     )}
 
-    ${helpers.predefined_type("border-%s-width" % side_name,
-                              "BorderSideWidth",
-                              "::values::computed::NonNegativeLength::new(3.)",
-                              computed_type="::values::computed::NonNegativeLength",
-                              alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
-                              spec=maybe_logical_spec(side, "width"),
-                              animation_value_type="NonNegativeLength",
-                              logical=is_logical,
-                              flags="APPLIES_TO_FIRST_LETTER",
-                              allow_quirks=not is_logical,
-                              servo_restyle_damage = "reflow rebuild_and_reflow_inline")}
+    ${helpers.predefined_type(
+        "border-%s-width" % side_name,
+        "BorderSideWidth",
+        "::values::computed::NonNegativeLength::new(3.)",
+        computed_type="::values::computed::NonNegativeLength",
+        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
+        spec=maybe_logical_spec(side, "width"),
+        animation_value_type="NonNegativeLength",
+        logical=is_logical,
+        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
+        allow_quirks=not is_logical,
+        servo_restyle_damage="reflow rebuild_and_reflow_inline"
+    )}
 % endfor
 
 ${helpers.gecko_keyword_conversion(Keyword('border-style',
                                    "none solid double dotted dashed hidden groove ridge inset outset"),
                                    type="::values::specified::BorderStyle")}
 
 // FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
 % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -398,24 +398,28 @@
     gecko_pref="layout.css.scroll-snap.enabled",
     spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)",
     animation_value_type="discrete",
     allow_empty="NotInitial"
 )}
 
 <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
 
-${helpers.predefined_type("transform", "Transform",
-                          "generics::transform::Transform::none()",
-                          extra_prefixes=transform_extra_prefixes,
-                          animation_value_type="ComputedValue",
-                          gecko_ffi_name="mSpecifiedTransform",
-                          flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
-                          spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
-                          servo_restyle_damage = "reflow_out_of_flow")}
+${helpers.predefined_type(
+    "transform",
+    "Transform",
+    "generics::transform::Transform::none()",
+    extra_prefixes=transform_extra_prefixes,
+    animation_value_type="ComputedValue",
+    gecko_ffi_name="mSpecifiedTransform",
+    flags="CREATES_STACKING_CONTEXT FIXPOS_CB \
+           GETCS_NEEDS_LAYOUT_FLUSH CAN_ANIMATE_ON_COMPOSITOR",
+    spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
+    servo_restyle_damage="reflow_out_of_flow"
+)}
 
 ${helpers.predefined_type("rotate", "Rotate",
                           "generics::transform::Rotate::None",
                           animation_value_type="ComputedValue",
                           boxed=True,
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           gecko_pref="layout.css.individual-transform.enabled",
                           spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
@@ -425,24 +429,27 @@
                           "generics::transform::Scale::None",
                           animation_value_type="ComputedValue",
                           boxed=True,
                           flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
                           gecko_pref="layout.css.individual-transform.enabled",
                           spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
                           servo_restyle_damage = "reflow_out_of_flow")}
 
-${helpers.predefined_type("translate", "Translate",
-                          "generics::transform::Translate::None",
-                          animation_value_type="ComputedValue",
-                          boxed=True,
-                          flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
-                          gecko_pref="layout.css.individual-transform.enabled",
-                          spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
-                          servo_restyle_damage = "reflow_out_of_flow")}
+${helpers.predefined_type(
+    "translate",
+    "Translate",
+    "generics::transform::Translate::None",
+    animation_value_type="ComputedValue",
+    boxed=True,
+    flags="CREATES_STACKING_CONTEXT FIXPOS_CB GETCS_NEEDS_LAYOUT_FLUSH",
+    gecko_pref="layout.css.individual-transform.enabled",
+    spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
+    servo_restyle_damage="reflow_out_of_flow"
+)}
 
 // CSSOM View Module
 // https://www.w3.org/TR/cssom-view-1/
 ${helpers.single_keyword("scroll-behavior",
                          "auto smooth",
                          gecko_pref="layout.css.scroll-behavior.property-enabled",
                          products="gecko",
                          spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
@@ -524,24 +531,27 @@
     gecko_ffi_name="mChildPerspective",
     spec="https://drafts.csswg.org/css-transforms/#perspective",
     extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="AnimatedPerspective",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
-${helpers.predefined_type("perspective-origin",
-                          "position::Position",
-                          "computed::position::Position::center()",
-                          boxed=True,
-                          extra_prefixes=transform_extra_prefixes,
-                          spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
-                          animation_value_type="ComputedValue",
-                          servo_restyle_damage = "reflow_out_of_flow")}
+${helpers.predefined_type(
+    "perspective-origin",
+    "position::Position",
+    "computed::position::Position::center()",
+    boxed=True,
+    extra_prefixes=transform_extra_prefixes,
+    spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
+    flags="GETCS_NEEDS_LAYOUT_FLUSH",
+    animation_value_type="ComputedValue",
+    servo_restyle_damage="reflow_out_of_flow"
+)}
 
 ${helpers.single_keyword("backface-visibility",
                          "visible hidden",
                          spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
                          extra_prefixes=transform_extra_prefixes,
                          animation_value_type="discrete")}
 
 ${helpers.single_keyword("transform-box",
@@ -560,25 +570,28 @@
     spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
     needs_context=False,
     extra_prefixes=transform_extra_prefixes,
     flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
     animation_value_type="discrete",
     servo_restyle_damage = "reflow_out_of_flow",
 )}
 
-${helpers.predefined_type("transform-origin",
-                          "TransformOrigin",
-                          "computed::TransformOrigin::initial_value()",
-                          animation_value_type="ComputedValue",
-                          extra_prefixes=transform_extra_prefixes,
-                          gecko_ffi_name="mTransformOrigin",
-                          boxed=True,
-                          spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
-                          servo_restyle_damage = "reflow_out_of_flow")}
+${helpers.predefined_type(
+    "transform-origin",
+    "TransformOrigin",
+    "computed::TransformOrigin::initial_value()",
+    animation_value_type="ComputedValue",
+    extra_prefixes=transform_extra_prefixes,
+    gecko_ffi_name="mTransformOrigin",
+    boxed=True,
+    flags="GETCS_NEEDS_LAYOUT_FLUSH",
+    spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
+    servo_restyle_damage="reflow_out_of_flow"
+)}
 
 ${helpers.predefined_type("contain",
                           "Contain",
                           "specified::Contain::empty()",
                           animation_value_type="discrete",
                           products="gecko",
                           flags="FIXPOS_CB",
                           gecko_pref="layout.css.contain.enabled",
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -2,23 +2,26 @@
  * 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 // Box-shadow, etc.
 <% data.new_style_struct("Effects", inherited=False) %>
 
-${helpers.predefined_type("opacity",
-                          "Opacity",
-                          "1.0",
-                          animation_value_type="ComputedValue",
-                          flags="CREATES_STACKING_CONTEXT APPLIES_TO_PLACEHOLDER",
-                          spec="https://drafts.csswg.org/css-color/#opacity",
-                          servo_restyle_damage = "reflow_out_of_flow")}
+${helpers.predefined_type(
+    "opacity",
+    "Opacity",
+    "1.0",
+    animation_value_type="ComputedValue",
+    flags="CREATES_STACKING_CONTEXT APPLIES_TO_PLACEHOLDER \
+           CAN_ANIMATE_ON_COMPOSITOR",
+    spec="https://drafts.csswg.org/css-color/#opacity",
+    servo_restyle_damage = "reflow_out_of_flow"
+)}
 
 ${helpers.predefined_type(
     "box-shadow",
     "BoxShadow",
     None,
     vector=True,
     animation_value_type="AnimatedBoxShadowList",
     extra_prefixes="webkit",
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -1,23 +1,26 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
-${helpers.predefined_type("line-height",
-                          "LineHeight",
-                          "computed::LineHeight::normal()",
-                          animation_value_type="LineHeight",
-                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
-                          spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
-                          servo_restyle_damage = "reflow")}
+${helpers.predefined_type(
+    "line-height",
+    "LineHeight",
+    "computed::LineHeight::normal()",
+    animation_value_type="LineHeight",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE \
+           APPLIES_TO_PLACEHOLDER GETCS_NEEDS_LAYOUT_FLUSH",
+    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
+    servo_restyle_damage="reflow"
+)}
 
 // CSS Text Module Level 3
 
 // TODO(pcwalton): `full-width`
 ${helpers.single_keyword("text-transform",
                          "none capitalize uppercase lowercase",
                          extra_gecko_values="full-width",
                          animation_value_type="discrete",
--- a/servo/components/style/properties/longhand/margin.mako.rs
+++ b/servo/components/style/properties/longhand/margin.mako.rs
@@ -7,17 +7,22 @@
 <% data.new_style_struct("Margin", inherited=False) %>
 
 % for side in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-margin-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-margin-%s" % side[1]
     %>
-    ${helpers.predefined_type("margin-%s" % side[0], "LengthOrPercentageOrAuto",
-                              "computed::LengthOrPercentageOrAuto::Length(computed::Length::new(0.))",
-                              alias=maybe_moz_logical_alias(product, side, "-moz-margin-%s"),
-                              allow_quirks=not side[1],
-                              animation_value_type="ComputedValue", logical = side[1], spec = spec,
-                              flags="APPLIES_TO_FIRST_LETTER",
-                              allowed_in_page_rule=True,
-                              servo_restyle_damage = "reflow")}
+    ${helpers.predefined_type(
+        "margin-%s" % side[0],
+        "LengthOrPercentageOrAuto",
+        "computed::LengthOrPercentageOrAuto::Length(computed::Length::new(0.))",
+        alias=maybe_moz_logical_alias(product, side, "-moz-margin-%s"),
+        allow_quirks=not side[1],
+        animation_value_type="ComputedValue",
+        logical=side[1],
+        spec=spec,
+        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
+        allowed_in_page_rule=True,
+        servo_restyle_damage="reflow"
+    )}
 % endfor
--- a/servo/components/style/properties/longhand/padding.mako.rs
+++ b/servo/components/style/properties/longhand/padding.mako.rs
@@ -9,18 +9,21 @@
 // APPLIES_TO_PLACEHOLDER so we can set it in UA  stylesheets.  But we use a
 // !important value there, so pages can't set it.
 % for side in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
-    ${helpers.predefined_type("padding-%s" % side[0], "NonNegativeLengthOrPercentage",
-                              "computed::NonNegativeLengthOrPercentage::zero()",
-                              alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
-                              animation_value_type="NonNegativeLengthOrPercentage",
-                              logical = side[1],
-                              spec = spec,
-                              flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_PLACEHOLDER",
-                              allow_quirks=not side[1],
-                              servo_restyle_damage = "reflow rebuild_and_reflow_inline")}
+    ${helpers.predefined_type(
+        "padding-%s" % side[0],
+        "NonNegativeLengthOrPercentage",
+        "computed::NonNegativeLengthOrPercentage::zero()",
+        alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
+        animation_value_type="NonNegativeLengthOrPercentage",
+        logical=side[1],
+        spec=spec,
+        flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_PLACEHOLDER GETCS_NEEDS_LAYOUT_FLUSH",
+        allow_quirks=not side[1],
+        servo_restyle_damage="reflow rebuild_and_reflow_inline"
+    )}
 % endfor
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -5,28 +5,38 @@
 <%! from data import to_rust_ident %>
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>
 
 <% data.new_style_struct("Position", inherited=False) %>
 
 // "top" / "left" / "bottom" / "right"
 % for side in PHYSICAL_SIDES:
-    ${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
-                              "computed::LengthOrPercentageOrAuto::Auto",
-                              spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
-                              animation_value_type="ComputedValue",
-                              allow_quirks=True, servo_restyle_damage = "reflow_out_of_flow")}
+    ${helpers.predefined_type(
+        side,
+        "LengthOrPercentageOrAuto",
+        "computed::LengthOrPercentageOrAuto::Auto",
+        spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
+        flags="GETCS_NEEDS_LAYOUT_FLUSH",
+        animation_value_type="ComputedValue",
+        allow_quirks=True,
+        servo_restyle_damage="reflow_out_of_flow"
+    )}
 % endfor
 // offset-* logical properties, map to "top" / "left" / "bottom" / "right"
 % for side in LOGICAL_SIDES:
-    ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
-                              "computed::LengthOrPercentageOrAuto::Auto",
-                              spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
-                              animation_value_type="ComputedValue", logical=True)}
+    ${helpers.predefined_type(
+        "offset-%s" % side,
+        "LengthOrPercentageOrAuto",
+        "computed::LengthOrPercentageOrAuto::Auto",
+        spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
+        flags="GETCS_NEEDS_LAYOUT_FLUSH",
+        animation_value_type="ComputedValue",
+        logical=True,
+    )}
 % endfor
 
 #[cfg(feature = "gecko")]
 macro_rules! impl_align_conversions {
     ($name: path) => {
         impl From<u8> for $name {
             fn from(bits: u8) -> $name {
                 $name(::values::specified::align::AlignFlags::from_bits(bits)
@@ -208,17 +218,18 @@ macro_rules! impl_align_conversions {
             size,
             "MozLength",
             "computed::MozLength::auto()",
             parse_function,
             logical=logical,
             allow_quirks=not logical,
             spec=spec % size,
             animation_value_type="MozLength",
-            servo_restyle_damage = "reflow"
+            flags="GETCS_NEEDS_LAYOUT_FLUSH",
+            servo_restyle_damage="reflow"
         )}
         // min-width, min-height, min-block-size, min-inline-size,
         ${helpers.predefined_type(
             "min-%s" % size,
             "MozLength",
             "computed::MozLength::auto()",
             parse_function,
             logical=logical,
@@ -313,23 +324,26 @@ macro_rules! impl_align_conversions {
     ${helpers.predefined_type("grid-auto-%ss" % kind,
                               "TrackSize",
                               "Default::default()",
                               animation_value_type="discrete",
                               spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                               products="gecko",
                               boxed=True)}
 
-    ${helpers.predefined_type("grid-template-%ss" % kind,
-                              "GridTemplateComponent",
-                              "specified::GenericGridTemplateComponent::None",
-                              products="gecko",
-                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
-                              boxed=True,
-                              animation_value_type="discrete")}
+    ${helpers.predefined_type(
+        "grid-template-%ss" % kind,
+        "GridTemplateComponent",
+        "specified::GenericGridTemplateComponent::None",
+        products="gecko",
+        spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
+        boxed=True,
+        flags="GETCS_NEEDS_LAYOUT_FLUSH",
+        animation_value_type="discrete"
+    )}
 
 % endfor
 
 ${helpers.predefined_type("grid-auto-flow",
                           "GridAutoFlow",
                           initial_value="computed::GridAutoFlow::row()",
                           products="gecko",
                           animation_value_type="discrete",
--- a/servo/components/style/properties/longhand/ui.mako.rs
+++ b/servo/components/style/properties/longhand/ui.mako.rs
@@ -43,31 +43,39 @@
 
 // TODO(bug 1419695) This should be hidden from content.
 ${helpers.predefined_type("-moz-window-opacity", "Opacity", "1.0", products="gecko",
                           gecko_ffi_name="mWindowOpacity",
                           animation_value_type="ComputedValue",
                           spec="None (Nonstandard internal property)")}
 
 // TODO(bug 1419695) This should be hidden from content.
-${helpers.predefined_type("-moz-window-transform", "Transform",
-                          "generics::transform::Transform::none()",
-                          products="gecko", gecko_ffi_name="mSpecifiedWindowTransform",
-                          animation_value_type="ComputedValue",
-                          spec="None (Nonstandard internal property)")}
+${helpers.predefined_type(
+    "-moz-window-transform",
+    "Transform",
+    "generics::transform::Transform::none()",
+    products="gecko",
+    gecko_ffi_name="mSpecifiedWindowTransform",
+    flags="GETCS_NEEDS_LAYOUT_FLUSH",
+    animation_value_type="ComputedValue",
+    spec="None (Nonstandard internal property)"
+)}
 
 // TODO(bug 1419695) This should be hidden from content.
-${helpers.predefined_type("-moz-window-transform-origin",
-                          "TransformOrigin",
-                          "computed::TransformOrigin::initial_value()",
-                          animation_value_type="ComputedValue",
-                          gecko_ffi_name="mWindowTransformOrigin",
-                          products="gecko",
-                          boxed=True,
-                          spec="None (Nonstandard internal property)")}
+${helpers.predefined_type(
+    "-moz-window-transform-origin",
+    "TransformOrigin",
+    "computed::TransformOrigin::initial_value()",
+    animation_value_type="ComputedValue",
+    gecko_ffi_name="mWindowTransformOrigin",
+    products="gecko",
+    boxed=True,
+    flags="GETCS_NEEDS_LAYOUT_FLUSH",
+    spec="None (Nonstandard internal property)"
+)}
 
 // TODO(emilio): Probably also should be hidden from content.
 ${helpers.predefined_type("-moz-force-broken-image-icon",
                           "MozForceBrokenImageIcon",
                           "computed::MozForceBrokenImageIcon::false_value()",
                           animation_value_type="discrete",
                           products="gecko",
                           spec="None (Nonstandard Firefox-only property)")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -776,16 +776,25 @@ bitflags! {
         /// absolutely positioned elements.
         const ABSPOS_CB = 1 << 2;
         /// This longhand property applies to ::first-letter.
         const APPLIES_TO_FIRST_LETTER = 1 << 3;
         /// This longhand property applies to ::first-line.
         const APPLIES_TO_FIRST_LINE = 1 << 4;
         /// This longhand property applies to ::placeholder.
         const APPLIES_TO_PLACEHOLDER = 1 << 5;
+
+        /* The following flags are currently not used in Rust code, they
+         * only need to be listed in corresponding properties so that
+         * they can be checked in the C++ side via ServoCSSPropList.h. */
+        /// This property's getComputedStyle implementation requires layout
+        /// to be flushed.
+        const GETCS_NEEDS_LAYOUT_FLUSH = 0;
+        /// This property can be animated on the compositor.
+        const CAN_ANIMATE_ON_COMPOSITOR = 0;
     }
 }
 
 /// An identifier for a given longhand property.
 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
 #[repr(u16)]
 pub enum LonghandId {
     % for i, property in enumerate(data.longhands):
--- a/taskcluster/ci/source-test/doc.yml
+++ b/taskcluster/ci/source-test/doc.yml
@@ -13,17 +13,17 @@ generate:
         artifacts:
             - type: file
               name: public/docs.tar.gz
               path: /builds/worker/checkouts/gecko/docs-out/main.tar.gz
     run:
         using: run-task
         command: >
             cd /builds/worker/checkouts/gecko &&
-            ./mach doc --outdir docs-out --no-open --archive
+            ./mach doc --outdir docs-out --no-open --no-serve --archive
         sparse-profile: sphinx-docs
     optimization:
         skip-unless-schedules: [docs]
 
 upload:
     description: Generate and upload the Sphinx documentation
     platform: lint/opt
     treeherder:
@@ -33,14 +33,14 @@ upload:
     run-on-projects: [mozilla-central]
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
         taskcluster-proxy: true
     run:
         using: run-task
-        command: cd /builds/worker/checkouts/gecko && ./mach doc --upload --no-open
+        command: cd /builds/worker/checkouts/gecko && ./mach doc --upload --no-open --no-serve
         sparse-profile: sphinx-docs
     scopes:
         - secrets:get:project/releng/gecko/build/level-{level}/gecko-docs-upload
     optimization:
         skip-unless-schedules: [docs]
--- a/testing/mozharness/configs/merge_day/release_to_esr.py
+++ b/testing/mozharness/configs/merge_day/release_to_esr.py
@@ -1,15 +1,12 @@
 import os
 
 ABS_WORK_DIR = os.path.join(os.getcwd(), "build")
-NEW_ESR_REPO = "https://hg.mozilla.org/releases/mozilla-esr59"
-# ESR-specific branding (logo) lives in the old repo:
-OLD_ESR_REPO = "https://hg.mozilla.org/releases/mozilla-esr52"
-OLD_ESR_CHANGESET = "df0931ac8b02"
+NEW_ESR_REPO = "https://hg.mozilla.org/releases/mozilla-esr60"
 
 config = {
     "log_name": "relese_to_esr",
     "version_files": [
         {"file": "browser/config/version.txt", "suffix": ""},
         {"file": "browser/config/version_display.txt", "suffix": ""},
         {"file": "config/milestone.txt", "suffix": ""},
     ],
@@ -39,15 +36,11 @@ config = {
     "tools_repo_url": "https://hg.mozilla.org/build/tools",
     "tools_repo_branch": "default",
     "from_repo_url": "https://hg.mozilla.org/releases/mozilla-release",
     "to_repo_url": NEW_ESR_REPO,
 
     "base_tag": "FIREFOX_ESR_%(major_version)s_BASE",
     "migration_behavior": "release_to_esr",
     "require_remove_locales": False,
-    "graft_patches": [
-        {"repo": OLD_ESR_REPO,
-         "changeset": OLD_ESR_CHANGESET},
-    ],
     "requires_head_merge": False,
     "pull_all_branches": False,
 }
--- a/testing/mozharness/scripts/merge_day/gecko_migration.py
+++ b/testing/mozharness/scripts/merge_day/gecko_migration.py
@@ -396,49 +396,24 @@ class GeckoMigration(MercurialScript, Ba
                 os.path.join(dirs['abs_to_dir'], "browser/locales/shipped-locales"),
                 self.config['remove_locales']
             )
         self.touch_clobber_file(dirs['abs_to_dir'])
 
     def release_to_esr(self, *args, **kwargs):
         """ mozilla-release -> mozilla-esrNN behavior. """
         dirs = self.query_abs_dirs()
-        for to_graft in self.config.get("graft_patches", []):
-            self.graft(repo=to_graft["repo"], changeset=to_graft["changeset"],
-                       cwd=dirs['abs_to_dir'])
         self.apply_replacements()
         self.touch_clobber_file(dirs['abs_to_dir'])
 
     def apply_replacements(self):
         dirs = self.query_abs_dirs()
         for f, from_, to in self.config["replacements"]:
             self.replace(os.path.join(dirs['abs_to_dir'], f), from_, to)
 
-    def graft(self, repo, changeset, cwd):
-        """Graft a Mercurial changeset from a remote repository."""
-        hg = self.query_exe("hg", return_type="list")
-        self.info("Pulling %s from %s" % (changeset, repo))
-        pull_cmd = hg + ["pull", "-r", changeset, repo]
-        status = self.run_command(
-            pull_cmd,
-            cwd=cwd,
-            error_list=HgErrorList,
-        )
-        if status != 0:
-            self.fatal("Cannot pull %s from %s properly" % (changeset, repo))
-        cmd = hg + ["graft", changeset]
-        self.info("Grafting %s from %s" % (changeset, repo))
-