Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / profiler / CPUProfileDataGrid.js
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 /**
27  * @constructor
28  * @extends {WebInspector.DataGridNode}
29  * @param {!ProfilerAgent.CPUProfileNode} profileNode
30  * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
31  * @param {boolean} hasChildren
32  */
33 WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren)
34 {
35     this.profileNode = profileNode;
36
37     WebInspector.DataGridNode.call(this, null, hasChildren);
38
39     this.tree = owningTree;
40
41     this.childrenByCallUID = {};
42     this.lastComparator = null;
43
44     this.callUID = profileNode.callUID;
45     this.selfTime = profileNode.selfTime;
46     this.totalTime = profileNode.totalTime;
47     this.functionName = WebInspector.CPUProfileDataModel.beautifyFunctionName(profileNode.functionName);
48     this._deoptReason = (!profileNode.deoptReason || profileNode.deoptReason === "no reason") ? "" : profileNode.deoptReason;
49     this.url = profileNode.url;
50 }
51
52 WebInspector.ProfileDataGridNode.prototype = {
53     /**
54      * @override
55      * @param {string} columnIdentifier
56      * @return {!Element}
57      */
58     createCell: function(columnIdentifier)
59     {
60         var cell = this._createValueCell(columnIdentifier) || WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
61
62         if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
63             cell.classList.add("highlight");
64         else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
65             cell.classList.add("highlight");
66
67         if (columnIdentifier !== "function")
68             return cell;
69
70         if (this._deoptReason)
71             cell.classList.add("not-optimized");
72
73         if (this._searchMatchedFunctionColumn)
74             cell.classList.add("highlight");
75
76         if (this.profileNode.scriptId !== "0") {
77             var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
78             var columnNumber = this.profileNode.columnNumber ? this.profileNode.columnNumber - 1 : 0;
79             var target = this.tree.profileView.target();
80             var urlElement = this.tree.profileView._linkifier.linkifyScriptLocation(target, this.profileNode.scriptId, this.profileNode.url, lineNumber, columnNumber, "profile-node-file");
81             urlElement.style.maxWidth = "75%";
82             cell.insertBefore(urlElement, cell.firstChild);
83         }
84
85         return cell;
86     },
87
88     /**
89      * @param {string} columnIdentifier
90      * @return {?Element}
91      */
92     _createValueCell: function(columnIdentifier)
93     {
94         if (columnIdentifier !== "self" && columnIdentifier !== "total")
95             return null;
96
97         var cell = document.createElement("td");
98         cell.className = "numeric-column";
99         var div = document.createElement("div");
100         var valueSpan = document.createElement("span");
101         valueSpan.textContent = this.data[columnIdentifier];
102         div.appendChild(valueSpan);
103         var percentColumn = columnIdentifier + "-percent";
104         if (percentColumn in this.data) {
105             var percentSpan = document.createElement("span");
106             percentSpan.className = "percent-column";
107             percentSpan.textContent = this.data[percentColumn];
108             div.appendChild(percentSpan);
109             div.classList.add("profile-multiple-values");
110         }
111         cell.appendChild(div);
112         return cell;
113     },
114
115     buildData: function()
116     {
117         function formatMilliseconds(time)
118         {
119             return WebInspector.UIString("%.1f\u2009ms", time);
120         }
121         function formatPercent(value)
122         {
123             return WebInspector.UIString("%.2f\u2009%", value);
124         }
125
126         var functionName;
127         if (this._deoptReason) {
128             var content = document.createDocumentFragment();
129             var marker = content.createChild("span", "profile-warn-marker");
130             marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason);
131             content.createTextChild(this.functionName);
132             functionName = content;
133         } else {
134             functionName = this.functionName;
135         }
136
137         this.data = {
138             "function": functionName,
139             "self-percent": formatPercent(this.selfPercent),
140             "self": formatMilliseconds(this.selfTime),
141             "total-percent": formatPercent(this.totalPercent),
142             "total": formatMilliseconds(this.totalTime),
143         };
144     },
145
146     select: function(supressSelectedEvent)
147     {
148         WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
149         this.tree.profileView._dataGridNodeSelected(this);
150     },
151
152     deselect: function(supressDeselectedEvent)
153     {
154         WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
155         this.tree.profileView._dataGridNodeDeselected(this);
156     },
157
158     /**
159      * @param {function(!T, !T)} comparator
160      * @param {boolean} force
161      * @template T
162      */
163     sort: function(comparator, force)
164     {
165         var gridNodeGroups = [[this]];
166
167         for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
168             var gridNodes = gridNodeGroups[gridNodeGroupIndex];
169             var count = gridNodes.length;
170
171             for (var index = 0; index < count; ++index) {
172                 var gridNode = gridNodes[index];
173
174                 // If the grid node is collapsed, then don't sort children (save operation for later).
175                 // If the grid node has the same sorting as previously, then there is no point in sorting it again.
176                 if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
177                     if (gridNode.children.length)
178                         gridNode.shouldRefreshChildren = true;
179                     continue;
180                 }
181
182                 gridNode.lastComparator = comparator;
183
184                 var children = gridNode.children;
185                 var childCount = children.length;
186
187                 if (childCount) {
188                     children.sort(comparator);
189
190                     for (var childIndex = 0; childIndex < childCount; ++childIndex)
191                         children[childIndex].recalculateSiblings(childIndex);
192
193                     gridNodeGroups.push(children);
194                 }
195             }
196         }
197     },
198
199     /**
200      * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
201      * @param {number} index
202      */
203     insertChild: function(profileDataGridNode, index)
204     {
205         WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
206
207         this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
208     },
209
210     /**
211      * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
212      */
213     removeChild: function(profileDataGridNode)
214     {
215         WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
216
217         delete this.childrenByCallUID[profileDataGridNode.callUID];
218     },
219
220     removeChildren: function()
221     {
222         WebInspector.DataGridNode.prototype.removeChildren.call(this);
223
224         this.childrenByCallUID = {};
225     },
226
227     /**
228      * @param {!WebInspector.ProfileDataGridNode} node
229      * @return {?WebInspector.ProfileDataGridNode}
230      */
231     findChild: function(node)
232     {
233         if (!node)
234             return null;
235         return this.childrenByCallUID[node.callUID];
236     },
237
238     get selfPercent()
239     {
240         return this.selfTime / this.tree.totalTime * 100.0;
241     },
242
243     get totalPercent()
244     {
245         return this.totalTime / this.tree.totalTime * 100.0;
246     },
247
248     populate: function()
249     {
250         WebInspector.ProfileDataGridNode.populate(this);
251     },
252
253     /**
254      * @protected
255      */
256     populateChildren: function()
257     {
258     },
259
260     // When focusing and collapsing we modify lots of nodes in the tree.
261     // This allows us to restore them all to their original state when we revert.
262
263     save: function()
264     {
265         if (this._savedChildren)
266             return;
267
268         this._savedSelfTime = this.selfTime;
269         this._savedTotalTime = this.totalTime;
270
271         this._savedChildren = this.children.slice();
272     },
273
274     /**
275      * When focusing and collapsing we modify lots of nodes in the tree.
276      * This allows us to restore them all to their original state when we revert.
277      * @protected
278      */
279     restore: function()
280     {
281         if (!this._savedChildren)
282             return;
283
284         this.selfTime = this._savedSelfTime;
285         this.totalTime = this._savedTotalTime;
286
287         this.removeChildren();
288
289         var children = this._savedChildren;
290         var count = children.length;
291
292         for (var index = 0; index < count; ++index) {
293             children[index].restore();
294             this.appendChild(children[index]);
295         }
296     },
297
298     /**
299      * @param {!WebInspector.ProfileDataGridNode} child
300      * @param {boolean} shouldAbsorb
301      */
302     merge: function(child, shouldAbsorb)
303     {
304         WebInspector.ProfileDataGridNode.merge(this, child, shouldAbsorb);
305     },
306
307     __proto__: WebInspector.DataGridNode.prototype
308 }
309
310 /**
311  * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container
312  * @param {!WebInspector.ProfileDataGridNode} child
313  * @param {boolean} shouldAbsorb
314  */
315 WebInspector.ProfileDataGridNode.merge = function(container, child, shouldAbsorb)
316 {
317     container.selfTime += child.selfTime;
318
319     if (!shouldAbsorb)
320         container.totalTime += child.totalTime;
321
322     var children = container.children.slice();
323
324     container.removeChildren();
325
326     var count = children.length;
327
328     for (var index = 0; index < count; ++index) {
329         if (!shouldAbsorb || children[index] !== child)
330             container.appendChild(children[index]);
331     }
332
333     children = child.children.slice();
334     count = children.length;
335
336     for (var index = 0; index < count; ++index) {
337         var orphanedChild = children[index];
338         var existingChild = container.childrenByCallUID[orphanedChild.callUID];
339
340         if (existingChild)
341             existingChild.merge(orphanedChild, false);
342         else
343             container.appendChild(orphanedChild);
344     }
345 }
346
347 /**
348  * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container
349  */
350 WebInspector.ProfileDataGridNode.populate = function(container)
351 {
352     if (container._populated)
353         return;
354     container._populated = true;
355
356     container.populateChildren();
357
358     var currentComparator = container.tree.lastComparator;
359
360     if (currentComparator)
361         container.sort(currentComparator, true);
362 }
363
364 /**
365  * @constructor
366  * @param {!WebInspector.CPUProfileView} profileView
367  * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
368  */
369 WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode)
370 {
371     this.tree = this;
372     this.children = [];
373
374     this.profileView = profileView;
375
376     this.totalTime = rootProfileNode.totalTime;
377     this.lastComparator = null;
378
379     this.childrenByCallUID = {};
380 }
381
382 WebInspector.ProfileDataGridTree.prototype = {
383     get expanded()
384     {
385         return true;
386     },
387
388     appendChild: function(child)
389     {
390         this.insertChild(child, this.children.length);
391     },
392
393     insertChild: function(child, index)
394     {
395         this.children.splice(index, 0, child);
396         this.childrenByCallUID[child.callUID] = child;
397     },
398
399     removeChildren: function()
400     {
401         this.children = [];
402         this.childrenByCallUID = {};
403     },
404
405     populateChildren: function()
406     {
407     },
408
409     findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
410     sort: WebInspector.ProfileDataGridNode.prototype.sort,
411
412     /**
413      * @protected
414      */
415     save: function()
416     {
417         if (this._savedChildren)
418             return;
419
420         this._savedTotalTime = this.totalTime;
421         this._savedChildren = this.children.slice();
422     },
423
424     restore: function()
425     {
426         if (!this._savedChildren)
427             return;
428
429         this.children = this._savedChildren;
430         this.totalTime = this._savedTotalTime;
431
432         var children = this.children;
433         var count = children.length;
434
435         for (var index = 0; index < count; ++index)
436             children[index].restore();
437
438         this._savedChildren = null;
439     }
440 }
441
442 WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
443
444 /**
445  * @param {string} property
446  * @param {boolean} isAscending
447  * @return {function(!Object.<string, *>, !Object.<string, *>)}
448  */
449 WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending)
450 {
451     var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
452
453     if (!comparator) {
454         if (isAscending) {
455             comparator = function(lhs, rhs)
456             {
457                 if (lhs[property] < rhs[property])
458                     return -1;
459
460                 if (lhs[property] > rhs[property])
461                     return 1;
462
463                 return 0;
464             }
465         } else {
466             comparator = function(lhs, rhs)
467             {
468                 if (lhs[property] > rhs[property])
469                     return -1;
470
471                 if (lhs[property] < rhs[property])
472                     return 1;
473
474                 return 0;
475             }
476         }
477
478         WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;
479     }
480
481     return comparator;
482 }