Home | History | Annotate | Download | only in profiler
      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 }
   1156