Chromium Code Reviews| Index: Source/devtools/front_end/timeline/TimelineTreeView.js |
| diff --git a/Source/devtools/front_end/timeline/TimelineTreeView.js b/Source/devtools/front_end/timeline/TimelineTreeView.js |
| index 24fd410b0f750689eb43b26c2500750794f21152..5fced5eed0fba8a4fd4fb2526df61b11b2373157 100644 |
| --- a/Source/devtools/front_end/timeline/TimelineTreeView.js |
| +++ b/Source/devtools/front_end/timeline/TimelineTreeView.js |
| @@ -15,7 +15,7 @@ WebInspector.TimelineTreeView = function(model) |
| this._model = model; |
| var columns = []; |
| columns.push({id: "self", title: WebInspector.UIString("Self Time"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); |
| - columns.push({id: "total", title: WebInspector.UIString("Total Time"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); |
| + columns.push({id: "total", title: WebInspector.UIString("Total Time"), width: "120px", sortable: true}); |
| columns.push({id: "activity", title: WebInspector.UIString("Activity"), disclosure: true, sortable: true}); |
| this._filters = [ |
| @@ -36,6 +36,14 @@ WebInspector.TimelineTreeView = function(model) |
| /** |
| * @enum {string} |
| */ |
| +WebInspector.TimelineTreeView.Mode = { |
| + TopDown: "TopDown", |
| + BottomUp: "BottomUp" |
| +} |
| + |
| +/** |
| + * @enum {string} |
| + */ |
| WebInspector.TimelineTreeView.GroupBy = { |
| None: "None", |
| Domain: "Domain", |
| @@ -51,132 +59,176 @@ WebInspector.TimelineTreeView.prototype = { |
| { |
| this._startTime = selection.startTime(); |
| this._endTime = selection.endTime(); |
| - this._refreshRecords(); |
| + this._refreshTree(); |
| }, |
| _createToolbar: function() |
| { |
| - this._panelToolbar = new WebInspector.Toolbar(this.element); |
| + var panelToolbar = new WebInspector.Toolbar(this.element); |
| + panelToolbar.appendToolbarItem(new WebInspector.ToolbarText(WebInspector.UIString("View"))); |
| + |
| + this._modeCombobox = new WebInspector.ToolbarComboBox(this._onTreeModeChanged.bind(this)); |
| + this._modeCombobox.addOption(this._modeCombobox.createOption(WebInspector.UIString("Costly Functions"), "", WebInspector.TimelineTreeView.Mode.BottomUp)); |
| + this._modeCombobox.addOption(this._modeCombobox.createOption(WebInspector.UIString("Costly Entrypoints"), "", WebInspector.TimelineTreeView.Mode.TopDown)); |
| + panelToolbar.appendToolbarItem(this._modeCombobox); |
| + |
| this._groupByCombobox = new WebInspector.ToolbarComboBox(this._onGroupByChanged.bind(this)); |
| /** |
| * @param {string} name |
| * @param {string} id |
| * @this {WebInspector.TimelineTreeView} |
| */ |
| - function addOption(name, id) |
| + function addGroupingOption(name, id) |
| { |
| var option = this._groupByCombobox.createOption(name, "", id); |
| this._groupByCombobox.addOption(option); |
| if (id === this._groupBySetting.get()) |
| this._groupByCombobox.select(option); |
| } |
| - addOption.call(this, WebInspector.UIString("No Grouping"), WebInspector.TimelineTreeView.GroupBy.None); |
| - addOption.call(this, WebInspector.UIString("Group by Domain"), WebInspector.TimelineTreeView.GroupBy.Domain); |
| - addOption.call(this, WebInspector.UIString("Group by Domain (2nd Level)"), WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel); |
| - addOption.call(this, WebInspector.UIString("Group by URL"), WebInspector.TimelineTreeView.GroupBy.URL); |
| - this._panelToolbar.appendToolbarItem(this._groupByCombobox); |
| + panelToolbar.appendToolbarItem(new WebInspector.ToolbarText(WebInspector.UIString("Group by"))); |
| + addGroupingOption.call(this, WebInspector.UIString("Function"), WebInspector.TimelineTreeView.GroupBy.None); |
| + addGroupingOption.call(this, WebInspector.UIString("Domain"), WebInspector.TimelineTreeView.GroupBy.Domain); |
| + addGroupingOption.call(this, WebInspector.UIString("Domain (2nd Level)"), WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel); |
| + addGroupingOption.call(this, WebInspector.UIString("URL"), WebInspector.TimelineTreeView.GroupBy.URL); |
| + panelToolbar.appendToolbarItem(this._groupByCombobox); |
| + }, |
| + |
| + _onTreeModeChanged: function() |
| + { |
| + this._refreshTree(); |
| }, |
| _onGroupByChanged: function() |
| { |
| this._groupBySetting.set(this._groupByCombobox.selectedOption().value); |
| - this._refreshRecords(); |
| + this._refreshTree(); |
| }, |
| - _refreshRecords: function() |
| + _refreshTree: function() |
| { |
| - var groupBy = this._groupBySetting.get(); |
| - var groupNodes = new Map(); |
| - |
| - /** |
| - * @param {string} id |
| - * @return {!WebInspector.TimelineModel.ProfileTreeNode} |
| - */ |
| - function groupNodeById(id) |
| - { |
| - var node = groupNodes.get(id); |
| - if (!node) { |
| - node = new WebInspector.TimelineModel.ProfileTreeNode(); |
| - node.name = id || WebInspector.UIString("(unknown)"); |
| - node.selfTime = 0; |
| - node.totalTime = 0; |
| - groupNodes.set(id, node); |
| - } |
| - return node; |
| - } |
| - |
| - /** |
| - * @return {?WebInspector.TimelineModel.ProfileTreeNode} |
| - */ |
| - function groupByNone() |
| - { |
| - return null; |
| - } |
| + if (this._modeCombobox.selectedOption().value === WebInspector.TimelineTreeView.Mode.TopDown) |
| + this._refreshTopDownTree(); |
| + else |
| + this._refreshBottomUpTree(); |
| + }, |
| + /** |
| + * @return {?function(!WebInspector.TimelineModel.ProfileTreeNode):string} |
| + */ |
| + _nodeToGroupIdFunction: function() |
| + { |
| /** |
| * @param {!WebInspector.TimelineModel.ProfileTreeNode} node |
| - * @return {?WebInspector.TimelineModel.ProfileTreeNode} |
| + * @return {string} |
| */ |
| function groupByURL(node) |
| { |
| - return groupNodeById(WebInspector.TimelineTreeView.eventURL(node.event) || ""); |
| + return WebInspector.TimelineTreeView.eventURL(node.event) || ""; |
| } |
| /** |
| * @param {!WebInspector.TimelineModel.ProfileTreeNode} node |
| - * @return {?WebInspector.TimelineModel.ProfileTreeNode} |
| + * @return {string} |
| */ |
| function groupByDomain(node) |
| { |
| var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL(); |
| - var domain = parsedURL && parsedURL.host || ""; |
| - return groupNodeById(domain); |
| + return parsedURL && parsedURL.host || ""; |
| } |
| /** |
| * @param {!WebInspector.TimelineModel.ProfileTreeNode} node |
| - * @return {?WebInspector.TimelineModel.ProfileTreeNode} |
| + * @return {string} |
| */ |
| function groupByDomainSecondLevel(node) |
| { |
| var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL(); |
| if (!parsedURL) |
| - return groupNodeById(""); |
| + return ""; |
| if (/^[.0-9]+$/.test(parsedURL.host)) |
| - return groupNodeById(parsedURL.host) |
| + return parsedURL.host; |
| var domainMatch = /([^.]*\.)?[^.]*$/.exec(parsedURL.host); |
| - return groupNodeById(domainMatch && domainMatch[0] || ""); |
| + return domainMatch && domainMatch[0] || ""; |
| } |
| + var groupByMap = /** @type {!Map<!WebInspector.TimelineTreeView.GroupBy,?function(!WebInspector.TimelineModel.ProfileTreeNode):string>} */ (new Map([ |
| + [WebInspector.TimelineTreeView.GroupBy.None, null], |
| + [WebInspector.TimelineTreeView.GroupBy.Domain, groupByDomain], |
| + [WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel, groupByDomainSecondLevel], |
| + [WebInspector.TimelineTreeView.GroupBy.URL, groupByURL] |
| + ])); |
| + return groupByMap.get(this._groupBySetting.get()) || null; |
| + }, |
| + |
| + /** |
| + * @param {function(!WebInspector.TimelineModel.ProfileTreeNode):string} nodeToGroupId |
| + * @param {!WebInspector.TimelineModel.ProfileTreeNode} node |
| + * @return {!WebInspector.TimelineModel.ProfileTreeNode} |
| + */ |
| + _nodeToGroupNode: function(nodeToGroupId, node) |
| + { |
| + var id = nodeToGroupId(node); |
| + var groupNode = this._groupNodes.get(id); |
| + if (!groupNode) { |
| + groupNode = new WebInspector.TimelineModel.ProfileTreeNode(); |
| + groupNode.name = id || WebInspector.UIString("(unknown)"); |
| + groupNode.selfTime = 0; |
| + groupNode.totalTime = 0; |
| + groupNode.children = new Map(); |
| + this._groupNodes.set(id, groupNode); |
| + } |
| + return groupNode; |
| + }, |
| + |
| + _refreshTopDownTree: function() |
| + { |
| + var nonessentialEvents = new Set([ |
|
caseq
2015/08/17 21:53:05
Also, please extract this fileter to a class so it
alph
2015/08/17 23:25:04
Done.
|
| + WebInspector.TimelineModel.RecordType.EventDispatch, |
| + WebInspector.TimelineModel.RecordType.FunctionCall, |
| + WebInspector.TimelineModel.RecordType.TimerFire |
| + ]); |
| /** |
| - * @param {!WebInspector.TracingModel.Event} e |
| - * @return {string} |
| + * @param {!WebInspector.TracingModel.Event} event |
| + * @return {boolean} |
| */ |
| - function eventId(e) |
| + function nonessentialEventsFilter(event) |
| { |
| - // Function call frames are always groupped by the URL |
| - if (e.name === "JSFrame") { |
| - var data = e.args["data"]; |
| - return "f:" + (data["callUID"] || WebInspector.TimelineTreeView.eventURL(e)); |
| + return !nonessentialEvents.has(event.name); |
| + } |
| + var filters = this._filters.concat(new WebInspector.CustomFilter(nonessentialEventsFilter)); |
| + |
| + this.dataGrid.rootNode().removeChildren(); |
| + var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.mainThreadEvents(), this._startTime, this._endTime, filters, this._eventId); |
| + var nodeToGroupId = this._nodeToGroupIdFunction(); |
| + if (nodeToGroupId) { |
| + this._groupNodes = new Map(); |
| + for (var node of topDown.children.values()) { |
| + var groupNode = this._nodeToGroupNode(nodeToGroupId, node); |
| + groupNode.selfTime += node.selfTime; |
| + groupNode.totalTime += node.totalTime; |
| + groupNode.children.set(node.id, node); |
| } |
| - // While the rest of events are groupped by the event type |
| - // unless the group by URL/Domain mode is on. |
| - if (groupBy === WebInspector.TimelineTreeView.GroupBy.URL) |
| - return e.name + ":@" + WebInspector.TimelineTreeView.eventURL(e); |
| - return e.name; |
| + topDown.children = this._groupNodes; |
| + this._groupNodes = null; |
| + } |
| + for (var child of topDown.children.values()) { |
|
caseq
2015/08/17 21:50:53
Can we extract some common prologues/epilogues of
alph
2015/08/17 23:25:04
Done.
|
| + // Exclude the idle time off the total calculation. |
| + var gridNode = new WebInspector.TimelineTreeView.GridNode(child, topDown.totalTime); |
| + this.dataGrid.insertChild(gridNode); |
| } |
| + this._sortingChanged(); |
| + }, |
| - var groupByMapper = new Map([ |
| - [WebInspector.TimelineTreeView.GroupBy.None, groupByNone], |
| - [WebInspector.TimelineTreeView.GroupBy.Domain, groupByDomain], |
| - [WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel, groupByDomainSecondLevel], |
| - [WebInspector.TimelineTreeView.GroupBy.URL, groupByURL] |
| - ]); |
| - var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.mainThreadEvents(), this._startTime, this._endTime, this._filters, eventId); |
| - var bottomUpRoot = WebInspector.TimelineModel.buildBottomUpTree(topDown, groupByMapper.get(groupBy)); |
| - for (var group of groupNodes) |
| - bottomUpRoot.children.set(group[0], group[1]); |
| + _refreshBottomUpTree: function() |
| + { |
| + this._groupNodes = new Map(); |
| + var nodeToGroupId = this._nodeToGroupIdFunction(); |
| + var nodeToGroupNode = nodeToGroupId ? this._nodeToGroupNode.bind(this, nodeToGroupId) : null; |
| this.dataGrid.rootNode().removeChildren(); |
| + var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.mainThreadEvents(), this._startTime, this._endTime, this._filters, this._eventId); |
| + var bottomUpRoot = WebInspector.TimelineModel.buildBottomUpTree(topDown, nodeToGroupNode); |
| + for (var group of this._groupNodes) |
| + bottomUpRoot.children.set(group[0], group[1]); |
| for (var child of bottomUpRoot.children.values()) { |
| // Exclude the idle time off the total calculation. |
| var gridNode = new WebInspector.TimelineTreeView.GridNode(child, topDown.totalTime); |
| @@ -210,6 +262,19 @@ WebInspector.TimelineTreeView.prototype = { |
| this.dataGrid.sortNodes(compareField.bind(null, field), !this.dataGrid.isSortOrderAscending()); |
| }, |
| + /** |
| + * @param {!WebInspector.TracingModel.Event} e |
| + * @return {string} |
| + */ |
| + _eventId: function(e) |
|
caseq
2015/08/17 21:50:53
can we make it a class method?
alph
2015/08/17 23:25:04
Done.
|
| + { |
| + if (e.name === WebInspector.TimelineModel.RecordType.JSFrame) { |
| + var data = e.args["data"]; |
| + return "f:" + (data["callUID"] || WebInspector.TimelineTreeView.eventURL(e)); |
| + } |
| + return e.name + ":@" + WebInspector.TimelineTreeView.eventURL(e); |
| + }, |
| + |
| __proto__: WebInspector.VBox.prototype |
| } |