OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2009 280 North Inc. All Rights Reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
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. | |
12 * | |
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. | |
24 */ | |
25 | |
26 WebInspector.ProfileDataGridNode = function(profileView, profileNode, owningTree
, hasChildren) | |
27 { | |
28 this.profileView = profileView; | |
29 this.profileNode = profileNode; | |
30 | |
31 WebInspector.DataGridNode.call(this, null, hasChildren); | |
32 | |
33 this.addEventListener("populate", this._populate, this); | |
34 | |
35 this.tree = owningTree; | |
36 | |
37 this.childrenByCallUID = {}; | |
38 this.lastComparator = null; | |
39 | |
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; | |
46 } | |
47 | |
48 WebInspector.ProfileDataGridNode.prototype = { | |
49 get data() | |
50 { | |
51 function formatMilliseconds(time) | |
52 { | |
53 return Number.secondsToString(time / 1000, WebInspector.UIString.bin
d(WebInspector), !Preferences.samplingCPUProfiler); | |
54 } | |
55 | |
56 var data = {}; | |
57 | |
58 data["function"] = this.functionName; | |
59 data["calls"] = this.numberOfCalls; | |
60 | |
61 if (this.profileView.showSelfTimeAsPercent) | |
62 data["self"] = WebInspector.UIString("%.2f%%", this.selfPercent); | |
63 else | |
64 data["self"] = formatMilliseconds(this.selfTime); | |
65 | |
66 if (this.profileView.showTotalTimeAsPercent) | |
67 data["total"] = WebInspector.UIString("%.2f%%", this.totalPercent); | |
68 else | |
69 data["total"] = formatMilliseconds(this.totalTime); | |
70 | |
71 if (this.profileView.showAverageTimeAsPercent) | |
72 data["average"] = WebInspector.UIString("%.2f%%", this.averagePercen
t); | |
73 else | |
74 data["average"] = formatMilliseconds(this.averageTime); | |
75 | |
76 return data; | |
77 }, | |
78 | |
79 createCell: function(columnIdentifier) | |
80 { | |
81 var cell = WebInspector.DataGridNode.prototype.createCell.call(this, col
umnIdentifier); | |
82 | |
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._searchMatchedAverageCol
umn) | |
88 cell.addStyleClass("highlight"); | |
89 else if (columnIdentifier === "calls" && this._searchMatchedCallsColumn) | |
90 cell.addStyleClass("highlight"); | |
91 | |
92 if (columnIdentifier !== "function") | |
93 return cell; | |
94 | |
95 if (this.profileNode._searchMatchedFunctionColumn) | |
96 cell.addStyleClass("highlight"); | |
97 | |
98 if (this.profileNode.url) { | |
99 var fileName = WebInspector.displayNameForURL(this.profileNode.url); | |
100 | |
101 var urlElement = document.createElement("a"); | |
102 urlElement.className = "profile-node-file webkit-html-resource-link"
; | |
103 urlElement.href = this.profileNode.url; | |
104 urlElement.lineNumber = this.profileNode.lineNumber; | |
105 | |
106 if (this.profileNode.lineNumber > 0) | |
107 urlElement.textContent = fileName + ":" + this.profileNode.lineN
umber; | |
108 else | |
109 urlElement.textContent = fileName; | |
110 | |
111 cell.insertBefore(urlElement, cell.firstChild); | |
112 } | |
113 | |
114 return cell; | |
115 }, | |
116 | |
117 select: function(supressSelectedEvent) | |
118 { | |
119 WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEve
nt); | |
120 this.profileView._dataGridNodeSelected(this); | |
121 }, | |
122 | |
123 deselect: function(supressDeselectedEvent) | |
124 { | |
125 WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselecte
dEvent); | |
126 this.profileView._dataGridNodeDeselected(this); | |
127 }, | |
128 | |
129 sort: function(/*Function*/ comparator, /*Boolean*/ force) | |
130 { | |
131 var gridNodeGroups = [[this]]; | |
132 | |
133 for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.len
gth; ++gridNodeGroupIndex) { | |
134 var gridNodes = gridNodeGroups[gridNodeGroupIndex]; | |
135 var count = gridNodes.length; | |
136 | |
137 for (var index = 0; index < count; ++index) { | |
138 var gridNode = gridNodes[index]; | |
139 | |
140 // If the grid node is collapsed, then don't sort children (save
operation for later). | |
141 // If the grid node has the same sorting as previously, then the
re is no point in sorting it again. | |
142 if (!force && (!gridNode.expanded || gridNode.lastComparator ===
comparator)) { | |
143 if (gridNode.children.length) | |
144 gridNode.shouldRefreshChildren = true; | |
145 continue; | |
146 } | |
147 | |
148 gridNode.lastComparator = comparator; | |
149 | |
150 var children = gridNode.children; | |
151 var childCount = children.length; | |
152 | |
153 if (childCount) { | |
154 children.sort(comparator); | |
155 | |
156 for (var childIndex = 0; childIndex < childCount; ++childInd
ex) | |
157 children[childIndex]._recalculateSiblings(childIndex); | |
158 | |
159 gridNodeGroups.push(children); | |
160 } | |
161 } | |
162 } | |
163 }, | |
164 | |
165 insertChild: function(/*ProfileDataGridNode*/ profileDataGridNode, index) | |
166 { | |
167 WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGr
idNode, index); | |
168 | |
169 this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNod
e; | |
170 }, | |
171 | |
172 removeChild: function(/*ProfileDataGridNode*/ profileDataGridNode) | |
173 { | |
174 WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGr
idNode); | |
175 | |
176 delete this.childrenByCallUID[profileDataGridNode.callUID]; | |
177 }, | |
178 | |
179 removeChildren: function(/*ProfileDataGridNode*/ profileDataGridNode) | |
180 { | |
181 WebInspector.DataGridNode.prototype.removeChildren.call(this); | |
182 | |
183 this.childrenByCallUID = {}; | |
184 }, | |
185 | |
186 findChild: function(/*Node*/ node) | |
187 { | |
188 if (!node) | |
189 return null; | |
190 return this.childrenByCallUID[node.callUID]; | |
191 }, | |
192 | |
193 get averageTime() | |
194 { | |
195 return this.selfTime / Math.max(1, this.numberOfCalls); | |
196 }, | |
197 | |
198 get averagePercent() | |
199 { | |
200 return this.averageTime / this.tree.totalTime * 100.0; | |
201 }, | |
202 | |
203 get selfPercent() | |
204 { | |
205 return this.selfTime / this.tree.totalTime * 100.0; | |
206 }, | |
207 | |
208 get totalPercent() | |
209 { | |
210 return this.totalTime / this.tree.totalTime * 100.0; | |
211 }, | |
212 | |
213 get _parent() | |
214 { | |
215 return this.parent !== this.dataGrid ? this.parent : this.tree; | |
216 }, | |
217 | |
218 _populate: function(event) | |
219 { | |
220 this._sharedPopulate(); | |
221 | |
222 if (this._parent) { | |
223 var currentComparator = this._parent.lastComparator; | |
224 | |
225 if (currentComparator) | |
226 this.sort(currentComparator, true); | |
227 } | |
228 | |
229 if (this.removeEventListener) | |
230 this.removeEventListener("populate", this._populate, this); | |
231 }, | |
232 | |
233 // When focusing and collapsing we modify lots of nodes in the tree. | |
234 // This allows us to restore them all to their original state when we revert
. | |
235 _save: function() | |
236 { | |
237 if (this._savedChildren) | |
238 return; | |
239 | |
240 this._savedSelfTime = this.selfTime; | |
241 this._savedTotalTime = this.totalTime; | |
242 this._savedNumberOfCalls = this.numberOfCalls; | |
243 | |
244 this._savedChildren = this.children.slice(); | |
245 }, | |
246 | |
247 // When focusing and collapsing we modify lots of nodes in the tree. | |
248 // This allows us to restore them all to their original state when we revert
. | |
249 _restore: function() | |
250 { | |
251 if (!this._savedChildren) | |
252 return; | |
253 | |
254 this.selfTime = this._savedSelfTime; | |
255 this.totalTime = this._savedTotalTime; | |
256 this.numberOfCalls = this._savedNumberOfCalls; | |
257 | |
258 this.removeChildren(); | |
259 | |
260 var children = this._savedChildren; | |
261 var count = children.length; | |
262 | |
263 for (var index = 0; index < count; ++index) { | |
264 children[index]._restore(); | |
265 this.appendChild(children[index]); | |
266 } | |
267 }, | |
268 | |
269 _merge: function(child, shouldAbsorb) | |
270 { | |
271 this.selfTime += child.selfTime; | |
272 | |
273 if (!shouldAbsorb) { | |
274 this.totalTime += child.totalTime; | |
275 this.numberOfCalls += child.numberOfCalls; | |
276 } | |
277 | |
278 var children = this.children.slice(); | |
279 | |
280 this.removeChildren(); | |
281 | |
282 var count = children.length; | |
283 | |
284 for (var index = 0; index < count; ++index) { | |
285 if (!shouldAbsorb || children[index] !== child) | |
286 this.appendChild(children[index]); | |
287 } | |
288 | |
289 children = child.children.slice(); | |
290 count = children.length; | |
291 | |
292 for (var index = 0; index < count; ++index) { | |
293 var orphanedChild = children[index], | |
294 existingChild = this.childrenByCallUID[orphanedChild.callUID]; | |
295 | |
296 if (existingChild) | |
297 existingChild._merge(orphanedChild, false); | |
298 else | |
299 this.appendChild(orphanedChild); | |
300 } | |
301 } | |
302 } | |
303 | |
304 WebInspector.ProfileDataGridNode.prototype.__proto__ = WebInspector.DataGridNode
.prototype; | |
305 | |
306 WebInspector.ProfileDataGridTree = function(profileView, profileNode) | |
307 { | |
308 this.tree = this; | |
309 this.children = []; | |
310 | |
311 this.profileView = profileView; | |
312 | |
313 this.totalTime = profileNode.totalTime; | |
314 this.lastComparator = null; | |
315 | |
316 this.childrenByCallUID = {}; | |
317 } | |
318 | |
319 WebInspector.ProfileDataGridTree.prototype = { | |
320 get expanded() | |
321 { | |
322 return true; | |
323 }, | |
324 | |
325 appendChild: function(child) | |
326 { | |
327 this.insertChild(child, this.children.length); | |
328 }, | |
329 | |
330 insertChild: function(child, index) | |
331 { | |
332 this.children.splice(index, 0, child); | |
333 this.childrenByCallUID[child.callUID] = child; | |
334 }, | |
335 | |
336 removeChildren: function() | |
337 { | |
338 this.children = []; | |
339 this.childrenByCallUID = {}; | |
340 }, | |
341 | |
342 findChild: WebInspector.ProfileDataGridNode.prototype.findChild, | |
343 sort: WebInspector.ProfileDataGridNode.prototype.sort, | |
344 | |
345 _save: function() | |
346 { | |
347 if (this._savedChildren) | |
348 return; | |
349 | |
350 this._savedTotalTime = this.totalTime; | |
351 this._savedChildren = this.children.slice(); | |
352 }, | |
353 | |
354 restore: function() | |
355 { | |
356 if (!this._savedChildren) | |
357 return; | |
358 | |
359 this.children = this._savedChildren; | |
360 this.totalTime = this._savedTotalTime; | |
361 | |
362 var children = this.children; | |
363 var count = children.length; | |
364 | |
365 for (var index = 0; index < count; ++index) | |
366 children[index]._restore(); | |
367 | |
368 this._savedChildren = null; | |
369 } | |
370 } | |
371 | |
372 WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}]; | |
373 | |
374 WebInspector.ProfileDataGridTree.propertyComparator = function(/*String*/ proper
ty, /*Boolean*/ isAscending) | |
375 { | |
376 var comparator = this.propertyComparators[(isAscending ? 1 : 0)][property]; | |
377 | |
378 if (!comparator) { | |
379 if (isAscending) { | |
380 comparator = function(lhs, rhs) | |
381 { | |
382 if (lhs[property] < rhs[property]) | |
383 return -1; | |
384 | |
385 if (lhs[property] > rhs[property]) | |
386 return 1; | |
387 | |
388 return 0; | |
389 } | |
390 } else { | |
391 comparator = function(lhs, rhs) | |
392 { | |
393 if (lhs[property] > rhs[property]) | |
394 return -1; | |
395 | |
396 if (lhs[property] < rhs[property]) | |
397 return 1; | |
398 | |
399 return 0; | |
400 } | |
401 } | |
402 | |
403 this.propertyComparators[(isAscending ? 1 : 0)][property] = comparator; | |
404 } | |
405 | |
406 return comparator; | |
407 } | |
OLD | NEW |