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 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  */
    321 WebInspector.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 
    333 WebInspector.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  */
    573 WebInspector.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 
    584 WebInspector.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  */
    619 WebInspector.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 
    630 WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
    631     ExpandRetainersComplete: "ExpandRetainersComplete"
    632 }
    633 
    634 WebInspector.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  */
    675 WebInspector.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 
    690 WebInspector.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  */
    834 WebInspector.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 
    848 WebInspector.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  */
    928 WebInspector.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 
    942 WebInspector.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