2
* Copyright (C) 2009 280 North Inc. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
13
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
* @extends {WebInspector.DataGridNode}
30
WebInspector.ProfileDataGridNode = function(profileView, profileNode, owningTree, hasChildren)
32
this.profileView = profileView;
33
this.profileNode = profileNode;
35
WebInspector.DataGridNode.call(this, null, hasChildren);
37
this.addEventListener("populate", this._populate, this);
39
this.tree = owningTree;
41
this.childrenByCallUID = {};
42
this.lastComparator = null;
44
this.callUID = profileNode.callUID;
45
this.selfTime = profileNode.selfTime;
46
this.totalTime = profileNode.totalTime;
47
this.functionName = profileNode.functionName;
48
this.numberOfCalls = profileNode.numberOfCalls;
49
this.url = profileNode.url;
52
WebInspector.ProfileDataGridNode.prototype = {
55
function formatMilliseconds(time)
57
return Number.secondsToString(time / 1000, !Capabilities.samplingCPUProfiler);
62
data["function"] = this.functionName;
63
data["calls"] = this.numberOfCalls;
65
if (this.profileView.showSelfTimeAsPercent.get())
66
data["self"] = WebInspector.UIString("%.2f%", this.selfPercent);
68
data["self"] = formatMilliseconds(this.selfTime);
70
if (this.profileView.showTotalTimeAsPercent.get())
71
data["total"] = WebInspector.UIString("%.2f%", this.totalPercent);
73
data["total"] = formatMilliseconds(this.totalTime);
75
if (this.profileView.showAverageTimeAsPercent.get())
76
data["average"] = WebInspector.UIString("%.2f%", this.averagePercent);
78
data["average"] = formatMilliseconds(this.averageTime);
83
createCell: function(columnIdentifier)
85
var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
87
if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
88
cell.addStyleClass("highlight");
89
else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
90
cell.addStyleClass("highlight");
91
else if (columnIdentifier === "average" && this._searchMatchedAverageColumn)
92
cell.addStyleClass("highlight");
93
else if (columnIdentifier === "calls" && this._searchMatchedCallsColumn)
94
cell.addStyleClass("highlight");
96
if (columnIdentifier !== "function")
99
if (this.profileNode._searchMatchedFunctionColumn)
100
cell.addStyleClass("highlight");
102
if (this.profileNode.url) {
103
// FIXME(62725): profileNode should reference a debugger location.
104
var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
105
var urlElement = this.profileView._linkifier.linkifyLocation(this.profileNode.url, lineNumber, 0, "profile-node-file");
106
urlElement.style.maxWidth = "75%";
107
cell.insertBefore(urlElement, cell.firstChild);
113
select: function(supressSelectedEvent)
115
WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
116
this.profileView._dataGridNodeSelected(this);
119
deselect: function(supressDeselectedEvent)
121
WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
122
this.profileView._dataGridNodeDeselected(this);
125
sort: function(/*Function*/ comparator, /*Boolean*/ force)
127
var gridNodeGroups = [[this]];
129
for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
130
var gridNodes = gridNodeGroups[gridNodeGroupIndex];
131
var count = gridNodes.length;
133
for (var index = 0; index < count; ++index) {
134
var gridNode = gridNodes[index];
136
// If the grid node is collapsed, then don't sort children (save operation for later).
137
// If the grid node has the same sorting as previously, then there is no point in sorting it again.
138
if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
139
if (gridNode.children.length)
140
gridNode.shouldRefreshChildren = true;
144
gridNode.lastComparator = comparator;
146
var children = gridNode.children;
147
var childCount = children.length;
150
children.sort(comparator);
152
for (var childIndex = 0; childIndex < childCount; ++childIndex)
153
children[childIndex]._recalculateSiblings(childIndex);
155
gridNodeGroups.push(children);
161
insertChild: function(/*ProfileDataGridNode*/ profileDataGridNode, index)
163
WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
165
this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
168
removeChild: function(/*ProfileDataGridNode*/ profileDataGridNode)
170
WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
172
delete this.childrenByCallUID[profileDataGridNode.callUID];
175
removeChildren: function(/*ProfileDataGridNode*/ profileDataGridNode)
177
WebInspector.DataGridNode.prototype.removeChildren.call(this);
179
this.childrenByCallUID = {};
182
findChild: function(/*Node*/ node)
186
return this.childrenByCallUID[node.callUID];
191
return this.selfTime / Math.max(1, this.numberOfCalls);
196
return this.averageTime / this.tree.totalTime * 100.0;
201
return this.selfTime / this.tree.totalTime * 100.0;
206
return this.totalTime / this.tree.totalTime * 100.0;
211
return this.parent !== this.dataGrid ? this.parent : this.tree;
214
_populate: function()
216
this._sharedPopulate();
219
var currentComparator = this._parent.lastComparator;
221
if (currentComparator)
222
this.sort(currentComparator, true);
225
if (this.removeEventListener)
226
this.removeEventListener("populate", this._populate, this);
229
// When focusing and collapsing we modify lots of nodes in the tree.
230
// This allows us to restore them all to their original state when we revert.
233
if (this._savedChildren)
236
this._savedSelfTime = this.selfTime;
237
this._savedTotalTime = this.totalTime;
238
this._savedNumberOfCalls = this.numberOfCalls;
240
this._savedChildren = this.children.slice();
243
// When focusing and collapsing we modify lots of nodes in the tree.
244
// This allows us to restore them all to their original state when we revert.
247
if (!this._savedChildren)
250
this.selfTime = this._savedSelfTime;
251
this.totalTime = this._savedTotalTime;
252
this.numberOfCalls = this._savedNumberOfCalls;
254
this.removeChildren();
256
var children = this._savedChildren;
257
var count = children.length;
259
for (var index = 0; index < count; ++index) {
260
children[index]._restore();
261
this.appendChild(children[index]);
265
_merge: function(child, shouldAbsorb)
267
this.selfTime += child.selfTime;
270
this.totalTime += child.totalTime;
271
this.numberOfCalls += child.numberOfCalls;
274
var children = this.children.slice();
276
this.removeChildren();
278
var count = children.length;
280
for (var index = 0; index < count; ++index) {
281
if (!shouldAbsorb || children[index] !== child)
282
this.appendChild(children[index]);
285
children = child.children.slice();
286
count = children.length;
288
for (var index = 0; index < count; ++index) {
289
var orphanedChild = children[index],
290
existingChild = this.childrenByCallUID[orphanedChild.callUID];
293
existingChild._merge(orphanedChild, false);
295
this.appendChild(orphanedChild);
299
__proto__: WebInspector.DataGridNode.prototype
305
WebInspector.ProfileDataGridTree = function(profileView, profileNode)
310
this.profileView = profileView;
312
this.totalTime = profileNode.totalTime;
313
this.lastComparator = null;
315
this.childrenByCallUID = {};
318
WebInspector.ProfileDataGridTree.prototype = {
324
appendChild: function(child)
326
this.insertChild(child, this.children.length);
329
insertChild: function(child, index)
331
this.children.splice(index, 0, child);
332
this.childrenByCallUID[child.callUID] = child;
335
removeChildren: function()
338
this.childrenByCallUID = {};
341
findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
342
sort: WebInspector.ProfileDataGridNode.prototype.sort,
346
if (this._savedChildren)
349
this._savedTotalTime = this.totalTime;
350
this._savedChildren = this.children.slice();
355
if (!this._savedChildren)
358
this.children = this._savedChildren;
359
this.totalTime = this._savedTotalTime;
361
var children = this.children;
362
var count = children.length;
364
for (var index = 0; index < count; ++index)
365
children[index]._restore();
367
this._savedChildren = null;
371
WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
373
WebInspector.ProfileDataGridTree.propertyComparator = function(/*String*/ property, /*Boolean*/ isAscending)
375
var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
379
comparator = function(lhs, rhs)
381
if (lhs[property] < rhs[property])
384
if (lhs[property] > rhs[property])
390
comparator = function(lhs, rhs)
392
if (lhs[property] > rhs[property])
395
if (lhs[property] < rhs[property])
402
WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;