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