OLD | NEW |
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 | |
5 /** | 4 /** |
6 * @constructor | 5 * @unrestricted |
7 * @extends {WebInspector.VBox} | |
8 */ | 6 */ |
9 WebInspector.TimelineTreeView = function() | 7 WebInspector.TimelineTreeView = class extends WebInspector.VBox { |
10 { | 8 constructor() { |
11 WebInspector.VBox.call(this); | 9 super(); |
12 this.element.classList.add("timeline-tree-view"); | 10 this.element.classList.add('timeline-tree-view'); |
| 11 } |
| 12 |
| 13 /** |
| 14 * @param {!WebInspector.TracingModel.Event} event |
| 15 * @return {string} |
| 16 */ |
| 17 static eventNameForSorting(event) { |
| 18 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) { |
| 19 var data = event.args['data']; |
| 20 return data['functionName'] + '@' + (data['scriptId'] || data['url'] || ''
); |
| 21 } |
| 22 return event.name + ':@' + WebInspector.TimelineProfileTree.eventURL(event); |
| 23 } |
| 24 |
| 25 /** |
| 26 * @param {!WebInspector.TimelineModel} model |
| 27 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| 28 */ |
| 29 _init(model, filters) { |
| 30 this._model = model; |
| 31 this._linkifier = new WebInspector.Linkifier(); |
| 32 |
| 33 this._filters = filters.slice(); |
| 34 |
| 35 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>} */
([]); |
| 36 this._populateColumns(columns); |
| 37 |
| 38 var mainView = new WebInspector.VBox(); |
| 39 this._populateToolbar(mainView.element); |
| 40 this._dataGrid = new WebInspector.SortableDataGrid(columns); |
| 41 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChanged,
this._sortingChanged, this); |
| 42 this._dataGrid.element.addEventListener('mousemove', this._onMouseMove.bind(
this), true); |
| 43 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); |
| 44 this._dataGrid.asWidget().show(mainView.element); |
| 45 |
| 46 this._splitWidget = new WebInspector.SplitWidget(true, true, 'timelineTreeVi
ewDetailsSplitWidget'); |
| 47 this._splitWidget.show(this.element); |
| 48 this._splitWidget.setMainWidget(mainView); |
| 49 |
| 50 this._detailsView = new WebInspector.VBox(); |
| 51 this._detailsView.element.classList.add('timeline-details-view', 'timeline-d
etails-view-body'); |
| 52 this._splitWidget.setSidebarWidget(this._detailsView); |
| 53 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, t
his._updateDetailsForSelection, this); |
| 54 |
| 55 /** @type {?WebInspector.TimelineProfileTree.Node|undefined} */ |
| 56 this._lastSelectedNode; |
| 57 } |
| 58 |
| 59 /** |
| 60 * @param {!WebInspector.TimelineSelection} selection |
| 61 */ |
| 62 updateContents(selection) { |
| 63 this.setRange(selection.startTime(), selection.endTime()); |
| 64 } |
| 65 |
| 66 /** |
| 67 * @param {number} startTime |
| 68 * @param {number} endTime |
| 69 */ |
| 70 setRange(startTime, endTime) { |
| 71 this._startTime = startTime; |
| 72 this._endTime = endTime; |
| 73 this._refreshTree(); |
| 74 } |
| 75 |
| 76 /** |
| 77 * @return {boolean} |
| 78 */ |
| 79 _exposePercentages() { |
| 80 return false; |
| 81 } |
| 82 |
| 83 /** |
| 84 * @param {!Element} parent |
| 85 */ |
| 86 _populateToolbar(parent) { |
| 87 } |
| 88 |
| 89 /** |
| 90 * @param {?WebInspector.TimelineProfileTree.Node} node |
| 91 */ |
| 92 _onHover(node) { |
| 93 } |
| 94 |
| 95 /** |
| 96 * @param {!WebInspector.TracingModel.Event} event |
| 97 * @return {?Element} |
| 98 */ |
| 99 _linkifyLocation(event) { |
| 100 var target = this._model.targetByEvent(event); |
| 101 if (!target) |
| 102 return null; |
| 103 var frame = WebInspector.TimelineProfileTree.eventStackFrame(event); |
| 104 if (!frame) |
| 105 return null; |
| 106 return this._linkifier.maybeLinkifyConsoleCallFrame(target, frame); |
| 107 } |
| 108 |
| 109 /** |
| 110 * @param {!WebInspector.TimelineProfileTree.Node} treeNode |
| 111 * @param {boolean} suppressSelectedEvent |
| 112 */ |
| 113 selectProfileNode(treeNode, suppressSelectedEvent) { |
| 114 var pathToRoot = []; |
| 115 for (var node = treeNode; node; node = node.parent) |
| 116 pathToRoot.push(node); |
| 117 for (var i = pathToRoot.length - 1; i > 0; --i) { |
| 118 var gridNode = this._dataGridNodeForTreeNode(pathToRoot[i]); |
| 119 if (gridNode && gridNode.dataGrid) |
| 120 gridNode.expand(); |
| 121 } |
| 122 var gridNode = this._dataGridNodeForTreeNode(treeNode); |
| 123 if (gridNode.dataGrid) { |
| 124 gridNode.reveal(); |
| 125 gridNode.select(suppressSelectedEvent); |
| 126 } |
| 127 } |
| 128 |
| 129 _refreshTree() { |
| 130 this._linkifier.reset(); |
| 131 this._dataGrid.rootNode().removeChildren(); |
| 132 var tree = this._buildTree(); |
| 133 if (!tree.children) |
| 134 return; |
| 135 var maxSelfTime = 0; |
| 136 var maxTotalTime = 0; |
| 137 for (var child of tree.children.values()) { |
| 138 maxSelfTime = Math.max(maxSelfTime, child.selfTime); |
| 139 maxTotalTime = Math.max(maxTotalTime, child.totalTime); |
| 140 } |
| 141 for (var child of tree.children.values()) { |
| 142 // Exclude the idle time off the total calculation. |
| 143 var gridNode = |
| 144 new WebInspector.TimelineTreeView.TreeGridNode(child, tree.totalTime,
maxSelfTime, maxTotalTime, this); |
| 145 this._dataGrid.insertChild(gridNode); |
| 146 } |
| 147 this._sortingChanged(); |
| 148 this._updateDetailsForSelection(); |
| 149 } |
| 150 |
| 151 /** |
| 152 * @return {!WebInspector.TimelineProfileTree.Node} |
| 153 */ |
| 154 _buildTree() { |
| 155 throw new Error('Not Implemented'); |
| 156 } |
| 157 |
| 158 /** |
| 159 * @param {function(!WebInspector.TracingModel.Event):(string|symbol)=} eventI
dCallback |
| 160 * @return {!WebInspector.TimelineProfileTree.Node} |
| 161 */ |
| 162 _buildTopDownTree(eventIdCallback) { |
| 163 return WebInspector.TimelineProfileTree.buildTopDown( |
| 164 this._model.mainThreadEvents(), this._filters, this._startTime, this._en
dTime, eventIdCallback); |
| 165 } |
| 166 |
| 167 /** |
| 168 * @param {!Array<!WebInspector.DataGrid.ColumnDescriptor>} columns |
| 169 */ |
| 170 _populateColumns(columns) { |
| 171 columns.push( |
| 172 {id: 'self', title: WebInspector.UIString('Self Time'), width: '110px',
fixedWidth: true, sortable: true}); |
| 173 columns.push( |
| 174 {id: 'total', title: WebInspector.UIString('Total Time'), width: '110px'
, fixedWidth: true, sortable: true}); |
| 175 columns.push({id: 'activity', title: WebInspector.UIString('Activity'), disc
losure: true, sortable: true}); |
| 176 } |
| 177 |
| 178 _sortingChanged() { |
| 179 var columnId = this._dataGrid.sortColumnId(); |
| 180 if (!columnId) |
| 181 return; |
| 182 var sortFunction; |
| 183 switch (columnId) { |
| 184 case 'startTime': |
| 185 sortFunction = compareStartTime; |
| 186 break; |
| 187 case 'self': |
| 188 sortFunction = compareNumericField.bind(null, 'selfTime'); |
| 189 break; |
| 190 case 'total': |
| 191 sortFunction = compareNumericField.bind(null, 'totalTime'); |
| 192 break; |
| 193 case 'activity': |
| 194 sortFunction = compareName; |
| 195 break; |
| 196 default: |
| 197 console.assert(false, 'Unknown sort field: ' + columnId); |
| 198 return; |
| 199 } |
| 200 this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending(
)); |
| 201 |
| 202 /** |
| 203 * @param {string} field |
| 204 * @param {!WebInspector.DataGridNode} a |
| 205 * @param {!WebInspector.DataGridNode} b |
| 206 * @return {number} |
| 207 */ |
| 208 function compareNumericField(field, a, b) { |
| 209 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (a)
; |
| 210 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (b)
; |
| 211 return nodeA._profileNode[field] - nodeB._profileNode[field]; |
| 212 } |
| 213 |
| 214 /** |
| 215 * @param {!WebInspector.DataGridNode} a |
| 216 * @param {!WebInspector.DataGridNode} b |
| 217 * @return {number} |
| 218 */ |
| 219 function compareStartTime(a, b) { |
| 220 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (a)
; |
| 221 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (b)
; |
| 222 return nodeA._profileNode.event.startTime - nodeB._profileNode.event.start
Time; |
| 223 } |
| 224 |
| 225 /** |
| 226 * @param {!WebInspector.DataGridNode} a |
| 227 * @param {!WebInspector.DataGridNode} b |
| 228 * @return {number} |
| 229 */ |
| 230 function compareName(a, b) { |
| 231 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (a)
; |
| 232 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (b)
; |
| 233 var nameA = WebInspector.TimelineTreeView.eventNameForSorting(nodeA._profi
leNode.event); |
| 234 var nameB = WebInspector.TimelineTreeView.eventNameForSorting(nodeB._profi
leNode.event); |
| 235 return nameA.localeCompare(nameB); |
| 236 } |
| 237 } |
| 238 |
| 239 _updateDetailsForSelection() { |
| 240 var selectedNode = this._dataGrid.selectedNode ? |
| 241 /** @type {!WebInspector.TimelineTreeView.TreeGridNode} */ (this._dataGr
id.selectedNode)._profileNode : |
| 242 null; |
| 243 if (selectedNode === this._lastSelectedNode) |
| 244 return; |
| 245 this._lastSelectedNode = selectedNode; |
| 246 this._detailsView.detachChildWidgets(); |
| 247 this._detailsView.element.removeChildren(); |
| 248 if (!selectedNode || !this._showDetailsForNode(selectedNode)) { |
| 249 var banner = this._detailsView.element.createChild('div', 'full-widget-dim
med-banner'); |
| 250 banner.createTextChild(WebInspector.UIString('Select item for details.')); |
| 251 } |
| 252 } |
| 253 |
| 254 /** |
| 255 * @param {!WebInspector.TimelineProfileTree.Node} node |
| 256 * @return {boolean} |
| 257 */ |
| 258 _showDetailsForNode(node) { |
| 259 return false; |
| 260 } |
| 261 |
| 262 /** |
| 263 * @param {!Event} event |
| 264 */ |
| 265 _onMouseMove(event) { |
| 266 var gridNode = event.target && (event.target instanceof Node) ? |
| 267 /** @type {?WebInspector.TimelineTreeView.TreeGridNode} */ ( |
| 268 this._dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.targ
et))) : |
| 269 null; |
| 270 var profileNode = gridNode && gridNode._profileNode; |
| 271 if (profileNode === this._lastHoveredProfileNode) |
| 272 return; |
| 273 this._lastHoveredProfileNode = profileNode; |
| 274 this._onHover(profileNode); |
| 275 } |
| 276 |
| 277 /** |
| 278 * @param {!WebInspector.TimelineProfileTree.Node} treeNode |
| 279 * @return {?WebInspector.TimelineTreeView.GridNode} |
| 280 */ |
| 281 _dataGridNodeForTreeNode(treeNode) { |
| 282 return treeNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol]
|| null; |
| 283 } |
13 }; | 284 }; |
14 | 285 |
15 WebInspector.TimelineTreeView.prototype = { | |
16 /** | |
17 * @param {!WebInspector.TimelineModel} model | |
18 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
19 */ | |
20 _init: function(model, filters) | |
21 { | |
22 this._model = model; | |
23 this._linkifier = new WebInspector.Linkifier(); | |
24 | |
25 this._filters = filters.slice(); | |
26 | |
27 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>
} */ ([]); | |
28 this._populateColumns(columns); | |
29 | |
30 var mainView = new WebInspector.VBox(); | |
31 this._populateToolbar(mainView.element); | |
32 this._dataGrid = new WebInspector.SortableDataGrid(columns); | |
33 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SortingChan
ged, this._sortingChanged, this); | |
34 this._dataGrid.element.addEventListener("mousemove", this._onMouseMove.b
ind(this), true); | |
35 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); | |
36 this._dataGrid.asWidget().show(mainView.element); | |
37 | |
38 this._splitWidget = new WebInspector.SplitWidget(true, true, "timelineTr
eeViewDetailsSplitWidget"); | |
39 this._splitWidget.show(this.element); | |
40 this._splitWidget.setMainWidget(mainView); | |
41 | |
42 this._detailsView = new WebInspector.VBox(); | |
43 this._detailsView.element.classList.add("timeline-details-view", "timeli
ne-details-view-body"); | |
44 this._splitWidget.setSidebarWidget(this._detailsView); | |
45 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNod
e, this._updateDetailsForSelection, this); | |
46 | |
47 /** @type {?WebInspector.TimelineProfileTree.Node|undefined} */ | |
48 this._lastSelectedNode; | |
49 }, | |
50 | |
51 /** | |
52 * @param {!WebInspector.TimelineSelection} selection | |
53 */ | |
54 updateContents: function(selection) | |
55 { | |
56 this.setRange(selection.startTime(), selection.endTime()); | |
57 }, | |
58 | |
59 /** | |
60 * @param {number} startTime | |
61 * @param {number} endTime | |
62 */ | |
63 setRange: function(startTime, endTime) | |
64 { | |
65 this._startTime = startTime; | |
66 this._endTime = endTime; | |
67 this._refreshTree(); | |
68 }, | |
69 | |
70 /** | |
71 * @return {boolean} | |
72 */ | |
73 _exposePercentages: function() | |
74 { | |
75 return false; | |
76 }, | |
77 | |
78 /** | |
79 * @param {!Element} parent | |
80 */ | |
81 _populateToolbar: function(parent) { }, | |
82 | |
83 /** | |
84 * @param {?WebInspector.TimelineProfileTree.Node} node | |
85 */ | |
86 _onHover: function(node) { }, | |
87 | |
88 /** | |
89 * @param {!WebInspector.TracingModel.Event} event | |
90 * @return {?Element} | |
91 */ | |
92 _linkifyLocation: function(event) | |
93 { | |
94 var target = this._model.targetByEvent(event); | |
95 if (!target) | |
96 return null; | |
97 var frame = WebInspector.TimelineProfileTree.eventStackFrame(event); | |
98 if (!frame) | |
99 return null; | |
100 return this._linkifier.maybeLinkifyConsoleCallFrame(target, frame); | |
101 }, | |
102 | |
103 /** | |
104 * @param {!WebInspector.TimelineProfileTree.Node} treeNode | |
105 * @param {boolean} suppressSelectedEvent | |
106 */ | |
107 selectProfileNode: function(treeNode, suppressSelectedEvent) | |
108 { | |
109 var pathToRoot = []; | |
110 for (var node = treeNode; node; node = node.parent) | |
111 pathToRoot.push(node); | |
112 for (var i = pathToRoot.length - 1; i > 0; --i) { | |
113 var gridNode = this._dataGridNodeForTreeNode(pathToRoot[i]); | |
114 if (gridNode && gridNode.dataGrid) | |
115 gridNode.expand(); | |
116 } | |
117 var gridNode = this._dataGridNodeForTreeNode(treeNode); | |
118 if (gridNode.dataGrid) { | |
119 gridNode.reveal(); | |
120 gridNode.select(suppressSelectedEvent); | |
121 } | |
122 }, | |
123 | |
124 _refreshTree: function() | |
125 { | |
126 this._linkifier.reset(); | |
127 this._dataGrid.rootNode().removeChildren(); | |
128 var tree = this._buildTree(); | |
129 if (!tree.children) | |
130 return; | |
131 var maxSelfTime = 0; | |
132 var maxTotalTime = 0; | |
133 for (var child of tree.children.values()) { | |
134 maxSelfTime = Math.max(maxSelfTime, child.selfTime); | |
135 maxTotalTime = Math.max(maxTotalTime, child.totalTime); | |
136 } | |
137 for (var child of tree.children.values()) { | |
138 // Exclude the idle time off the total calculation. | |
139 var gridNode = new WebInspector.TimelineTreeView.TreeGridNode(child,
tree.totalTime, maxSelfTime, maxTotalTime, this); | |
140 this._dataGrid.insertChild(gridNode); | |
141 } | |
142 this._sortingChanged(); | |
143 this._updateDetailsForSelection(); | |
144 }, | |
145 | |
146 /** | |
147 * @return {!WebInspector.TimelineProfileTree.Node} | |
148 */ | |
149 _buildTree: function() | |
150 { | |
151 throw new Error("Not Implemented"); | |
152 }, | |
153 | |
154 /** | |
155 * @param {function(!WebInspector.TracingModel.Event):(string|symbol)=} even
tIdCallback | |
156 * @return {!WebInspector.TimelineProfileTree.Node} | |
157 */ | |
158 _buildTopDownTree: function(eventIdCallback) | |
159 { | |
160 return WebInspector.TimelineProfileTree.buildTopDown(this._model.mainThr
eadEvents(), this._filters, this._startTime, this._endTime, eventIdCallback); | |
161 }, | |
162 | |
163 /** | |
164 * @param {!Array<!WebInspector.DataGrid.ColumnDescriptor>} columns | |
165 */ | |
166 _populateColumns: function(columns) | |
167 { | |
168 columns.push({id: "self", title: WebInspector.UIString("Self Time"), wid
th: "110px", fixedWidth: true, sortable: true}); | |
169 columns.push({id: "total", title: WebInspector.UIString("Total Time"), w
idth: "110px", fixedWidth: true, sortable: true}); | |
170 columns.push({id: "activity", title: WebInspector.UIString("Activity"),
disclosure: true, sortable: true}); | |
171 }, | |
172 | |
173 _sortingChanged: function() | |
174 { | |
175 var columnId = this._dataGrid.sortColumnId(); | |
176 if (!columnId) | |
177 return; | |
178 var sortFunction; | |
179 switch (columnId) { | |
180 case "startTime": | |
181 sortFunction = compareStartTime; | |
182 break; | |
183 case "self": | |
184 sortFunction = compareNumericField.bind(null, "selfTime"); | |
185 break; | |
186 case "total": | |
187 sortFunction = compareNumericField.bind(null, "totalTime"); | |
188 break; | |
189 case "activity": | |
190 sortFunction = compareName; | |
191 break; | |
192 default: | |
193 console.assert(false, "Unknown sort field: " + columnId); | |
194 return; | |
195 } | |
196 this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscend
ing()); | |
197 | |
198 /** | |
199 * @param {string} field | |
200 * @param {!WebInspector.DataGridNode} a | |
201 * @param {!WebInspector.DataGridNode} b | |
202 * @return {number} | |
203 */ | |
204 function compareNumericField(field, a, b) | |
205 { | |
206 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (a); | |
207 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (b); | |
208 return nodeA._profileNode[field] - nodeB._profileNode[field]; | |
209 } | |
210 | |
211 /** | |
212 * @param {!WebInspector.DataGridNode} a | |
213 * @param {!WebInspector.DataGridNode} b | |
214 * @return {number} | |
215 */ | |
216 function compareStartTime(a, b) | |
217 { | |
218 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (a); | |
219 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (b); | |
220 return nodeA._profileNode.event.startTime - nodeB._profileNode.event
.startTime; | |
221 } | |
222 | |
223 /** | |
224 * @param {!WebInspector.DataGridNode} a | |
225 * @param {!WebInspector.DataGridNode} b | |
226 * @return {number} | |
227 */ | |
228 function compareName(a, b) | |
229 { | |
230 var nodeA = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (a); | |
231 var nodeB = /** @type {!WebInspector.TimelineTreeView.TreeGridNode}
*/ (b); | |
232 var nameA = WebInspector.TimelineTreeView.eventNameForSorting(nodeA.
_profileNode.event); | |
233 var nameB = WebInspector.TimelineTreeView.eventNameForSorting(nodeB.
_profileNode.event); | |
234 return nameA.localeCompare(nameB); | |
235 } | |
236 }, | |
237 | |
238 _updateDetailsForSelection: function() | |
239 { | |
240 var selectedNode = this._dataGrid.selectedNode ? /** @type {!WebInspecto
r.TimelineTreeView.TreeGridNode} */ (this._dataGrid.selectedNode)._profileNode :
null; | |
241 if (selectedNode === this._lastSelectedNode) | |
242 return; | |
243 this._lastSelectedNode = selectedNode; | |
244 this._detailsView.detachChildWidgets(); | |
245 this._detailsView.element.removeChildren(); | |
246 if (!selectedNode || !this._showDetailsForNode(selectedNode)) { | |
247 var banner = this._detailsView.element.createChild("div", "full-widg
et-dimmed-banner"); | |
248 banner.createTextChild(WebInspector.UIString("Select item for detail
s.")); | |
249 } | |
250 }, | |
251 | |
252 /** | |
253 * @param {!WebInspector.TimelineProfileTree.Node} node | |
254 * @return {boolean} | |
255 */ | |
256 _showDetailsForNode: function(node) | |
257 { | |
258 return false; | |
259 }, | |
260 | |
261 /** | |
262 * @param {!Event} event | |
263 */ | |
264 _onMouseMove: function(event) | |
265 { | |
266 var gridNode = event.target && (event.target instanceof Node) | |
267 ? /** @type {?WebInspector.TimelineTreeView.TreeGridNode} */ (this._
dataGrid.dataGridNodeFromNode(/** @type {!Node} */ (event.target))) | |
268 : null; | |
269 var profileNode = gridNode && gridNode._profileNode; | |
270 if (profileNode === this._lastHoveredProfileNode) | |
271 return; | |
272 this._lastHoveredProfileNode = profileNode; | |
273 this._onHover(profileNode); | |
274 }, | |
275 | |
276 /** | |
277 * @param {!WebInspector.TimelineProfileTree.Node} treeNode | |
278 * @return {?WebInspector.TimelineTreeView.GridNode} | |
279 */ | |
280 _dataGridNodeForTreeNode: function(treeNode) | |
281 { | |
282 return treeNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymb
ol] || null; | |
283 }, | |
284 | |
285 __proto__: WebInspector.VBox.prototype | |
286 }; | |
287 | 286 |
288 /** | 287 /** |
289 * @param {!WebInspector.TracingModel.Event} event | 288 * @unrestricted |
290 * @return {string} | |
291 */ | 289 */ |
292 WebInspector.TimelineTreeView.eventNameForSorting = function(event) | 290 WebInspector.TimelineTreeView.GridNode = class extends WebInspector.SortableData
GridNode { |
293 { | 291 /** |
294 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) { | 292 * @param {!WebInspector.TimelineProfileTree.Node} profileNode |
295 var data = event.args["data"]; | 293 * @param {number} grandTotalTime |
296 return data["functionName"] + "@" + (data["scriptId"] || data["url"] ||
""); | 294 * @param {number} maxSelfTime |
297 } | 295 * @param {number} maxTotalTime |
298 return event.name + ":@" + WebInspector.TimelineProfileTree.eventURL(event); | 296 * @param {!WebInspector.TimelineTreeView} treeView |
299 }; | 297 */ |
300 | 298 constructor(profileNode, grandTotalTime, maxSelfTime, maxTotalTime, treeView)
{ |
301 /** | 299 super(null, false); |
302 * @constructor | |
303 * @extends {WebInspector.SortableDataGridNode} | |
304 * @param {!WebInspector.TimelineProfileTree.Node} profileNode | |
305 * @param {number} grandTotalTime | |
306 * @param {number} maxSelfTime | |
307 * @param {number} maxTotalTime | |
308 * @param {!WebInspector.TimelineTreeView} treeView | |
309 */ | |
310 WebInspector.TimelineTreeView.GridNode = function(profileNode, grandTotalTime, m
axSelfTime, maxTotalTime, treeView) | |
311 { | |
312 WebInspector.SortableDataGridNode.call(this, null, false); | |
313 | 300 |
314 this._populated = false; | 301 this._populated = false; |
315 this._profileNode = profileNode; | 302 this._profileNode = profileNode; |
316 this._treeView = treeView; | 303 this._treeView = treeView; |
317 this._grandTotalTime = grandTotalTime; | 304 this._grandTotalTime = grandTotalTime; |
318 this._maxSelfTime = maxSelfTime; | 305 this._maxSelfTime = maxSelfTime; |
319 this._maxTotalTime = maxTotalTime; | 306 this._maxTotalTime = maxTotalTime; |
320 }; | 307 } |
321 | 308 |
322 WebInspector.TimelineTreeView.GridNode.prototype = { | 309 /** |
323 /** | 310 * @override |
324 * @override | 311 * @param {string} columnId |
325 * @param {string} columnId | 312 * @return {!Element} |
326 * @return {!Element} | 313 */ |
327 */ | 314 createCell(columnId) { |
328 createCell: function(columnId) | 315 if (columnId === 'activity') |
329 { | 316 return this._createNameCell(columnId); |
330 if (columnId === "activity") | 317 return this._createValueCell(columnId) || super.createCell(columnId); |
331 return this._createNameCell(columnId); | 318 } |
332 return this._createValueCell(columnId) || WebInspector.DataGridNode.prot
otype.createCell.call(this, columnId); | 319 |
333 }, | 320 /** |
334 | 321 * @param {string} columnId |
335 /** | 322 * @return {!Element} |
336 * @param {string} columnId | 323 */ |
337 * @return {!Element} | 324 _createNameCell(columnId) { |
338 */ | 325 var cell = this.createTD(columnId); |
339 _createNameCell: function(columnId) | 326 var container = cell.createChild('div', 'name-container'); |
340 { | 327 var icon = container.createChild('div', 'activity-icon'); |
341 var cell = this.createTD(columnId); | 328 var name = container.createChild('div', 'activity-name'); |
342 var container = cell.createChild("div", "name-container"); | 329 var event = this._profileNode.event; |
343 var icon = container.createChild("div", "activity-icon"); | 330 if (this._profileNode.isGroupNode()) { |
344 var name = container.createChild("div", "activity-name"); | 331 var treeView = /** @type {!WebInspector.AggregatedTimelineTreeView} */ (th
is._treeView); |
345 var event = this._profileNode.event; | 332 var info = treeView._displayInfoForGroupNode(this._profileNode); |
346 if (this._profileNode.isGroupNode()) { | 333 name.textContent = info.name; |
347 var treeView = /** @type {!WebInspector.AggregatedTimelineTreeView}
*/ (this._treeView); | 334 icon.style.backgroundColor = info.color; |
348 var info = treeView._displayInfoForGroupNode(this._profileNode); | 335 } else if (event) { |
349 name.textContent = info.name; | 336 var data = event.args['data']; |
350 icon.style.backgroundColor = info.color; | 337 var deoptReason = data && data['deoptReason']; |
351 } else if (event) { | 338 if (deoptReason) |
352 var data = event.args["data"]; | 339 container.createChild('div', 'activity-warning').title = |
353 var deoptReason = data && data["deoptReason"]; | 340 WebInspector.UIString('Not optimized: %s', deoptReason); |
354 if (deoptReason) | 341 name.textContent = event.name === WebInspector.TimelineModel.RecordType.JS
Frame ? |
355 container.createChild("div", "activity-warning").title = WebInsp
ector.UIString("Not optimized: %s", deoptReason); | 342 WebInspector.beautifyFunctionName(event.args['data']['functionName'])
: |
356 name.textContent = event.name === WebInspector.TimelineModel.RecordT
ype.JSFrame | 343 WebInspector.TimelineUIUtils.eventTitle(event); |
357 ? WebInspector.beautifyFunctionName(event.args["data"]["function
Name"]) | 344 var link = this._treeView._linkifyLocation(event); |
358 : WebInspector.TimelineUIUtils.eventTitle(event); | 345 if (link) |
359 var link = this._treeView._linkifyLocation(event); | 346 container.createChild('div', 'activity-link').appendChild(link); |
360 if (link) | 347 icon.style.backgroundColor = WebInspector.TimelineUIUtils.eventColor(event
); |
361 container.createChild("div", "activity-link").appendChild(link); | 348 } |
362 icon.style.backgroundColor = WebInspector.TimelineUIUtils.eventColor
(event); | 349 return cell; |
363 } | 350 } |
364 return cell; | 351 |
365 }, | 352 /** |
366 | 353 * @param {string} columnId |
367 /** | 354 * @return {?Element} |
368 * @param {string} columnId | 355 */ |
369 * @return {?Element} | 356 _createValueCell(columnId) { |
370 */ | 357 if (columnId !== 'self' && columnId !== 'total' && columnId !== 'startTime') |
371 _createValueCell: function(columnId) | 358 return null; |
372 { | 359 |
373 if (columnId !== "self" && columnId !== "total" && columnId !== "startTi
me") | 360 var showPercents = false; |
374 return null; | 361 var value; |
375 | 362 var maxTime; |
376 var showPercents = false; | 363 switch (columnId) { |
377 var value; | 364 case 'startTime': |
378 var maxTime; | 365 value = this._profileNode.event.startTime - this._treeView._model.minimu
mRecordTime(); |
379 switch (columnId) { | 366 break; |
380 case "startTime": | 367 case 'self': |
381 value = this._profileNode.event.startTime - this._treeView._model.mi
nimumRecordTime(); | 368 value = this._profileNode.selfTime; |
382 break; | 369 maxTime = this._maxSelfTime; |
383 case "self": | 370 showPercents = true; |
384 value = this._profileNode.selfTime; | 371 break; |
385 maxTime = this._maxSelfTime; | 372 case 'total': |
386 showPercents = true; | 373 value = this._profileNode.totalTime; |
387 break; | 374 maxTime = this._maxTotalTime; |
388 case "total": | 375 showPercents = true; |
389 value = this._profileNode.totalTime; | 376 break; |
390 maxTime = this._maxTotalTime; | 377 default: |
391 showPercents = true; | 378 return null; |
392 break; | 379 } |
393 default: | 380 var cell = this.createTD(columnId); |
394 return null; | 381 cell.className = 'numeric-column'; |
395 } | 382 var textDiv = cell.createChild('div'); |
396 var cell = this.createTD(columnId); | 383 textDiv.createChild('span').textContent = WebInspector.UIString('%.1f\u2009m
s', value); |
397 cell.className = "numeric-column"; | 384 |
398 var textDiv = cell.createChild("div"); | 385 if (showPercents && this._treeView._exposePercentages()) |
399 textDiv.createChild("span").textContent = WebInspector.UIString("%.1f\u2
009ms", value); | 386 textDiv.createChild('span', 'percent-column').textContent = |
400 | 387 WebInspector.UIString('%.1f\u2009%%', value / this._grandTotalTime * 1
00); |
401 if (showPercents && this._treeView._exposePercentages()) | 388 if (maxTime) { |
402 textDiv.createChild("span", "percent-column").textContent = WebInspe
ctor.UIString("%.1f\u2009%%", value / this._grandTotalTime * 100); | 389 textDiv.classList.add('background-percent-bar'); |
403 if (maxTime) { | 390 cell.createChild('div', 'background-bar-container').createChild('div', 'ba
ckground-bar').style.width = |
404 textDiv.classList.add("background-percent-bar"); | 391 (value * 100 / maxTime).toFixed(1) + '%'; |
405 cell.createChild("div", "background-bar-container").createChild("div
", "background-bar").style.width = (value * 100 / maxTime).toFixed(1) + "%"; | 392 } |
406 } | 393 return cell; |
407 return cell; | 394 } |
408 }, | |
409 | |
410 __proto__: WebInspector.SortableDataGridNode.prototype | |
411 }; | 395 }; |
412 | 396 |
413 /** | 397 /** |
414 * @constructor | 398 * @unrestricted |
415 * @extends {WebInspector.TimelineTreeView.GridNode} | |
416 * @param {!WebInspector.TimelineProfileTree.Node} profileNode | |
417 * @param {number} grandTotalTime | |
418 * @param {number} maxSelfTime | |
419 * @param {number} maxTotalTime | |
420 * @param {!WebInspector.TimelineTreeView} treeView | |
421 */ | 399 */ |
422 WebInspector.TimelineTreeView.TreeGridNode = function(profileNode, grandTotalTim
e, maxSelfTime, maxTotalTime, treeView) | 400 WebInspector.TimelineTreeView.TreeGridNode = class extends WebInspector.Timeline
TreeView.GridNode { |
423 { | 401 /** |
424 WebInspector.TimelineTreeView.GridNode.call(this, profileNode, grandTotalTim
e, maxSelfTime, maxTotalTime, treeView); | 402 * @param {!WebInspector.TimelineProfileTree.Node} profileNode |
| 403 * @param {number} grandTotalTime |
| 404 * @param {number} maxSelfTime |
| 405 * @param {number} maxTotalTime |
| 406 * @param {!WebInspector.TimelineTreeView} treeView |
| 407 */ |
| 408 constructor(profileNode, grandTotalTime, maxSelfTime, maxTotalTime, treeView)
{ |
| 409 super(profileNode, grandTotalTime, maxSelfTime, maxTotalTime, treeView); |
425 this.hasChildren = this._profileNode.children ? this._profileNode.children.s
ize > 0 : false; | 410 this.hasChildren = this._profileNode.children ? this._profileNode.children.s
ize > 0 : false; |
426 profileNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol] = th
is; | 411 profileNode[WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol] = th
is; |
427 }; | 412 } |
428 | 413 |
429 WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol = Symbol("treeGridNod
e"); | 414 /** |
430 | 415 * @override |
431 WebInspector.TimelineTreeView.TreeGridNode.prototype = { | 416 */ |
432 /** | 417 populate() { |
433 * @override | 418 if (this._populated) |
434 */ | 419 return; |
435 populate: function() | 420 this._populated = true; |
436 { | 421 if (!this._profileNode.children) |
437 if (this._populated) | 422 return; |
438 return; | 423 for (var node of this._profileNode.children.values()) { |
439 this._populated = true; | 424 var gridNode = new WebInspector.TimelineTreeView.TreeGridNode( |
440 if (!this._profileNode.children) | 425 node, this._grandTotalTime, this._maxSelfTime, this._maxTotalTime, thi
s._treeView); |
441 return; | 426 this.insertChildOrdered(gridNode); |
442 for (var node of this._profileNode.children.values()) { | 427 } |
443 var gridNode = new WebInspector.TimelineTreeView.TreeGridNode(node,
this._grandTotalTime, this._maxSelfTime, this._maxTotalTime, this._treeView); | 428 } |
444 this.insertChildOrdered(gridNode); | 429 }; |
445 } | 430 |
446 }, | 431 WebInspector.TimelineTreeView.TreeGridNode._gridNodeSymbol = Symbol('treeGridNod
e'); |
447 | |
448 __proto__: WebInspector.TimelineTreeView.GridNode.prototype | |
449 }; | |
450 | |
451 | 432 |
452 /** | 433 /** |
453 * @constructor | 434 * @unrestricted |
454 * @extends {WebInspector.TimelineTreeView} | |
455 * @param {!WebInspector.TimelineModel} model | |
456 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
457 */ | 435 */ |
458 WebInspector.AggregatedTimelineTreeView = function(model, filters) | 436 WebInspector.AggregatedTimelineTreeView = class extends WebInspector.TimelineTre
eView { |
459 { | 437 /** |
460 WebInspector.TimelineTreeView.call(this); | 438 * @param {!WebInspector.TimelineModel} model |
461 this._groupBySetting = WebInspector.settings.createSetting("timelineTreeGrou
pBy", WebInspector.TimelineAggregator.GroupBy.Category); | 439 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| 440 */ |
| 441 constructor(model, filters) { |
| 442 super(); |
| 443 this._groupBySetting = |
| 444 WebInspector.settings.createSetting('timelineTreeGroupBy', WebInspector.
TimelineAggregator.GroupBy.Category); |
462 this._init(model, filters); | 445 this._init(model, filters); |
463 var nonessentialEvents = [ | 446 var nonessentialEvents = [ |
464 WebInspector.TimelineModel.RecordType.EventDispatch, | 447 WebInspector.TimelineModel.RecordType.EventDispatch, WebInspector.Timeline
Model.RecordType.FunctionCall, |
465 WebInspector.TimelineModel.RecordType.FunctionCall, | 448 WebInspector.TimelineModel.RecordType.TimerFire |
466 WebInspector.TimelineModel.RecordType.TimerFire | |
467 ]; | 449 ]; |
468 this._filters.push(new WebInspector.ExclusiveNameFilter(nonessentialEvents))
; | 450 this._filters.push(new WebInspector.ExclusiveNameFilter(nonessentialEvents))
; |
469 this._stackView = new WebInspector.TimelineStackView(this); | 451 this._stackView = new WebInspector.TimelineStackView(this); |
470 this._stackView.addEventListener(WebInspector.TimelineStackView.Events.Selec
tionChanged, this._onStackViewSelectionChanged, this); | 452 this._stackView.addEventListener( |
471 }; | 453 WebInspector.TimelineStackView.Events.SelectionChanged, this._onStackVie
wSelectionChanged, this); |
472 | 454 } |
473 WebInspector.AggregatedTimelineTreeView.prototype = { | 455 |
| 456 /** |
| 457 * @override |
| 458 * @param {!WebInspector.TimelineSelection} selection |
| 459 */ |
| 460 updateContents(selection) { |
| 461 this._updateExtensionResolver(); |
| 462 super.updateContents(selection); |
| 463 var rootNode = this._dataGrid.rootNode(); |
| 464 if (rootNode.children.length) |
| 465 rootNode.children[0].revealAndSelect(); |
| 466 } |
| 467 |
| 468 _updateExtensionResolver() { |
| 469 this._executionContextNamesByOrigin = new Map(); |
| 470 for (var target of WebInspector.targetManager.targets()) { |
| 471 for (var context of target.runtimeModel.executionContexts()) |
| 472 this._executionContextNamesByOrigin.set(context.origin, context.name); |
| 473 } |
| 474 } |
| 475 |
| 476 /** |
| 477 * @param {!WebInspector.TimelineProfileTree.Node} node |
| 478 * @return {!{name: string, color: string}} |
| 479 */ |
| 480 _displayInfoForGroupNode(node) { |
| 481 var categories = WebInspector.TimelineUIUtils.categories(); |
| 482 var color = node.id ? WebInspector.TimelineUIUtils.eventColor(node.event) :
categories['other'].color; |
| 483 |
| 484 switch (this._groupBySetting.get()) { |
| 485 case WebInspector.TimelineAggregator.GroupBy.Category: |
| 486 var category = categories[node.id] || categories['other']; |
| 487 return {name: category.title, color: category.color}; |
| 488 |
| 489 case WebInspector.TimelineAggregator.GroupBy.Domain: |
| 490 case WebInspector.TimelineAggregator.GroupBy.Subdomain: |
| 491 var name = node.id; |
| 492 if (WebInspector.TimelineAggregator.isExtensionInternalURL(name)) |
| 493 name = WebInspector.UIString('[Chrome extensions overhead]'); |
| 494 else if (name.startsWith('chrome-extension')) |
| 495 name = this._executionContextNamesByOrigin.get(name) || name; |
| 496 return {name: name || WebInspector.UIString('unattributed'), color: colo
r}; |
| 497 |
| 498 case WebInspector.TimelineAggregator.GroupBy.EventName: |
| 499 var name = node.event.name === WebInspector.TimelineModel.RecordType.JSF
rame ? |
| 500 WebInspector.UIString('JavaScript') : |
| 501 WebInspector.TimelineUIUtils.eventTitle(node.event); |
| 502 return { |
| 503 name: name, |
| 504 color: node.event.name === WebInspector.TimelineModel.RecordType.JSFra
me ? |
| 505 WebInspector.TimelineUIUtils.eventStyle(node.event).category.color
: |
| 506 color |
| 507 }; |
| 508 |
| 509 case WebInspector.TimelineAggregator.GroupBy.URL: |
| 510 break; |
| 511 |
| 512 default: |
| 513 console.assert(false, 'Unexpected aggregation type'); |
| 514 } |
| 515 return {name: node.id || WebInspector.UIString('unattributed'), color: color
}; |
| 516 } |
| 517 |
| 518 /** |
| 519 * @override |
| 520 * @param {!Element} parent |
| 521 */ |
| 522 _populateToolbar(parent) { |
| 523 var panelToolbar = new WebInspector.Toolbar('', parent); |
| 524 this._groupByCombobox = new WebInspector.ToolbarComboBox(this._onGroupByChan
ged.bind(this)); |
474 /** | 525 /** |
475 * @override | 526 * @param {string} name |
476 * @param {!WebInspector.TimelineSelection} selection | 527 * @param {string} id |
| 528 * @this {WebInspector.TimelineTreeView} |
477 */ | 529 */ |
478 updateContents: function(selection) | 530 function addGroupingOption(name, id) { |
479 { | 531 var option = this._groupByCombobox.createOption(name, '', id); |
480 this._updateExtensionResolver(); | 532 this._groupByCombobox.addOption(option); |
481 WebInspector.TimelineTreeView.prototype.updateContents.call(this, select
ion); | 533 if (id === this._groupBySetting.get()) |
482 var rootNode = this._dataGrid.rootNode(); | 534 this._groupByCombobox.select(option); |
483 if (rootNode.children.length) | 535 } |
484 rootNode.children[0].revealAndSelect(); | 536 addGroupingOption.call(this, WebInspector.UIString('No Grouping'), WebInspec
tor.TimelineAggregator.GroupBy.None); |
485 }, | 537 addGroupingOption.call( |
486 | 538 this, WebInspector.UIString('Group by Activity'), WebInspector.TimelineA
ggregator.GroupBy.EventName); |
487 _updateExtensionResolver: function() | 539 addGroupingOption.call( |
488 { | 540 this, WebInspector.UIString('Group by Category'), WebInspector.TimelineA
ggregator.GroupBy.Category); |
489 this._executionContextNamesByOrigin = new Map(); | 541 addGroupingOption.call( |
490 for (var target of WebInspector.targetManager.targets()) { | 542 this, WebInspector.UIString('Group by Domain'), WebInspector.TimelineAgg
regator.GroupBy.Domain); |
491 for (var context of target.runtimeModel.executionContexts()) | 543 addGroupingOption.call( |
492 this._executionContextNamesByOrigin.set(context.origin, context.
name); | 544 this, WebInspector.UIString('Group by Subdomain'), WebInspector.Timeline
Aggregator.GroupBy.Subdomain); |
493 } | 545 addGroupingOption.call(this, WebInspector.UIString('Group by URL'), WebInspe
ctor.TimelineAggregator.GroupBy.URL); |
494 }, | 546 panelToolbar.appendToolbarItem(this._groupByCombobox); |
495 | 547 } |
496 /** | 548 |
497 * @param {!WebInspector.TimelineProfileTree.Node} node | 549 /** |
498 * @return {!{name: string, color: string}} | 550 * @param {!WebInspector.TimelineProfileTree.Node} treeNode |
499 */ | 551 * @return {!Array<!WebInspector.TimelineProfileTree.Node>} |
500 _displayInfoForGroupNode: function(node) | 552 */ |
501 { | 553 _buildHeaviestStack(treeNode) { |
502 var categories = WebInspector.TimelineUIUtils.categories(); | 554 console.assert(!!treeNode.parent, 'Attempt to build stack for tree root'); |
503 var color = node.id ? WebInspector.TimelineUIUtils.eventColor(node.event
) : categories["other"].color; | 555 var result = []; |
504 | 556 // Do not add root to the stack, as it's the tree itself. |
505 switch (this._groupBySetting.get()) { | 557 for (var node = treeNode; node && node.parent; node = node.parent) |
506 case WebInspector.TimelineAggregator.GroupBy.Category: | 558 result.push(node); |
507 var category = categories[node.id] || categories["other"]; | 559 result = result.reverse(); |
508 return {name: category.title, color: category.color}; | 560 for (node = treeNode; node && node.children && node.children.size;) { |
509 | 561 var children = Array.from(node.children.values()); |
510 case WebInspector.TimelineAggregator.GroupBy.Domain: | 562 node = children.reduce((a, b) => a.totalTime > b.totalTime ? a : b); |
511 case WebInspector.TimelineAggregator.GroupBy.Subdomain: | 563 result.push(node); |
512 var name = node.id; | 564 } |
513 if (WebInspector.TimelineAggregator.isExtensionInternalURL(name)) | 565 return result; |
514 name = WebInspector.UIString("[Chrome extensions overhead]"); | 566 } |
515 else if (name.startsWith("chrome-extension")) | 567 |
516 name = this._executionContextNamesByOrigin.get(name) || name; | 568 /** |
517 return { | 569 * @override |
518 name: name || WebInspector.UIString("unattributed"), | 570 * @return {boolean} |
519 color: color | 571 */ |
520 }; | 572 _exposePercentages() { |
521 | 573 return true; |
522 case WebInspector.TimelineAggregator.GroupBy.EventName: | 574 } |
523 var name = node.event.name === WebInspector.TimelineModel.RecordType
.JSFrame ? | 575 |
524 WebInspector.UIString("JavaScript") : WebInspector.TimelineUIUti
ls.eventTitle(node.event); | 576 _onGroupByChanged() { |
525 return { | 577 this._groupBySetting.set(this._groupByCombobox.selectedOption().value); |
526 name: name, | 578 this._refreshTree(); |
527 color: node.event.name === WebInspector.TimelineModel.RecordType
.JSFrame ? | 579 } |
528 WebInspector.TimelineUIUtils.eventStyle(node.event).category
.color : color | 580 |
529 }; | 581 _onStackViewSelectionChanged() { |
530 | 582 var treeNode = this._stackView.selectedTreeNode(); |
531 case WebInspector.TimelineAggregator.GroupBy.URL: | 583 if (treeNode) |
532 break; | 584 this.selectProfileNode(treeNode, true); |
533 | 585 } |
534 default: | 586 |
535 console.assert(false, "Unexpected aggregation type"); | 587 /** |
536 } | 588 * @override |
537 return { | 589 * @param {!WebInspector.TimelineProfileTree.Node} node |
538 name: node.id || WebInspector.UIString("unattributed"), | 590 * @return {boolean} |
539 color: color | 591 */ |
540 }; | 592 _showDetailsForNode(node) { |
541 }, | 593 var stack = this._buildHeaviestStack(node); |
542 | 594 this._stackView.setStack(stack, node); |
543 /** | 595 this._stackView.show(this._detailsView.element); |
544 * @override | 596 return true; |
545 * @param {!Element} parent | 597 } |
546 */ | 598 |
547 _populateToolbar: function(parent) | 599 /** |
548 { | 600 * @return {!WebInspector.TimelineAggregator} |
549 var panelToolbar = new WebInspector.Toolbar("", parent); | 601 */ |
550 this._groupByCombobox = new WebInspector.ToolbarComboBox(this._onGroupBy
Changed.bind(this)); | 602 _createAggregator() { |
551 /** | 603 return new WebInspector.TimelineAggregator( |
552 * @param {string} name | 604 event => WebInspector.TimelineUIUtils.eventStyle(event).title, |
553 * @param {string} id | 605 event => WebInspector.TimelineUIUtils.eventStyle(event).category.name); |
554 * @this {WebInspector.TimelineTreeView} | 606 } |
555 */ | |
556 function addGroupingOption(name, id) | |
557 { | |
558 var option = this._groupByCombobox.createOption(name, "", id); | |
559 this._groupByCombobox.addOption(option); | |
560 if (id === this._groupBySetting.get()) | |
561 this._groupByCombobox.select(option); | |
562 } | |
563 addGroupingOption.call(this, WebInspector.UIString("No Grouping"), WebIn
spector.TimelineAggregator.GroupBy.None); | |
564 addGroupingOption.call(this, WebInspector.UIString("Group by Activity"),
WebInspector.TimelineAggregator.GroupBy.EventName); | |
565 addGroupingOption.call(this, WebInspector.UIString("Group by Category"),
WebInspector.TimelineAggregator.GroupBy.Category); | |
566 addGroupingOption.call(this, WebInspector.UIString("Group by Domain"), W
ebInspector.TimelineAggregator.GroupBy.Domain); | |
567 addGroupingOption.call(this, WebInspector.UIString("Group by Subdomain")
, WebInspector.TimelineAggregator.GroupBy.Subdomain); | |
568 addGroupingOption.call(this, WebInspector.UIString("Group by URL"), WebI
nspector.TimelineAggregator.GroupBy.URL); | |
569 panelToolbar.appendToolbarItem(this._groupByCombobox); | |
570 }, | |
571 | |
572 /** | |
573 * @param {!WebInspector.TimelineProfileTree.Node} treeNode | |
574 * @return {!Array<!WebInspector.TimelineProfileTree.Node>} | |
575 */ | |
576 _buildHeaviestStack: function(treeNode) | |
577 { | |
578 console.assert(!!treeNode.parent, "Attempt to build stack for tree root"
); | |
579 var result = []; | |
580 // Do not add root to the stack, as it's the tree itself. | |
581 for (var node = treeNode; node && node.parent; node = node.parent) | |
582 result.push(node); | |
583 result = result.reverse(); | |
584 for (node = treeNode; node && node.children && node.children.size;) { | |
585 var children = Array.from(node.children.values()); | |
586 node = children.reduce((a, b) => a.totalTime > b.totalTime ? a : b); | |
587 result.push(node); | |
588 } | |
589 return result; | |
590 }, | |
591 | |
592 /** | |
593 * @override | |
594 * @return {boolean} | |
595 */ | |
596 _exposePercentages: function() | |
597 { | |
598 return true; | |
599 }, | |
600 | |
601 _onGroupByChanged: function() | |
602 { | |
603 this._groupBySetting.set(this._groupByCombobox.selectedOption().value); | |
604 this._refreshTree(); | |
605 }, | |
606 | |
607 _onStackViewSelectionChanged: function() | |
608 { | |
609 var treeNode = this._stackView.selectedTreeNode(); | |
610 if (treeNode) | |
611 this.selectProfileNode(treeNode, true); | |
612 }, | |
613 | |
614 /** | |
615 * @override | |
616 * @param {!WebInspector.TimelineProfileTree.Node} node | |
617 * @return {boolean} | |
618 */ | |
619 _showDetailsForNode: function(node) | |
620 { | |
621 var stack = this._buildHeaviestStack(node); | |
622 this._stackView.setStack(stack, node); | |
623 this._stackView.show(this._detailsView.element); | |
624 return true; | |
625 }, | |
626 | |
627 /** | |
628 * @return {!WebInspector.TimelineAggregator} | |
629 */ | |
630 _createAggregator: function() | |
631 { | |
632 return new WebInspector.TimelineAggregator( | |
633 event => WebInspector.TimelineUIUtils.eventStyle(event).title, | |
634 event => WebInspector.TimelineUIUtils.eventStyle(event).category.nam
e | |
635 ); | |
636 }, | |
637 | |
638 __proto__: WebInspector.TimelineTreeView.prototype, | |
639 }; | 607 }; |
640 | 608 |
641 /** | 609 /** |
642 * @constructor | 610 * @unrestricted |
643 * @extends {WebInspector.AggregatedTimelineTreeView} | |
644 * @param {!WebInspector.TimelineModel} model | |
645 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
646 */ | 611 */ |
647 WebInspector.CallTreeTimelineTreeView = function(model, filters) | 612 WebInspector.CallTreeTimelineTreeView = class extends WebInspector.AggregatedTim
elineTreeView { |
648 { | 613 /** |
649 WebInspector.AggregatedTimelineTreeView.call(this, model, filters); | 614 * @param {!WebInspector.TimelineModel} model |
650 this._dataGrid.markColumnAsSortedBy("total", WebInspector.DataGrid.Order.Des
cending); | 615 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
651 }; | 616 */ |
652 | 617 constructor(model, filters) { |
653 WebInspector.CallTreeTimelineTreeView.prototype = { | 618 super(model, filters); |
654 /** | 619 this._dataGrid.markColumnAsSortedBy('total', WebInspector.DataGrid.Order.Des
cending); |
655 * @override | 620 } |
656 * @return {!WebInspector.TimelineProfileTree.Node} | 621 |
657 */ | 622 /** |
658 _buildTree: function() | 623 * @override |
659 { | 624 * @return {!WebInspector.TimelineProfileTree.Node} |
660 var topDown = this._buildTopDownTree(WebInspector.TimelineAggregator.eve
ntId); | 625 */ |
661 return this._createAggregator().performGrouping(topDown, this._groupBySe
tting.get()); | 626 _buildTree() { |
662 }, | 627 var topDown = this._buildTopDownTree(WebInspector.TimelineAggregator.eventId
); |
663 | 628 return this._createAggregator().performGrouping(topDown, this._groupBySettin
g.get()); |
664 __proto__: WebInspector.AggregatedTimelineTreeView.prototype, | 629 } |
665 }; | 630 }; |
666 | 631 |
667 /** | 632 /** |
668 * @constructor | 633 * @unrestricted |
669 * @extends {WebInspector.AggregatedTimelineTreeView} | |
670 * @param {!WebInspector.TimelineModel} model | |
671 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
672 */ | 634 */ |
673 WebInspector.BottomUpTimelineTreeView = function(model, filters) | 635 WebInspector.BottomUpTimelineTreeView = class extends WebInspector.AggregatedTim
elineTreeView { |
674 { | 636 /** |
675 WebInspector.AggregatedTimelineTreeView.call(this, model, filters); | 637 * @param {!WebInspector.TimelineModel} model |
676 this._dataGrid.markColumnAsSortedBy("self", WebInspector.DataGrid.Order.Desc
ending); | 638 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
677 }; | 639 */ |
678 | 640 constructor(model, filters) { |
679 WebInspector.BottomUpTimelineTreeView.prototype = { | 641 super(model, filters); |
680 /** | 642 this._dataGrid.markColumnAsSortedBy('self', WebInspector.DataGrid.Order.Desc
ending); |
681 * @override | 643 } |
682 * @return {!WebInspector.TimelineProfileTree.Node} | 644 |
683 */ | 645 /** |
684 _buildTree: function() | 646 * @override |
685 { | 647 * @return {!WebInspector.TimelineProfileTree.Node} |
686 var topDown = this._buildTopDownTree(WebInspector.TimelineAggregator.eve
ntId); | 648 */ |
687 return WebInspector.TimelineProfileTree.buildBottomUp(topDown, this._cre
ateAggregator().groupFunction(this._groupBySetting.get())); | 649 _buildTree() { |
688 }, | 650 var topDown = this._buildTopDownTree(WebInspector.TimelineAggregator.eventId
); |
689 | 651 return WebInspector.TimelineProfileTree.buildBottomUp( |
690 __proto__: WebInspector.AggregatedTimelineTreeView.prototype | 652 topDown, this._createAggregator().groupFunction(this._groupBySetting.get
())); |
| 653 } |
691 }; | 654 }; |
692 | 655 |
693 /** | 656 /** |
694 * @constructor | 657 * @unrestricted |
695 * @extends {WebInspector.TimelineTreeView} | |
696 * @param {!WebInspector.TimelineModel} model | |
697 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | |
698 * @param {!WebInspector.TimelineModeViewDelegate} delegate | |
699 */ | 658 */ |
700 WebInspector.EventsTimelineTreeView = function(model, filters, delegate) | 659 WebInspector.EventsTimelineTreeView = class extends WebInspector.TimelineTreeVie
w { |
701 { | 660 /** |
702 WebInspector.TimelineTreeView.call(this); | 661 * @param {!WebInspector.TimelineModel} model |
| 662 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| 663 * @param {!WebInspector.TimelineModeViewDelegate} delegate |
| 664 */ |
| 665 constructor(model, filters, delegate) { |
| 666 super(); |
703 this._filtersControl = new WebInspector.TimelineFilters(); | 667 this._filtersControl = new WebInspector.TimelineFilters(); |
704 this._filtersControl.addEventListener(WebInspector.TimelineFilters.Events.Fi
lterChanged, this._onFilterChanged, this); | 668 this._filtersControl.addEventListener( |
| 669 WebInspector.TimelineFilters.Events.FilterChanged, this._onFilterChanged
, this); |
705 this._init(model, filters); | 670 this._init(model, filters); |
706 this._delegate = delegate; | 671 this._delegate = delegate; |
707 this._filters.push.apply(this._filters, this._filtersControl.filters()); | 672 this._filters.push.apply(this._filters, this._filtersControl.filters()); |
708 this._dataGrid.markColumnAsSortedBy("startTime", WebInspector.DataGrid.Order
.Ascending); | 673 this._dataGrid.markColumnAsSortedBy('startTime', WebInspector.DataGrid.Order
.Ascending); |
709 }; | 674 } |
710 | 675 |
711 WebInspector.EventsTimelineTreeView.prototype = { | 676 /** |
| 677 * @override |
| 678 * @param {!WebInspector.TimelineSelection} selection |
| 679 */ |
| 680 updateContents(selection) { |
| 681 super.updateContents(selection); |
| 682 if (selection.type() === WebInspector.TimelineSelection.Type.TraceEvent) { |
| 683 var event = /** @type {!WebInspector.TracingModel.Event} */ (selection.obj
ect()); |
| 684 this._selectEvent(event, true); |
| 685 } |
| 686 } |
| 687 |
| 688 /** |
| 689 * @override |
| 690 * @return {!WebInspector.TimelineProfileTree.Node} |
| 691 */ |
| 692 _buildTree() { |
| 693 this._currentTree = this._buildTopDownTree(); |
| 694 return this._currentTree; |
| 695 } |
| 696 |
| 697 _onFilterChanged() { |
| 698 var selectedEvent = this._lastSelectedNode && this._lastSelectedNode.event; |
| 699 this._refreshTree(); |
| 700 if (selectedEvent) |
| 701 this._selectEvent(selectedEvent, false); |
| 702 } |
| 703 |
| 704 /** |
| 705 * @param {!WebInspector.TracingModel.Event} event |
| 706 * @return {?WebInspector.TimelineProfileTree.Node} |
| 707 */ |
| 708 _findNodeWithEvent(event) { |
| 709 var iterators = [this._currentTree.children.values()]; |
| 710 |
| 711 while (iterators.length) { |
| 712 var iterator = iterators.peekLast().next(); |
| 713 if (iterator.done) { |
| 714 iterators.pop(); |
| 715 continue; |
| 716 } |
| 717 var child = /** @type {!WebInspector.TimelineProfileTree.Node} */ (iterato
r.value); |
| 718 if (child.event === event) |
| 719 return child; |
| 720 if (child.children) |
| 721 iterators.push(child.children.values()); |
| 722 } |
| 723 return null; |
| 724 } |
| 725 |
| 726 /** |
| 727 * @param {!WebInspector.TracingModel.Event} event |
| 728 * @param {boolean=} expand |
| 729 */ |
| 730 _selectEvent(event, expand) { |
| 731 var node = this._findNodeWithEvent(event); |
| 732 if (!node) |
| 733 return; |
| 734 this.selectProfileNode(node, false); |
| 735 if (expand) |
| 736 this._dataGridNodeForTreeNode(node).expand(); |
| 737 } |
| 738 |
| 739 /** |
| 740 * @override |
| 741 * @param {!Array<!WebInspector.DataGrid.ColumnDescriptor>} columns |
| 742 */ |
| 743 _populateColumns(columns) { |
| 744 columns.push({ |
| 745 id: 'startTime', |
| 746 title: WebInspector.UIString('Start Time'), |
| 747 width: '110px', |
| 748 fixedWidth: true, |
| 749 sortable: true |
| 750 }); |
| 751 super._populateColumns(columns); |
| 752 } |
| 753 |
| 754 /** |
| 755 * @override |
| 756 * @param {!Element} parent |
| 757 */ |
| 758 _populateToolbar(parent) { |
| 759 var filtersWidget = this._filtersControl.filtersWidget(); |
| 760 filtersWidget.forceShowFilterBar(); |
| 761 filtersWidget.show(parent); |
| 762 } |
| 763 |
| 764 /** |
| 765 * @override |
| 766 * @param {!WebInspector.TimelineProfileTree.Node} node |
| 767 * @return {boolean} |
| 768 */ |
| 769 _showDetailsForNode(node) { |
| 770 var traceEvent = node.event; |
| 771 if (!traceEvent) |
| 772 return false; |
| 773 WebInspector.TimelineUIUtils.buildTraceEventDetails( |
| 774 traceEvent, this._model, this._linkifier, false, showDetails.bind(this))
; |
| 775 return true; |
| 776 |
712 /** | 777 /** |
713 * @override | 778 * @param {!DocumentFragment} fragment |
714 * @param {!WebInspector.TimelineSelection} selection | 779 * @this {WebInspector.EventsTimelineTreeView} |
715 */ | 780 */ |
716 updateContents: function(selection) | 781 function showDetails(fragment) { |
717 { | 782 this._detailsView.element.appendChild(fragment); |
718 WebInspector.TimelineTreeView.prototype.updateContents.call(this, select
ion); | 783 } |
719 if (selection.type() === WebInspector.TimelineSelection.Type.TraceEvent)
{ | 784 } |
720 var event = /** @type {!WebInspector.TracingModel.Event} */ (selecti
on.object()); | 785 |
721 this._selectEvent(event, true); | 786 /** |
722 } | 787 * @override |
723 }, | 788 * @param {?WebInspector.TimelineProfileTree.Node} node |
724 | 789 */ |
725 /** | 790 _onHover(node) { |
726 * @override | 791 this._delegate.highlightEvent(node && node.event); |
727 * @return {!WebInspector.TimelineProfileTree.Node} | 792 } |
728 */ | |
729 _buildTree: function() | |
730 { | |
731 this._currentTree = this._buildTopDownTree(); | |
732 return this._currentTree; | |
733 }, | |
734 | |
735 _onFilterChanged: function() | |
736 { | |
737 var selectedEvent = this._lastSelectedNode && this._lastSelectedNode.eve
nt; | |
738 this._refreshTree(); | |
739 if (selectedEvent) | |
740 this._selectEvent(selectedEvent, false); | |
741 }, | |
742 | |
743 /** | |
744 * @param {!WebInspector.TracingModel.Event} event | |
745 * @return {?WebInspector.TimelineProfileTree.Node} | |
746 */ | |
747 _findNodeWithEvent: function(event) | |
748 { | |
749 var iterators = [this._currentTree.children.values()]; | |
750 | |
751 while (iterators.length) { | |
752 var iterator = iterators.peekLast().next(); | |
753 if (iterator.done) { | |
754 iterators.pop(); | |
755 continue; | |
756 } | |
757 var child = /** @type {!WebInspector.TimelineProfileTree.Node} */ (i
terator.value); | |
758 if (child.event === event) | |
759 return child; | |
760 if (child.children) | |
761 iterators.push(child.children.values()); | |
762 } | |
763 return null; | |
764 }, | |
765 | |
766 /** | |
767 * @param {!WebInspector.TracingModel.Event} event | |
768 * @param {boolean=} expand | |
769 */ | |
770 _selectEvent: function(event, expand) | |
771 { | |
772 var node = this._findNodeWithEvent(event); | |
773 if (!node) | |
774 return; | |
775 this.selectProfileNode(node, false); | |
776 if (expand) | |
777 this._dataGridNodeForTreeNode(node).expand(); | |
778 }, | |
779 | |
780 /** | |
781 * @override | |
782 * @param {!Array<!WebInspector.DataGrid.ColumnDescriptor>} columns | |
783 */ | |
784 _populateColumns: function(columns) | |
785 { | |
786 columns.push({id: "startTime", title: WebInspector.UIString("Start Time"
), width: "110px", fixedWidth: true, sortable: true}); | |
787 WebInspector.TimelineTreeView.prototype._populateColumns.call(this, colu
mns); | |
788 }, | |
789 | |
790 /** | |
791 * @override | |
792 * @param {!Element} parent | |
793 */ | |
794 _populateToolbar: function(parent) | |
795 { | |
796 var filtersWidget = this._filtersControl.filtersWidget(); | |
797 filtersWidget.forceShowFilterBar(); | |
798 filtersWidget.show(parent); | |
799 }, | |
800 | |
801 /** | |
802 * @override | |
803 * @param {!WebInspector.TimelineProfileTree.Node} node | |
804 * @return {boolean} | |
805 */ | |
806 _showDetailsForNode: function(node) | |
807 { | |
808 var traceEvent = node.event; | |
809 if (!traceEvent) | |
810 return false; | |
811 WebInspector.TimelineUIUtils.buildTraceEventDetails(traceEvent, this._mo
del, this._linkifier, false, showDetails.bind(this)); | |
812 return true; | |
813 | |
814 /** | |
815 * @param {!DocumentFragment} fragment | |
816 * @this {WebInspector.EventsTimelineTreeView} | |
817 */ | |
818 function showDetails(fragment) | |
819 { | |
820 this._detailsView.element.appendChild(fragment); | |
821 } | |
822 }, | |
823 | |
824 /** | |
825 * @override | |
826 * @param {?WebInspector.TimelineProfileTree.Node} node | |
827 */ | |
828 _onHover: function(node) | |
829 { | |
830 this._delegate.highlightEvent(node && node.event); | |
831 }, | |
832 | |
833 __proto__: WebInspector.TimelineTreeView.prototype | |
834 }; | 793 }; |
835 | 794 |
836 /** | 795 /** |
837 * @constructor | 796 * @unrestricted |
838 * @extends {WebInspector.VBox} | |
839 */ | 797 */ |
840 WebInspector.TimelineStackView = function(treeView) | 798 WebInspector.TimelineStackView = class extends WebInspector.VBox { |
841 { | 799 constructor(treeView) { |
842 WebInspector.VBox.call(this); | 800 super(); |
843 var header = this.element.createChild("div", "timeline-stack-view-header"); | 801 var header = this.element.createChild('div', 'timeline-stack-view-header'); |
844 header.textContent = WebInspector.UIString("Heaviest stack"); | 802 header.textContent = WebInspector.UIString('Heaviest stack'); |
845 this._treeView = treeView; | 803 this._treeView = treeView; |
846 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>} */
([ | 804 var columns = /** @type {!Array<!WebInspector.DataGrid.ColumnDescriptor>} */
([ |
847 {id: "total", title: WebInspector.UIString("Total Time"), fixedWidth: tr
ue, width: "110px"}, | 805 {id: 'total', title: WebInspector.UIString('Total Time'), fixedWidth: true
, width: '110px'}, |
848 {id: "activity", title: WebInspector.UIString("Activity")} | 806 {id: 'activity', title: WebInspector.UIString('Activity')} |
849 ]); | 807 ]); |
850 this._dataGrid = new WebInspector.ViewportDataGrid(columns); | 808 this._dataGrid = new WebInspector.ViewportDataGrid(columns); |
851 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); | 809 this._dataGrid.setResizeMethod(WebInspector.DataGrid.ResizeMethod.Last); |
852 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, t
his._onSelectionChanged, this); | 810 this._dataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, t
his._onSelectionChanged, this); |
853 this._dataGrid.asWidget().show(this.element); | 811 this._dataGrid.asWidget().show(this.element); |
| 812 } |
| 813 |
| 814 /** |
| 815 * @param {!Array<!WebInspector.TimelineProfileTree.Node>} stack |
| 816 * @param {!WebInspector.TimelineProfileTree.Node} selectedNode |
| 817 */ |
| 818 setStack(stack, selectedNode) { |
| 819 var rootNode = this._dataGrid.rootNode(); |
| 820 rootNode.removeChildren(); |
| 821 var nodeToReveal = null; |
| 822 var totalTime = Math.max.apply(Math, stack.map(node => node.totalTime)); |
| 823 for (var node of stack) { |
| 824 var gridNode = new WebInspector.TimelineTreeView.GridNode(node, totalTime,
totalTime, totalTime, this._treeView); |
| 825 rootNode.appendChild(gridNode); |
| 826 if (node === selectedNode) |
| 827 nodeToReveal = gridNode; |
| 828 } |
| 829 nodeToReveal.revealAndSelect(); |
| 830 } |
| 831 |
| 832 /** |
| 833 * @return {?WebInspector.TimelineProfileTree.Node} |
| 834 */ |
| 835 selectedTreeNode() { |
| 836 var selectedNode = this._dataGrid.selectedNode; |
| 837 return selectedNode && /** @type {!WebInspector.TimelineTreeView.GridNode} *
/ (selectedNode)._profileNode; |
| 838 } |
| 839 |
| 840 _onSelectionChanged() { |
| 841 this.dispatchEventToListeners(WebInspector.TimelineStackView.Events.Selectio
nChanged); |
| 842 } |
854 }; | 843 }; |
855 | 844 |
856 /** @enum {symbol} */ | 845 /** @enum {symbol} */ |
857 WebInspector.TimelineStackView.Events = { | 846 WebInspector.TimelineStackView.Events = { |
858 SelectionChanged: Symbol("SelectionChanged") | 847 SelectionChanged: Symbol('SelectionChanged') |
859 }; | 848 }; |
860 | |
861 WebInspector.TimelineStackView.prototype = { | |
862 /** | |
863 * @param {!Array<!WebInspector.TimelineProfileTree.Node>} stack | |
864 * @param {!WebInspector.TimelineProfileTree.Node} selectedNode | |
865 */ | |
866 setStack: function(stack, selectedNode) | |
867 { | |
868 var rootNode = this._dataGrid.rootNode(); | |
869 rootNode.removeChildren(); | |
870 var nodeToReveal = null; | |
871 var totalTime = Math.max.apply(Math, stack.map(node => node.totalTime)); | |
872 for (var node of stack) { | |
873 var gridNode = new WebInspector.TimelineTreeView.GridNode(node, tota
lTime, totalTime, totalTime, this._treeView); | |
874 rootNode.appendChild(gridNode); | |
875 if (node === selectedNode) | |
876 nodeToReveal = gridNode; | |
877 } | |
878 nodeToReveal.revealAndSelect(); | |
879 }, | |
880 | |
881 /** | |
882 * @return {?WebInspector.TimelineProfileTree.Node} | |
883 */ | |
884 selectedTreeNode: function() | |
885 { | |
886 var selectedNode = this._dataGrid.selectedNode; | |
887 return selectedNode && /** @type {!WebInspector.TimelineTreeView.GridNod
e} */ (selectedNode)._profileNode; | |
888 }, | |
889 | |
890 _onSelectionChanged: function() | |
891 { | |
892 this.dispatchEventToListeners(WebInspector.TimelineStackView.Events.Sele
ctionChanged); | |
893 }, | |
894 | |
895 __proto__: WebInspector.VBox.prototype | |
896 }; | |
OLD | NEW |