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.
26 WebInspector.ProfileDataGridNode = function(profileView, profileNode, owningTree, hasChildren)
28 this.profileView = profileView;
29 this.profileNode = profileNode;
31 WebInspector.DataGridNode.call(this, null, hasChildren);
33 this.addEventListener("populate", this._populate, this);
35 this.tree = owningTree;
37 this.childrenByCallUID = {};
38 this.lastComparator = null;
40 this.callUID = profileNode.callUID;
41 this.selfTime = profileNode.selfTime;
42 this.totalTime = profileNode.totalTime;
43 this.functionName = profileNode.functionName;
44 this.numberOfCalls = profileNode.numberOfCalls;
45 this.url = profileNode.url;
48 WebInspector.ProfileDataGridNode.prototype = {
51 function formatMilliseconds(time)
53 return Number.secondsToString(time / 1000, WebInspector.UIString, !Preferences.samplingCPUProfiler);
58 data["function"] = this.functionName;
59 data["calls"] = this.numberOfCalls;
61 if (this.profileView.showSelfTimeAsPercent)
62 data["self"] = WebInspector.UIString("%.2f%%", this.selfPercent);
64 data["self"] = formatMilliseconds(this.selfTime);
66 if (this.profileView.showTotalTimeAsPercent)
67 data["total"] = WebInspector.UIString("%.2f%%", this.totalPercent);
69 data["total"] = formatMilliseconds(this.totalTime);
71 if (this.profileView.showAverageTimeAsPercent)
72 data["average"] = WebInspector.UIString("%.2f%%", this.averagePercent);
74 data["average"] = formatMilliseconds(this.averageTime);
79 createCell: function(columnIdentifier)
81 var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
83 if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
84 cell.addStyleClass("highlight");
85 else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
86 cell.addStyleClass("highlight");
87 else if (columnIdentifier === "average" && this._searchMatchedAverageColumn)
88 cell.addStyleClass("highlight");
89 else if (columnIdentifier === "calls" && this._searchMatchedCallsColumn)
90 cell.addStyleClass("highlight");
92 if (columnIdentifier !== "function")
95 if (this.profileNode._searchMatchedFunctionColumn)
96 cell.addStyleClass("highlight");
98 if (this.profileNode.url) {
100 if (this.profileNode.lineNumber > 0)
101 lineNumber = this.profileNode.lineNumber;
102 var urlElement = WebInspector.linkifyResourceAsNode(this.profileNode.url, "scripts", lineNumber, "profile-node-file");
103 cell.insertBefore(urlElement, cell.firstChild);
109 select: function(supressSelectedEvent)
111 WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
112 this.profileView._dataGridNodeSelected(this);
115 deselect: function(supressDeselectedEvent)
117 WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
118 this.profileView._dataGridNodeDeselected(this);
121 sort: function(/*Function*/ comparator, /*Boolean*/ force)
123 var gridNodeGroups = [[this]];
125 for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
126 var gridNodes = gridNodeGroups[gridNodeGroupIndex];
127 var count = gridNodes.length;
129 for (var index = 0; index < count; ++index) {
130 var gridNode = gridNodes[index];
132 // If the grid node is collapsed, then don't sort children (save operation for later).
133 // If the grid node has the same sorting as previously, then there is no point in sorting it again.
134 if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
135 if (gridNode.children.length)
136 gridNode.shouldRefreshChildren = true;
140 gridNode.lastComparator = comparator;
142 var children = gridNode.children;
143 var childCount = children.length;
146 children.sort(comparator);
148 for (var childIndex = 0; childIndex < childCount; ++childIndex)
149 children[childIndex]._recalculateSiblings(childIndex);
151 gridNodeGroups.push(children);
157 insertChild: function(/*ProfileDataGridNode*/ profileDataGridNode, index)
159 WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
161 this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
164 removeChild: function(/*ProfileDataGridNode*/ profileDataGridNode)
166 WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
168 delete this.childrenByCallUID[profileDataGridNode.callUID];
171 removeChildren: function(/*ProfileDataGridNode*/ profileDataGridNode)
173 WebInspector.DataGridNode.prototype.removeChildren.call(this);
175 this.childrenByCallUID = {};
178 findChild: function(/*Node*/ node)
182 return this.childrenByCallUID[node.callUID];
187 return this.selfTime / Math.max(1, this.numberOfCalls);
192 return this.averageTime / this.tree.totalTime * 100.0;
197 return this.selfTime / this.tree.totalTime * 100.0;
202 return this.totalTime / this.tree.totalTime * 100.0;
207 return this.parent !== this.dataGrid ? this.parent : this.tree;
210 _populate: function(event)
212 this._sharedPopulate();
215 var currentComparator = this._parent.lastComparator;
217 if (currentComparator)
218 this.sort(currentComparator, true);
221 if (this.removeEventListener)
222 this.removeEventListener("populate", this._populate, this);
225 // When focusing and collapsing we modify lots of nodes in the tree.
226 // This allows us to restore them all to their original state when we revert.
229 if (this._savedChildren)
232 this._savedSelfTime = this.selfTime;
233 this._savedTotalTime = this.totalTime;
234 this._savedNumberOfCalls = this.numberOfCalls;
236 this._savedChildren = this.children.slice();
239 // When focusing and collapsing we modify lots of nodes in the tree.
240 // This allows us to restore them all to their original state when we revert.
243 if (!this._savedChildren)
246 this.selfTime = this._savedSelfTime;
247 this.totalTime = this._savedTotalTime;
248 this.numberOfCalls = this._savedNumberOfCalls;
250 this.removeChildren();
252 var children = this._savedChildren;
253 var count = children.length;
255 for (var index = 0; index < count; ++index) {
256 children[index]._restore();
257 this.appendChild(children[index]);
261 _merge: function(child, shouldAbsorb)
263 this.selfTime += child.selfTime;
266 this.totalTime += child.totalTime;
267 this.numberOfCalls += child.numberOfCalls;
270 var children = this.children.slice();
272 this.removeChildren();
274 var count = children.length;
276 for (var index = 0; index < count; ++index) {
277 if (!shouldAbsorb || children[index] !== child)
278 this.appendChild(children[index]);
281 children = child.children.slice();
282 count = children.length;
284 for (var index = 0; index < count; ++index) {
285 var orphanedChild = children[index],
286 existingChild = this.childrenByCallUID[orphanedChild.callUID];
289 existingChild._merge(orphanedChild, false);
291 this.appendChild(orphanedChild);
296 WebInspector.ProfileDataGridNode.prototype.__proto__ = WebInspector.DataGridNode.prototype;
298 WebInspector.ProfileDataGridTree = function(profileView, profileNode)
303 this.profileView = profileView;
305 this.totalTime = profileNode.totalTime;
306 this.lastComparator = null;
308 this.childrenByCallUID = {};
311 WebInspector.ProfileDataGridTree.prototype = {
317 appendChild: function(child)
319 this.insertChild(child, this.children.length);
322 insertChild: function(child, index)
324 this.children.splice(index, 0, child);
325 this.childrenByCallUID[child.callUID] = child;
328 removeChildren: function()
331 this.childrenByCallUID = {};
334 findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
335 sort: WebInspector.ProfileDataGridNode.prototype.sort,
339 if (this._savedChildren)
342 this._savedTotalTime = this.totalTime;
343 this._savedChildren = this.children.slice();
348 if (!this._savedChildren)
351 this.children = this._savedChildren;
352 this.totalTime = this._savedTotalTime;
354 var children = this.children;
355 var count = children.length;
357 for (var index = 0; index < count; ++index)
358 children[index]._restore();
360 this._savedChildren = null;
364 WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
366 WebInspector.ProfileDataGridTree.propertyComparator = function(/*String*/ property, /*Boolean*/ isAscending)
368 var comparator = this.propertyComparators[(isAscending ? 1 : 0)][property];
372 comparator = function(lhs, rhs)
374 if (lhs[property] < rhs[property])
377 if (lhs[property] > rhs[property])
383 comparator = function(lhs, rhs)
385 if (lhs[property] > rhs[property])
388 if (lhs[property] < rhs[property])
395 this.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;