• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 */
335WebInspector.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
351WebInspector.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 */
598WebInspector.HeapSnapshotPaddingNode = function()
599{
600    this.element = document.createElement("tr");
601    this.element.classList.add("revealed");
602    this.setHeight(0);
603}
604
605WebInspector.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 */
625WebInspector.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
636WebInspector.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 */
671WebInspector.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
682WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
683    ExpandRetainersComplete: "ExpandRetainersComplete"
684}
685
686WebInspector.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 */
727WebInspector.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
742WebInspector.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 */
886WebInspector.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
900WebInspector.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 */
979WebInspector.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
990WebInspector.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 */
1078WebInspector.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
1091WebInspector.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}
1156