Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / profiler / HeapSnapshotDataGrids.js
1 /*
2  * Copyright (C) 2012 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.DataGrid}
34  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
35  * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns
36  */
37 WebInspector.HeapSnapshotSortableDataGrid = function(dataDisplayDelegate, columns)
38 {
39     WebInspector.DataGrid.call(this, columns);
40     this._dataDisplayDelegate = dataDisplayDelegate;
41
42     /**
43      * @type {number}
44      */
45     this._recursiveSortingDepth = 0;
46     /**
47      * @type {?WebInspector.HeapSnapshotGridNode}
48      */
49     this._highlightedNode = null;
50     /**
51      * @type {boolean}
52      */
53     this._populatedAndSorted = false;
54     /**
55      * @type {?WebInspector.StatusBarInput}
56      */
57     this._nameFilter = null;
58     this.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this);
59     this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this);
60 }
61
62 WebInspector.HeapSnapshotSortableDataGrid.Events = {
63     ContentShown: "ContentShown",
64     SortingComplete: "SortingComplete"
65 }
66
67 WebInspector.HeapSnapshotSortableDataGrid.prototype = {
68     /**
69      * @param {!WebInspector.StatusBarInput} nameFilter
70      */
71     setNameFilter: function(nameFilter)
72     {
73         this._nameFilter = nameFilter;
74     },
75
76     /**
77      * @return {number}
78      */
79     defaultPopulateCount: function()
80     {
81         return 100;
82     },
83
84     _disposeAllNodes: function()
85     {
86         var children = this.topLevelNodes();
87         for (var i = 0, l = children.length; i < l; ++i)
88             children[i].dispose();
89     },
90
91     /**
92      * @override
93      */
94     wasShown: function()
95     {
96         if (this._nameFilter) {
97             this._nameFilter.addEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this);
98             this.updateVisibleNodes(true);
99         }
100         if (this._populatedAndSorted)
101             this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
102     },
103
104     _sortingComplete: function()
105     {
106         this.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete, this._sortingComplete, this);
107         this._populatedAndSorted = true;
108         this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
109     },
110
111     /**
112      * @override
113      */
114     willHide: function()
115     {
116         if (this._nameFilter)
117             this._nameFilter.removeEventListener(WebInspector.StatusBarInput.Event.TextChanged, this._onNameFilterChanged, this);
118         this._clearCurrentHighlight();
119     },
120
121     /**
122      * @param {!WebInspector.ContextMenu} contextMenu
123      * @param {?Event} event
124      */
125     populateContextMenu: function(contextMenu, event)
126     {
127         var td = event.target.enclosingNodeOrSelfWithNodeName("td");
128         if (!td)
129             return;
130         var node = td.heapSnapshotNode;
131
132         /**
133          * @this {WebInspector.HeapSnapshotSortableDataGrid}
134          */
135         function revealInDominatorsView()
136         {
137             this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Dominators");
138         }
139
140         /**
141          * @this {WebInspector.HeapSnapshotSortableDataGrid}
142          */
143         function revealInSummaryView()
144         {
145             this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary");
146         }
147
148         if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode) {
149             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
150             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
151         } else if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) {
152             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
153         } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) {
154             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
155         }
156     },
157
158     resetSortingCache: function()
159     {
160         delete this._lastSortColumnIdentifier;
161         delete this._lastSortAscending;
162     },
163
164     /**
165      * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
166      */
167     topLevelNodes: function()
168     {
169         return this.rootNode().children;
170     },
171
172     /**
173      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
174      * @param {function(boolean)} callback
175      */
176     highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback)
177     {
178     },
179
180     /**
181      * @param {!WebInspector.HeapSnapshotGridNode} node
182      */
183     highlightNode: function(node)
184     {
185         var prevNode = this._highlightedNode;
186         this._clearCurrentHighlight();
187         this._highlightedNode = node;
188         WebInspector.runCSSAnimationOnce(this._highlightedNode.element, "highlighted-row");
189     },
190
191     nodeWasDetached: function(node)
192     {
193         if (this._highlightedNode === node)
194             this._clearCurrentHighlight();
195     },
196
197     _clearCurrentHighlight: function()
198     {
199         if (!this._highlightedNode)
200             return
201         this._highlightedNode.element.classList.remove("highlighted-row");
202         this._highlightedNode = null;
203     },
204
205     resetNameFilter: function()
206     {
207         this._nameFilter.setValue("");
208         this._onNameFilterChanged();
209     },
210
211     _onNameFilterChanged: function()
212     {
213         this.updateVisibleNodes(true);
214     },
215
216     sortingChanged: function()
217     {
218         var sortAscending = this.isSortOrderAscending();
219         var sortColumnIdentifier = this.sortColumnIdentifier();
220         if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
221             return;
222         this._lastSortColumnIdentifier = sortColumnIdentifier;
223         this._lastSortAscending = sortAscending;
224         var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
225
226         function SortByTwoFields(nodeA, nodeB)
227         {
228             var field1 = nodeA[sortFields[0]];
229             var field2 = nodeB[sortFields[0]];
230             var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
231             if (!sortFields[1])
232                 result = -result;
233             if (result !== 0)
234                 return result;
235             field1 = nodeA[sortFields[2]];
236             field2 = nodeB[sortFields[2]];
237             result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
238             if (!sortFields[3])
239                 result = -result;
240             return result;
241         }
242         this._performSorting(SortByTwoFields);
243     },
244
245     _performSorting: function(sortFunction)
246     {
247         this.recursiveSortingEnter();
248         var children = this.allChildren(this.rootNode());
249         this.rootNode().removeChildren();
250         children.sort(sortFunction);
251         for (var i = 0, l = children.length; i < l; ++i) {
252             var child = children[i];
253             this.appendChildAfterSorting(child);
254             if (child.expanded)
255                 child.sort();
256         }
257         this.recursiveSortingLeave();
258     },
259
260     appendChildAfterSorting: function(child)
261     {
262         var revealed = child.revealed;
263         this.rootNode().appendChild(child);
264         child.revealed = revealed;
265     },
266
267     recursiveSortingEnter: function()
268     {
269         ++this._recursiveSortingDepth;
270     },
271
272     recursiveSortingLeave: function()
273     {
274         if (!this._recursiveSortingDepth)
275             return;
276         if (--this._recursiveSortingDepth)
277             return;
278         this.updateVisibleNodes(true);
279         this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
280     },
281
282     /**
283      * @param {boolean} force
284      */
285     updateVisibleNodes: function(force)
286     {
287     },
288
289     /**
290      * @param {!WebInspector.DataGridNode} parent
291      * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
292      */
293     allChildren: function(parent)
294     {
295         return parent.children;
296     },
297
298     /**
299      * @param {!WebInspector.DataGridNode} parent
300      * @param {!WebInspector.DataGridNode} node
301      * @param {number} index
302      */
303     insertChild: function(parent, node, index)
304     {
305         parent.insertChild(node, index);
306     },
307
308     /**
309      * @param {!WebInspector.HeapSnapshotGridNode} parent
310      * @param {number} index
311      */
312     removeChildByIndex: function(parent, index)
313     {
314         parent.removeChild(parent.children[index]);
315     },
316
317     /**
318      * @param {!WebInspector.HeapSnapshotGridNode} parent
319      */
320     removeAllChildren: function(parent)
321     {
322         parent.removeChildren();
323     },
324
325     __proto__: WebInspector.DataGrid.prototype
326 }
327
328
329 /**
330  * @constructor
331  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
332  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
333  * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns
334  */
335 WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns)
336 {
337     WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
338     this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
339     /**
340      * @type {?WebInspector.HeapSnapshotGridNode}
341      */
342     this._nodeToHighlightAfterScroll = null;
343     this._topPadding = new WebInspector.HeapSnapshotPaddingNode();
344     this._topPaddingHeight = 0;
345     this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild);
346     this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode();
347     this._bottomPaddingHeight = 0;
348     this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild);
349 }
350
351 WebInspector.HeapSnapshotViewportDataGrid.prototype = {
352     /**
353      * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
354      */
355     topLevelNodes: function()
356     {
357         return this.allChildren(this.rootNode());
358     },
359
360     appendChildAfterSorting: function(child)
361     {
362         // Do nothing here, it will be added in updateVisibleNodes.
363     },
364
365     /**
366      * @override
367      * @param {boolean} force
368      * @param {!Array.<!WebInspector.HeapSnapshotGridNode>=} pathToReveal
369      */
370     updateVisibleNodes: function(force, pathToReveal)
371     {
372         // Guard zone is used to ensure there are always some extra items
373         // above and below the viewport to support keyboard navigation.
374         var guardZoneHeight = 40;
375         var scrollHeight = this.scrollContainer.scrollHeight;
376         var scrollTop = this.scrollContainer.scrollTop;
377         var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight;
378         scrollTop = Math.max(0, scrollTop - guardZoneHeight);
379         scrollBottom = Math.max(0, scrollBottom - guardZoneHeight);
380         var viewPortHeight = scrollHeight - scrollTop - scrollBottom;
381         if (!pathToReveal) {
382             // Do nothing if populated nodes still fit the viewport.
383             if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight)
384                 return;
385             var hysteresisHeight = 500;
386             scrollTop -= hysteresisHeight;
387             viewPortHeight += 2 * hysteresisHeight;
388         }
389         var selectedNode = this.selectedNode;
390         this.rootNode().removeChildren();
391
392         this._topPaddingHeight = 0;
393         this._bottomPaddingHeight = 0;
394
395         this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight, pathToReveal || null);
396
397         this._topPadding.setHeight(this._topPaddingHeight);
398         this._bottomPadding.setHeight(this._bottomPaddingHeight);
399
400         if (selectedNode) {
401             // Keep selection even if the node is not in the current viewport.
402             if (selectedNode.parent)
403                 selectedNode.select(true);
404             else
405                 this.selectedNode = selectedNode;
406         }
407     },
408
409     /**
410      * @param {!WebInspector.DataGridNode} parentNode
411      * @param {number} topBound
412      * @param {number} bottomBound
413      * @param {?Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
414      * @return {number}
415      */
416     _addVisibleNodes: function(parentNode, topBound, bottomBound, pathToReveal)
417     {
418         if (!parentNode.expanded)
419             return 0;
420
421         var nodeToReveal = pathToReveal ? pathToReveal[0] : null;
422         var restPathToReveal = pathToReveal && pathToReveal.length > 1 ? pathToReveal.slice(1) : null;
423         var children = this.allChildren(parentNode);
424         var topPadding = 0;
425         var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : "";
426         // Iterate over invisible nodes beyond the upper bound of viewport.
427         // Do not insert them into the grid, but count their total height.
428         for (var i = 0; i < children.length; ++i) {
429             var child = children[i];
430             if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
431                 continue;
432             var newTop = topPadding + this._nodeHeight(child);
433             if (nodeToReveal === child || (!nodeToReveal && newTop > topBound))
434                 break;
435             topPadding = newTop;
436         }
437
438         // Put visible nodes into the data grid.
439         var position = topPadding;
440         for (; i < children.length && (nodeToReveal || position < bottomBound); ++i) {
441             var child = children[i];
442             if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
443                 continue;
444             var hasChildren = child.hasChildren;
445             child.removeChildren();
446             child.hasChildren = hasChildren;
447             child.revealed = true;
448             parentNode.appendChild(child);
449             position += child.nodeSelfHeight();
450             position += this._addVisibleNodes(child, topBound - position, bottomBound - position, restPathToReveal);
451             if (nodeToReveal === child)
452                 break;
453         }
454
455         // Count the invisible nodes beyond the bottom bound of the viewport.
456         var bottomPadding = 0;
457         for (; i < children.length; ++i) {
458             var child = children[i];
459             if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
460                 continue;
461             bottomPadding += this._nodeHeight(child);
462         }
463
464         this._topPaddingHeight += topPadding;
465         this._bottomPaddingHeight += bottomPadding;
466         return position + bottomPadding;
467     },
468
469     /**
470      * @param {!WebInspector.HeapSnapshotGridNode} node
471      * @return {number}
472      */
473     _nodeHeight: function(node)
474     {
475         if (!node.revealed)
476             return 0;
477         var result = node.nodeSelfHeight();
478         if (!node.expanded)
479             return result;
480         var children = this.allChildren(node);
481         for (var i = 0; i < children.length; i++)
482             result += this._nodeHeight(children[i]);
483         return result;
484     },
485
486     /**
487      * @override
488      * @return {?Element}
489      */
490     defaultAttachLocation: function()
491     {
492         return this._bottomPadding.element;
493     },
494
495     /**
496      * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
497      */
498     revealTreeNode: function(pathToReveal)
499     {
500         this.updateVisibleNodes(true, pathToReveal);
501     },
502
503     /**
504      * @param {!WebInspector.DataGridNode} parent
505      * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
506      */
507     allChildren: function(parent)
508     {
509         return parent._allChildren || (parent._allChildren = []);
510     },
511
512     /**
513      * @param {!WebInspector.DataGridNode} parent
514      * @param {!WebInspector.DataGridNode} node
515      */
516     appendNode: function(parent, node)
517     {
518         this.allChildren(parent).push(node);
519     },
520
521     /**
522      * @param {!WebInspector.DataGridNode} parent
523      * @param {!WebInspector.DataGridNode} node
524      * @param {number} index
525      */
526     insertChild: function(parent, node, index)
527     {
528         this.allChildren(parent).splice(index, 0, node);
529     },
530
531     removeChildByIndex: function(parent, index)
532     {
533         this.allChildren(parent).splice(index, 1);
534     },
535
536     removeAllChildren: function(parent)
537     {
538         parent._allChildren = [];
539     },
540
541     removeTopLevelNodes: function()
542     {
543         this._disposeAllNodes();
544         this.rootNode().removeChildren();
545         this.rootNode()._allChildren = [];
546     },
547
548     /**
549      * @override
550      * @param {!WebInspector.HeapSnapshotGridNode} node
551      */
552     highlightNode: function(node)
553     {
554         if (this._isScrolledIntoView(node.element)) {
555             this.updateVisibleNodes(true);
556             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
557         } else {
558             node.element.scrollIntoViewIfNeeded(true);
559             this._nodeToHighlightAfterScroll = node;
560         }
561     },
562
563     /**
564      * @param {!Element} element
565      * @return {boolean}
566      */
567     _isScrolledIntoView: function(element)
568     {
569         var viewportTop = this.scrollContainer.scrollTop;
570         var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
571         var elemTop = element.offsetTop
572         var elemBottom = elemTop + element.offsetHeight;
573         return elemBottom <= viewportBottom && elemTop >= viewportTop;
574     },
575
576     onResize: function()
577     {
578         WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
579         this.updateVisibleNodes(false);
580     },
581
582     _onScroll: function(event)
583     {
584         this.updateVisibleNodes(false);
585
586         if (this._nodeToHighlightAfterScroll) {
587             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
588             this._nodeToHighlightAfterScroll = null;
589         }
590     },
591
592     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
593 }
594
595 /**
596  * @constructor
597  */
598 WebInspector.HeapSnapshotPaddingNode = function()
599 {
600     this.element = document.createElement("tr");
601     this.element.classList.add("revealed");
602     this.setHeight(0);
603 }
604
605 WebInspector.HeapSnapshotPaddingNode.prototype = {
606    setHeight: function(height)
607    {
608        this.element.style.height = height + "px";
609    },
610    removeFromTable: function()
611    {
612         var parent = this.element.parentNode;
613         if (parent)
614             parent.removeChild(this.element);
615    }
616 }
617
618
619 /**
620  * @constructor
621  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
622  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
623  * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
624  */
625 WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns)
626 {
627     columns = columns || [
628         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
629         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true},
630         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
631         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
632     ];
633     WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
634 }
635
636 WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
637     /**
638      * @param {!WebInspector.HeapSnapshotProxy} snapshot
639      * @param {number} nodeIndex
640      */
641     setDataSource: function(snapshot, nodeIndex)
642     {
643         this.snapshot = snapshot;
644         var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex };
645         var fakeEdge = { node: node };
646         this.setRootNode(this._createRootNode(snapshot, fakeEdge));
647         this.rootNode().sort();
648     },
649
650     _createRootNode: function(snapshot, fakeEdge)
651     {
652         return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null);
653     },
654
655     sortingChanged: function()
656     {
657         var rootNode = this.rootNode();
658         if (rootNode.hasChildren)
659             rootNode.sort();
660     },
661
662     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
663 }
664
665
666 /**
667  * @constructor
668  * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
669  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
670  */
671 WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate)
672 {
673     var columns = [
674         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
675         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending},
676         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
677         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}
678     ];
679     WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns);
680 }
681
682 WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
683     ExpandRetainersComplete: "ExpandRetainersComplete"
684 }
685
686 WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
687     _createRootNode: function(snapshot, fakeEdge)
688     {
689         return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null);
690     },
691
692     _sortFields: function(sortColumn, sortAscending)
693     {
694         return {
695             object: ["_name", sortAscending, "_count", false],
696             count: ["_count", sortAscending, "_name", true],
697             shallowSize: ["_shallowSize", sortAscending, "_name", true],
698             retainedSize: ["_retainedSize", sortAscending, "_name", true],
699             distance: ["_distance", sortAscending, "_name", true]
700         }[sortColumn];
701     },
702
703     reset: function()
704     {
705         this.rootNode().removeChildren();
706         this.resetSortingCache();
707     },
708
709     /**
710      * @param {!WebInspector.HeapSnapshotProxy} snapshot
711      * @param {number} nodeIndex
712      */
713     setDataSource: function(snapshot, nodeIndex)
714     {
715         WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
716         this.rootNode().expand();
717     },
718
719     __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
720 }
721
722 /**
723  * @constructor
724  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
725  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
726  */
727 WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate)
728 {
729     var columns = [
730         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
731         {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
732         {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
733         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
734         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
735     ];
736     WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
737     this._profileIndex = -1;
738
739     this._objectIdToSelect = null;
740 }
741
742 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
743     _sortFields: function(sortColumn, sortAscending)
744     {
745         return {
746             object: ["_name", sortAscending, "_count", false],
747             distance: ["_distance", sortAscending, "_retainedSize", true],
748             count: ["_count", sortAscending, "_name", true],
749             shallowSize: ["_shallowSize", sortAscending, "_name", true],
750             retainedSize: ["_retainedSize", sortAscending, "_name", true]
751         }[sortColumn];
752     },
753
754     /**
755      * @override
756      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
757      * @param {function(boolean)} callback
758      */
759     highlightObjectByHeapSnapshotId: function(id, callback)
760     {
761         if (!this.snapshot) {
762             this._objectIdToSelect = id;
763             return;
764         }
765
766         /**
767          * @param {?string} className
768          * @this {WebInspector.HeapSnapshotConstructorsDataGrid}
769          */
770         function didGetClassName(className)
771         {
772             if (!className) {
773                 callback(false);
774                 return;
775             }
776             var constructorNodes = this.topLevelNodes();
777             for (var i = 0; i < constructorNodes.length; i++) {
778                 var parent = constructorNodes[i];
779                 if (parent._name === className) {
780                     parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback);
781                     return;
782                 }
783             }
784         }
785         this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
786     },
787
788     clear: function()
789     {
790         this._nextRequestedFilter = null;
791         this._lastFilter = null;
792         this.removeTopLevelNodes();
793     },
794
795     setDataSource: function(snapshot)
796     {
797         this.snapshot = snapshot;
798         if (this._profileIndex === -1)
799             this._populateChildren();
800
801         if (this._objectIdToSelect) {
802             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
803             this._objectIdToSelect = null;
804         }
805     },
806
807     /**
808       * @param {number} minNodeId
809       * @param {number} maxNodeId
810       */
811     setSelectionRange: function(minNodeId, maxNodeId)
812     {
813         this._populateChildren(new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId));
814     },
815
816     /**
817       * @param {number} allocationNodeId
818       */
819     setAllocationNodeId: function(allocationNodeId)
820     {
821         var filter = new WebInspector.HeapSnapshotCommon.NodeFilter();
822         filter.allocationNodeId = allocationNodeId;
823         this._populateChildren(filter);
824     },
825
826     /**
827      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
828      * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates
829      */
830     _aggregatesReceived: function(nodeFilter, aggregates)
831     {
832         this._filterInProgress = null;
833         if (this._nextRequestedFilter) {
834             this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter));
835             this._filterInProgress = this._nextRequestedFilter;
836             this._nextRequestedFilter = null;
837         }
838         this.removeTopLevelNodes();
839         this.resetSortingCache();
840         for (var constructor in aggregates)
841             this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter));
842         this.sortingChanged();
843         this._lastFilter = nodeFilter;
844     },
845
846     /**
847       * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter
848       */
849     _populateChildren: function(nodeFilter)
850     {
851         nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter();
852
853         if (this._filterInProgress) {
854             this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter;
855             return;
856         }
857         if (this._lastFilter && this._lastFilter.equals(nodeFilter))
858             return;
859         this._filterInProgress = nodeFilter;
860         this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter));
861     },
862
863     filterSelectIndexChanged: function(profiles, profileIndex)
864     {
865         this._profileIndex = profileIndex;
866
867         var nodeFilter;
868         if (profileIndex !== -1) {
869             var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
870             var maxNodeId = profiles[profileIndex].maxJSObjectId;
871             nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId)
872         }
873
874         this._populateChildren(nodeFilter);
875     },
876
877     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
878 }
879
880
881 /**
882  * @constructor
883  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
884  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
885  */
886 WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate)
887 {
888     var columns = [
889         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
890         {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
891         {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
892         {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true},
893         {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
894         {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
895         {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true}
896     ];
897     WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
898 }
899
900 WebInspector.HeapSnapshotDiffDataGrid.prototype = {
901     /**
902      * @override
903      * @return {number}
904      */
905     defaultPopulateCount: function()
906     {
907         return 50;
908     },
909
910     _sortFields: function(sortColumn, sortAscending)
911     {
912         return {
913             object: ["_name", sortAscending, "_count", false],
914             addedCount: ["_addedCount", sortAscending, "_name", true],
915             removedCount: ["_removedCount", sortAscending, "_name", true],
916             countDelta: ["_countDelta", sortAscending, "_name", true],
917             addedSize: ["_addedSize", sortAscending, "_name", true],
918             removedSize: ["_removedSize", sortAscending, "_name", true],
919             sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
920         }[sortColumn];
921     },
922
923     setDataSource: function(snapshot)
924     {
925         this.snapshot = snapshot;
926     },
927
928     /**
929      * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot
930      */
931     setBaseDataSource: function(baseSnapshot)
932     {
933         this.baseSnapshot = baseSnapshot;
934         this.removeTopLevelNodes();
935         this.resetSortingCache();
936         if (this.baseSnapshot === this.snapshot) {
937             this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
938             return;
939         }
940         this._populateChildren();
941     },
942
943     _populateChildren: function()
944     {
945         /**
946          * @this {WebInspector.HeapSnapshotDiffDataGrid}
947          */
948         function aggregatesForDiffReceived(aggregatesForDiff)
949         {
950             this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
951
952             /**
953              * @this {WebInspector.HeapSnapshotDiffDataGrid}
954              */
955             function didCalculateSnapshotDiff(diffByClassName)
956             {
957                 for (var className in diffByClassName) {
958                     var diff = diffByClassName[className];
959                     this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotDiffNode(this, className, diff));
960                 }
961                 this.sortingChanged();
962             }
963         }
964         // Two snapshots live in different workers isolated from each other. That is why
965         // we first need to collect information about the nodes in the first snapshot and
966         // then pass it to the second snapshot to calclulate the diff.
967         this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
968     },
969
970     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
971 }
972
973
974 /**
975  * @constructor
976  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
977  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
978  */
979 WebInspector.HeapSnapshotDominatorsDataGrid = function(dataDisplayDelegate)
980 {
981     var columns = [
982         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
983         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
984         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
985     ];
986     WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
987     this._objectIdToSelect = null;
988 }
989
990 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
991     /**
992      * @override
993      * @return {number}
994      */
995     defaultPopulateCount: function()
996     {
997         return 25;
998     },
999
1000     setDataSource: function(snapshot)
1001     {
1002         this.snapshot = snapshot;
1003
1004         var fakeNode = new WebInspector.HeapSnapshotCommon.Node(-1, "", 0, this.snapshot.rootNodeIndex, 0, 0, "");
1005         this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode));
1006         this.rootNode().sort();
1007
1008         if (this._objectIdToSelect) {
1009             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
1010             this._objectIdToSelect = null;
1011         }
1012     },
1013
1014     sortingChanged: function()
1015     {
1016         this.rootNode().sort();
1017     },
1018
1019     /**
1020      * @override
1021      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
1022      * @param {function(boolean)} callback
1023      */
1024     highlightObjectByHeapSnapshotId: function(id, callback)
1025     {
1026         if (!this.snapshot) {
1027             this._objectIdToSelect = id;
1028             callback(false);
1029             return;
1030         }
1031
1032         /**
1033          * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
1034          */
1035         function didGetDominators(dominatorIds)
1036         {
1037             if (!dominatorIds) {
1038                 console.error("Cannot find corresponding heap snapshot node");
1039                 callback(false);
1040                 return;
1041             }
1042             var dominatorNode = this.rootNode();
1043             expandNextDominator.call(this, dominatorIds, dominatorNode);
1044         }
1045
1046         /**
1047          * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
1048          */
1049         function expandNextDominator(dominatorIds, dominatorNode)
1050         {
1051             if (!dominatorNode) {
1052                 console.error("Cannot find dominator node");
1053                 callback(false);
1054                 return;
1055             }
1056             if (!dominatorIds.length) {
1057                 this.highlightNode(dominatorNode);
1058                 dominatorNode.element.scrollIntoViewIfNeeded(true);
1059                 callback(true);
1060                 return;
1061             }
1062             var snapshotObjectId = dominatorIds.pop();
1063             dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds));
1064         }
1065
1066         this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this));
1067     },
1068
1069     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
1070 }
1071
1072
1073 /**
1074  * @constructor
1075  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
1076  * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1077  */
1078 WebInspector.AllocationDataGrid = function(dataDisplayDelegate)
1079 {
1080     var columns = [
1081         {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true},
1082         {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true},
1083         {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true},
1084         {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
1085         {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true},
1086     ];
1087     WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
1088     this._linkifier = new WebInspector.Linkifier();
1089 }
1090
1091 WebInspector.AllocationDataGrid.prototype = {
1092     dispose: function()
1093     {
1094         this._linkifier.reset();
1095     },
1096
1097     setDataSource: function(snapshot)
1098     {
1099         this.snapshot = snapshot;
1100         this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this));
1101
1102         /**
1103          * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops
1104          * @this {WebInspector.AllocationDataGrid}
1105          */
1106         function didReceiveAllocationTracesTops(tops)
1107         {
1108             this._topNodes = tops;
1109             this._populateChildren();
1110         }
1111     },
1112
1113     _populateChildren: function()
1114     {
1115         this.removeTopLevelNodes();
1116         var root = this.rootNode();
1117         var tops = this._topNodes;
1118         for (var i = 0; i < tops.length; i++)
1119             this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i]));
1120         this.updateVisibleNodes(true);
1121     },
1122
1123     sortingChanged: function()
1124     {
1125         this._topNodes.sort(this._createComparator());
1126         this.rootNode().removeChildren();
1127         this._populateChildren();
1128     },
1129
1130
1131     /**
1132      * @return {function(!Object, !Object):number}
1133      */
1134      _createComparator: function()
1135      {
1136         var fieldName = this.sortColumnIdentifier();
1137         var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1;
1138         /**
1139          * @param {!Object} a
1140          * @param {!Object} b
1141          * @return {number}
1142          */
1143         function compare(a, b)
1144         {
1145             if (a[fieldName] > b[fieldName])
1146                 return compareResult;
1147             if (a[fieldName] < b[fieldName])
1148                 return -compareResult;
1149             return 0;
1150         }
1151         return compare;
1152      },
1153
1154     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
1155 }