Bug 1087877 - [timeline] User should be able to filter out any type of marker. r=vporof

This commit is contained in:
Paul Rouget 2014-12-15 05:07:00 -05:00
Родитель 77d594ab26
Коммит 4406c2c5e0
16 изменённых файлов: 373 добавлений и 36 удалений

Просмотреть файл

@ -1429,6 +1429,7 @@ 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

Просмотреть файл

@ -616,14 +616,19 @@ AbstractCanvasGraph.prototype = {
/**
* 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;

Просмотреть файл

@ -6,6 +6,7 @@ support-files =
[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]

Просмотреть файл

@ -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();
});

Просмотреть файл

@ -129,4 +129,45 @@ function waitUntil(predicate, interval = 10) {
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;
}

Просмотреть файл

@ -7,6 +7,7 @@ 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",
@ -20,6 +21,8 @@ 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");
@ -30,6 +33,14 @@ devtools.lazyImporter(this, "PluralForm",
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.
@ -250,8 +261,9 @@ 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);
@ -265,7 +277,10 @@ let TimelineView = {
this.waterfall.on("unselected", this._onMarkerSelected);
yield this.markersOverview.ready();
yield this.waterfall.recalculateBounds();
this._buildFilterPopup();
}),
/**
@ -434,7 +449,101 @@ let TimelineView = {
_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);
}
},
};
/**

Просмотреть файл

@ -17,6 +17,10 @@
<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">
@ -27,6 +31,10 @@
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()"

Просмотреть файл

@ -16,13 +16,11 @@ 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";
@ -50,13 +48,16 @@ const OVERVIEW_GROUP_ALTERNATING_BACKGROUND = "rgba(0,0,0,0.05)";
*
* @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]);
this.once("ready", () => {
// Set the list of names, properties and colors used to paint this overview.
this.setBlueprint(TIMELINE_BLUEPRINT);
// Set the list of names, properties and colors used to paint this overview.
this.setBlueprint(blueprint);
this.once("ready", () => {
// Populate this overview with some dummy initial data.
this.setData({ interval: { startTime: 0, endTime: 1000 }, markers: [] });
});
@ -68,14 +69,14 @@ MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
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);
},
/**
@ -119,14 +120,17 @@ MarkersOverview.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
// 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;

Просмотреть файл

@ -12,8 +12,6 @@ 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");
@ -50,8 +48,10 @@ const WATERFALL_ROWCOUNT_ONPAGEUPDOWN = 10;
* 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;
@ -75,7 +75,7 @@ function Waterfall(parent, container) {
// 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;
@ -120,6 +120,14 @@ Waterfall.prototype = {
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.
*/
@ -251,6 +259,10 @@ Waterfall.prototype = {
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.

Просмотреть файл

@ -19,15 +19,19 @@
- 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">

Просмотреть файл

@ -270,6 +270,7 @@ browser.jar:
* 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)

Просмотреть файл

@ -396,6 +396,7 @@ browser.jar:
* 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)

Просмотреть файл

@ -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>

После

Ширина:  |  Высота:  |  Размер: 941 B

Просмотреть файл

@ -5,6 +5,7 @@
#record-button {
list-style-image: url(profiler-stopwatch.svg);
min-width: 24px;
}
#record-button[checked] {
@ -15,6 +16,35 @@
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;
@ -185,7 +215,6 @@
.marker-details-bullet {
width: 8px;
height: 8px;
margin: 0 8px;
border: 1px solid;
border-radius: 1px;
}

Просмотреть файл

@ -161,25 +161,25 @@
}
/* 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 */
}

Просмотреть файл

@ -306,6 +306,7 @@ browser.jar:
* 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)
@ -753,6 +754,7 @@ browser.jar:
* 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)