- Revision
- 188321
- Author
- [email protected]
- Date
- 2015-08-11 20:16:15 -0700 (Tue, 11 Aug 2015)
Log Message
Web Inspector: Add the ability to filter out tasks in the Rendering Frames timeline
https://bugs.webkit.org/show_bug.cgi?id=147389
Reviewed by Timothy Hatcher.
Added filtering by task type to the Rendering Frames timeline view. Legend item checkboxes
in the timeline sidebar toggle filtering for the corresponding task. The "Other" category
cannot be filtered, since all rendering frame records include some "other" time in addition to
child records from at least one additional task type.
A row is filtered (hidden) from the data grid when the corresponding rendering frame has no
unfiltered child records.
* UserInterface/Base/Main.js:
(WebInspector._windowFocused):
(WebInspector._windowBlurred):
Added inactive style for docked window.
* UserInterface/Views/ChartDetailsSectionRow.css:
(.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > .color-key):
(.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label):
(.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]):
(body.window-inactive .details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]):
(.details-section > .content > .group > .row.chart > .defs-only):
(.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label > .color-swatch): Deleted.
(.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label): Deleted.
Switched to label elements, added checkbox styles, and updated color swatch style for non-checkbox items.
* UserInterface/Views/ChartDetailsSectionRow.js:
(WebInspector.ChartDetailsSectionRow):
Added svg filter container and style sheet element.
(WebInspector.ChartDetailsSectionRow.prototype.set data):
(WebInspector.ChartDetailsSectionRow.prototype._createLegend.createGammaPrimitive):
(WebInspector.ChartDetailsSectionRow.prototype._createLegend.createCheckboxFilterElement):
Helper function to create an svg:filter to blend legend key color over the native checkbox element.
Filter assumes grayscale input, and each checkbox requires a separate filter.
(WebInspector.ChartDetailsSectionRow.prototype._createLegend):
Creates legend items, svg filters, and checkbox style sheet.
(WebInspector.ChartDetailsSectionRow.prototype._legendItemCheckboxValueChanged):
Repackage and forward checkbox changed event.
(WebInspector.ChartDetailsSectionRow.prototype._createLegendItem): Deleted.
Logic moved to WebInspector.ChartDetailsSectionRow.prototype._createLegend.
* UserInterface/Views/TimelineSidebarPanel.js:
Added set of rendering frame task types against which tree elements can be filtered.
(WebInspector.TimelineSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
If filter is not empty, check ancestor rendering frame record for unfiltered data.
(WebInspector.TimelineSidebarPanel.prototype._frameSelectionLegendItemChecked):
Update task filter in response when legend items are checked/unchecked.
Modified Paths
Diff
Modified: trunk/Source/WebInspectorUI/ChangeLog (188320 => 188321)
--- trunk/Source/WebInspectorUI/ChangeLog 2015-08-12 02:35:34 UTC (rev 188320)
+++ trunk/Source/WebInspectorUI/ChangeLog 2015-08-12 03:16:15 UTC (rev 188321)
@@ -1,3 +1,55 @@
+2015-08-11 Matt Baker <[email protected]>
+
+ Web Inspector: Add the ability to filter out tasks in the Rendering Frames timeline
+ https://bugs.webkit.org/show_bug.cgi?id=147389
+
+ Reviewed by Timothy Hatcher.
+
+ Added filtering by task type to the Rendering Frames timeline view. Legend item checkboxes
+ in the timeline sidebar toggle filtering for the corresponding task. The "Other" category
+ cannot be filtered, since all rendering frame records include some "other" time in addition to
+ child records from at least one additional task type.
+
+ A row is filtered (hidden) from the data grid when the corresponding rendering frame has no
+ unfiltered child records.
+
+ * UserInterface/Base/Main.js:
+ (WebInspector._windowFocused):
+ (WebInspector._windowBlurred):
+ Added inactive style for docked window.
+
+ * UserInterface/Views/ChartDetailsSectionRow.css:
+ (.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > .color-key):
+ (.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label):
+ (.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]):
+ (body.window-inactive .details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]):
+ (.details-section > .content > .group > .row.chart > .defs-only):
+ (.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label > .color-swatch): Deleted.
+ (.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label): Deleted.
+ Switched to label elements, added checkbox styles, and updated color swatch style for non-checkbox items.
+
+ * UserInterface/Views/ChartDetailsSectionRow.js:
+ (WebInspector.ChartDetailsSectionRow):
+ Added svg filter container and style sheet element.
+ (WebInspector.ChartDetailsSectionRow.prototype.set data):
+ (WebInspector.ChartDetailsSectionRow.prototype._createLegend.createGammaPrimitive):
+ (WebInspector.ChartDetailsSectionRow.prototype._createLegend.createCheckboxFilterElement):
+ Helper function to create an svg:filter to blend legend key color over the native checkbox element.
+ Filter assumes grayscale input, and each checkbox requires a separate filter.
+ (WebInspector.ChartDetailsSectionRow.prototype._createLegend):
+ Creates legend items, svg filters, and checkbox style sheet.
+ (WebInspector.ChartDetailsSectionRow.prototype._legendItemCheckboxValueChanged):
+ Repackage and forward checkbox changed event.
+ (WebInspector.ChartDetailsSectionRow.prototype._createLegendItem): Deleted.
+ Logic moved to WebInspector.ChartDetailsSectionRow.prototype._createLegend.
+
+ * UserInterface/Views/TimelineSidebarPanel.js:
+ Added set of rendering frame task types against which tree elements can be filtered.
+ (WebInspector.TimelineSidebarPanel.prototype.matchTreeElementAgainstCustomFilters):
+ If filter is not empty, check ancestor rendering frame record for unfiltered data.
+ (WebInspector.TimelineSidebarPanel.prototype._frameSelectionLegendItemChecked):
+ Update task filter in response when legend items are checked/unchecked.
+
2015-08-11 Brent Fulgham <[email protected]>
[Win] Switch Windows build to Visual Studio 2015
Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Main.js (188320 => 188321)
--- trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2015-08-12 02:35:34 UTC (rev 188320)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Main.js 2015-08-12 03:16:15 UTC (rev 188321)
@@ -1214,20 +1214,20 @@
WebInspector._windowFocused = function(event)
{
- if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
+ if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
return;
// FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
- document.body.classList.remove("window-inactive");
+ document.body.classList.remove(this.docked ? "window-docked-inactive" : "window-inactive");
};
WebInspector._windowBlurred = function(event)
{
- if (event.target.document.nodeType !== Node.DOCUMENT_NODE || this.docked)
+ if (event.target.document.nodeType !== Node.DOCUMENT_NODE)
return;
// FIXME: We should use the :window-inactive pseudo class once https://webkit.org/b/38927 is fixed.
- document.body.classList.add("window-inactive");
+ document.body.classList.add(this.docked ? "window-docked-inactive" : "window-inactive");
};
WebInspector._windowResized = function(event)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css (188320 => 188321)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css 2015-08-12 02:35:34 UTC (rev 188320)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.css 2015-08-12 03:16:15 UTC (rev 188321)
@@ -52,17 +52,13 @@
height: 16px;
}
-.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label > .color-swatch {
+.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > .color-key {
display: inline-block;
-
- width: 8px;
- height: 8px;
- margin-right: 6px;
-
- border: solid 1px hsl(0, 0%, 40%);
+ border: solid 1px hsla(0, 0%, 0%, 0.15);
+ border-radius: 2px;
}
-.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > .label {
+.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label {
color: hsl(0, 0%, 20%);
}
@@ -74,3 +70,21 @@
display: table-cell;
vertical-align: middle;
}
+
+.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > .color-key,
+.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox] {
+ width: 10px;
+ height: 10px;
+ vertical-align: top;
+ margin: 0 8px 0 3px;
+}
+
+.details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]:not(:checked),
+body.window-docked-inactive .details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox],
+body.window-inactive .details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox] {
+ -webkit-filter: none !important;
+}
+
+.details-section > .content > .group > .row.chart > .defs-only {
+ display: none;
+}
\ No newline at end of file
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js (188320 => 188321)
--- trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js 2015-08-12 02:35:34 UTC (rev 188320)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ChartDetailsSectionRow.js 2015-08-12 03:16:15 UTC (rev 188321)
@@ -55,6 +55,14 @@
this._innerLabelFontSize = 11;
this._shadowColor = "rgba(0, 0, 0, 0.6)";
this._total = 0;
+
+ this._svgFiltersElement = document.createElement("svg");
+ this._svgFiltersElement.classList.add("defs-only");
+ this.element.append(this._svgFiltersElement);
+
+ this._checkboxStyleElement = document.createElement("style");
+ this._checkboxStyleElement.id = "checkbox-styles";
+ document.getElementsByTagName("head")[0].append(this._checkboxStyleElement);
}
// Public
@@ -108,40 +116,108 @@
this._items = items;
this._total = this._items.reduce(function(previousValue, currentValue) { return previousValue + currentValue.value; }, 0);;
- this._legendElement.removeChildren();
- this._items.forEach(function(item) { this._legendElement.appendChild(this._createLegendItem(item)); }, this);
-
+ this._createLegend();
this._refresh();
}
// Private
- _createLegendItem(item)
+ _addCheckboxColorFilter(id, r, g, b)
{
- var colorSwatchElement = document.createElement("div");
- colorSwatchElement.className = "color-swatch";
- colorSwatchElement.style.backgroundColor = item.color;
+ for (let i = 0; i < this._svgFiltersElement.childNodes.length; ++i) {
+ if (this._svgFiltersElement.childNodes[i].id === id)
+ return;
+ }
- var labelElement = document.createElement("div");
- labelElement.className = "label";
- labelElement.append(colorSwatchElement, item.label);
+ r /= 255;
+ b /= 255;
+ g /= 255;
- var valueElement = document.createElement("div");
- valueElement.className = "value";
+ // Create an svg:filter element that approximates "background-blend-mode: color", for grayscale input.
+ let filterElement = createSVGElement("filter");
+ filterElement.id = id;
+ filterElement.setAttribute("color-interpolation-filters", "sRGB");
- if (this._delegate && typeof this._delegate.formatChartValue === "function")
- valueElement.textContent = this._delegate.formatChartValue(item.value);
- else
- valueElement.textContent = item.value;
+ let values = [1 - r, 0, 0, 0, r,
+ 1 - g, 0, 0, 0, g,
+ 1 - b, 0, 0, 0, b,
+ 0, 0, 0, 1, 0];
- var legendItemElement = document.createElement("div");
- legendItemElement.className = "legend-item";
- legendItemElement.appendChild(labelElement);
- legendItemElement.appendChild(valueElement);
+ let colorMatrixPrimitive = createSVGElement("feColorMatrix");
+ colorMatrixPrimitive.setAttribute("type", "matrix");
+ colorMatrixPrimitive.setAttribute("values", values.join(" "));
- return legendItemElement;
+ function createGammaPrimitive(tagName, value)
+ {
+ let gammaPrimitive = createSVGElement(tagName);
+ gammaPrimitive.setAttribute("type", "gamma");
+ gammaPrimitive.setAttribute("value", value);
+ return gammaPrimitive;
+ }
+
+ let componentTransferPrimitive = createSVGElement("feComponentTransfer");
+ componentTransferPrimitive.append(createGammaPrimitive("feFuncR", 1.2), createGammaPrimitive("feFuncG", 1.2), createGammaPrimitive("feFuncB", 1.2));
+ filterElement.append(colorMatrixPrimitive, componentTransferPrimitive);
+
+ this._svgFiltersElement.append(filterElement);
+
+ let styleSheet = this._checkboxStyleElement.sheet;
+ styleSheet.insertRule(".details-section > .content > .group > .row.chart > .chart-content > .legend > .legend-item > label > input[type=checkbox]." + id + " { -webkit-filter: grayscale(1) url(#" + id + ") }", 0);
}
+ _createLegend()
+ {
+ this._legendElement.removeChildren();
+
+ for (let item of this._items) {
+ let labelElement = document.createElement("label");
+ let keyElement;
+ if (item.checkbox) {
+ let className = item.id.toLowerCase();
+ let rgb = item.color.substring(4, item.color.length - 1).replace(/ /g, "").split(",");
+ if (rgb[0] === rgb[1] && rgb[1] === rgb[2])
+ rgb[0] = rgb[1] = rgb[2] = Math.min(160, rgb[0]);
+
+ keyElement = document.createElement("input");
+ keyElement.type = "checkbox";
+ keyElement.classList.add(className);
+ keyElement.checked = item.checked || true;
+ keyElement[WebInspector.ChartDetailsSectionRow.DataItemIdSymbol] = item.id;
+
+ keyElement.addEventListener("change", this._legendItemCheckboxValueChanged.bind(this));
+
+ this._addCheckboxColorFilter(className, rgb[0], rgb[1], rgb[2]);
+ } else {
+ keyElement = document.createElement("div");
+ keyElement.classList.add("color-key");
+ keyElement.style.backgroundColor = item.color;
+ }
+
+ labelElement.append(keyElement, item.label);
+
+ let valueElement = document.createElement("div");
+ valueElement.classList.add("value");
+
+ if (this._delegate && typeof this._delegate.formatChartValue === "function")
+ valueElement.textContent = this._delegate.formatChartValue(item.value);
+ else
+ valueElement.textContent = item.value;
+
+ let legendItemElement = document.createElement("div");
+ legendItemElement.classList.add("legend-item");
+ legendItemElement.append(labelElement, valueElement);
+
+ this._legendElement.append(legendItemElement);
+ }
+ }
+
+ _legendItemCheckboxValueChanged(event)
+ {
+ let checkbox = event.target;
+ let id = checkbox[WebInspector.ChartDetailsSectionRow.DataItemIdSymbol];
+ this.dispatchEventToListeners(WebInspector.ChartDetailsSectionRow.Event.LegendItemChecked, {id, checked: checkbox.checked});
+ }
+
_refresh()
{
var width = this._canvas.clientWidth * window.devicePixelRatio;
@@ -194,3 +270,9 @@
}
}
};
+
+WebInspector.ChartDetailsSectionRow.DataItemIdSymbol = Symbol("chart-details-section-row-data-item-id");
+
+WebInspector.ChartDetailsSectionRow.Event = {
+ LegendItemChecked: "chart-details-section-row-legend-item-checked"
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js (188320 => 188321)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js 2015-08-12 02:35:34 UTC (rev 188320)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineSidebarPanel.js 2015-08-12 03:16:15 UTC (rev 188321)
@@ -95,7 +95,10 @@
this._frameSelectionChartRow = new WebInspector.ChartDetailsSectionRow(this);
this._frameSelectionChartRow.innerRadius = 0.5;
+ this._frameSelectionChartRow.addEventListener(WebInspector.ChartDetailsSectionRow.Event.LegendItemChecked, this._frameSelectionLegendItemChecked, this);
+ this._renderingFrameTaskFilter = new Set;
+
var chartGroup = new WebInspector.DetailsSectionGroup([this._frameSelectionChartRow]);
this._frameSelectionChartSection = new WebInspector.DetailsSection("frames-selection-chart", WebInspector.UIString("Selected Frames"), [chartGroup], null, true);
this._timelinesContentContainerElement.appendChild(this._frameSelectionChartSection.element);
@@ -391,6 +394,30 @@
if (!this._displayedContentView)
return true;
+ if (this._viewMode === WebInspector.TimelineSidebarPanel.ViewMode.RenderingFrames && this._renderingFrameTaskFilter.size) {
+ while (treeElement && !(treeElement.record instanceof WebInspector.RenderingFrameTimelineRecord))
+ treeElement = treeElement.parent;
+
+ console.assert(treeElement, "Cannot apply task filter: no RenderingFrameTimelineRecord found.");
+ if (!treeElement)
+ return false;
+
+ var visible = false;
+ for (var key in WebInspector.RenderingFrameTimelineRecord.TaskType) {
+ var taskType = WebInspector.RenderingFrameTimelineRecord.TaskType[key];
+ if (taskType === WebInspector.RenderingFrameTimelineRecord.TaskType.Other)
+ continue;
+
+ if (!this._renderingFrameTaskFilter.has(taskType) && treeElement.record.durationForTask(taskType) > 0) {
+ visible = true;
+ break;
+ }
+ }
+
+ if (!visible)
+ return false;
+ }
+
return this._displayedContentView.matchTreeElementAgainstCustomFilters(treeElement);
}
@@ -820,6 +847,16 @@
this.updateFilter();
}
+ _frameSelectionLegendItemChecked(event)
+ {
+ if (event.data.checked)
+ this._renderingFrameTaskFilter.delete(event.data.id);
+ else
+ this._renderingFrameTaskFilter.add(event.data.id);
+
+ this.updateFilter();
+ }
+
_refreshFrameSelectionChart()
{
if (!this.visible)
@@ -856,7 +893,7 @@
var label = WebInspector.RenderingFrameTimelineRecord.displayNameForTaskType(taskType);
var value = records.reduce(function(previousValue, currentValue) { return previousValue + currentValue.durationForTask(taskType); }, 0);
var color = this._chartColors.get(taskType);
- return {label, value, color};
+ return {id: taskType, label, value, color, checkbox: taskType !== WebInspector.RenderingFrameTimelineRecord.TaskType.Other};
}, this);
this._frameSelectionChartRow.data = ""