Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(491)

Side by Side Diff: Source/devtools/front_end/timeline/TimelineTreeView.js

Issue 1298543002: DevTools: Support Top-Down tree view on Timeline (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Source/devtools/front_end/timeline/TimelinePanel.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @constructor 6 * @constructor
7 * @extends {WebInspector.VBox} 7 * @extends {WebInspector.VBox}
8 * @param {!WebInspector.TimelineModel} model 8 * @param {!WebInspector.TimelineModel} model
9 */ 9 */
10 WebInspector.TimelineTreeView = function(model) 10 WebInspector.TimelineTreeView = function(model)
11 { 11 {
12 WebInspector.VBox.call(this); 12 WebInspector.VBox.call(this);
13 this.element.classList.add("timeline-tree-view"); 13 this.element.classList.add("timeline-tree-view");
14 14
15 this._model = model; 15 this._model = model;
16 var columns = []; 16 var columns = [];
17 columns.push({id: "self", title: WebInspector.UIString("Self Time"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); 17 columns.push({id: "self", title: WebInspector.UIString("Self Time"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true});
18 columns.push({id: "total", title: WebInspector.UIString("Total Time"), width : "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}); 18 columns.push({id: "total", title: WebInspector.UIString("Total Time"), width : "120px", sortable: true});
19 columns.push({id: "activity", title: WebInspector.UIString("Activity"), disc losure: true, sortable: true}); 19 columns.push({id: "activity", title: WebInspector.UIString("Activity"), disc losure: true, sortable: true});
20 20
21 this._filters = [ 21 this._filters = [
22 WebInspector.TimelineUIUtils.hiddenEventsFilter(), 22 WebInspector.TimelineUIUtils.hiddenEventsFilter(),
23 new WebInspector.ExcludeTopLevelFilter() 23 new WebInspector.ExcludeTopLevelFilter()
24 ]; 24 ];
25 25
26 this._groupBySetting = WebInspector.settings.createSetting("timelineTreeGrou pBy", WebInspector.TimelineTreeView.GroupBy.None); 26 this._groupBySetting = WebInspector.settings.createSetting("timelineTreeGrou pBy", WebInspector.TimelineTreeView.GroupBy.None);
27 27
28 this.dataGrid = new WebInspector.SortableDataGrid(columns); 28 this.dataGrid = new WebInspector.SortableDataGrid(columns);
29 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortingChanged, this); 29 this.dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this._sortingChanged, this);
30 30
31 this._createToolbar(); 31 this._createToolbar();
32 32
33 this.dataGrid.show(this.element); 33 this.dataGrid.show(this.element);
34 } 34 }
35 35
36 /** 36 /**
37 * @enum {string} 37 * @enum {string}
38 */ 38 */
39 WebInspector.TimelineTreeView.Mode = {
40 TopDown: "TopDown",
41 BottomUp: "BottomUp"
42 }
43
44 /**
45 * @enum {string}
46 */
39 WebInspector.TimelineTreeView.GroupBy = { 47 WebInspector.TimelineTreeView.GroupBy = {
40 None: "None", 48 None: "None",
41 Domain: "Domain", 49 Domain: "Domain",
42 DomainSecondLevel: "DomainSecondLevel", 50 DomainSecondLevel: "DomainSecondLevel",
43 URL: "URL" 51 URL: "URL"
44 } 52 }
45 53
46 WebInspector.TimelineTreeView.prototype = { 54 WebInspector.TimelineTreeView.prototype = {
47 /** 55 /**
48 * @param {!WebInspector.TimelineSelection} selection 56 * @param {!WebInspector.TimelineSelection} selection
49 */ 57 */
50 updateContents: function(selection) 58 updateContents: function(selection)
51 { 59 {
52 this._startTime = selection.startTime(); 60 this._startTime = selection.startTime();
53 this._endTime = selection.endTime(); 61 this._endTime = selection.endTime();
54 this._refreshRecords(); 62 this._refreshTree();
55 }, 63 },
56 64
57 _createToolbar: function() 65 _createToolbar: function()
58 { 66 {
59 this._panelToolbar = new WebInspector.Toolbar(this.element); 67 var panelToolbar = new WebInspector.Toolbar(this.element);
68 panelToolbar.appendToolbarItem(new WebInspector.ToolbarText(WebInspector .UIString("View")));
69
70 this._modeCombobox = new WebInspector.ToolbarComboBox(this._onTreeModeCh anged.bind(this));
71 this._modeCombobox.addOption(this._modeCombobox.createOption(WebInspecto r.UIString("Costly Functions"), "", WebInspector.TimelineTreeView.Mode.BottomUp) );
72 this._modeCombobox.addOption(this._modeCombobox.createOption(WebInspecto r.UIString("Costly Entrypoints"), "", WebInspector.TimelineTreeView.Mode.TopDown ));
73 panelToolbar.appendToolbarItem(this._modeCombobox);
74
60 this._groupByCombobox = new WebInspector.ToolbarComboBox(this._onGroupBy Changed.bind(this)); 75 this._groupByCombobox = new WebInspector.ToolbarComboBox(this._onGroupBy Changed.bind(this));
61 /** 76 /**
62 * @param {string} name 77 * @param {string} name
63 * @param {string} id 78 * @param {string} id
64 * @this {WebInspector.TimelineTreeView} 79 * @this {WebInspector.TimelineTreeView}
65 */ 80 */
66 function addOption(name, id) 81 function addGroupingOption(name, id)
67 { 82 {
68 var option = this._groupByCombobox.createOption(name, "", id); 83 var option = this._groupByCombobox.createOption(name, "", id);
69 this._groupByCombobox.addOption(option); 84 this._groupByCombobox.addOption(option);
70 if (id === this._groupBySetting.get()) 85 if (id === this._groupBySetting.get())
71 this._groupByCombobox.select(option); 86 this._groupByCombobox.select(option);
72 } 87 }
73 addOption.call(this, WebInspector.UIString("No Grouping"), WebInspector. TimelineTreeView.GroupBy.None); 88 panelToolbar.appendToolbarItem(new WebInspector.ToolbarText(WebInspector .UIString("Group by")));
74 addOption.call(this, WebInspector.UIString("Group by Domain"), WebInspec tor.TimelineTreeView.GroupBy.Domain); 89 addGroupingOption.call(this, WebInspector.UIString("Function"), WebInspe ctor.TimelineTreeView.GroupBy.None);
75 addOption.call(this, WebInspector.UIString("Group by Domain (2nd Level)" ), WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel); 90 addGroupingOption.call(this, WebInspector.UIString("Domain"), WebInspect or.TimelineTreeView.GroupBy.Domain);
76 addOption.call(this, WebInspector.UIString("Group by URL"), WebInspector .TimelineTreeView.GroupBy.URL); 91 addGroupingOption.call(this, WebInspector.UIString("Domain (2nd Level)") , WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel);
77 this._panelToolbar.appendToolbarItem(this._groupByCombobox); 92 addGroupingOption.call(this, WebInspector.UIString("URL"), WebInspector. TimelineTreeView.GroupBy.URL);
93 panelToolbar.appendToolbarItem(this._groupByCombobox);
94 },
95
96 _onTreeModeChanged: function()
97 {
98 this._refreshTree();
78 }, 99 },
79 100
80 _onGroupByChanged: function() 101 _onGroupByChanged: function()
81 { 102 {
82 this._groupBySetting.set(this._groupByCombobox.selectedOption().value); 103 this._groupBySetting.set(this._groupByCombobox.selectedOption().value);
83 this._refreshRecords(); 104 this._refreshTree();
84 }, 105 },
85 106
86 _refreshRecords: function() 107 _refreshTree: function()
87 { 108 {
88 var groupBy = this._groupBySetting.get(); 109 if (this._modeCombobox.selectedOption().value === WebInspector.TimelineT reeView.Mode.TopDown)
89 var groupNodes = new Map(); 110 this._refreshTopDownTree();
111 else
112 this._refreshBottomUpTree();
113 },
90 114
115 /**
116 * @return {?function(!WebInspector.TimelineModel.ProfileTreeNode):string}
117 */
118 _nodeToGroupIdFunction: function()
119 {
91 /** 120 /**
92 * @param {string} id 121 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node
93 * @return {!WebInspector.TimelineModel.ProfileTreeNode} 122 * @return {string}
94 */ 123 */
95 function groupNodeById(id) 124 function groupByURL(node)
96 { 125 {
97 var node = groupNodes.get(id); 126 return WebInspector.TimelineTreeView.eventURL(node.event) || "";
98 if (!node) {
99 node = new WebInspector.TimelineModel.ProfileTreeNode();
100 node.name = id || WebInspector.UIString("(unknown)");
101 node.selfTime = 0;
102 node.totalTime = 0;
103 groupNodes.set(id, node);
104 }
105 return node;
106 }
107
108 /**
109 * @return {?WebInspector.TimelineModel.ProfileTreeNode}
110 */
111 function groupByNone()
112 {
113 return null;
114 } 127 }
115 128
116 /** 129 /**
117 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node 130 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node
118 * @return {?WebInspector.TimelineModel.ProfileTreeNode} 131 * @return {string}
119 */ 132 */
120 function groupByURL(node) 133 function groupByDomain(node)
121 { 134 {
122 return groupNodeById(WebInspector.TimelineTreeView.eventURL(node.eve nt) || ""); 135 var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL();
136 return parsedURL && parsedURL.host || "";
123 } 137 }
124 138
125 /** 139 /**
126 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node 140 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node
127 * @return {?WebInspector.TimelineModel.ProfileTreeNode} 141 * @return {string}
128 */
129 function groupByDomain(node)
130 {
131 var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL();
132 var domain = parsedURL && parsedURL.host || "";
133 return groupNodeById(domain);
134 }
135
136 /**
137 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node
138 * @return {?WebInspector.TimelineModel.ProfileTreeNode}
139 */ 142 */
140 function groupByDomainSecondLevel(node) 143 function groupByDomainSecondLevel(node)
141 { 144 {
142 var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL(); 145 var parsedURL = (WebInspector.TimelineTreeView.eventURL(node.event) || "").asParsedURL();
143 if (!parsedURL) 146 if (!parsedURL)
144 return groupNodeById(""); 147 return "";
145 if (/^[.0-9]+$/.test(parsedURL.host)) 148 if (/^[.0-9]+$/.test(parsedURL.host))
146 return groupNodeById(parsedURL.host) 149 return parsedURL.host;
147 var domainMatch = /([^.]*\.)?[^.]*$/.exec(parsedURL.host); 150 var domainMatch = /([^.]*\.)?[^.]*$/.exec(parsedURL.host);
148 return groupNodeById(domainMatch && domainMatch[0] || ""); 151 return domainMatch && domainMatch[0] || "";
149 } 152 }
150 153
151 /** 154 var groupByMap = /** @type {!Map<!WebInspector.TimelineTreeView.GroupBy, ?function(!WebInspector.TimelineModel.ProfileTreeNode):string>} */ (new Map([
152 * @param {!WebInspector.TracingModel.Event} e 155 [WebInspector.TimelineTreeView.GroupBy.None, null],
153 * @return {string}
154 */
155 function eventId(e)
156 {
157 // Function call frames are always groupped by the URL
158 if (e.name === "JSFrame") {
159 var data = e.args["data"];
160 return "f:" + (data["callUID"] || WebInspector.TimelineTreeView. eventURL(e));
161 }
162 // While the rest of events are groupped by the event type
163 // unless the group by URL/Domain mode is on.
164 if (groupBy === WebInspector.TimelineTreeView.GroupBy.URL)
165 return e.name + ":@" + WebInspector.TimelineTreeView.eventURL(e) ;
166 return e.name;
167 }
168
169 var groupByMapper = new Map([
170 [WebInspector.TimelineTreeView.GroupBy.None, groupByNone],
171 [WebInspector.TimelineTreeView.GroupBy.Domain, groupByDomain], 156 [WebInspector.TimelineTreeView.GroupBy.Domain, groupByDomain],
172 [WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel, groupByDom ainSecondLevel], 157 [WebInspector.TimelineTreeView.GroupBy.DomainSecondLevel, groupByDom ainSecondLevel],
173 [WebInspector.TimelineTreeView.GroupBy.URL, groupByURL] 158 [WebInspector.TimelineTreeView.GroupBy.URL, groupByURL]
159 ]));
160 return groupByMap.get(this._groupBySetting.get()) || null;
161 },
162
163 /**
164 * @param {function(!WebInspector.TimelineModel.ProfileTreeNode):string} nod eToGroupId
165 * @param {!WebInspector.TimelineModel.ProfileTreeNode} node
166 * @return {!WebInspector.TimelineModel.ProfileTreeNode}
167 */
168 _nodeToGroupNode: function(nodeToGroupId, node)
169 {
170 var id = nodeToGroupId(node);
171 var groupNode = this._groupNodes.get(id);
172 if (!groupNode) {
173 groupNode = new WebInspector.TimelineModel.ProfileTreeNode();
174 groupNode.name = id || WebInspector.UIString("(unknown)");
175 groupNode.selfTime = 0;
176 groupNode.totalTime = 0;
177 groupNode.children = new Map();
178 this._groupNodes.set(id, groupNode);
179 }
180 return groupNode;
181 },
182
183 _refreshTopDownTree: function()
184 {
185 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.
186 WebInspector.TimelineModel.RecordType.EventDispatch,
187 WebInspector.TimelineModel.RecordType.FunctionCall,
188 WebInspector.TimelineModel.RecordType.TimerFire
174 ]); 189 ]);
175 var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.ma inThreadEvents(), this._startTime, this._endTime, this._filters, eventId); 190 /**
176 var bottomUpRoot = WebInspector.TimelineModel.buildBottomUpTree(topDown, groupByMapper.get(groupBy)); 191 * @param {!WebInspector.TracingModel.Event} event
177 for (var group of groupNodes) 192 * @return {boolean}
193 */
194 function nonessentialEventsFilter(event)
195 {
196 return !nonessentialEvents.has(event.name);
197 }
198 var filters = this._filters.concat(new WebInspector.CustomFilter(nonesse ntialEventsFilter));
199
200 this.dataGrid.rootNode().removeChildren();
201 var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.ma inThreadEvents(), this._startTime, this._endTime, filters, this._eventId);
202 var nodeToGroupId = this._nodeToGroupIdFunction();
203 if (nodeToGroupId) {
204 this._groupNodes = new Map();
205 for (var node of topDown.children.values()) {
206 var groupNode = this._nodeToGroupNode(nodeToGroupId, node);
207 groupNode.selfTime += node.selfTime;
208 groupNode.totalTime += node.totalTime;
209 groupNode.children.set(node.id, node);
210 }
211 topDown.children = this._groupNodes;
212 this._groupNodes = null;
213 }
214 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.
215 // Exclude the idle time off the total calculation.
216 var gridNode = new WebInspector.TimelineTreeView.GridNode(child, top Down.totalTime);
217 this.dataGrid.insertChild(gridNode);
218 }
219 this._sortingChanged();
220 },
221
222 _refreshBottomUpTree: function()
223 {
224 this._groupNodes = new Map();
225 var nodeToGroupId = this._nodeToGroupIdFunction();
226 var nodeToGroupNode = nodeToGroupId ? this._nodeToGroupNode.bind(this, n odeToGroupId) : null;
227 this.dataGrid.rootNode().removeChildren();
228 var topDown = WebInspector.TimelineModel.buildTopDownTree(this._model.ma inThreadEvents(), this._startTime, this._endTime, this._filters, this._eventId);
229 var bottomUpRoot = WebInspector.TimelineModel.buildBottomUpTree(topDown, nodeToGroupNode);
230 for (var group of this._groupNodes)
178 bottomUpRoot.children.set(group[0], group[1]); 231 bottomUpRoot.children.set(group[0], group[1]);
179 this.dataGrid.rootNode().removeChildren();
180 for (var child of bottomUpRoot.children.values()) { 232 for (var child of bottomUpRoot.children.values()) {
181 // Exclude the idle time off the total calculation. 233 // Exclude the idle time off the total calculation.
182 var gridNode = new WebInspector.TimelineTreeView.GridNode(child, top Down.totalTime); 234 var gridNode = new WebInspector.TimelineTreeView.GridNode(child, top Down.totalTime);
183 this.dataGrid.insertChild(gridNode); 235 this.dataGrid.insertChild(gridNode);
184 } 236 }
185 this._sortingChanged(); 237 this._sortingChanged();
186 }, 238 },
187 239
188 _sortingChanged: function() 240 _sortingChanged: function()
189 { 241 {
(...skipping 13 matching lines...) Expand all
203 return valueA === valueB ? 0 : valueA > valueB ? 1 : -1; 255 return valueA === valueB ? 0 : valueA > valueB ? 1 : -1;
204 } 256 }
205 var field = { 257 var field = {
206 "self": "selfTime", 258 "self": "selfTime",
207 "total": "totalTime", 259 "total": "totalTime",
208 "activity": "name" 260 "activity": "name"
209 }[columnIdentifier]; 261 }[columnIdentifier];
210 this.dataGrid.sortNodes(compareField.bind(null, field), !this.dataGrid.i sSortOrderAscending()); 262 this.dataGrid.sortNodes(compareField.bind(null, field), !this.dataGrid.i sSortOrderAscending());
211 }, 263 },
212 264
265 /**
266 * @param {!WebInspector.TracingModel.Event} e
267 * @return {string}
268 */
269 _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.
270 {
271 if (e.name === WebInspector.TimelineModel.RecordType.JSFrame) {
272 var data = e.args["data"];
273 return "f:" + (data["callUID"] || WebInspector.TimelineTreeView.even tURL(e));
274 }
275 return e.name + ":@" + WebInspector.TimelineTreeView.eventURL(e);
276 },
277
213 __proto__: WebInspector.VBox.prototype 278 __proto__: WebInspector.VBox.prototype
214 } 279 }
215 280
216 /** 281 /**
217 * @param {!WebInspector.TracingModel.Event} event 282 * @param {!WebInspector.TracingModel.Event} event
218 * @return {?string} 283 * @return {?string}
219 */ 284 */
220 WebInspector.TimelineTreeView.eventURL = function(event) 285 WebInspector.TimelineTreeView.eventURL = function(event)
221 { 286 {
222 var data = event.args["data"] || event.args["beginData"]; 287 var data = event.args["data"] || event.args["beginData"];
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 if (!this._profileNode.children) 413 if (!this._profileNode.children)
349 return; 414 return;
350 for (var node of this._profileNode.children.values()) { 415 for (var node of this._profileNode.children.values()) {
351 var gridNode = new WebInspector.TimelineTreeView.GridNode(node, this ._totalTime); 416 var gridNode = new WebInspector.TimelineTreeView.GridNode(node, this ._totalTime);
352 this.insertChildOrdered(gridNode); 417 this.insertChildOrdered(gridNode);
353 } 418 }
354 }, 419 },
355 420
356 __proto__: WebInspector.SortableDataGridNode.prototype 421 __proto__: WebInspector.SortableDataGridNode.prototype
357 } 422 }
OLDNEW
« no previous file with comments | « Source/devtools/front_end/timeline/TimelinePanel.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698