--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1424,16 +1424,17 @@ pref("devtools.debugger.ui.variables-sea
// Enable the Profiler and the Timeline
pref("devtools.profiler.enabled", true);
#ifdef MOZ_DEV_EDITION
pref("devtools.timeline.enabled", true);
#else
pref("devtools.timeline.enabled", false);
#endif
+pref("devtools.timeline.hiddenMarkers", "[]");
// Enable perftools via build command
#ifdef MOZ_DEVTOOLS_PERFTOOLS
pref("devtools.performance_dev.enabled", true);
#else
pref("devtools.performance_dev.enabled", false);
#endif
--- a/browser/devtools/shared/widgets/Graphs.jsm
+++ b/browser/devtools/shared/widgets/Graphs.jsm
@@ -611,24 +611,29 @@ AbstractCanvasGraph.prototype = {
let { x } = this._cursor;
return this._regions.find(({ start, end }) =>
(start < end && start < x && end > x) ||
(start > end && end < x && start > x));
},
/**
* Updates this graph to reflect the new dimensions of the parent node.
+ *
+ * @param boolean options.force
+ * Force redrawing everything
*/
- refresh: function() {
+ refresh: function(options={}) {
let bounds = this._parent.getBoundingClientRect();
let newWidth = this.fixedWidth || bounds.width;
let newHeight = this.fixedHeight || bounds.height;
- // Prevent redrawing everything if the graph's width & height won't change.
- if (this._width == newWidth * this._pixelRatio &&
+ // Prevent redrawing everything if the graph's width & height won't change,
+ // except if force=true.
+ if (!options.force &&
+ this._width == newWidth * this._pixelRatio &&
this._height == newHeight * this._pixelRatio) {
this.emit("refresh-cancelled");
return;
}
bounds.width = newWidth;
bounds.height = newHeight;
this._iframe.setAttribute("width", bounds.width);
--- a/browser/devtools/timeline/test/browser.ini
+++ b/browser/devtools/timeline/test/browser.ini
@@ -1,16 +1,17 @@
[DEFAULT]
subsuite = devtools
support-files =
doc_simple-test.html
head.js
[browser_timeline_aaa_run_first_leaktest.js]
[browser_timeline_blueprint.js]
+[browser_timeline_filters.js]
[browser_timeline_overview-initial-selection-01.js]
[browser_timeline_overview-initial-selection-02.js]
[browser_timeline_overview-update.js]
[browser_timeline_panels.js]
[browser_timeline_recording-without-memory.js]
[browser_timeline_recording.js]
[browser_timeline_waterfall-background.js]
[browser_timeline_waterfall-generic.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/timeline/test/browser_timeline_filters.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests markers filtering mechanism.
+ */
+
+let test = Task.async(function*() {
+ let { target, panel } = yield initTimelinePanel(SIMPLE_URL);
+ let { $, $$, TimelineController, TimelineView } = panel.panelWin;
+
+ yield TimelineController.toggleRecording();
+ ok(true, "Recording has started.");
+
+ yield waitUntil(() => {
+ // Wait until we get 3 different markers.
+ let markers = TimelineController.getMarkers();
+ return markers.some(m => m.name == "Styles") &&
+ markers.some(m => m.name == "Reflow") &&
+ markers.some(m => m.name == "Paint");
+ });
+
+ yield TimelineController.toggleRecording();
+
+ let overview = TimelineView.markersOverview;
+
+ // Select everything
+ overview.setSelection({ start: 0, end: overview.width })
+
+ $("#filter-button").click();
+
+ let menuItem1 = $("menuitem[marker-type=Styles]");
+ let menuItem2 = $("menuitem[marker-type=Reflow]");
+ let menuItem3 = $("menuitem[marker-type=Paint]");
+
+ let originalHeight = overview.fixedHeight;
+
+ ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker");
+ ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker");
+ ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker");
+
+ let heightBefore = overview.fixedHeight;
+ EventUtils.synthesizeMouseAtCenter(menuItem1, {type: "mouseup"}, panel.panelWin);
+ yield once(menuItem1, "command");
+ // A row is 11px. See markers-overview.js
+ is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
+ ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker");
+ ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker");
+ ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker");
+
+ heightBefore = overview.fixedHeight;
+ EventUtils.synthesizeMouseAtCenter(menuItem2, {type: "mouseup"}, panel.panelWin);
+ yield once(menuItem2, "command");
+ is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
+ ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker");
+ ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker");
+ ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker");
+
+ heightBefore = overview.fixedHeight;
+ EventUtils.synthesizeMouseAtCenter(menuItem3, {type: "mouseup"}, panel.panelWin);
+ yield once(menuItem3, "command");
+ is(overview.fixedHeight, heightBefore - 11, "Overview is smaller");
+ ok(!$(".waterfall-marker-bar[type=Styles]"), "No 'Styles' marker");
+ ok(!$(".waterfall-marker-bar[type=Reflow]"), "No 'Reflow' marker");
+ ok(!$(".waterfall-marker-bar[type=Paint]"), "No 'Paint' marker");
+
+ for (let item of [menuItem1, menuItem2, menuItem3]) {
+ EventUtils.synthesizeMouseAtCenter(item, {type: "mouseup"}, panel.panelWin);
+ yield once(item, "command");
+ }
+
+ ok($(".waterfall-marker-bar[type=Styles]"), "Found at least one 'Styles' marker");
+ ok($(".waterfall-marker-bar[type=Reflow]"), "Found at least one 'Reflow' marker");
+ ok($(".waterfall-marker-bar[type=Paint]"), "Found at least one 'Paint' marker");
+
+ is(overview.fixedHeight, originalHeight, "Overview restored");
+
+ $(".waterfall-marker-bar[type=Styles]");
+
+ yield teardown(panel);
+ finish();
+});
--- a/browser/devtools/timeline/test/head.js
+++ b/browser/devtools/timeline/test/head.js
@@ -124,9 +124,50 @@ function waitUntil(predicate, interval =
if (predicate()) {
return promise.resolve(true);
}
let deferred = promise.defer();
setTimeout(function() {
waitUntil(predicate).then(() => deferred.resolve(true));
}, interval);
return deferred.promise;
+
}
+
+/**
+ * Wait until next tick.
+ */
+function nextTick() {
+ let def = promise.defer();
+ executeSoon(() => def.resolve())
+ return def.promise;
+}
+
+/**
+ * Wait for eventName on target.
+ * @param {Object} target An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture=false) {
+ info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+ let deferred = promise.defer();
+
+ for (let [add, remove] of [
+ ["addEventListener", "removeEventListener"],
+ ["addListener", "removeListener"],
+ ["on", "off"]
+ ]) {
+ if ((add in target) && (remove in target)) {
+ target[add](eventName, function onEvent(...aArgs) {
+ info("Got event: '" + eventName + "' on " + target + ".");
+ target[remove](eventName, onEvent, useCapture);
+ deferred.resolve.apply(deferred, aArgs);
+ }, useCapture);
+ break;
+ }
+ }
+
+ return deferred.promise;
+}
--- a/browser/devtools/timeline/timeline.js
+++ b/browser/devtools/timeline/timeline.js
@@ -2,39 +2,50 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
+Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
devtools.lazyRequireGetter(this, "promise");
devtools.lazyRequireGetter(this, "EventEmitter",
"devtools/toolkit/event-emitter");
devtools.lazyRequireGetter(this, "MarkersOverview",
"devtools/timeline/markers-overview", true);
devtools.lazyRequireGetter(this, "MemoryOverview",
"devtools/timeline/memory-overview", true);
devtools.lazyRequireGetter(this, "Waterfall",
"devtools/timeline/waterfall", true);
devtools.lazyRequireGetter(this, "MarkerDetails",
"devtools/timeline/marker-details", true);
+devtools.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
+ "devtools/timeline/global", true);
devtools.lazyImporter(this, "CanvasGraphUtils",
"resource:///modules/devtools/Graphs.jsm");
devtools.lazyImporter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
const OVERVIEW_UPDATE_INTERVAL = 200;
const OVERVIEW_INITIAL_SELECTION_RATIO = 0.15;
+/**
+ * Preference for devtools.timeline.hiddenMarkers.
+ * Stores which markers should be hidden.
+ */
+const Prefs = new ViewHelpers.Prefs("devtools.timeline", {
+ hiddenMarkers: ["Json", "hiddenMarkers"]
+});
+
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
// When a recording is started or stopped, via the `stopwatch` button.
RECORDING_STARTED: "Timeline:RecordingStarted",
RECORDING_ENDED: "Timeline:RecordingEnded",
// When the overview graphs are populated with new markers.
OVERVIEW_UPDATED: "Timeline:OverviewUpdated",
@@ -245,32 +256,36 @@ let TimelineController = {
/**
* Functions handling the timeline frontend view.
*/
let TimelineView = {
/**
* Initialization function, called when the tool is started.
*/
initialize: Task.async(function*() {
- this.markersOverview = new MarkersOverview($("#markers-overview"));
- this.waterfall = new Waterfall($("#timeline-waterfall"), $("#timeline-pane"));
+ let blueprint = this._getFilteredBluePrint();
+ this.markersOverview = new MarkersOverview($("#markers-overview"), blueprint);
+ this.waterfall = new Waterfall($("#timeline-waterfall"), $("#timeline-pane"), blueprint);
this.markerDetails = new MarkerDetails($("#timeline-waterfall-details"), $("#timeline-waterfall-container > splitter"));
this._onSelecting = this._onSelecting.bind(this);
this._onRefresh = this._onRefresh.bind(this);
this.markersOverview.on("selecting", this._onSelecting);
this.markersOverview.on("refresh", this._onRefresh);
this.markerDetails.on("resize", this._onRefresh);
this._onMarkerSelected = this._onMarkerSelected.bind(this);
this.waterfall.on("selected", this._onMarkerSelected);
this.waterfall.on("unselected", this._onMarkerSelected);
yield this.markersOverview.ready();
+
yield this.waterfall.recalculateBounds();
+
+ this._buildFilterPopup();
}),
/**
* Destruction function, called when the tool is closed.
*/
destroy: function() {
this.markerDetails.off("resize", this._onRefresh);
this.markerDetails.destroy();
@@ -429,17 +444,111 @@ let TimelineView = {
},
/**
* Callback handling the "refresh" event on the timeline overview.
*/
_onRefresh: function() {
this.waterfall.recalculateBounds();
this.updateWaterfall();
- }
+ },
+
+ /**
+ * Rebuild a blueprint without hidden markers.
+ */
+ _getFilteredBluePrint: function() {
+ let hiddenMarkers = Prefs.hiddenMarkers;
+ let filteredBlueprint = Cu.cloneInto(TIMELINE_BLUEPRINT, {});
+ let maybeRemovedGroups = new Set();
+ let removedGroups = new Set();
+
+ // 1. Remove hidden markers from the blueprint.
+
+ for (let hiddenMarkerName of hiddenMarkers) {
+ maybeRemovedGroups.add(filteredBlueprint[hiddenMarkerName].group);
+ delete filteredBlueprint[hiddenMarkerName];
+ }
+
+ // 2. Get a list of all the groups that will be removed.
+
+ for (let removedGroup of maybeRemovedGroups) {
+ let markerNames = Object.keys(filteredBlueprint);
+ let allGroupsRemoved = markerNames.every(e => filteredBlueprint[e].group != removedGroup);
+ if (allGroupsRemoved) {
+ removedGroups.add(removedGroup);
+ }
+ }
+
+ // 3. Offset groups.
+
+ for (let removedGroup of removedGroups) {
+ for (let [, markerDetails] of Iterator(filteredBlueprint)) {
+ if (markerDetails.group > removedGroup) {
+ markerDetails.group--;
+ }
+ }
+ }
+
+ return filteredBlueprint;
+
+ },
+
+ /**
+ * When the list of hidden markers changes, update waterfall
+ * and overview.
+ */
+ _onHiddenMarkersChanged: function(e) {
+ let menuItems = $$("#timelineFilterPopup menuitem[marker-type]:not([checked])");
+ let hiddenMarkers = Array.map(menuItems, e => e.getAttribute("marker-type"));
+
+ Prefs.hiddenMarkers = hiddenMarkers;
+ let blueprint = this._getFilteredBluePrint();
+
+ this.waterfall.setBlueprint(blueprint);
+ this.updateWaterfall();
+
+ this.markersOverview.setBlueprint(blueprint);
+ this.markersOverview.refresh({ force: true });
+ },
+
+ /**
+ * Creates the filter popup.
+ */
+ _buildFilterPopup: function() {
+ let popup = $("#timelineFilterPopup");
+ let button = $("#filter-button");
+
+ popup.addEventListener("popupshowing", () => button.setAttribute("open", "true"));
+ popup.addEventListener("popuphiding", () => button.removeAttribute("open"));
+
+ this._onHiddenMarkersChanged = this._onHiddenMarkersChanged.bind(this);
+
+ for (let [markerName, markerDetails] of Iterator(TIMELINE_BLUEPRINT)) {
+ let menuitem = document.createElement("menuitem");
+ menuitem.setAttribute("closemenu", "none");
+ menuitem.setAttribute("type", "checkbox");
+ menuitem.setAttribute("marker-type", markerName);
+ menuitem.setAttribute("label", markerDetails.label);
+ menuitem.setAttribute("flex", "1");
+ menuitem.setAttribute("align", "center");
+
+ menuitem.addEventListener("command", this._onHiddenMarkersChanged);
+
+ if (Prefs.hiddenMarkers.indexOf(markerName) == -1) {
+ menuitem.setAttribute("checked", "true");
+ }
+
+ // Style used by pseudo element ::before in timeline.css.in
+ let bulletStyle = `--bullet-bg: ${markerDetails.fill};`
+ bulletStyle += `--bullet-border: ${markerDetails.stroke}`;
+ menuitem.setAttribute("style", bulletStyle);
+
+ popup.appendChild(menuitem);
+ }
+ },
};
/**
* Convenient way of emitting events from the panel window.
*/
EventEmitter.decorate(this);
/**
--- a/browser/devtools/timeline/timeline.xul
+++ b/browser/devtools/timeline/timeline.xul
@@ -12,26 +12,34 @@
<!ENTITY % timelineDTD SYSTEM "chrome://browser/locale/devtools/timeline.dtd">
%timelineDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://browser/content/devtools/theme-switching.js"/>
<script type="application/javascript" src="timeline.js"/>
+ <popupset id="timelinePopupset">
+ <menupopup id="timelineFilterPopup" position="after_start"/>
+ </popupset>
+
<vbox class="theme-body" flex="1">
<toolbar id="timeline-toolbar"
class="devtools-toolbar">
<hbox id="recordings-controls"
class="devtools-toolbarbutton-group"
align="center">
<toolbarbutton id="record-button"
class="devtools-toolbarbutton"
oncommand="TimelineController.toggleRecording()"
tooltiptext="&timelineUI.recordButton.tooltip;"/>
+ <toolbarbutton id="filter-button"
+ popup="timelineFilterPopup"
+ class="devtools-toolbarbutton"
+ tooltiptext="&timelineUI.filterButton.tooltip;"/>
<checkbox id="memory-checkbox"
label="&timelineUI.memoryCheckbox.label;"
oncommand="TimelineController.updateMemoryRecording()"
tooltiptext="&timelineUI.memoryCheckbox.tooltip;"/>
<label id="record-label"
value="&timelineUI.recordLabel;"/>
</hbox>
</toolbar>
--- a/browser/devtools/timeline/widgets/markers-overview.js
+++ b/browser/devtools/timeline/widgets/markers-overview.js
@@ -11,23 +11,21 @@
const {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource:///modules/devtools/Graphs.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
loader.lazyRequireGetter(this, "L10N",
"devtools/timeline/global", true);
-loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
- "devtools/timeline/global", true);
const HTML_NS = "http://www.w3.org/1999/xhtml";
const OVERVIEW_HEADER_HEIGHT = 14; // px
-const OVERVIEW_BODY_HEIGHT = 55; // 11px * 5 groups
+const OVERVIEW_ROW_HEIGHT = 11; // row height
const OVERVIEW_BACKGROUND_COLOR = "#fff";
const OVERVIEW_CLIPHEAD_LINE_COLOR = "#666";
const OVERVIEW_SELECTION_LINE_COLOR = "#555";
const OVERVIEW_SELECTION_BACKGROUND_COLOR = "rgba(76,158,217,0.25)";
const OVERVIEW_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
const OVERVIEW_HEADER_TICKS_MULTIPLE = 100; // ms
@@ -45,42 +43,45 @@ const OVERVIEW_MARKER_DURATION_MIN = 4;
const OVERVIEW_GROUP_VERTICAL_PADDING = 5; // px
const OVERVIEW_GROUP_ALTERNATING_BACKGROUND = "rgba(0,0,0,0.05)";
/**
* An overview for the markers data.
*
* @param nsIDOMNode parent
* The parent node holding the overview.
+ * @param Object blueprint
+ * List of names and colors defining markers.
*/
-function MarkersOverview(parent, ...args) {
+function MarkersOverview(parent, blueprint, ...args) {
AbstractCanvasGraph.apply(this, [parent, "markers-overview", ...args]);
+
+ // Set the list of names, properties and colors used to paint this overview.
+ this.setBlueprint(blueprint);
+
this.once("ready", () => {
- // Set the list of names, properties and colors used to paint this overview.
- this.setBlueprint(TIMELINE_BLUEPRINT);
-
// Populate this overview with some dummy initial data.
this.setData({ interval: { startTime: 0, endTime: 1000 }, markers: [] });
});
}
MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
clipheadLineColor: OVERVIEW_CLIPHEAD_LINE_COLOR,
selectionLineColor: OVERVIEW_SELECTION_LINE_COLOR,
selectionBackgroundColor: OVERVIEW_SELECTION_BACKGROUND_COLOR,
selectionStripesColor: OVERVIEW_SELECTION_STRIPES_COLOR,
headerHeight: OVERVIEW_HEADER_HEIGHT,
- bodyHeight: OVERVIEW_BODY_HEIGHT,
+ rowHeight: OVERVIEW_ROW_HEIGHT,
groupPadding: OVERVIEW_GROUP_VERTICAL_PADDING,
/**
* Compute the height of the overview.
*/
get fixedHeight() {
- return this.headerHeight + this.bodyHeight;
+ return this.headerHeight + this.rowHeight * (this._lastGroup + 1);
},
/**
* List of names and colors used to paint this overview.
* @see TIMELINE_BLUEPRINT in timeline/widgets/global.js
*/
setBlueprint: function(blueprint) {
this._paintBatches = new Map();
@@ -114,24 +115,27 @@ MarkersOverview.prototype = Heritage.ext
let canvasHeight = this._height;
let safeBounds = OVERVIEW_HEADER_SAFE_BOUNDS * this._pixelRatio;
let availableWidth = canvasWidth - safeBounds;
// Group markers into separate paint batches. This is necessary to
// draw all markers sharing the same style at once.
for (let marker of markers) {
- this._paintBatches.get(marker.name).batch.push(marker);
+ let markerType = this._paintBatches.get(marker.name);
+ if (markerType) {
+ markerType.batch.push(marker);
+ }
}
// Calculate each group's height, and the time-based scaling.
let totalGroups = this._lastGroup + 1;
let headerHeight = this.headerHeight * this._pixelRatio;
- let groupHeight = this.bodyHeight * this._pixelRatio / totalGroups;
+ let groupHeight = this.rowHeight * this._pixelRatio;
let groupPadding = this.groupPadding * this._pixelRatio;
let totalTime = (endTime - startTime) || 0;
let dataScale = this.dataScaleX = availableWidth / totalTime;
// Draw the header and overview background.
ctx.fillStyle = OVERVIEW_HEADER_BACKGROUND;
--- a/browser/devtools/timeline/widgets/waterfall.js
+++ b/browser/devtools/timeline/widgets/waterfall.js
@@ -7,18 +7,16 @@
* This file contains the "waterfall" view, essentially a detailed list
* of all the markers in the timeline data.
*/
const {Ci, Cu} = require("chrome");
loader.lazyRequireGetter(this, "L10N",
"devtools/timeline/global", true);
-loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
- "devtools/timeline/global", true);
loader.lazyImporter(this, "setNamedTimeout",
"resource:///modules/devtools/ViewHelpers.jsm");
loader.lazyImporter(this, "clearNamedTimeout",
"resource:///modules/devtools/ViewHelpers.jsm");
loader.lazyRequireGetter(this, "EventEmitter",
"devtools/toolkit/event-emitter");
@@ -45,18 +43,20 @@ const WATERFALL_ROWCOUNT_ONPAGEUPDOWN =
/**
* A detailed waterfall view for the timeline data.
*
* @param nsIDOMNode parent
* The parent node holding the waterfall.
* @param nsIDOMNode container
* The container node that key events should be bound to.
+ * @param Object blueprint
+ * List of names and colors defining markers.
*/
-function Waterfall(parent, container) {
+function Waterfall(parent, container, blueprint) {
EventEmitter.decorate(this);
this._parent = parent;
this._document = parent.ownerDocument;
this._container = container;
this._fragment = this._document.createDocumentFragment();
this._outstandingMarkers = [];
@@ -70,17 +70,17 @@ function Waterfall(parent, container) {
this._parent.appendChild(this._listContents);
this.setupKeys();
this._isRTL = this._getRTL();
// Lazy require is a bit slow, and these are hot objects.
this._l10n = L10N;
- this._blueprint = TIMELINE_BLUEPRINT;
+ this._blueprint = blueprint
this._setNamedTimeout = setNamedTimeout;
this._clearNamedTimeout = clearNamedTimeout;
// Selected row index. By default, we want the first
// row to be selected.
this._selectedRowIdx = 0;
// Default rowCount
@@ -116,16 +116,24 @@ Waterfall.prototype = {
// Label the header as if the first possible marker was at T=0.
this._buildHeader(this._headerContents, startTime - timeEpoch, dataScale);
this._buildMarkers(this._listContents, markers, startTime, endTime, dataScale);
this.selectRow(this._selectedRowIdx);
},
/**
+ * List of names and colors used to paint markers.
+ * @see TIMELINE_BLUEPRINT in timeline/widgets/global.js
+ */
+ setBlueprint: function(blueprint) {
+ this._blueprint = blueprint;
+ },
+
+ /**
* Keybindings.
*/
setupKeys: function() {
let pane = this._container;
pane.parentNode.parentNode.addEventListener("keydown", e => {
if (e.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_UP) {
e.preventDefault();
this.selectNearestRow(this._selectedRowIdx - 1);
@@ -246,16 +254,20 @@ Waterfall.prototype = {
let rowsCount = 0;
let markerIdx = -1;
for (let marker of markers) {
markerIdx++;
if (!isMarkerInRange(marker, startTime, endTime)) {
continue;
}
+ if (!(marker.name in this._blueprint)) {
+ continue;
+ }
+
// Only build and display a finite number of markers initially, to
// preserve a snappy UI. After a certain delay, continue building the
// outstanding markers while there's (hopefully) no user interaction.
let arguments_ = [this._fragment, marker, startTime, dataScale, markerIdx, rowsCount];
if (rowsCount++ < WATERFALL_IMMEDIATE_DRAW_MARKERS_COUNT) {
this._buildMarker.apply(this, arguments_);
} else {
this._outstandingMarkers.push(arguments_);
--- a/browser/locales/en-US/chrome/browser/devtools/timeline.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/timeline.dtd
@@ -14,25 +14,29 @@
<!-- LOCALIZATION NOTE (timelineUI.recordButton): This string is displayed
- on a button that starts a new recording. -->
<!ENTITY timelineUI.recordButton.tooltip "Record timeline operations">
<!-- LOCALIZATION NOTE (timelineUI.recordLabel): This string is displayed
- as a label to signal that a recording is in progress. -->
<!ENTITY timelineUI.recordLabel "Recording…">
-<!-- LOCALIZATION NOTE (timelineUI.timelineUI.memoryCheckbox.label): This string
+<!-- LOCALIZATION NOTE (timelineUI.memoryCheckbox.label): This string
- is displayed next to a checkbox determining whether or not memory
- measurements are enabled. -->
<!ENTITY timelineUI.memoryCheckbox.label "Memory">
-<!-- LOCALIZATION NOTE (timelineUI.timelineUI.memoryCheckbox.tooltip): This string
+<!-- LOCALIZATION NOTE (timelineUI.memoryCheckbox.tooltip): This string
- is displayed next to the memory checkbox -->
<!ENTITY timelineUI.memoryCheckbox.tooltip "Enable memory measurements">
+<!-- LOCALIZATION NOTE (timelineUI.filterButton.tooltip): This string
+ - is displayed next to the filter button-->
+<!ENTITY timelineUI.filterButton.tooltip "Select what data to display">
+
<!-- LOCALIZATION NOTE (timelineUI.emptyNotice1/2): This is the label shown
- in the timeline view when empty. -->
<!ENTITY timelineUI.emptyNotice1 "Click on the">
<!ENTITY timelineUI.emptyNotice2 "button to start recording timeline events.">
<!-- LOCALIZATION NOTE (timelineUI.stopNotice1/2): This is the label shown
- in the timeline view while recording. -->
<!ENTITY timelineUI.stopNotice1 "Click on the">
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -265,16 +265,17 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
* skin/classic/browser/devtools/canvasdebugger.css (devtools/canvasdebugger.css)
* skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
* skin/classic/browser/devtools/performance.css (devtools/performance.css)
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
+ skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
* skin/classic/browser/devtools/splitview.css (../shared/devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (../shared/devtools/styleeditor.css)
skin/classic/browser/devtools/storage.css (../shared/devtools/storage.css)
* skin/classic/browser/devtools/webaudioeditor.css (devtools/webaudioeditor.css)
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -391,16 +391,17 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
* skin/classic/browser/devtools/canvasdebugger.css (devtools/canvasdebugger.css)
* skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
* skin/classic/browser/devtools/performance.css (devtools/performance.css)
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
+ skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
* skin/classic/browser/devtools/splitview.css (../shared/devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (../shared/devtools/styleeditor.css)
skin/classic/browser/devtools/storage.css (../shared/devtools/storage.css)
* skin/classic/browser/devtools/webaudioeditor.css (devtools/webaudioeditor.css)
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/devtools/images/timeline-filter.svg
@@ -0,0 +1,37 @@
+<?xml version="1.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/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px"
+ width="16" height="16"
+ viewBox="0 0 16 16"
+ enable-background="new 0 0 16 16"
+ xml:space="preserve">
+<style>
+use:not(:target) {
+ display: none;
+}
+
+use {
+ fill: #EDF0F1;
+}
+
+use[id$="-disabled"] {
+ fill-opacity: 0.5;
+}
+
+use[id$="-open"] {
+ fill: #3BACE5;
+}
+
+</style>
+<defs style="display:none">
+ <path id="filter-shape"
+ d="M 2,2 v 3 l 5,4 v 6 h 2 v -6 l 5,-4 v -3 L 14,2 z"/>
+</defs>
+<use id="filter" xlink:href="#filter-shape"/>
+<use id="filter-disabled" xlink:href="#filter-shape"/>
+<use id="filter-open" xlink:href="#filter-shape"/>
+</svg>
--- a/browser/themes/shared/devtools/timeline.inc.css
+++ b/browser/themes/shared/devtools/timeline.inc.css
@@ -1,25 +1,55 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
#record-button {
list-style-image: url(profiler-stopwatch.svg);
+ min-width: 24px;
}
#record-button[checked] {
list-style-image: url(profiler-stopwatch-checked.svg);
}
#record-button:not([checked]) ~ #record-label {
visibility: hidden;
}
+#memory-checkbox .checkbox-label {
+ line-height: 100%;
+}
+
+#filter-button {
+ list-style-image: url(timeline-filter.svg#filter);
+ min-width: 24px;
+}
+
+#filter-button[disabled] {
+ list-style-image: url(timeline-filter.svg#filter-disabled);
+}
+
+#filter-button[open] {
+ list-style-image: url(timeline-filter.svg#filter-open);
+}
+
+#timelineFilterPopup > menuitem:before {
+ content: "";
+ display: block;
+ width: 8px;
+ height: 8px;
+ margin: 0 8px;
+ border: 1px solid;
+ border-radius: 1px;
+ background-color: var(--bullet-bg);
+ border-color: var(--bullet-border);
+}
+
.notice-container {
font-size: 120%;
padding-bottom: 35vh;
}
.theme-dark .notice-container {
background: #343c45; /* Toolbars */
color: #f5f7fa; /* Light foreground text */
@@ -180,17 +210,16 @@
-moz-padding-end: 8px;
padding-top: 8vh;
overflow: auto;
}
.marker-details-bullet {
width: 8px;
height: 8px;
- margin: 0 8px;
border: 1px solid;
border-radius: 1px;
}
.marker-details-start,
.marker-details-end,
.marker-details-duration {
padding-top: 3px;
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -156,35 +156,35 @@
background-color: rgba(170, 170, 170, .2); /* Splitter */
}
.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]),
.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image] {
background-color: rgba(0, 0, 0, .2); /* Splitter */
}
/* Button States */
-.theme-dark .devtools-toolbarbutton:hover,
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover,
-.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-dark .devtools-toolbarbutton:not([disabled]):hover,
+.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
+.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
background: rgba(0, 0, 0, .3); /* Splitters */
}
-.theme-light .devtools-toolbarbutton:hover,
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover,
-.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover {
+.theme-light .devtools-toolbarbutton:not([disabled]):hover,
+.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover,
+.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover {
background: rgba(170, 170, 170, .3); /* Splitters */
}
-.theme-dark .devtools-toolbarbutton:hover:active,
-.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover:active,
-.theme-dark .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-dark .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-dark #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
+.theme-dark .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
background: rgba(0, 0, 0, .4); /* Splitters */
}
-.theme-light .devtools-toolbarbutton:hover:active,
-.theme-light #toolbox-buttons .devtools-toolbarbutton[text-as-image]:hover:active,
-.theme-light .devtools-toolbarbutton[label]:not([text-as-image]):not([type=menu-button]):hover:active {
+.theme-light .devtools-toolbarbutton:not([disabled]):hover:active,
+.theme-light #toolbox-buttons .devtools-toolbarbutton:not([disabled])[text-as-image]:hover:active,
+.theme-light .devtools-toolbarbutton:not([disabled])[label]:not([text-as-image]):not([type=menu-button]):hover:active {
background: rgba(170, 170, 170, .4); /* Splitters */
}
/* Menu type buttons and checked states */
.theme-dark .devtools-toolbarbutton[checked=true],
.theme-dark #toolbox-buttons .devtools-toolbarbutton[text-as-image][checked] {
background: rgba(29, 79, 115, .7); /* Select highlight blue */
color: var(--theme-selection-color);
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -301,16 +301,17 @@ browser.jar:
skin/classic/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/browser/devtools/canvasdebugger.css (devtools/canvasdebugger.css)
* skin/classic/browser/devtools/debugger.css (devtools/debugger.css)
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
* skin/classic/browser/devtools/performance.css (devtools/performance.css)
* skin/classic/browser/devtools/timeline.css (devtools/timeline.css)
+ skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
* skin/classic/browser/devtools/shadereditor.css (devtools/shadereditor.css)
skin/classic/browser/devtools/storage.css (../shared/devtools/storage.css)
* skin/classic/browser/devtools/splitview.css (../shared/devtools/splitview.css)
skin/classic/browser/devtools/styleeditor.css (../shared/devtools/styleeditor.css)
* skin/classic/browser/devtools/webaudioeditor.css (devtools/webaudioeditor.css)
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
@@ -748,16 +749,17 @@ browser.jar:
skin/classic/aero/browser/devtools/breadcrumbs-scrollbutton@2x.png (../shared/devtools/images/breadcrumbs-scrollbutton@2x.png)
* skin/classic/aero/browser/devtools/canvasdebugger.css (devtools/canvasdebugger.css)
* skin/classic/aero/browser/devtools/debugger.css (devtools/debugger.css)
skin/classic/aero/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
* skin/classic/aero/browser/devtools/performance.css (devtools/performance.css)
* skin/classic/aero/browser/devtools/timeline.css (devtools/timeline.css)
+ skin/classic/aero/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css)
* skin/classic/aero/browser/devtools/shadereditor.css (devtools/shadereditor.css)
* skin/classic/aero/browser/devtools/splitview.css (../shared/devtools/splitview.css)
skin/classic/aero/browser/devtools/styleeditor.css (../shared/devtools/styleeditor.css)
skin/classic/aero/browser/devtools/storage.css (../shared/devtools/storage.css)
* skin/classic/aero/browser/devtools/webaudioeditor.css (devtools/webaudioeditor.css)
skin/classic/aero/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
skin/classic/aero/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)