Merge mozilla-central to inbound a=merge on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Tue, 12 Feb 2019 18:36:58 +0200
changeset 458715 c7fdf51b2b74
parent 458714 b1e2b327f134 (current diff)
parent 458703 c849fb69e2e7 (diff)
child 458716 13c2cd492b95
push id35546
push userrmaries@mozilla.com
push dateWed, 13 Feb 2019 04:27:59 +0000
treeherdermozilla-central@636d2c00234d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.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 inbound a=merge on a CLOSED TREE
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_connection-prompt.js
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1087,18 +1087,22 @@
 
           // XXXmano: this is a temporary workaround for bug 345399
           // We need to manually update the scroll buttons disabled state
           // if a tab was inserted to the overflow area or removed from it
           // without any scrolling and when the tabbar has already
           // overflowed.
           this.arrowScrollbox._updateScrollButtonsDisabledState();
 
-          // Preload the next about:newtab if there isn't one already.
-          gBrowser._createPreloadBrowser();
+          // If this browser isn't lazy (indicating it's probably created by
+          // session restore), preload the next about:newtab if we don't
+          // already have a preloaded browser.
+          if (tab.linkedPanel) {
+            gBrowser._createPreloadBrowser();
+          }
         ]]></body>
       </method>
 
       <method name="_canAdvanceToTab">
         <parameter name="aTab"/>
         <body>
         <![CDATA[
           return !aTab.closing;
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -92,8 +92,9 @@ skip-if = os == 'win' && processor == "x
 [browser_telemetry.js]
 # Skip this test on Android as FHR and Telemetry are separate systems there.
 skip-if = !healthreport || !telemetry || (os == 'linux' && debug) || (os == 'android')
 [browser_warning_permanent_private_browsing.js]
 [browser_containers_name_input.js]
 run-if = nightly_build # Containers is enabled only on Nightly
 [browser_fluent.js]
 [browser_hometab_restore_defaults.js]
+skip-if = debug #Bug 1517966
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -255,17 +255,18 @@ class UrlbarView {
   _alignPanel() {
     // Make the panel span the width of the window.
     let documentRect =
       this._getBoundsWithoutFlushing(this.document.documentElement);
     let width = documentRect.right - documentRect.left;
     this.panel.setAttribute("width", width);
 
     // Subtract two pixels for left and right borders on the panel.
-    this._mainContainer.style.maxWidth = (width - 2) + "px";
+    let contentWidth = width - 2;
+    this._mainContainer.style.maxWidth = contentWidth + "px";
 
     // Keep the popup items' site icons aligned with the input's identity
     // icon if it's not too far from the edge of the window.  We define
     // "too far" as "more than 30% of the window's width AND more than
     // 250px".
     let boundToCheck = this.window.RTL_UI ? "right" : "left";
     let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
     let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
@@ -284,20 +285,22 @@ class UrlbarView {
       let identityIcon = this.document.getElementById("identity-icon");
       let identityRect = this._getBoundsWithoutFlushing(identityIcon);
       let start = this.window.RTL_UI ?
                     documentRect.right - identityRect.right :
                     identityRect.left;
 
       this.panel.style.setProperty("--item-padding-start", Math.round(start) + "px");
       this.panel.style.setProperty("--item-padding-end", Math.round(endOffset) + "px");
+      contentWidth -= start + endOffset;
     } else {
       this.panel.style.removeProperty("--item-padding-start");
       this.panel.style.removeProperty("--item-padding-end");
     }
+    this.panel.style.setProperty("--item-content-width", Math.round(contentWidth) + "px");
   }
 
   _createRow(resultIndex) {
     let result = this._queryContext.results[resultIndex];
     let item = this._createElement("div");
     item.className = "urlbarView-row";
     item.setAttribute("resultIndex", resultIndex);
 
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -102,16 +102,25 @@
   background-image: url(chrome://browser/skin/bookmark.svg);
 }
 
 .urlbarView-row[type=remotetab] > .urlbarView-row-inner > .urlbarView-type-icon,
 #urlbar-results:not([actionoverride]) .urlbarView-row[type=switchtab] > .urlbarView-row-inner > .urlbarView-type-icon {
   background-image: url(chrome://browser/skin/tab.svg);
 }
 
+.urlbarView-title {
+  display: inline-block;
+  vertical-align: text-bottom;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  max-width: calc(var(--item-content-width) * .7 - 2 * (16px + @urlbarViewIconMarginEnd@));
+}
+
 .urlbarView-secondary::before {
   content: "\2014";
   color: var(--panel-disabled-color);
   margin: 0 .4em;
 }
 
 .urlbarView-title:empty + .urlbarView-secondary::before {
   display: none;
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -7,16 +7,17 @@
 */
 @import "resource://devtools/client/themes/variables.css";
 @import "resource://devtools/client/aboutdebugging-new/src/base.css";
 
 /*
 * Components
 */
 @import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/RuntimePage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectPage.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/ConnectSteps.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsForm.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/connect/NetworkLocationsList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
 @import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetPane.css";
--- a/devtools/client/aboutdebugging-new/src/actions/ui.js
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -8,22 +8,24 @@ const {
   ADB_ADDON_INSTALL_START,
   ADB_ADDON_INSTALL_SUCCESS,
   ADB_ADDON_INSTALL_FAILURE,
   ADB_ADDON_UNINSTALL_START,
   ADB_ADDON_UNINSTALL_SUCCESS,
   ADB_ADDON_UNINSTALL_FAILURE,
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
+  HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   PAGE_TYPES,
   SELECT_PAGE_FAILURE,
   SELECT_PAGE_START,
   SELECT_PAGE_SUCCESS,
   SELECTED_RUNTIME_ID_UPDATED,
+  SHOW_PROFILER_DIALOG,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 const NetworkLocationsModule = require("../modules/network-locations");
 const { adbAddon } = require("devtools/shared/adb/adb-addon");
 const { refreshUSBRuntimes } = require("../modules/usb-runtimes");
 
@@ -46,16 +48,21 @@ function selectPage(page, runtimeId) {
       }
 
       const currentPage = getState().ui.selectedPage;
       // Nothing to dispatch if the page is the same as the current page
       if (isSamePage(currentPage, page)) {
         return;
       }
 
+      // Stop showing the profiler dialog if we are navigating to another page.
+      if (getState().ui.showProfilerDialog) {
+        await dispatch({ type: HIDE_PROFILER_DIALOG });
+      }
+
       // Stop watching current runtime, if currently on a RUNTIME page.
       if (currentPage === PAGE_TYPES.RUNTIME) {
         const currentRuntimeId = getState().runtimes.selectedRuntimeId;
         await dispatch(Actions.unwatchRuntime(currentRuntimeId));
       }
 
       // Always update the selected runtime id.
       // If we are navigating to a non-runtime page, the Runtime page components are no
@@ -88,16 +95,24 @@ function addNetworkLocation(location) {
 }
 
 function removeNetworkLocation(location) {
   return (dispatch, getState) => {
     NetworkLocationsModule.removeNetworkLocation(location);
   };
 }
 
+function showProfilerDialog() {
+  return { type: SHOW_PROFILER_DIALOG };
+}
+
+function hideProfilerDialog() {
+  return { type: HIDE_PROFILER_DIALOG };
+}
+
 function updateAdbAddonStatus(adbAddonStatus) {
   return { type: ADB_ADDON_STATUS_UPDATED, adbAddonStatus };
 }
 
 function updateNetworkLocations(locations) {
   return (dispatch, getState) => {
     dispatch(Actions.updateNetworkRuntimes(locations));
     dispatch({ type: NETWORK_LOCATIONS_UPDATED, locations });
@@ -142,17 +157,19 @@ function scanUSBRuntimes() {
     dispatch({ type: USB_RUNTIMES_SCAN_START });
     await refreshUSBRuntimes();
     dispatch({ type: USB_RUNTIMES_SCAN_SUCCESS });
   };
 }
 
 module.exports = {
   addNetworkLocation,
+  hideProfilerDialog,
   installAdbAddon,
   removeNetworkLocation,
   scanUSBRuntimes,
   selectPage,
+  showProfilerDialog,
   uninstallAdbAddon,
   updateAdbAddonStatus,
   updateDebugTargetCollapsibility,
   updateNetworkLocations,
 };
--- a/devtools/client/aboutdebugging-new/src/base.css
+++ b/devtools/client/aboutdebugging-new/src/base.css
@@ -29,17 +29,20 @@
 
   /* Colors from Photon */
   --success-50: #30e60b;
   --warning-50: #ffe900;
   --warning-90: #3e2800;
   --error-50: #ff0039;
   --error-60: #d70022;
   --highlight-50: #0a84ff;
+  --grey-20: #ededf0;
   --grey-30: #d7d7db; /* for ui, no special semantic */
+  --grey-90-a60: rgba(12, 12, 13, 0.6);
+  --grey-90-a80: rgba(12, 12, 13, 0.8);
   --white-100: #fff; /* for ui, no special semantic */
 
   /* Global layout vars */
   --page-width: 664px;
   --base-unit: 4px;
 
   /* Global styles */
   --base-font-style: message-box;
@@ -259,16 +262,36 @@ Form controls
 /* smaller size for a default button */
 .default-button--micro {
   padding-inline-start: calc(2 * var(--base-unit));
   padding-inline-end: calc(2 * var(--base-unit));
   font-size: var(--micro-font-size);
   height: calc(var(--base-unit) * 6);
 }
 
+/* ghost button. icon button with no background from Photon guidelines */
+.ghost-button {
+  border: none;
+  border-radius: calc(var(--base-unit) / 2);
+  fill: var(--grey-90-a80);
+  height: calc(var(--base-unit) * 8);
+  padding: calc(var(--base-unit) * 2);
+  width: calc(var(--base-unit) * 8);
+
+  -moz-context-properties: fill;
+}
+
+.ghost-button:hover {
+  background-color: var(--grey-30);
+}
+
+.ghost-button:active {
+  background-color: var(--grey-40);
+}
+
 /* standard inputs */
 .default-input {
   line-height: unset;
   padding: 0 calc(var(--base-unit) * 2);
   height: 100%;
 
   border: 1px solid var(--box-border-color);
   border-radius: 2px;
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.css
@@ -0,0 +1,55 @@
+/* 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/. */
+
+.profiler-dialog__frame {
+  border: none;
+  height: 100%;
+  width: 100%;
+}
+
+/*
+ * The current layout of the dialog header is
+ *
+ *  +-----------------------------+---+
+ *  | dialog title (auto)         | X |
+ *  +-----------------------------+---+
+ */
+.profiler-dialog__header {
+  align-items: center;
+  background-color: var(--grey-20);
+  display: grid;
+  grid-template-columns: 1fr max-content;
+  padding: var(--base-unit);
+}
+
+.profiler-dialog__header__title {
+  margin: 0;
+  margin-inline-start: calc(var(--base-unit) * 2);
+
+  /* Reset <h1> styles */
+  font-size: 15px;
+  font-weight: normal;
+}
+
+.profiler-dialog__inner {
+  background-color: var(--white-100);
+  display: grid;
+  height: calc(var(--base-unit) * 150); /* 600px */
+  grid-template-rows: max-content auto;
+  max-height: calc(100% - calc(var(--base-unit) * 25)); /* 100% - 100px */
+  position: fixed;
+  width: calc(var(--base-unit) * 150); /* 600px */
+}
+
+.profiler-dialog__mask {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: var(--grey-90-a60);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/ProfilerDialog.js
@@ -0,0 +1,84 @@
+/* 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 { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+const FluentReact = require("devtools/client/shared/vendor/fluent-react");
+const Localized = createFactory(FluentReact.Localized);
+
+const Actions = require("../actions/index");
+const Types = require("../types/index");
+
+/**
+ * This component a modal dialog containing the performance profiler UI.
+ */
+class ProfilerDialog extends PureComponent {
+  static get propTypes() {
+    return {
+      runtimeDetails: Types.runtimeDetails.isRequired,
+      dispatch: PropTypes.func.isRequired,
+    };
+  }
+
+  hide() {
+    this.props.dispatch(Actions.hideProfilerDialog());
+  }
+
+  render() {
+    const { clientWrapper } = this.props.runtimeDetails;
+
+    return dom.div(
+      {
+        className: "profiler-dialog__mask js-profiler-dialog-mask",
+        onClick: () => this.hide(),
+      },
+      dom.article(
+        {
+          className: "profiler-dialog__inner js-profiler-dialog",
+          onClick: e => e.stopPropagation(),
+        },
+        dom.header(
+          {
+            className: "profiler-dialog__header",
+          },
+          Localized(
+            {
+              id: "about-debugging-profiler-dialog-title",
+            },
+            dom.h1(
+              {
+                className: "profiler-dialog__header__title",
+              },
+              "Performance Profiler",
+            )
+          ),
+          dom.button(
+            {
+              className: "ghost-button js-profiler-dialog-close",
+              onClick: () => this.hide(),
+            },
+            dom.img(
+              {
+                src: "chrome://devtools/skin/images/close.svg",
+              }
+            )
+          )
+        ),
+        dom.iframe({
+          className: "profiler-dialog__frame",
+          src: clientWrapper.getPerformancePanelUrl(),
+          onLoad: (e) => {
+            clientWrapper.loadPerformanceProfiler(e.target.contentWindow);
+          },
+        })
+      )
+    );
+  }
+}
+
+module.exports = ProfilerDialog;
--- a/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
+++ b/devtools/client/aboutdebugging-new/src/components/RuntimePage.js
@@ -11,16 +11,17 @@ const PropTypes = require("devtools/clie
 
 const FluentReact = require("devtools/client/shared/vendor/fluent-react");
 const Localized = createFactory(FluentReact.Localized);
 
 const ConnectionPromptSetting = createFactory(require("./ConnectionPromptSetting"));
 const DebugTargetPane = createFactory(require("./debugtarget/DebugTargetPane"));
 const ExtensionDetail = createFactory(require("./debugtarget/ExtensionDetail"));
 const InspectAction = createFactory(require("./debugtarget/InspectAction"));
+const ProfilerDialog = createFactory(require("./ProfilerDialog"));
 const RuntimeInfo = createFactory(require("./RuntimeInfo"));
 const ServiceWorkerAction = createFactory(require("./debugtarget/ServiceWorkerAction"));
 const ServiceWorkersWarning = createFactory(require("./ServiceWorkersWarning"));
 const TabDetail = createFactory(require("./debugtarget/TabDetail"));
 const TemporaryExtensionAction = createFactory(require("./debugtarget/TemporaryExtensionAction"));
 const TemporaryExtensionDetail = createFactory(require("./debugtarget/TemporaryExtensionDetail"));
 const TemporaryExtensionInstaller =
   createFactory(require("./debugtarget/TemporaryExtensionInstaller"));
@@ -39,39 +40,68 @@ class RuntimePage extends PureComponent 
       collapsibilities: Types.collapsibilities.isRequired,
       dispatch: PropTypes.func.isRequired,
       installedExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
       otherWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
       runtimeDetails: Types.runtimeDetails,
       runtimeId: PropTypes.string.isRequired,
       serviceWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
       sharedWorkers: PropTypes.arrayOf(PropTypes.object).isRequired,
+      showProfilerDialog: PropTypes.bool.isRequired,
       tabs: PropTypes.arrayOf(PropTypes.object).isRequired,
       temporaryExtensions: PropTypes.arrayOf(PropTypes.object).isRequired,
       temporaryInstallError: PropTypes.string,
     };
   }
 
   // TODO: avoid the use of this method
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1508688
   componentWillMount() {
     const { dispatch, runtimeId } = this.props;
     dispatch(Actions.selectPage(PAGE_TYPES.RUNTIME, runtimeId));
   }
 
-  renderConnectionPromptSetting() {
-    const { runtimeDetails, dispatch } = this.props;
+  onProfilerButtonClick() {
+    this.props.dispatch(Actions.showProfilerDialog());
+  }
+
+  renderRemoteRuntimeActions() {
+    const { runtimeDetails, runtimeId, dispatch } = this.props;
     const { connectionPromptEnabled } = runtimeDetails;
 
-    return dom.div(
-      {
-        className: "connection-prompt-setting",
-      },
-      ConnectionPromptSetting({ connectionPromptEnabled, dispatch }),
-    );
+    if (runtimeId === RUNTIMES.THIS_FIREFOX) {
+      // Connection prompt and Profiling are only available on remote runtimes.
+      return null;
+    }
+
+    return [
+      dom.div(
+        {
+          className: "connection-prompt-setting",
+          key: "connection-prompt-setting",
+        },
+        ConnectionPromptSetting({ connectionPromptEnabled, dispatch }),
+      ),
+      dom.p(
+        {},
+        Localized(
+          {
+            id: "about-debugging-runtime-profile-button",
+            key: "profile-runtime-button",
+          },
+          dom.button(
+            {
+              className: "default-button js-profile-runtime-button",
+              onClick: () => this.onProfilerButtonClick(),
+            },
+            "Profile Runtime"
+          ),
+        ),
+      ),
+    ];
   }
 
   renderDebugTargetPane(name, targets, actionComponent,
                         detailComponent, paneKey, localizationId) {
     const { collapsibilities, dispatch, runtimeDetails } = this.props;
 
     if (!isSupportedDebugTargetPane(runtimeDetails.info.type, paneKey)) {
       return null;
@@ -95,43 +125,37 @@ class RuntimePage extends PureComponent 
   }
 
   render() {
     const {
       dispatch,
       installedExtensions,
       otherWorkers,
       runtimeDetails,
-      runtimeId,
       serviceWorkers,
       sharedWorkers,
+      showProfilerDialog,
       tabs,
       temporaryExtensions,
       temporaryInstallError,
     } = this.props;
 
     if (!runtimeDetails) {
       // runtimeInfo can be null when the selectPage action navigates from a runtime A
       // to a runtime B (between unwatchRuntime and watchRuntime).
       return null;
     }
 
-    // do not show the connection prompt setting in 'This Firefox'
-    const shallShowPromptSetting = runtimeId !== RUNTIMES.THIS_FIREFOX;
-
     const { type } = runtimeDetails.info;
     return dom.article(
       {
         className: "page js-runtime-page",
       },
       RuntimeInfo(runtimeDetails.info),
-      shallShowPromptSetting
-        ? this.renderConnectionPromptSetting()
-        : null,
-
+      this.renderRemoteRuntimeActions(),
       runtimeDetails.serviceWorkersAvailable ? null : ServiceWorkersWarning(),
       isSupportedDebugTargetPane(type, DEBUG_TARGET_PANE.TEMPORARY_EXTENSION)
         ? TemporaryExtensionInstaller({
             dispatch,
             temporaryInstallError,
         }) : null,
       this.renderDebugTargetPane("Temporary Extensions",
                                  temporaryExtensions,
@@ -164,27 +188,30 @@ class RuntimePage extends PureComponent 
                                  DEBUG_TARGET_PANE.SHARED_WORKER,
                                  "about-debugging-runtime-shared-workers"),
       this.renderDebugTargetPane("Other Workers",
                                  otherWorkers,
                                  InspectAction,
                                  WorkerDetail,
                                  DEBUG_TARGET_PANE.OTHER_WORKER,
                                  "about-debugging-runtime-other-workers"),
+
+      showProfilerDialog ? ProfilerDialog({ dispatch, runtimeDetails }) : null,
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
     collapsibilities: state.ui.debugTargetCollapsibilities,
     installedExtensions: state.debugTargets.installedExtensions,
     otherWorkers: state.debugTargets.otherWorkers,
     runtimeDetails: getCurrentRuntimeDetails(state.runtimes),
     serviceWorkers: state.debugTargets.serviceWorkers,
     sharedWorkers: state.debugTargets.sharedWorkers,
+    showProfilerDialog: state.ui.showProfilerDialog,
     tabs: state.debugTargets.tabs,
     temporaryExtensions: state.debugTargets.temporaryExtensions,
     temporaryInstallError: state.ui.temporaryInstallError,
   };
 };
 
 module.exports = connect(mapStateToProps)(RuntimePage);
--- a/devtools/client/aboutdebugging-new/src/components/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/moz.build
@@ -8,13 +8,15 @@ DIRS += [
     'shared',
     'sidebar',
 ]
 
 DevToolsModules(
     'App.css',
     'App.js',
     'ConnectionPromptSetting.js',
+    'ProfilerDialog.css',
+    'ProfilerDialog.js',
     'RuntimeInfo.js',
     'RuntimePage.css',
     'RuntimePage.js',
     'ServiceWorkersWarning.js',
 )
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -17,31 +17,33 @@ const actionTypes = {
   ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
+  HIDE_PROFILER_DIALOG: "HIDE_PROFILER_DIALOG",
   NETWORK_LOCATIONS_UPDATED: "NETWORK_LOCATIONS_UPDATED",
   REMOTE_RUNTIMES_UPDATED: "REMOTE_RUNTIMES_UPDATED",
   REQUEST_EXTENSIONS_FAILURE: "REQUEST_EXTENSIONS_FAILURE",
   REQUEST_EXTENSIONS_START: "REQUEST_EXTENSIONS_START",
   REQUEST_EXTENSIONS_SUCCESS: "REQUEST_EXTENSIONS_SUCCESS",
   REQUEST_TABS_FAILURE: "REQUEST_TABS_FAILURE",
   REQUEST_TABS_START: "REQUEST_TABS_START",
   REQUEST_TABS_SUCCESS: "REQUEST_TABS_SUCCESS",
   REQUEST_WORKERS_FAILURE: "REQUEST_WORKERS_FAILURE",
   REQUEST_WORKERS_START: "REQUEST_WORKERS_START",
   REQUEST_WORKERS_SUCCESS: "REQUEST_WORKERS_SUCCESS",
   SELECT_PAGE_FAILURE: "SELECT_PAGE_FAILURE",
   SELECT_PAGE_START: "SELECT_PAGE_START",
   SELECT_PAGE_SUCCESS: "SELECT_PAGE_SUCCESS",
   SELECTED_RUNTIME_ID_UPDATED: "SELECTED_RUNTIME_ID_UPDATED",
+  SHOW_PROFILER_DIALOG: "SHOW_PROFILER_DIALOG",
   TELEMETRY_RECORD: "TELEMETRY_RECORD",
   TEMPORARY_EXTENSION_INSTALL_FAILURE: "TEMPORARY_EXTENSION_INSTALL_FAILURE",
   TEMPORARY_EXTENSION_INSTALL_START: "TEMPORARY_EXTENSION_INSTALL_START",
   TEMPORARY_EXTENSION_INSTALL_SUCCESS: "TEMPORARY_EXTENSION_INSTALL_SUCCESS",
   THIS_FIREFOX_RUNTIME_CREATED: "THIS_FIREFOX_RUNTIME_CREATED",
   UNWATCH_RUNTIME_FAILURE: "UNWATCH_RUNTIME_FAILURE",
   UNWATCH_RUNTIME_START: "UNWATCH_RUNTIME_START",
   UNWATCH_RUNTIME_SUCCESS: "UNWATCH_RUNTIME_SUCCESS",
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -149,11 +149,23 @@ class ClientWrapper {
 
   async close() {
     return this.client.close();
   }
 
   isClosed() {
     return this.client._closed;
   }
+
+  // This method will be mocked to return a dummy URL during mochitests
+  getPerformancePanelUrl() {
+    return "chrome://devtools/content/performance-new/index.xhtml";
+  }
+
+  async loadPerformanceProfiler(win) {
+    const preferenceFront = await this.getFront("preference");
+    const perfFront = await this.getFront("perf");
+    const perfActorVersion = this.client.mainRoot.traits.perfActorVersion;
+    win.gInit(perfFront, preferenceFront, perfActorVersion);
+  }
 }
 
 exports.ClientWrapper = ClientWrapper;
--- a/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -2,34 +2,37 @@
  * 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 {
   ADB_ADDON_STATUS_UPDATED,
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED,
+  HIDE_PROFILER_DIALOG,
   NETWORK_LOCATIONS_UPDATED,
   SELECT_PAGE_SUCCESS,
+  SHOW_PROFILER_DIALOG,
   TEMPORARY_EXTENSION_INSTALL_FAILURE,
   TEMPORARY_EXTENSION_INSTALL_SUCCESS,
   USB_RUNTIMES_SCAN_START,
   USB_RUNTIMES_SCAN_SUCCESS,
 } = require("../constants");
 
 function UiState(locations = [], debugTargetCollapsibilities = {},
                  networkEnabled = false, wifiEnabled = false,
                  showSystemAddons = false) {
   return {
     adbAddonStatus: null,
     debugTargetCollapsibilities,
     isScanningUsb: false,
     networkEnabled,
     networkLocations: locations,
     selectedPage: null,
+    showProfilerDialog: false,
     showSystemAddons,
     temporaryInstallError: null,
     wifiEnabled,
   };
 }
 
 function uiReducer(state = UiState(), action) {
   switch (action.type) {
@@ -50,16 +53,24 @@ function uiReducer(state = UiState(), ac
       return Object.assign({}, state, { networkLocations: locations });
     }
 
     case SELECT_PAGE_SUCCESS: {
       const { page } = action;
       return Object.assign({}, state, { selectedPage: page });
     }
 
+    case SHOW_PROFILER_DIALOG: {
+      return Object.assign({}, state, { showProfilerDialog: true });
+    }
+
+    case HIDE_PROFILER_DIALOG: {
+      return Object.assign({}, state, { showProfilerDialog: false });
+    }
+
     case USB_RUNTIMES_SCAN_START: {
       return Object.assign({}, state, { isScanningUsb: true });
     }
 
     case USB_RUNTIMES_SCAN_SUCCESS: {
       return Object.assign({}, state, { isScanningUsb: false });
     }
 
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -54,18 +54,19 @@ skip-if = (os == 'linux' && bits == 32) 
 [browser_aboutdebugging_devtoolstoolbox_contextmenu_markupview.js]
 [browser_aboutdebugging_devtoolstoolbox_menubar.js]
 [browser_aboutdebugging_devtoolstoolbox_reload.js]
 [browser_aboutdebugging_devtoolstoolbox_shortcuts.js]
 skip-if = (os == "win" && ccov) # Bug 1521349
 [browser_aboutdebugging_devtoolstoolbox_tooltip_markupview.js]
 [browser_aboutdebugging_navigate.js]
 [browser_aboutdebugging_persist_connection.js]
+[browser_aboutdebugging_profiler_dialog.js]
 [browser_aboutdebugging_routes.js]
-[browser_aboutdebugging_runtime_connection-prompt.js]
+[browser_aboutdebugging_runtime_remote_runtime_buttons.js]
 [browser_aboutdebugging_runtime_usbclient_closed.js]
 [browser_aboutdebugging_select_network_runtime.js]
 [browser_aboutdebugging_select_page_with_serviceworker.js]
 [browser_aboutdebugging_serviceworker_fetch_flag.js]
 [browser_aboutdebugging_serviceworker_multie10s.js]
 [browser_aboutdebugging_serviceworker_not_compatible.js]
 [browser_aboutdebugging_serviceworker_push.js]
 [browser_aboutdebugging_serviceworker_pushservice_url.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_profiler_dialog.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from helper-mocks.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
+
+/**
+ * Test opening and closing the profiler dialog.
+ */
+add_task(async function() {
+  // enable USB devices mocks
+  const mocks = new Mocks();
+  const usbClient = mocks.createUSBRuntime("1337id", {
+    deviceName: "Fancy Phone",
+    name: "Lorem ipsum",
+  });
+
+  const { document, tab, window } = await openAboutDebugging();
+
+  mocks.emitUSBUpdate();
+  await connectToRuntime("Fancy Phone", document);
+  await selectRuntime("Fancy Phone", "Lorem ipsum", document);
+
+  info("Open the profiler dialog");
+  await openProfilerDialog(usbClient, document);
+  assertDialogVisible(document);
+
+  info("Click on the close button and wait until the dialog disappears");
+  const closeDialogButton = document.querySelector(".js-profiler-dialog-close");
+  closeDialogButton.click();
+  await waitUntil(() => !document.querySelector(".js-profiler-dialog"));
+  assertDialogHidden(document);
+
+  info("Open the profiler dialog again");
+  await openProfilerDialog(usbClient, document);
+  assertDialogVisible(document);
+
+  info("Click on the mask element and wait until the dialog disappears");
+  const mask = document.querySelector(".js-profiler-dialog-mask");
+  EventUtils.synthesizeMouse(mask, 5, 5, {}, window);
+  await waitUntil(() => !document.querySelector(".js-profiler-dialog"));
+  assertDialogHidden(document);
+
+  info("Open the profiler dialog again");
+  await openProfilerDialog(usbClient, document);
+  assertDialogVisible(document);
+
+  info("Navigate to this-firefox and wait until the dialog disappears");
+  document.location.hash = "#/runtime/this-firefox";
+  await waitUntil(() => !document.querySelector(".js-profiler-dialog"));
+  assertDialogHidden(document);
+
+  info("Select the remote runtime again, check the dialog is still hidden");
+  await selectRuntime("Fancy Phone", "Lorem ipsum", document);
+  assertDialogHidden(document);
+
+  await removeTab(tab);
+});
+
+function assertDialogVisible(doc) {
+  ok(doc.querySelector(".js-profiler-dialog"), "Dialog is displayed");
+  ok(doc.querySelector(".js-profiler-dialog-mask"), "Dialog mask is displayed");
+}
+
+function assertDialogHidden(doc) {
+  ok(!document.querySelector(".js-profiler-dialog"), "Dialog is removed");
+  ok(!document.querySelector(".js-profiler-dialog-mask"), "Dialog mask is removed");
+}
+
+function openProfilerDialog(client, doc) {
+  const onProfilerLoaded = new Promise(r => {
+    client.loadPerformanceProfiler = r;
+  });
+
+  info("Click on the Profile Runtime button");
+  const profileButton = doc.querySelector(".js-profile-runtime-button");
+  profileButton.click();
+
+  info("Wait for the loadPerformanceProfiler callback to be executed on client-wrapper");
+  return onProfilerLoaded;
+}
rename from devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_connection-prompt.js
rename to devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_remote_runtime_buttons.js
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_connection-prompt.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_runtime_remote_runtime_buttons.js
@@ -3,34 +3,37 @@
 
 "use strict";
 
 /* import-globals-from helper-mocks.js */
 Services.scriptloader.loadSubScript(
   CHROME_URL_ROOT + "helper-mocks.js", this);
 
 /**
- * Test that remote runtimes show the connection prompt,
- * but it's hidden in 'This Firefox'
+ * Test that remote runtimes show action buttons that are hidden for 'This Firefox'.
  */
 add_task(async function() {
   // enable USB devices mocks
   const mocks = new Mocks();
   mocks.createUSBRuntime("1337id", {
     deviceName: "Fancy Phone",
     name: "Lorem ipsum",
   });
 
   const { document, tab } = await openAboutDebugging();
 
   info("Checking This Firefox");
   ok(!document.querySelector(".js-connection-prompt-toggle-button"),
     "This Firefox does not contain the connection prompt button");
+  ok(!document.querySelector(".js-profile-runtime-button"),
+    "This Firefox does not contain the profile runtime button");
 
   info("Checking a USB runtime");
   mocks.emitUSBUpdate();
   await connectToRuntime("Fancy Phone", document);
   await selectRuntime("Fancy Phone", "Lorem ipsum", document);
   ok(!!document.querySelector(".js-connection-prompt-toggle-button"),
     "Runtime contains the connection prompt button");
+  ok(!!document.querySelector(".js-profile-runtime-button"),
+    "Remote runtime contains the profile runtime button");
 
   await removeTab(tab);
 });
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/helper-client-wrapper-mock.js
@@ -82,16 +82,18 @@ function createClientMock() {
     // no-op
     getFront: () => {},
     // no-op
     onFront: () => {},
     // stores the preference locally (doesn't update about:config)
     setPreference: function(prefName, value) {
       this._preferences[prefName] = value;
     },
+    getPerformancePanelUrl: () => "data:text/html;charset=UTF-8,fake_profiler_page",
+    loadPerformanceProfiler: () => {},
   };
 }
 
 // Create a ClientWrapper mock that can be used to replace the this-firefox runtime.
 function createThisFirefoxClientMock() {
   const mockThisFirefoxDescription = {
     name: "Firefox",
     channel: "nightly",
--- a/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
+++ b/devtools/client/aboutdebugging-new/tmp-locale/en-US/aboutdebugging.notftl
@@ -121,16 +121,20 @@ about-debugging-runtime-extensions = Ext
 about-debugging-runtime-tabs = Tabs
 # Title of the service workers category.
 about-debugging-runtime-service-workers = Service Workers
 # Title of the shared workers category.
 about-debugging-runtime-shared-workers = Shared Workers
 # Title of the other workers category.
 about-debugging-runtime-other-workers = Other Workers
 
+# Label of the button opening the performance profiler panel in runtime pages for remote
+# runtimes.
+about-debugging-runtime-profile-button = Profile Runtime
+
 # This string is displayed in about:debugging#workers if the current configuration of the
 # browser is incompatible with service workers. Learn more points to MDN.
 # https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging#Service_workers_not_compatible
 about-debugging-runtime-service-workers-not-compatible = Your browser configuration is not compatible with Service Workers. <a>Learn more</a>
 
 # Displayed in the categories of "runtime" pages that don't have any debug target to
 # show. Debug targets depend on the category (extensions, tabs, workers...).
 about-debugging-debug-target-list-empty = Nothing yet.
@@ -274,8 +278,11 @@ about-debugging-page-title-selected-page
   }
 
 # Page title with the runtime displayed in the tab
 # { $selectedRuntimeId } is the id of the current runtime, such as "this-firefox", "localhost:6080", ...
 about-debugging-page-title-with-runtime = { -application-title } - { about-debugging-page-title-selected-page } / { $selectedRuntimeId }
 
 # Page title without the runtime displayed in the tab
 about-debugging-page-title = { -application-title } - { about-debugging-page-title-selected-page }
+
+# Title of a modal dialog displayed on remote runtime pages after clicking on the Profile Runtime button.
+about-debugging-profiler-dialog-title = Performance Profiler
--- a/devtools/client/webconsole/test/mochitest/.eslintrc.js
+++ b/devtools/client/webconsole/test/mochitest/.eslintrc.js
@@ -1,6 +1,12 @@
 "use strict";
 
 module.exports = {
   // Extend from the shared list of defined globals for mochitests.
-  "extends": "../../../../.eslintrc.mochitests.js"
+  "extends": "../../../../.eslintrc.mochitests.js",
+  "overrides": [{
+    "files": ["test-dynamic-import.js"],
+    "parserOptions": {
+      "sourceType": "module",
+    },
+  }]
 };
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -67,16 +67,18 @@ support-files =
   test-console-table.html
   test-console-trace-async.html
   test-console-workers.html
   test-console.html
   test-cu-reporterror.js
   test-data.json
   test-data.json^headers^
   test-duplicate-error.html
+  test-dynamic-import.html
+  test-dynamic-import.js
   test-encoding-ISO-8859-1.html
   test-error.html
   test-eval-in-stackframe.html
   test-exception-stackframe.html
   test-external-script-errors.html
   test-external-script-errors.js
   test-file-location.js
   test-filter.html
@@ -212,16 +214,17 @@ skip-if = verify
 [browser_jsterm_autocomplete_paste_undo.js]
 [browser_jsterm_autocomplete_return_key_no_selection.js]
 [browser_jsterm_autocomplete_return_key.js]
 [browser_jsterm_autocomplete_width.js]
 [browser_jsterm_autocomplete_will_navigate.js]
 [browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
 [browser_jsterm_await_assignments.js]
 [browser_jsterm_await_concurrent.js]
+[browser_jsterm_await_dynamic_import.js]
 [browser_jsterm_await_error.js]
 [browser_jsterm_await_helper_dollar_underscore.js]
 [browser_jsterm_await_paused.js]
 [browser_jsterm_await.js]
 [browser_jsterm_completion_bracket_cached_results.js]
 [browser_jsterm_completion_bracket.js]
 [browser_jsterm_completion_case_sensitivity.js]
 [browser_jsterm_completion_dollar_underscore.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_await_dynamic_import.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that top-level await with dynamic import works as expected.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/mochitest/test-dynamic-import.html";
+
+add_task(async function() {
+  // Enable dynamic import
+  await pushPref("javascript.options.dynamicImport", true);
+  // Enable await mapping.
+  await pushPref("devtools.debugger.features.map-await-expression", true);
+
+  // Run test with legacy JsTerm
+  await pushPref("devtools.webconsole.jsterm.codeMirror", false);
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  const executeAndWaitForResultMessage = (input, expectedOutput) =>
+    executeAndWaitForMessage(hud, input, expectedOutput, ".result");
+
+  info("Evaluate an expression with a dynamic import");
+  let importAwaitExpression = `
+    var {sum} = await import("./test-dynamic-import.js");
+    sum(1, 2, 3);
+  `;
+  await executeAndWaitForResultMessage(importAwaitExpression, `1 + 2 + 3 = 6`);
+  ok(true, "The `sum` module was imported and used successfully");
+
+  info("Import the same module a second time");
+  // This used to make the content page crash (See Bug 1523897).
+  importAwaitExpression = `
+    var {sum} = await import("./test-dynamic-import.js");
+    sum(2, 3, 4);
+  `;
+  await executeAndWaitForResultMessage(importAwaitExpression, `2 + 3 + 4 = 9`);
+  ok(true, "The `sum` module was imported and used successfully a second time");
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-dynamic-import.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Test dynamic import usage in console</title>
+  </head>
+  <body>
+    <h1>Test dynamic import usage in console</h1>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/test-dynamic-import.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * @param  {Number} numbers that will be summed.
+ * @returns {String} A string of the following form: `${arg1} + ${arg2} ${argn} = ${sum}`
+ */
+function sum(...args) {
+  return `${args.join(" + ")} = ${args.reduce((acc, i) => acc + i)}`;
+}
+
+export {sum};
--- a/devtools/docs/getting-started/build.md
+++ b/devtools/docs/getting-started/build.md
@@ -58,16 +58,18 @@ After it finishes, you might be asked to
 
 Then run this:
 
 ```bash
 ./mach configure
 ./mach build
 ```
 
+Please note, if this fails it might be possible you need to run the `bootstrap.py` script first. Download the [bootstrap.py script](https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py) and save it in your project directory. Then run `python bootstrap.py` and follow the prompted steps.
+
 **Note:** if using Windows, you might need to type the commands without the `./`:
 
 ```bash
 mach bootstrap
 mach configure
 mach build
 ```
 
--- a/dom/media/mp4/MP4Demuxer.cpp
+++ b/dom/media/mp4/MP4Demuxer.cpp
@@ -9,19 +9,22 @@
 #include <stdint.h>
 
 #include "MP4Demuxer.h"
 
 #include "AnnexB.h"
 #include "BufferStream.h"
 #include "H264.h"
 #include "Index.h"
+#include "MP4Decoder.h"
 #include "MP4Metadata.h"
 #include "MoofParser.h"
 #include "ResourceStream.h"
+#include "VPXDecoder.h"
+#include "mozilla/Span.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "nsAutoPtr.h"
 #include "nsPrintfCString.h"
 
 extern mozilla::LazyLogModule gMediaDemuxerLog;
 mozilla::LogModule* GetDemuxerLog() { return gMediaDemuxerLog; }
 
@@ -66,17 +69,21 @@ class MP4TrackDemuxer : public MediaTrac
   UniquePtr<TrackInfo> mInfo;
   RefPtr<Index> mIndex;
   UniquePtr<SampleIterator> mIterator;
   Maybe<media::TimeUnit> mNextKeyframeTime;
   // Queued samples extracted by the demuxer, but not yet returned.
   RefPtr<MediaRawData> mQueuedSample;
   bool mNeedReIndex;
   bool mNeedSPSForTelemetry;
-  bool mIsH264 = false;
+  enum CodecType {
+    kH264,
+    kVP9,
+    kOther
+  } mType = kOther;
 };
 
 // Returns true if no SPS was found and search for it should continue.
 bool AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata) {
   SPSData spsdata;
   if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) {
     uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) |
                           (spsdata.constraint_set1_flag ? (1 << 1) : 0) |
@@ -345,31 +352,33 @@ MP4TrackDemuxer::MP4TrackDemuxer(MediaRe
       mInfo(std::move(aInfo)),
       mIndex(new Index(aIndices, mStream, mInfo->mTrackId, mInfo->IsAudio())),
       mIterator(MakeUnique<SampleIterator>(mIndex)),
       mNeedReIndex(true) {
   EnsureUpToDateIndex();  // Force update of index
 
   VideoInfo* videoInfo = mInfo->GetAsVideoInfo();
   // Collect telemetry from h264 AVCC SPS.
-  if (videoInfo && (mInfo->mMimeType.EqualsLiteral("video/mp4") ||
-                    mInfo->mMimeType.EqualsLiteral("video/avc"))) {
-    mIsH264 = true;
+  if (videoInfo && MP4Decoder::IsH264(mInfo->mMimeType)) {
+    mType = kH264;
     RefPtr<MediaByteBuffer> extraData = videoInfo->mExtraData;
     mNeedSPSForTelemetry = AccumulateSPSTelemetry(extraData);
     SPSData spsdata;
     if (H264::DecodeSPSFromExtraData(extraData, spsdata) &&
         spsdata.pic_width > 0 && spsdata.pic_height > 0 &&
         H264::EnsureSPSIsSane(spsdata)) {
       videoInfo->mImage.width = spsdata.pic_width;
       videoInfo->mImage.height = spsdata.pic_height;
       videoInfo->mDisplay.width = spsdata.display_width;
       videoInfo->mDisplay.height = spsdata.display_height;
     }
   } else {
+    if (videoInfo && VPXDecoder::IsVP9(mInfo->mMimeType)) {
+      mType = kVP9;
+    }
     // No SPS to be found.
     mNeedSPSForTelemetry = false;
   }
 }
 
 UniquePtr<TrackInfo> MP4TrackDemuxer::GetInfo() const { return mInfo->Clone(); }
 
 void MP4TrackDemuxer::EnsureUpToDateIndex() {
@@ -417,17 +426,17 @@ RefPtr<MP4TrackDemuxer::SeekPromise> MP4
 
 already_AddRefed<MediaRawData> MP4TrackDemuxer::GetNextSample() {
   RefPtr<MediaRawData> sample = mIterator->GetNext();
   if (!sample) {
     return nullptr;
   }
   if (mInfo->GetAsVideoInfo()) {
     sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
-    if (mIsH264 && !sample->mCrypto.IsEncrypted()) {
+    if (mType == kH264 && !sample->mCrypto.IsEncrypted()) {
       H264::FrameType type = H264::GetFrameType(sample);
       switch (type) {
         case H264::FrameType::I_FRAME:
           MOZ_FALLTHROUGH;
         case H264::FrameType::OTHER: {
           bool keyframe = type == H264::FrameType::I_FRAME;
           if (sample->mKeyframe != keyframe) {
             NS_WARNING(nsPrintfCString("Frame incorrectly marked as %skeyframe "
@@ -450,16 +459,30 @@ already_AddRefed<MediaRawData> MP4TrackD
                                      sample->mTimecode.ToMicroseconds())
                          .get());
           // We could reject the sample now, however demuxer errors are fatal.
           // So we keep the invalid frame, relying on the H264 decoder to
           // handle the error later.
           // TODO: make demuxer errors non-fatal.
           break;
       }
+    } else if (mType == kVP9 && !sample->mCrypto.IsEncrypted()) {
+      bool keyframe = VPXDecoder::IsKeyframe(
+          MakeSpan<const uint8_t>(sample->Data(), sample->Size()),
+          VPXDecoder::Codec::VP9);
+      if (sample->mKeyframe != keyframe) {
+        NS_WARNING(nsPrintfCString(
+                       "Frame incorrectly marked as %skeyframe "
+                       "@ pts:%" PRId64 " dur:%" PRId64 " dts:%" PRId64,
+                       keyframe ? "" : "non-", sample->mTime.ToMicroseconds(),
+                       sample->mDuration.ToMicroseconds(),
+                       sample->mTimecode.ToMicroseconds())
+                       .get());
+        sample->mKeyframe = keyframe;
+      }
     }
   }
 
   return sample.forget();
 }
 
 RefPtr<MP4TrackDemuxer::SamplesPromise> MP4TrackDemuxer::GetSamples(
     int32_t aNumSamples) {
@@ -486,17 +509,17 @@ RefPtr<MP4TrackDemuxer::SamplesPromise> 
   }
 
   if (samples->mSamples.IsEmpty()) {
     return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM,
                                            __func__);
   }
   for (const auto& sample : samples->mSamples) {
     // Collect telemetry from h264 Annex B SPS.
-    if (mNeedSPSForTelemetry && mIsH264 && AnnexB::IsAVCC(sample)) {
+    if (mNeedSPSForTelemetry && mType == kH264 && AnnexB::IsAVCC(sample)) {
       RefPtr<MediaByteBuffer> extradata = H264::ExtractExtraData(sample);
       if (H264::HasSPS(extradata)) {
         RefPtr<MediaByteBuffer> extradata = H264::ExtractExtraData(sample);
         mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata);
       }
     }
   }
 
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -169,32 +169,30 @@ impl<'a> FrameBuildingState<'a> {
         self.dirty_region_stack.pop().unwrap();
     }
 }
 
 /// Immutable context of a picture when processing children.
 #[derive(Debug)]
 pub struct PictureContext {
     pub pic_index: PictureIndex,
-    pub pipeline_id: PipelineId,
     pub apply_local_clip_rect: bool,
     pub allow_subpixel_aa: bool,
     pub is_passthrough: bool,
     pub raster_space: RasterSpace,
     pub surface_spatial_node_index: SpatialNodeIndex,
     pub raster_spatial_node_index: SpatialNodeIndex,
     /// The surface that this picture will render on.
     pub surface_index: SurfaceIndex,
     pub dirty_region_count: usize,
 }
 
 /// Mutable state of a picture that gets modified when
 /// the children are processed.
 pub struct PictureState {
-    pub is_cacheable: bool,
     pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
     pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
     pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
     pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
     /// If the plane splitter, the primitives get added to it instead of
     /// batching into their parent pictures.
     pub plane_splitter: Option<PlaneSplitter>,
 }
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -2380,17 +2380,16 @@ impl PicturePrimitive {
             }
             Picture3DContext::In { root_data: None, .. } => {
                 None
             }
         };
 
         let state = PictureState {
             //TODO: check for MAX_CACHE_SIZE here?
-            is_cacheable: true,
             map_local_to_pic,
             map_pic_to_world,
             map_pic_to_raster,
             map_raster_to_world,
             plane_splitter,
         };
 
         // Disallow subpixel AA if an intermediate surface is needed.
@@ -2427,17 +2426,16 @@ impl PicturePrimitive {
         if inflation_factor > 0.0 {
             let inflated_region = frame_state.current_dirty_region().inflate(inflation_factor);
             frame_state.push_dirty_region(inflated_region);
             dirty_region_count += 1;
         }
 
         let context = PictureContext {
             pic_index,
-            pipeline_id: self.pipeline_id,
             apply_local_clip_rect: self.apply_local_clip_rect,
             allow_subpixel_aa,
             is_passthrough: self.raster_config.is_none(),
             raster_space: self.requested_raster_space,
             raster_spatial_node_index,
             surface_spatial_node_index,
             surface_index,
             dirty_region_count,
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -2265,20 +2265,16 @@ impl PrimitiveStore {
 
                 // And now undo the clip stack logic above.
                 if is_passthrough {
                     frame_state.clip_chain_stack.pop_clip();
                 } else {
                     frame_state.clip_chain_stack.pop_surface();
                 }
 
-                if !pic_state_for_children.is_cacheable {
-                    pic_state.is_cacheable = false;
-                }
-
                 // Restore the dependencies (borrow check dance)
                 self.pictures[pic_context_for_children.pic_index.0]
                     .restore_context(
                         prim_list,
                         pic_context_for_children,
                         pic_state_for_children,
                         frame_state,
                     );
@@ -2314,21 +2310,16 @@ impl PrimitiveStore {
             }
         }
 
         #[cfg(debug_assertions)]
         {
             prim_instance.prepared_frame_id = frame_state.render_tasks.frame_id();
         }
 
-        pic_state.is_cacheable &= prim_instance.is_cacheable(
-            &data_stores,
-            frame_state.resource_cache,
-        );
-
         match prim_instance.kind {
             PrimitiveInstanceKind::Picture { pic_index, .. } => {
                 let pic = &mut self.pictures[pic_index.0];
                 let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize];
                 if pic.prepare_for_render(
                     pic_index,
                     prim_instance,
                     prim_info.clipped_world_rect,
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -65,17 +65,17 @@ class NoTypePolicy {
   struct Data {
     static const TypePolicy* thisTypePolicy() { return nullptr; }
   };
 };
 
 class BoxInputsPolicy final : public TypePolicy {
  public:
   constexpr BoxInputsPolicy() {}
-  SPECIALIZATION_DATA_;
+  EMPTY_DATA_;
   static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc,
                                               MInstruction* def);
   MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc,
                                  MInstruction* def) const override {
     return staticAdjustInputs(alloc, def);
   }
 };
 
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -6612,61 +6612,23 @@ nsresult PresShell::EventHandler::Handle
       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
                    "Unexpected document");
       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
       if (capturingFrame) {
         frameToHandleEvent = capturingFrame;
       }
     }
 
+    if (NS_WARN_IF(!frameToHandleEvent)) {
+      return NS_OK;
+    }
+
     // Suppress mouse event if it's being targeted at an element inside
     // a document which needs events suppressed
-    if (aGUIEvent->mClass == eMouseEventClass &&
-        frameToHandleEvent->PresContext()
-            ->Document()
-            ->EventHandlingSuppressed()) {
-      if (aGUIEvent->mMessage == eMouseDown) {
-        mPresShell->mNoDelayedMouseEvents = true;
-      } else if (!mPresShell->mNoDelayedMouseEvents &&
-                 (aGUIEvent->mMessage == eMouseUp ||
-                  // contextmenu is triggered after right mouseup on Windows and
-                  // right mousedown on other platforms.
-                  aGUIEvent->mMessage == eContextMenu)) {
-        auto event = MakeUnique<DelayedMouseEvent>(aGUIEvent->AsMouseEvent());
-        PushDelayedEventIntoQueue(std::move(event));
-      }
-
-      // If there is a suppressed event listener associated with the document,
-      // notify it about the suppressed mouse event. This allows devtools
-      // features to continue receiving mouse events even when the devtools
-      // debugger has paused execution in a page.
-      RefPtr<EventListener> suppressedListener =
-          frameToHandleEvent->PresContext()
-              ->Document()
-              ->GetSuppressedEventListener();
-      if (suppressedListener && aGUIEvent->AsMouseEvent()->mReason !=
-                                    WidgetMouseEvent::eSynthesized) {
-        nsCOMPtr<nsIContent> targetContent;
-        frameToHandleEvent->GetContentForEvent(aGUIEvent,
-                                               getter_AddRefs(targetContent));
-        if (targetContent) {
-          aGUIEvent->mTarget = targetContent;
-        }
-
-        nsCOMPtr<EventTarget> et = aGUIEvent->mTarget;
-        RefPtr<Event> event = EventDispatcher::CreateEvent(
-            et, frameToHandleEvent->PresContext(), aGUIEvent, EmptyString());
-
-        suppressedListener->HandleEvent(*event);
-      }
-
-      return NS_OK;
-    }
-
-    if (NS_WARN_IF(!frameToHandleEvent)) {
+    if (MaybeDiscardOrDelayMouseEvent(frameToHandleEvent, aGUIEvent)) {
       return NS_OK;
     }
 
     RefPtr<PresShell> shell =
         static_cast<PresShell*>(frameToHandleEvent->PresShell());
     // Check if we have an active EventStateManager which isn't the
     // EventStateManager of the current PresContext.
     // If that is the case, and mouse is over some ancestor document,
@@ -7255,16 +7217,71 @@ bool PresShell::EventHandler::MaybeDisca
     UniquePtr<DelayedKeyEvent> delayedKeyEvent =
         MakeUnique<DelayedKeyEvent>(aGUIEvent->AsKeyboardEvent());
     PushDelayedEventIntoQueue(std::move(delayedKeyEvent));
   }
   aGUIEvent->mFlags.mIsSuppressedOrDelayed = true;
   return true;
 }
 
+bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
+    nsIFrame* aFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
+  MOZ_ASSERT(aFrameToHandleEvent);
+  MOZ_ASSERT(aGUIEvent);
+
+  if (aGUIEvent->mClass != eMouseEventClass) {
+    return false;
+  }
+
+  if (!aFrameToHandleEvent->PresContext()
+           ->Document()
+           ->EventHandlingSuppressed()) {
+    return false;
+  }
+
+  if (aGUIEvent->mMessage == eMouseDown) {
+    mPresShell->mNoDelayedMouseEvents = true;
+  } else if (!mPresShell->mNoDelayedMouseEvents &&
+             (aGUIEvent->mMessage == eMouseUp ||
+              // contextmenu is triggered after right mouseup on Windows and
+              // right mousedown on other platforms.
+              aGUIEvent->mMessage == eContextMenu)) {
+    UniquePtr<DelayedMouseEvent> delayedMouseEvent =
+        MakeUnique<DelayedMouseEvent>(aGUIEvent->AsMouseEvent());
+    PushDelayedEventIntoQueue(std::move(delayedMouseEvent));
+  }
+
+  // If there is a suppressed event listener associated with the document,
+  // notify it about the suppressed mouse event. This allows devtools
+  // features to continue receiving mouse events even when the devtools
+  // debugger has paused execution in a page.
+  RefPtr<EventListener> suppressedListener = aFrameToHandleEvent->PresContext()
+                                                 ->Document()
+                                                 ->GetSuppressedEventListener();
+  if (!suppressedListener ||
+      aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
+    return true;
+  }
+
+  nsCOMPtr<nsIContent> targetContent;
+  aFrameToHandleEvent->GetContentForEvent(aGUIEvent,
+                                          getter_AddRefs(targetContent));
+  if (targetContent) {
+    aGUIEvent->mTarget = targetContent;
+  }
+
+  nsCOMPtr<EventTarget> eventTarget = aGUIEvent->mTarget;
+  RefPtr<Event> event = EventDispatcher::CreateEvent(
+      eventTarget, aFrameToHandleEvent->PresContext(), aGUIEvent,
+      EmptyString());
+
+  suppressedListener->HandleEvent(*event);
+  return true;
+}
+
 nsIFrame* PresShell::EventHandler::MaybeFlushThrottledStyles(
     nsIFrame* aFrameForPresShell) {
 
   if (!GetDocument()) {
     // XXX Only when mPresShell has document, we'll try to look for a frame
     //     containing mPresShell even if given frame is nullptr.  Does this
     //     make sense?
     return aFrameForPresShell;
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -707,16 +707,32 @@ class PresShell final : public nsIPresSh
      *
      * @param aGUIEvent         The handling event.
      * @return                  true if this method discard the event or
      *                          put it into the delayed event queue.
      */
     bool MaybeDiscardOrDelayKeyboardEvent(WidgetGUIEvent* aGUIEvent);
 
     /**
+     * MaybeDiscardOrDelayMouseEvent() may discard or put aGUIEvent into the
+     * delayed event queue if it's a mouse event and if we should do so.
+     * If aGUIEvent is not a mouse event, this does nothing.
+     * If there is suppressed event listener like debugger of devtools, this
+     * notifies it of the event after discard or put it into the delayed
+     * event queue.
+     *
+     * @param aFrameToHandleEvent       The frame to handle aGUIEvent.
+     * @param aGUIEvent                 The handling event.
+     * @return                          true if this method discard the event
+     *                                  or put it into the delayed event queue.
+     */
+    bool MaybeDiscardOrDelayMouseEvent(nsIFrame* aFrameToHandleEvent,
+                                       WidgetGUIEvent* aGUIEvent);
+
+    /**
      * MaybeFlushThrottledStyles() tries to flush pending animation.  If it's
      * flushed and then aFrameForPresShell is destroyed, returns new frame
      * which contains mPresShell.
      *
      * @param aFrameForPresShell        The frame for mPresShell.  This can be
      *                                  nullptr.
      * @return                          Maybe new frame for mPresShell.
      *                                  If aFrameForPresShell is not nullptr
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -248,17 +248,17 @@ class BaseBootstrapper(object):
 
         GeckoView/Firefox for Android Artifact Mode needs an application and an ABI set,
         and it needs paths to the Android SDK.
         '''
         raise NotImplementedError(
             '%s does not yet implement suggest_mobile_android_artifact_mode_mozconfig()'
             % __name__)
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
         '''
         Install the clang static analysis package
         '''
         raise NotImplementedError(
             '%s does not yet implement ensure_clang_static_analysis_package()'
             % __name__)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
@@ -271,33 +271,25 @@ class BaseBootstrapper(object):
 
     def ensure_node_packages(self, state_dir, checkout_root):
         '''
         Install any necessary packages needed to supply NodeJS'''
         raise NotImplementedError(
             '%s does not yet implement ensure_node_packages()'
             % __name__)
 
-    def install_toolchain_static_analysis(self, checkout_root):
-        mach_binary = os.path.join(checkout_root, 'mach')
-        mach_binary = os.path.abspath(mach_binary)
-        if not os.path.exists(mach_binary):
-            raise ValueError("mach not found at %s" % mach_binary)
-
-        if not sys.executable:
-            raise ValueError("cannot determine path to Python executable")
+    def install_toolchain_static_analysis(self, state_dir, checkout_root, toolchain_job):
+        clang_tools_path = os.path.join(state_dir, 'clang-tools')
+        import shutil
+        if os.path.exists(clang_tools_path):
+            shutil.rmtree(clang_tools_path)
 
-        cmd = [sys.executable, mach_binary, 'static-analysis', 'install',
-               '--force', '--minimal-install']
-
-        from subprocess import CalledProcessError
-        try:
-            subprocess.check_call(cmd)
-        except CalledProcessError as e:
-            print(e.output)
+        # Re-create the directory for clang_tools
+        os.mkdir(clang_tools_path)
+        self.install_toolchain_artifact(clang_tools_path, checkout_root, toolchain_job)
 
     def install_toolchain_artifact(self, state_dir, checkout_root, toolchain_job):
         mach_binary = os.path.join(checkout_root, 'mach')
         mach_binary = os.path.abspath(mach_binary)
         if not os.path.exists(mach_binary):
             raise ValueError("mach not found at %s" % mach_binary)
 
         # If Python can't figure out what its own executable is, there's little
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -377,17 +377,17 @@ class Bootstrapper(object):
         if not have_clone:
             print(STYLE_NODEJS_REQUIRES_CLONE)
             sys.exit(1)
 
         self.instance.state_dir = state_dir
         self.instance.ensure_node_packages(state_dir, checkout_root)
         if not self.instance.artifact_mode:
             self.instance.ensure_stylo_packages(state_dir, checkout_root)
-            self.instance.ensure_clang_static_analysis_package(checkout_root)
+            self.instance.ensure_clang_static_analysis_package(state_dir, checkout_root)
 
     def check_telemetry_opt_in(self, state_dir):
         # We can't prompt the user.
         if self.instance.no_interactive:
             return
         # Don't prompt if the user already has a setting for this value.
         if self.mach_context is not None and 'telemetry' in self.mach_context.settings.build:
             return
--- a/python/mozboot/mozboot/freebsd.py
+++ b/python/mozboot/mozboot/freebsd.py
@@ -61,17 +61,17 @@ class FreeBSDBootstrapper(BaseBootstrapp
 
     def install_browser_artifact_mode_packages(self):
         self.ensure_browser_packages(artifact_mode=True)
 
     def ensure_browser_packages(self, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         self.pkg_install(*self.browser_packages)
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
         # TODO: we don't ship clang base static analysis for this platform
         pass
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # Clang / llvm already installed as browser package
         self.pkg_install('rust-cbindgen')
 
     def ensure_node_packages(self, state_dir, checkout_root):
--- a/python/mozboot/mozboot/linux_common.py
+++ b/python/mozboot/mozboot/linux_common.py
@@ -44,15 +44,17 @@ class NodeInstall(object):
         from mozboot import node
         self.install_toolchain_artifact(state_dir, checkout_root, node.LINUX)
 
 
 class ClangStaticAnalysisInstall(object):
     def __init__(self, **kwargs):
         pass
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
         if is_non_x86_64():
             print('Cannot install static analysis tools from taskcluster.\n'
                   'Please install these tools manually.')
             return
 
-        self.install_toolchain_static_analysis(checkout_root)
+        from mozboot import static_analysis
+        self.install_toolchain_static_analysis(
+            state_dir, checkout_root, static_analysis.LINUX_CLANG_TIDY)
--- a/python/mozboot/mozboot/mozillabuild.py
+++ b/python/mozboot/mozboot/mozillabuild.py
@@ -67,18 +67,20 @@ class MozillaBuildBootstrapper(BaseBoots
         pass
 
     def install_mobile_android_packages(self):
         pass
 
     def install_mobile_android_artifact_mode_packages(self):
         pass
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
-        self.install_toolchain_static_analysis(checkout_root)
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
+        from mozboot import static_analysis
+        self.install_toolchain_static_analysis(
+            state_dir, checkout_root, static_analysis.WINDOWS_CLANG_TIDY)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # On-device artifact builds are supported; on-device desktop builds are not.
         if is_aarch64_host():
             raise Exception('You should not be performing desktop builds on an '
                             'AArch64 device.  If you want to do artifact builds '
                             'instead, please choose the appropriate artifact build '
                             'option when beginning bootstrap.')
--- a/python/mozboot/mozboot/openbsd.py
+++ b/python/mozboot/mozboot/openbsd.py
@@ -42,17 +42,17 @@ class OpenBSDBootstrapper(BaseBootstrapp
     def install_browser_artifact_mode_packages(self):
         self.ensure_browser_packages(artifact_mode=True)
 
     def ensure_browser_packages(self, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         # we use -z because there's no other way to say "any autoconf-2.13"
         self.run_as_root(['pkg_add', '-z'] + self.browser_packages)
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
         # TODO: we don't ship clang base static analysis for this platform
         pass
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # Clang / llvm already installed as browser package
         self.run_as_root(['pkg_add', 'cbindgen'])
 
     def ensure_node_packages(self, state_dir, checkout_root):
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -511,18 +511,20 @@ class OSXBootstrapper(BaseBootstrapper):
 
             for check in ('/bin', '/usr/bin'):
                 if path == check:
                     print(BAD_PATH_ORDER % (check, p_dir, p_dir, check, p_dir))
                     sys.exit(1)
 
         return active_name.lower()
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
-        self.install_toolchain_static_analysis(checkout_root)
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
+        from mozboot import static_analysis
+        self.install_toolchain_static_analysis(
+            state_dir, checkout_root, static_analysis.MACOS_CLANG_TIDY)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         from mozboot import stylo
         # We installed clang via homebrew earlier.  However, on Android, we're
         # seeing many compiler errors so we use our own toolchain clang.
         if 'mobile_android' in self.application:
             self.install_toolchain_artifact(state_dir, checkout_root, stylo.MACOS_CLANG)
         self.install_toolchain_artifact(state_dir, checkout_root, stylo.MACOS_CBINDGEN)
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/static_analysis.py
@@ -0,0 +1,9 @@
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+WINDOWS_CLANG_TIDY = 'win64-clang-tidy'
+LINUX_CLANG_TIDY = 'linux64-clang-tidy'
+MACOS_CLANG_TIDY = 'macosx64-clang-tidy'
--- a/python/mozboot/mozboot/windows.py
+++ b/python/mozboot/mozboot/windows.py
@@ -94,18 +94,20 @@ class WindowsBootstrapper(BaseBootstrapp
         self.pacman_install(*self.BROWSER_PACKAGES)
 
     def install_mobile_android_packages(self):
         raise NotImplementedError('We do not support building Android on Windows. Sorry!')
 
     def install_mobile_android_artifact_mode_packages(self):
         raise NotImplementedError('We do not support building Android on Windows. Sorry!')
 
-    def ensure_clang_static_analysis_package(self, checkout_root):
-        self.install_toolchain_static_analysis(checkout_root)
+    def ensure_clang_static_analysis_package(self, state_dir, checkout_root):
+        from mozboot import static_analysis
+        self.install_toolchain_static_analysis(
+            state_dir, checkout_root, static_analysis.WINDOWS_CLANG_TIDY)
 
     def ensure_stylo_packages(self, state_dir, checkout_root):
         # On-device artifact builds are supported; on-device desktop builds are not.
         if is_aarch64_host():
             raise Exception('You should not be performing desktop builds on an '
                             'AArch64 device.  If you want to do artifact builds '
                             'instead, please choose the appropriate artifact build '
                             'option when beginning bootstrap.')
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -2812,17 +2812,18 @@ class StaticAnalysis(MachCommandBase):
                     subs.sort()
                     for filename in sorted(files):
                         f_in_dir = os.path.join(folder, filename)
                         if (f_in_dir.endswith(extensions)
                             and not self._is_ignored_path(ignored_dir_re, f_in_dir)):
                             # Supported extension and accepted path
                             path_list.append(f_in_dir)
             else:
-                if f.endswith(extensions):
+                # Make sure that the file exists and it has a supported extension
+                if os.path.isfile(f) and f.endswith(extensions):
                     path_list.append(f)
 
         return path_list
 
     def _run_clang_format_in_console(self, clang_format, paths, assume_filename):
         path_list = self._generate_path_list(assume_filename, False)
 
         if path_list == []:
--- a/taskcluster/ci/toolchain/rust.yml
+++ b/taskcluster/ci/toolchain/rust.yml
@@ -48,17 +48,17 @@ linux64-rust-1.32:
         toolchain-alias: linux64-rust
 
 linux64-rust-nightly:
     description: "rust nightly repack"
     treeherder:
         symbol: TL(rust-nightly)
     run:
         arguments: [
-            '--channel', 'nightly-2018-10-05',
+            '--channel', 'nightly-2018-12-14',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'i686-unknown-linux-gnu',
         ]
 
 linux64-rust-macos-1.31:
     description: "rust repack with macos-cross support"
     treeherder:
@@ -85,17 +85,17 @@ linux64-rust-macos-1.32:
         toolchain-alias: linux64-rust-macos
 
 linux64-rust-nightly-macos:
     description: "rust nightly repack with macos-cross support"
     treeherder:
         symbol: TL(rust-nightly-macos)
     run:
         arguments: [
-            '--channel', 'nightly-2018-10-05',
+            '--channel', 'nightly-2018-12-14',
             '--host', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-unknown-linux-gnu',
             '--target', 'x86_64-apple-darwin',
         ]
 
 linux64-rust-android-1.32:
     description: "rust repack with android-cross support"
     treeherder:
@@ -138,17 +138,17 @@ win64-rust-1.32:
         toolchain-artifact: public/build/rustc.tar.bz2
 
 win64-rust-nightly:
     description: "rust nightly repack"
     treeherder:
         symbol: TW64(rust-nightly)
     run:
         arguments: [
-            '--channel', 'nightly-2018-10-05',
+            '--channel', 'nightly-2018-12-14',
             '--host', 'x86_64-pc-windows-msvc',
             '--target', 'x86_64-pc-windows-msvc',
             '--target', 'i686-pc-windows-msvc',
         ]
         toolchain-artifact: public/build/rustc.tar.bz2
 
 win64-aarch64-rust:
     description: "rust repack with aarch64 support"
--- a/testing/mozbase/mozprofile/setup.py
+++ b/testing/mozbase/mozprofile/setup.py
@@ -2,17 +2,17 @@
 # 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/.
 
 from __future__ import absolute_import
 
 from setuptools import setup
 
 PACKAGE_NAME = 'mozprofile'
-PACKAGE_VERSION = '2.1.0'
+PACKAGE_VERSION = '2.2.0'
 
 deps = [
     'mozfile>=1.2',
     'mozlog~=4.0',
     'six>=1.10.0,<2',
 ]
 
 setup(name=PACKAGE_NAME,