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 */
37WebInspector.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
62WebInspector.HeapSnapshotSortableDataGrid.Events = {
63    ContentShown: "ContentShown",
64    SortingComplete: "SortingComplete"
65}
66
67WebInspector.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 revealInSummaryView()
136        {
137            this._dataDisplayDelegate.showObject(node.snapshotNodeId, "Summary");
138        }
139
140        if (node instanceof WebInspector.HeapSnapshotRetainingObjectNode)
141            contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
142    },
143
144    resetSortingCache: function()
145    {
146        delete this._lastSortColumnIdentifier;
147        delete this._lastSortAscending;
148    },
149
150    /**
151     * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
152     */
153    topLevelNodes: function()
154    {
155        return this.rootNode().children;
156    },
157
158    /**
159     * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
160     * @param {function(boolean)} callback
161     */
162    highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback)
163    {
164    },
165
166    /**
167     * @param {!WebInspector.HeapSnapshotGridNode} node
168     */
169    highlightNode: function(node)
170    {
171        var prevNode = this._highlightedNode;
172        this._clearCurrentHighlight();
173        this._highlightedNode = node;
174        WebInspector.runCSSAnimationOnce(this._highlightedNode.element(), "highlighted-row");
175    },
176
177    nodeWasDetached: function(node)
178    {
179        if (this._highlightedNode === node)
180            this._clearCurrentHighlight();
181    },
182
183    _clearCurrentHighlight: function()
184    {
185        if (!this._highlightedNode)
186            return
187        this._highlightedNode.element().classList.remove("highlighted-row");
188        this._highlightedNode = null;
189    },
190
191    resetNameFilter: function()
192    {
193        this._nameFilter.setValue("");
194        this._onNameFilterChanged();
195    },
196
197    _onNameFilterChanged: function()
198    {
199        this.updateVisibleNodes(true);
200    },
201
202    sortingChanged: function()
203    {
204        var sortAscending = this.isSortOrderAscending();
205        var sortColumnIdentifier = this.sortColumnIdentifier();
206        if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
207            return;
208        this._lastSortColumnIdentifier = sortColumnIdentifier;
209        this._lastSortAscending = sortAscending;
210        var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
211
212        function SortByTwoFields(nodeA, nodeB)
213        {
214            var field1 = nodeA[sortFields[0]];
215            var field2 = nodeB[sortFields[0]];
216            var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
217            if (!sortFields[1])
218                result = -result;
219            if (result !== 0)
220                return result;
221            field1 = nodeA[sortFields[2]];
222            field2 = nodeB[sortFields[2]];
223            result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
224            if (!sortFields[3])
225                result = -result;
226            return result;
227        }
228        this._performSorting(SortByTwoFields);
229    },
230
231    _performSorting: function(sortFunction)
232    {
233        this.recursiveSortingEnter();
234        var children = this.allChildren(this.rootNode());
235        this.rootNode().removeChildren();
236        children.sort(sortFunction);
237        for (var i = 0, l = children.length; i < l; ++i) {
238            var child = children[i];
239            this.appendChildAfterSorting(child);
240            if (child.expanded)
241                child.sort();
242        }
243        this.recursiveSortingLeave();
244    },
245
246    appendChildAfterSorting: function(child)
247    {
248        var revealed = child.revealed;
249        this.rootNode().appendChild(child);
250        child.revealed = revealed;
251    },
252
253    recursiveSortingEnter: function()
254    {
255        ++this._recursiveSortingDepth;
256    },
257
258    recursiveSortingLeave: function()
259    {
260        if (!this._recursiveSortingDepth)
261            return;
262        if (--this._recursiveSortingDepth)
263            return;
264        this.updateVisibleNodes(true);
265        this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
266    },
267
268    /**
269     * @param {boolean} force
270     */
271    updateVisibleNodes: function(force)
272    {
273    },
274
275    /**
276     * @param {!WebInspector.DataGridNode} parent
277     * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
278     */
279    allChildren: function(parent)
280    {
281        return parent.children;
282    },
283
284    /**
285     * @param {!WebInspector.DataGridNode} parent
286     * @param {!WebInspector.DataGridNode} node
287     * @param {number} index
288     */
289    insertChild: function(parent, node, index)
290    {
291        parent.insertChild(node, index);
292    },
293
294    /**
295     * @param {!WebInspector.HeapSnapshotGridNode} parent
296     * @param {number} index
297     */
298    removeChildByIndex: function(parent, index)
299    {
300        parent.removeChild(parent.children[index]);
301    },
302
303    /**
304     * @param {!WebInspector.HeapSnapshotGridNode} parent
305     */
306    removeAllChildren: function(parent)
307    {
308        parent.removeChildren();
309    },
310
311    __proto__: WebInspector.DataGrid.prototype
312}
313
314
315/**
316 * @constructor
317 * @extends {WebInspector.HeapSnapshotSortableDataGrid}
318 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
319 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>} columns
320 */
321WebInspector.HeapSnapshotViewportDataGrid = function(dataDisplayDelegate, columns)
322{
323    WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
324    this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
325    /**
326     * @type {?WebInspector.HeapSnapshotGridNode}
327     */
328    this._nodeToHighlightAfterScroll = null;
329    this._topPaddingHeight = 0;
330    this._bottomPaddingHeight = 0;
331}
332
333WebInspector.HeapSnapshotViewportDataGrid.prototype = {
334    /**
335     * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
336     */
337    topLevelNodes: function()
338    {
339        return this.allChildren(this.rootNode());
340    },
341
342    appendChildAfterSorting: function(child)
343    {
344        // Do nothing here, it will be added in updateVisibleNodes.
345    },
346
347    /**
348     * @override
349     * @param {boolean} force
350     * @param {!Array.<!WebInspector.HeapSnapshotGridNode>=} pathToReveal
351     */
352    updateVisibleNodes: function(force, pathToReveal)
353    {
354        // Guard zone is used to ensure there are always some extra items
355        // above and below the viewport to support keyboard navigation.
356        var guardZoneHeight = 40;
357        var scrollHeight = this.scrollContainer.scrollHeight;
358        var scrollTop = this.scrollContainer.scrollTop;
359        var scrollBottom = scrollHeight - scrollTop - this.scrollContainer.offsetHeight;
360        scrollTop = Math.max(0, scrollTop - guardZoneHeight);
361        scrollBottom = Math.max(0, scrollBottom - guardZoneHeight);
362        var viewPortHeight = scrollHeight - scrollTop - scrollBottom;
363        if (!pathToReveal) {
364            // Do nothing if populated nodes still fit the viewport.
365            if (!force && scrollTop >= this._topPaddingHeight && scrollBottom >= this._bottomPaddingHeight)
366                return;
367            var hysteresisHeight = 500;
368            scrollTop -= hysteresisHeight;
369            viewPortHeight += 2 * hysteresisHeight;
370        }
371        var selectedNode = this.selectedNode;
372        this.rootNode().removeChildren();
373
374        this._topPaddingHeight = 0;
375        this._bottomPaddingHeight = 0;
376
377        this._addVisibleNodes(this.rootNode(), scrollTop, scrollTop + viewPortHeight, pathToReveal || null);
378
379        this.setVerticalPadding(this._topPaddingHeight, this._bottomPaddingHeight);
380
381        if (selectedNode) {
382            // Keep selection even if the node is not in the current viewport.
383            if (selectedNode.parent)
384                selectedNode.select(true);
385            else
386                this.selectedNode = selectedNode;
387        }
388    },
389
390    /**
391     * @param {!WebInspector.DataGridNode} parentNode
392     * @param {number} topBound
393     * @param {number} bottomBound
394     * @param {?Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
395     * @return {number}
396     */
397    _addVisibleNodes: function(parentNode, topBound, bottomBound, pathToReveal)
398    {
399        if (!parentNode.expanded)
400            return 0;
401
402        var nodeToReveal = pathToReveal ? pathToReveal[0] : null;
403        var restPathToReveal = pathToReveal && pathToReveal.length > 1 ? pathToReveal.slice(1) : null;
404        var children = this.allChildren(parentNode);
405        var topPadding = 0;
406        var nameFilterValue = this._nameFilter ? this._nameFilter.value().toLowerCase() : "";
407        // Iterate over invisible nodes beyond the upper bound of viewport.
408        // Do not insert them into the grid, but count their total height.
409        for (var i = 0; i < children.length; ++i) {
410            var child = children[i];
411            if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
412                continue;
413            var newTop = topPadding + this._nodeHeight(child);
414            if (nodeToReveal === child || (!nodeToReveal && newTop > topBound))
415                break;
416            topPadding = newTop;
417        }
418
419        // Put visible nodes into the data grid.
420        var position = topPadding;
421        for (; i < children.length && (nodeToReveal || position < bottomBound); ++i) {
422            var child = children[i];
423            if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
424                continue;
425            var hasChildren = child.hasChildren;
426            child.removeChildren();
427            child.hasChildren = hasChildren;
428            child.revealed = true;
429            parentNode.appendChild(child);
430            position += child.nodeSelfHeight();
431            position += this._addVisibleNodes(child, topBound - position, bottomBound - position, restPathToReveal);
432            if (nodeToReveal === child)
433                break;
434        }
435
436        // Count the invisible nodes beyond the bottom bound of the viewport.
437        var bottomPadding = 0;
438        for (; i < children.length; ++i) {
439            var child = children[i];
440            if (nameFilterValue && child.filteredOut && child.filteredOut(nameFilterValue))
441                continue;
442            bottomPadding += this._nodeHeight(child);
443        }
444
445        this._topPaddingHeight += topPadding;
446        this._bottomPaddingHeight += bottomPadding;
447        return position + bottomPadding;
448    },
449
450    /**
451     * @param {!WebInspector.HeapSnapshotGridNode} node
452     * @return {number}
453     */
454    _nodeHeight: function(node)
455    {
456        if (!node.revealed)
457            return 0;
458        var result = node.nodeSelfHeight();
459        if (!node.expanded)
460            return result;
461        var children = this.allChildren(node);
462        for (var i = 0; i < children.length; i++)
463            result += this._nodeHeight(children[i]);
464        return result;
465    },
466
467    /**
468     * @param {!Array.<!WebInspector.HeapSnapshotGridNode>} pathToReveal
469     */
470    revealTreeNode: function(pathToReveal)
471    {
472        this.updateVisibleNodes(true, pathToReveal);
473    },
474
475    /**
476     * @param {!WebInspector.DataGridNode} parent
477     * @return {!Array.<!WebInspector.HeapSnapshotGridNode>}
478     */
479    allChildren: function(parent)
480    {
481        return parent._allChildren || (parent._allChildren = []);
482    },
483
484    /**
485     * @param {!WebInspector.DataGridNode} parent
486     * @param {!WebInspector.DataGridNode} node
487     */
488    appendNode: function(parent, node)
489    {
490        this.allChildren(parent).push(node);
491    },
492
493    /**
494     * @param {!WebInspector.DataGridNode} parent
495     * @param {!WebInspector.DataGridNode} node
496     * @param {number} index
497     */
498    insertChild: function(parent, node, index)
499    {
500        this.allChildren(parent).splice(index, 0, node);
501    },
502
503    removeChildByIndex: function(parent, index)
504    {
505        this.allChildren(parent).splice(index, 1);
506    },
507
508    removeAllChildren: function(parent)
509    {
510        parent._allChildren = [];
511    },
512
513    removeTopLevelNodes: function()
514    {
515        this._disposeAllNodes();
516        this.rootNode().removeChildren();
517        this.rootNode()._allChildren = [];
518    },
519
520    /**
521     * @override
522     * @param {!WebInspector.HeapSnapshotGridNode} node
523     */
524    highlightNode: function(node)
525    {
526        if (this._isScrolledIntoView(node.element())) {
527            this.updateVisibleNodes(true);
528            WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
529        } else {
530            node.element().scrollIntoViewIfNeeded(true);
531            this._nodeToHighlightAfterScroll = node;
532        }
533    },
534
535    /**
536     * @param {!Element} element
537     * @return {boolean}
538     */
539    _isScrolledIntoView: function(element)
540    {
541        var viewportTop = this.scrollContainer.scrollTop;
542        var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
543        var elemTop = element.offsetTop
544        var elemBottom = elemTop + element.offsetHeight;
545        return elemBottom <= viewportBottom && elemTop >= viewportTop;
546    },
547
548    onResize: function()
549    {
550        WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
551        this.updateVisibleNodes(false);
552    },
553
554    _onScroll: function(event)
555    {
556        this.updateVisibleNodes(false);
557
558        if (this._nodeToHighlightAfterScroll) {
559            WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
560            this._nodeToHighlightAfterScroll = null;
561        }
562    },
563
564    __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
565}
566
567/**
568 * @constructor
569 * @extends {WebInspector.HeapSnapshotSortableDataGrid}
570 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
571 * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
572 */
573WebInspector.HeapSnapshotContainmentDataGrid = function(dataDisplayDelegate, columns)
574{
575    columns = columns || [
576        {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
577        {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true},
578        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
579        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
580    ];
581    WebInspector.HeapSnapshotSortableDataGrid.call(this, dataDisplayDelegate, columns);
582}
583
584WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
585    /**
586     * @param {!WebInspector.HeapSnapshotProxy} snapshot
587     * @param {number} nodeIndex
588     */
589    setDataSource: function(snapshot, nodeIndex)
590    {
591        this.snapshot = snapshot;
592        var node = { nodeIndex: nodeIndex || snapshot.rootNodeIndex };
593        var fakeEdge = { node: node };
594        this.setRootNode(this._createRootNode(snapshot, fakeEdge));
595        this.rootNode().sort();
596    },
597
598    _createRootNode: function(snapshot, fakeEdge)
599    {
600        return new WebInspector.HeapSnapshotObjectNode(this, snapshot, fakeEdge, null);
601    },
602
603    sortingChanged: function()
604    {
605        var rootNode = this.rootNode();
606        if (rootNode.hasChildren)
607            rootNode.sort();
608    },
609
610    __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
611}
612
613
614/**
615 * @constructor
616 * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
617 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
618 */
619WebInspector.HeapSnapshotRetainmentDataGrid = function(dataDisplayDelegate)
620{
621    var columns = [
622        {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
623        {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending},
624        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
625        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}
626    ];
627    WebInspector.HeapSnapshotContainmentDataGrid.call(this, dataDisplayDelegate, columns);
628}
629
630WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
631    ExpandRetainersComplete: "ExpandRetainersComplete"
632}
633
634WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
635    _createRootNode: function(snapshot, fakeEdge)
636    {
637        return new WebInspector.HeapSnapshotRetainingObjectNode(this, snapshot, fakeEdge, null);
638    },
639
640    _sortFields: function(sortColumn, sortAscending)
641    {
642        return {
643            object: ["_name", sortAscending, "_count", false],
644            count: ["_count", sortAscending, "_name", true],
645            shallowSize: ["_shallowSize", sortAscending, "_name", true],
646            retainedSize: ["_retainedSize", sortAscending, "_name", true],
647            distance: ["_distance", sortAscending, "_name", true]
648        }[sortColumn];
649    },
650
651    reset: function()
652    {
653        this.rootNode().removeChildren();
654        this.resetSortingCache();
655    },
656
657    /**
658     * @param {!WebInspector.HeapSnapshotProxy} snapshot
659     * @param {number} nodeIndex
660     */
661    setDataSource: function(snapshot, nodeIndex)
662    {
663        WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
664        this.rootNode().expand();
665    },
666
667    __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
668}
669
670/**
671 * @constructor
672 * @extends {WebInspector.HeapSnapshotViewportDataGrid}
673 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
674 */
675WebInspector.HeapSnapshotConstructorsDataGrid = function(dataDisplayDelegate)
676{
677    var columns = [
678        {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
679        {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
680        {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
681        {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
682        {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
683    ];
684    WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
685    this._profileIndex = -1;
686
687    this._objectIdToSelect = null;
688}
689
690WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
691    _sortFields: function(sortColumn, sortAscending)
692    {
693        return {
694            object: ["_name", sortAscending, "_count", false],
695            distance: ["_distance", sortAscending, "_retainedSize", true],
696            count: ["_count", sortAscending, "_name", true],
697            shallowSize: ["_shallowSize", sortAscending, "_name", true],
698            retainedSize: ["_retainedSize", sortAscending, "_name", true]
699        }[sortColumn];
700    },
701
702    /**
703     * @override
704     * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
705     * @param {function(boolean)} callback
706     */
707    highlightObjectByHeapSnapshotId: function(id, callback)
708    {
709        if (!this.snapshot) {
710            this._objectIdToSelect = id;
711            return;
712        }
713
714        /**
715         * @param {?string} className
716         * @this {WebInspector.HeapSnapshotConstructorsDataGrid}
717         */
718        function didGetClassName(className)
719        {
720            if (!className) {
721                callback(false);
722                return;
723            }
724            var constructorNodes = this.topLevelNodes();
725            for (var i = 0; i < constructorNodes.length; i++) {
726                var parent = constructorNodes[i];
727                if (parent._name === className) {
728                    parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback);
729                    return;
730                }
731            }
732        }
733        this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
734    },
735
736    clear: function()
737    {
738        this._nextRequestedFilter = null;
739        this._lastFilter = null;
740        this.removeTopLevelNodes();
741    },
742
743    setDataSource: function(snapshot)
744    {
745        this.snapshot = snapshot;
746        if (this._profileIndex === -1)
747            this._populateChildren();
748
749        if (this._objectIdToSelect) {
750            this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
751            this._objectIdToSelect = null;
752        }
753    },
754
755    /**
756      * @param {number} minNodeId
757      * @param {number} maxNodeId
758      */
759    setSelectionRange: function(minNodeId, maxNodeId)
760    {
761        this._populateChildren(new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId));
762    },
763
764    /**
765      * @param {number} allocationNodeId
766      */
767    setAllocationNodeId: function(allocationNodeId)
768    {
769        var filter = new WebInspector.HeapSnapshotCommon.NodeFilter();
770        filter.allocationNodeId = allocationNodeId;
771        this._populateChildren(filter);
772    },
773
774    /**
775     * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
776     * @param {!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>} aggregates
777     */
778    _aggregatesReceived: function(nodeFilter, aggregates)
779    {
780        this._filterInProgress = null;
781        if (this._nextRequestedFilter) {
782            this.snapshot.aggregatesWithFilter(this._nextRequestedFilter, this._aggregatesReceived.bind(this, this._nextRequestedFilter));
783            this._filterInProgress = this._nextRequestedFilter;
784            this._nextRequestedFilter = null;
785        }
786        this.removeTopLevelNodes();
787        this.resetSortingCache();
788        for (var constructor in aggregates)
789            this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], nodeFilter));
790        this.sortingChanged();
791        this._lastFilter = nodeFilter;
792    },
793
794    /**
795      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter=} nodeFilter
796      */
797    _populateChildren: function(nodeFilter)
798    {
799        nodeFilter = nodeFilter || new WebInspector.HeapSnapshotCommon.NodeFilter();
800
801        if (this._filterInProgress) {
802            this._nextRequestedFilter = this._filterInProgress.equals(nodeFilter) ? null : nodeFilter;
803            return;
804        }
805        if (this._lastFilter && this._lastFilter.equals(nodeFilter))
806            return;
807        this._filterInProgress = nodeFilter;
808        this.snapshot.aggregatesWithFilter(nodeFilter, this._aggregatesReceived.bind(this, nodeFilter));
809    },
810
811    filterSelectIndexChanged: function(profiles, profileIndex)
812    {
813        this._profileIndex = profileIndex;
814
815        var nodeFilter;
816        if (profileIndex !== -1) {
817            var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
818            var maxNodeId = profiles[profileIndex].maxJSObjectId;
819            nodeFilter = new WebInspector.HeapSnapshotCommon.NodeFilter(minNodeId, maxNodeId)
820        }
821
822        this._populateChildren(nodeFilter);
823    },
824
825    __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
826}
827
828
829/**
830 * @constructor
831 * @extends {WebInspector.HeapSnapshotViewportDataGrid}
832 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
833 */
834WebInspector.HeapSnapshotDiffDataGrid = function(dataDisplayDelegate)
835{
836    var columns = [
837        {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
838        {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
839        {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
840        {id: "countDelta", title: WebInspector.UIString("# Delta"), width: "64px", sortable: true},
841        {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
842        {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
843        {id: "sizeDelta", title: WebInspector.UIString("Size Delta"), width: "72px", sortable: true}
844    ];
845    WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
846}
847
848WebInspector.HeapSnapshotDiffDataGrid.prototype = {
849    /**
850     * @override
851     * @return {number}
852     */
853    defaultPopulateCount: function()
854    {
855        return 50;
856    },
857
858    _sortFields: function(sortColumn, sortAscending)
859    {
860        return {
861            object: ["_name", sortAscending, "_count", false],
862            addedCount: ["_addedCount", sortAscending, "_name", true],
863            removedCount: ["_removedCount", sortAscending, "_name", true],
864            countDelta: ["_countDelta", sortAscending, "_name", true],
865            addedSize: ["_addedSize", sortAscending, "_name", true],
866            removedSize: ["_removedSize", sortAscending, "_name", true],
867            sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
868        }[sortColumn];
869    },
870
871    setDataSource: function(snapshot)
872    {
873        this.snapshot = snapshot;
874    },
875
876    /**
877     * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot
878     */
879    setBaseDataSource: function(baseSnapshot)
880    {
881        this.baseSnapshot = baseSnapshot;
882        this.removeTopLevelNodes();
883        this.resetSortingCache();
884        if (this.baseSnapshot === this.snapshot) {
885            this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.SortingComplete);
886            return;
887        }
888        this._populateChildren();
889    },
890
891    _populateChildren: function()
892    {
893        /**
894         * @this {WebInspector.HeapSnapshotDiffDataGrid}
895         */
896        function aggregatesForDiffReceived(aggregatesForDiff)
897        {
898            this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
899
900            /**
901             * @this {WebInspector.HeapSnapshotDiffDataGrid}
902             */
903            function didCalculateSnapshotDiff(diffByClassName)
904            {
905                for (var className in diffByClassName) {
906                    var diff = diffByClassName[className];
907                    this.appendNode(this.rootNode(), new WebInspector.HeapSnapshotDiffNode(this, className, diff));
908                }
909                this.sortingChanged();
910            }
911        }
912        // Two snapshots live in different workers isolated from each other. That is why
913        // we first need to collect information about the nodes in the first snapshot and
914        // then pass it to the second snapshot to calclulate the diff.
915        this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
916    },
917
918    __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
919}
920
921
922/**
923 * @constructor
924 * @extends {WebInspector.HeapSnapshotViewportDataGrid}
925 * @param {?WebInspector.Target} target
926 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
927 */
928WebInspector.AllocationDataGrid = function(target, dataDisplayDelegate)
929{
930    var columns = [
931        {id: "liveCount", title: WebInspector.UIString("Live Count"), width: "72px", sortable: true},
932        {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true},
933        {id: "liveSize", title: WebInspector.UIString("Live Size"), width: "72px", sortable: true},
934        {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
935        {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true},
936    ];
937    WebInspector.HeapSnapshotViewportDataGrid.call(this, dataDisplayDelegate, columns);
938    this._target = target;
939    this._linkifier = new WebInspector.Linkifier();
940}
941
942WebInspector.AllocationDataGrid.prototype = {
943
944    /**
945     * @return {?WebInspector.Target}
946     */
947    target: function()
948    {
949        return this._target;
950    },
951
952    dispose: function()
953    {
954        this._linkifier.reset();
955    },
956
957    setDataSource: function(snapshot)
958    {
959        this.snapshot = snapshot;
960        this.snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this));
961
962        /**
963         * @param {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>} tops
964         * @this {WebInspector.AllocationDataGrid}
965         */
966        function didReceiveAllocationTracesTops(tops)
967        {
968            this._topNodes = tops;
969            this._populateChildren();
970        }
971    },
972
973    _populateChildren: function()
974    {
975        this.removeTopLevelNodes();
976        var root = this.rootNode();
977        var tops = this._topNodes;
978        for (var i = 0; i < tops.length; i++)
979            this.appendNode(root, new WebInspector.AllocationGridNode(this, tops[i]));
980        this.updateVisibleNodes(true);
981    },
982
983    sortingChanged: function()
984    {
985        this._topNodes.sort(this._createComparator());
986        this.rootNode().removeChildren();
987        this._populateChildren();
988    },
989
990
991    /**
992     * @return {function(!Object, !Object):number}
993     */
994     _createComparator: function()
995     {
996        var fieldName = this.sortColumnIdentifier();
997        var compareResult = (this.sortOrder() === WebInspector.DataGrid.Order.Ascending) ? +1 : -1;
998        /**
999         * @param {!Object} a
1000         * @param {!Object} b
1001         * @return {number}
1002         */
1003        function compare(a, b)
1004        {
1005            if (a[fieldName] > b[fieldName])
1006                return compareResult;
1007            if (a[fieldName] < b[fieldName])
1008                return -compareResult;
1009            return 0;
1010        }
1011        return compare;
1012     },
1013
1014    __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
1015}
1016