Home | History | Annotate | Download | only in front_end
      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  */
     35 WebInspector.HeapSnapshotSortableDataGrid = function(columns)
     36 {
     37     WebInspector.DataGrid.call(this, columns);
     38 
     39     /**
     40      * @type {number}
     41      */
     42     this._recursiveSortingDepth = 0;
     43     /**
     44      * @type {?WebInspector.HeapSnapshotGridNode}
     45      */
     46     this._highlightedNode = null;
     47     /**
     48      * @type {boolean}
     49      */
     50     this._populatedAndSorted = false;
     51     this.addEventListener("sorting complete", this._sortingComplete, this);
     52     this.addEventListener(WebInspector.DataGrid.Events.SortingChanged, this.sortingChanged, this);
     53 }
     54 
     55 WebInspector.HeapSnapshotSortableDataGrid.Events = {
     56     ContentShown: "ContentShown"
     57 }
     58 
     59 WebInspector.HeapSnapshotSortableDataGrid.prototype = {
     60     /**
     61      * @return {number}
     62      */
     63     defaultPopulateCount: function()
     64     {
     65         return 100;
     66     },
     67 
     68     dispose: function()
     69     {
     70         var children = this.topLevelNodes();
     71         for (var i = 0, l = children.length; i < l; ++i)
     72             children[i].dispose();
     73     },
     74 
     75     /**
     76      * @override
     77      */
     78     wasShown: function()
     79     {
     80         if (this._populatedAndSorted)
     81             this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
     82     },
     83 
     84     _sortingComplete: function()
     85     {
     86         this.removeEventListener("sorting complete", this._sortingComplete, this);
     87         this._populatedAndSorted = true;
     88         this.dispatchEventToListeners(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, this);
     89     },
     90 
     91     /**
     92      * @override
     93      */
     94     willHide: function()
     95     {
     96         this._clearCurrentHighlight();
     97     },
     98 
     99     /**
    100      * @param {!WebInspector.ProfilesPanel} profilesPanel
    101      * @param {!WebInspector.ContextMenu} contextMenu
    102      * @param {?Event} event
    103      */
    104     populateContextMenu: function(profilesPanel, contextMenu, event)
    105     {
    106         var td = event.target.enclosingNodeOrSelfWithNodeName("td");
    107         if (!td)
    108             return;
    109         var node = td.heapSnapshotNode;
    110         function revealInDominatorsView()
    111         {
    112                 profilesPanel.showObject(node.snapshotNodeId, "Dominators");
    113         }
    114         function revealInSummaryView()
    115         {
    116                 profilesPanel.showObject(node.snapshotNodeId, "Summary");
    117         }
    118         if(node && node.showRetainingEdges) {
    119             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
    120             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
    121         }
    122         else if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) {
    123             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
    124         } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) {
    125             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
    126         }
    127     },
    128 
    129     resetSortingCache: function()
    130     {
    131         delete this._lastSortColumnIdentifier;
    132         delete this._lastSortAscending;
    133     },
    134 
    135     topLevelNodes: function()
    136     {
    137         return this.rootNode().children;
    138     },
    139 
    140     /**
    141      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
    142      * @param {function(boolean)} callback
    143      */
    144     highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId, callback)
    145     {
    146     },
    147 
    148     /**
    149      * @param {!WebInspector.HeapSnapshotGridNode} node
    150      */
    151     highlightNode: function(node)
    152     {
    153         var prevNode = this._highlightedNode;
    154         this._clearCurrentHighlight();
    155         this._highlightedNode = node;
    156         this._highlightedNode.element.classList.add("highlighted-row");
    157         // If highlighted node hasn't changed reinsert it to make the highlight animation restart.
    158         if (node === prevNode) {
    159             var element = node.element;
    160             var parent = element.parentElement;
    161             var nextSibling = element.nextSibling;
    162             parent.removeChild(element);
    163             parent.insertBefore(element, nextSibling);
    164         }
    165     },
    166 
    167     nodeWasDetached: function(node)
    168     {
    169         if (this._highlightedNode === node)
    170             this._clearCurrentHighlight();
    171     },
    172 
    173     _clearCurrentHighlight: function()
    174     {
    175         if (!this._highlightedNode)
    176             return
    177         this._highlightedNode.element.classList.remove("highlighted-row");
    178         this._highlightedNode = null;
    179     },
    180 
    181     changeNameFilter: function(filter)
    182     {
    183         filter = filter.toLowerCase();
    184         var children = this.topLevelNodes();
    185         for (var i = 0, l = children.length; i < l; ++i) {
    186             var node = children[i];
    187             if (node.depth === 0)
    188                 node.revealed = node._name.toLowerCase().indexOf(filter) !== -1;
    189         }
    190         this.updateVisibleNodes();
    191     },
    192 
    193     sortingChanged: function()
    194     {
    195         var sortAscending = this.isSortOrderAscending();
    196         var sortColumnIdentifier = this.sortColumnIdentifier();
    197         if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
    198             return;
    199         this._lastSortColumnIdentifier = sortColumnIdentifier;
    200         this._lastSortAscending = sortAscending;
    201         var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
    202 
    203         function SortByTwoFields(nodeA, nodeB)
    204         {
    205             var field1 = nodeA[sortFields[0]];
    206             var field2 = nodeB[sortFields[0]];
    207             var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
    208             if (!sortFields[1])
    209                 result = -result;
    210             if (result !== 0)
    211                 return result;
    212             field1 = nodeA[sortFields[2]];
    213             field2 = nodeB[sortFields[2]];
    214             result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
    215             if (!sortFields[3])
    216                 result = -result;
    217             return result;
    218         }
    219         this._performSorting(SortByTwoFields);
    220     },
    221 
    222     _performSorting: function(sortFunction)
    223     {
    224         this.recursiveSortingEnter();
    225         var children = this._topLevelNodes;
    226         this.rootNode().removeChildren();
    227         children.sort(sortFunction);
    228         for (var i = 0, l = children.length; i < l; ++i) {
    229             var child = children[i];
    230             this.appendChildAfterSorting(child);
    231             if (child.expanded)
    232                 child.sort();
    233         }
    234         this.updateVisibleNodes();
    235         this.recursiveSortingLeave();
    236     },
    237 
    238     appendChildAfterSorting: function(child)
    239     {
    240         var revealed = child.revealed;
    241         this.rootNode().appendChild(child);
    242         child.revealed = revealed;
    243     },
    244 
    245     updateVisibleNodes: function()
    246     {
    247     },
    248 
    249     recursiveSortingEnter: function()
    250     {
    251         ++this._recursiveSortingDepth;
    252     },
    253 
    254     recursiveSortingLeave: function()
    255     {
    256         if (!this._recursiveSortingDepth)
    257             return;
    258         if (!--this._recursiveSortingDepth)
    259             this.dispatchEventToListeners("sorting complete");
    260     },
    261 
    262     __proto__: WebInspector.DataGrid.prototype
    263 }
    264 
    265 
    266 
    267 /**
    268  * @constructor
    269  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    270  */
    271 WebInspector.HeapSnapshotViewportDataGrid = function(columns)
    272 {
    273     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    274     this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
    275     this._topLevelNodes = [];
    276     this._topPadding = new WebInspector.HeapSnapshotPaddingNode();
    277     this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode();
    278     /**
    279      * @type {?WebInspector.HeapSnapshotGridNode}
    280      */
    281     this._nodeToHighlightAfterScroll = null;
    282 }
    283 
    284 WebInspector.HeapSnapshotViewportDataGrid.prototype = {
    285     topLevelNodes: function()
    286     {
    287         return this._topLevelNodes;
    288     },
    289 
    290     appendChildAfterSorting: function(child)
    291     {
    292         // Do nothing here, it will be added in updateVisibleNodes.
    293     },
    294 
    295     updateVisibleNodes: function()
    296     {
    297         var scrollTop = this.scrollContainer.scrollTop;
    298         var children = this._topLevelNodes;
    299 
    300         var i = 0;
    301         var topPadding = 0;
    302         while (i < children.length) {
    303             if (children[i].revealed) {
    304                 var newTop = topPadding + children[i].nodeHeight();
    305                 if (newTop > scrollTop)
    306                     break;
    307                 topPadding = newTop;
    308             }
    309             ++i;
    310         }
    311 
    312         this._addVisibleNodes(i, scrollTop - topPadding, topPadding);
    313     },
    314 
    315     _addVisibleNodes: function(firstVisibleNodeIndex, firstNodeHiddenHeight, topPadding)
    316     {
    317         var viewPortHeight = this.scrollContainer.offsetHeight;
    318         this._removePaddingRows();
    319 
    320         var children = this._topLevelNodes;
    321         var selectedNode = this.selectedNode;
    322 
    323         this.rootNode().removeChildren();
    324         // The height of the view port + invisible top part.
    325         var heightToFill = viewPortHeight + firstNodeHiddenHeight;
    326         var filledHeight = 0;
    327         var i = firstVisibleNodeIndex;
    328         while (i < children.length && filledHeight < heightToFill) {
    329             if (children[i].revealed) {
    330                 this.rootNode().appendChild(children[i]);
    331                 filledHeight += children[i].nodeHeight();
    332             }
    333             ++i;
    334         }
    335 
    336         var bottomPadding = 0;
    337         while (i < children.length) {
    338             bottomPadding += children[i].nodeHeight();
    339             ++i;
    340         }
    341 
    342         this._addPaddingRows(topPadding, bottomPadding);
    343 
    344         if (selectedNode) {
    345             if (selectedNode.parent) {
    346                 selectedNode.select(true);
    347             } else {
    348                 // Keep selection even if the node is not in the current viewport.
    349                 this.selectedNode = selectedNode;
    350             }
    351         }
    352     },
    353 
    354     _revealTopLevelNode: function(nodeToReveal)
    355     {
    356         var children = this._topLevelNodes;
    357 
    358         var i = 0;
    359         var topPadding = 0;
    360         while (i < children.length) {
    361             if (children[i] === nodeToReveal)
    362                 break;
    363             if (children[i].revealed) {
    364                 var newTop = topPadding + children[i].nodeHeight();
    365                 topPadding = newTop;
    366             }
    367             ++i;
    368         }
    369 
    370         this._addVisibleNodes(i, 0, topPadding);
    371     },
    372 
    373     appendTopLevelNode: function(node)
    374     {
    375         this._topLevelNodes.push(node);
    376     },
    377 
    378     removeTopLevelNodes: function()
    379     {
    380         this.rootNode().removeChildren();
    381         this._topLevelNodes = [];
    382     },
    383 
    384     /**
    385      * @override
    386      * @param {!WebInspector.HeapSnapshotGridNode} node
    387      */
    388     highlightNode: function(node)
    389     {
    390         if (this._isScrolledIntoView(node.element))
    391             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
    392         else {
    393             node.element.scrollIntoViewIfNeeded(true);
    394             this._nodeToHighlightAfterScroll = node;
    395         }
    396     },
    397 
    398     _isScrolledIntoView: function(element)
    399     {
    400         var viewportTop = this.scrollContainer.scrollTop;
    401         var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
    402         var elemTop = element.offsetTop
    403         var elemBottom = elemTop + element.offsetHeight;
    404         return elemBottom <= viewportBottom && elemTop >= viewportTop;
    405     },
    406 
    407     _addPaddingRows: function(top, bottom)
    408     {
    409         if (this._topPadding.element.parentNode !== this.dataTableBody)
    410             this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild);
    411         if (this._bottomPadding.element.parentNode !== this.dataTableBody)
    412             this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild);
    413         this._topPadding.setHeight(top);
    414         this._bottomPadding.setHeight(bottom);
    415     },
    416 
    417     _removePaddingRows: function()
    418     {
    419         this._bottomPadding.removeFromTable();
    420         this._topPadding.removeFromTable();
    421     },
    422 
    423     onResize: function()
    424     {
    425         WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
    426         this.updateVisibleNodes();
    427     },
    428 
    429     _onScroll: function(event)
    430     {
    431         this.updateVisibleNodes();
    432 
    433         if (this._nodeToHighlightAfterScroll) {
    434             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
    435             this._nodeToHighlightAfterScroll = null;
    436         }
    437     },
    438 
    439     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    440 }
    441 
    442 /**
    443  * @constructor
    444  */
    445 WebInspector.HeapSnapshotPaddingNode = function()
    446 {
    447     this.element = document.createElement("tr");
    448     this.element.classList.add("revealed");
    449 }
    450 
    451 WebInspector.HeapSnapshotPaddingNode.prototype = {
    452    setHeight: function(height)
    453    {
    454        this.element.style.height = height + "px";
    455    },
    456    removeFromTable: function()
    457    {
    458         var parent = this.element.parentNode;
    459         if (parent)
    460             parent.removeChild(this.element);
    461    }
    462 }
    463 
    464 
    465 /**
    466  * @constructor
    467  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    468  * @param {!Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
    469  */
    470 WebInspector.HeapSnapshotContainmentDataGrid = function(columns)
    471 {
    472     columns = columns || [
    473         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    474         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true},
    475         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    476         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
    477     ];
    478     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    479 }
    480 
    481 WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
    482     setDataSource: function(snapshot, nodeIndex)
    483     {
    484         this.snapshot = snapshot;
    485         var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex);
    486         var fakeEdge = { node: node };
    487         this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null));
    488         this.rootNode().sort();
    489     },
    490 
    491     sortingChanged: function()
    492     {
    493         this.rootNode().sort();
    494     },
    495 
    496     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    497 }
    498 
    499 
    500 /**
    501  * @constructor
    502  * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
    503  */
    504 WebInspector.HeapSnapshotRetainmentDataGrid = function()
    505 {
    506     this.showRetainingEdges = true;
    507     var columns = [
    508         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    509         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending},
    510         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    511         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}
    512     ];
    513     WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns);
    514 }
    515 
    516 WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
    517     ExpandRetainersComplete: "ExpandRetainersComplete"
    518 }
    519 
    520 WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
    521     _sortFields: function(sortColumn, sortAscending)
    522     {
    523         return {
    524             object: ["_name", sortAscending, "_count", false],
    525             count: ["_count", sortAscending, "_name", true],
    526             shallowSize: ["_shallowSize", sortAscending, "_name", true],
    527             retainedSize: ["_retainedSize", sortAscending, "_name", true],
    528             distance: ["_distance", sortAscending, "_name", true]
    529         }[sortColumn];
    530     },
    531 
    532     reset: function()
    533     {
    534         this.rootNode().removeChildren();
    535         this.resetSortingCache();
    536     },
    537 
    538     /**
    539      * @param {!WebInspector.HeapSnapshotProxy} snapshot
    540      * @param {number} nodeIndex
    541      */
    542     setDataSource: function(snapshot, nodeIndex)
    543     {
    544         WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
    545 
    546         var dataGrid = this;
    547         var maxExpandLevels = 20;
    548         /**
    549          * @this {!WebInspector.HeapSnapshotObjectNode}
    550          */
    551         function populateComplete()
    552         {
    553             this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
    554             this.expand();
    555             if (--maxExpandLevels > 0 && this.children.length > 0) {
    556                 var retainer = this.children[0];
    557                 if (retainer._distance > 1) {
    558                     retainer.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, retainer);
    559                     retainer.populate();
    560                     return;
    561                 }
    562             }
    563             dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
    564         }
    565         this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode());
    566     },
    567 
    568     __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
    569 }
    570 
    571 /**
    572  * @constructor
    573  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
    574  */
    575 WebInspector.HeapSnapshotConstructorsDataGrid = function()
    576 {
    577     var columns = [
    578         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
    579         {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
    580         {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
    581         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    582         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
    583     ];
    584     WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
    585     this._profileIndex = -1;
    586     this._topLevelNodes = [];
    587 
    588     this._objectIdToSelect = null;
    589 }
    590 
    591 /**
    592  * @constructor
    593  * @param {number=} minNodeId
    594  * @param {number=} maxNodeId
    595  */
    596 WebInspector.HeapSnapshotConstructorsDataGrid.Request = function(minNodeId, maxNodeId)
    597 {
    598     if (typeof minNodeId === "number") {
    599         this.key = minNodeId + ".." + maxNodeId;
    600         this.filter = "function(node) { var id = node.id(); return id > " + minNodeId + " && id <= " + maxNodeId + "; }";
    601     } else {
    602         this.key = "allObjects";
    603         this.filter = null;
    604     }
    605 }
    606 
    607 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
    608     _sortFields: function(sortColumn, sortAscending)
    609     {
    610         return {
    611             object: ["_name", sortAscending, "_count", false],
    612             distance: ["_distance", sortAscending, "_retainedSize", true],
    613             count: ["_count", sortAscending, "_name", true],
    614             shallowSize: ["_shallowSize", sortAscending, "_name", true],
    615             retainedSize: ["_retainedSize", sortAscending, "_name", true]
    616         }[sortColumn];
    617     },
    618 
    619     /**
    620      * @override
    621      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
    622      * @param {function(boolean)} callback
    623      */
    624     highlightObjectByHeapSnapshotId: function(id, callback)
    625     {
    626         if (!this.snapshot) {
    627             this._objectIdToSelect = id;
    628             return;
    629         }
    630 
    631         /**
    632          * @param {?string} className
    633          * @this {WebInspector.HeapSnapshotConstructorsDataGrid}
    634          */
    635         function didGetClassName(className)
    636         {
    637             if (!className) {
    638                 callback(false);
    639                 return;
    640             }
    641             var constructorNodes = this.topLevelNodes();
    642             for (var i = 0; i < constructorNodes.length; i++) {
    643                 var parent = constructorNodes[i];
    644                 if (parent._name === className) {
    645                     if (!parent.dataGrid) {
    646                         // Make sure Constructor node is within the view port and added
    647                         // to the data grid
    648                         this._revealTopLevelNode(parent);
    649                     }
    650                     parent.revealNodeBySnapshotObjectId(parseInt(id, 10), callback);
    651                     return;
    652                 }
    653             }
    654         }
    655         this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
    656     },
    657 
    658     setDataSource: function(snapshot)
    659     {
    660         this.snapshot = snapshot;
    661         if (this._profileIndex === -1)
    662             this._populateChildren();
    663 
    664         if (this._objectIdToSelect) {
    665             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
    666             this._objectIdToSelect = null;
    667         }
    668     },
    669 
    670     /**
    671       * @param {number} minNodeId
    672       * @param {number} maxNodeId
    673       */
    674     setSelectionRange: function(minNodeId, maxNodeId)
    675     {
    676         this._populateChildren(new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId));
    677     },
    678 
    679     _aggregatesReceived: function(key, aggregates)
    680     {
    681         this._requestInProgress = null;
    682         if (this._nextRequest) {
    683             this.snapshot.aggregates(false, this._nextRequest.key, this._nextRequest.filter, this._aggregatesReceived.bind(this, this._nextRequest.key));
    684             this._requestInProgress = this._nextRequest;
    685             this._nextRequest = null;
    686         }
    687         this.dispose();
    688         this.removeTopLevelNodes();
    689         this.resetSortingCache();
    690         for (var constructor in aggregates)
    691             this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key));
    692         this.sortingChanged();
    693         this._lastKey = key;
    694     },
    695 
    696     /**
    697       * @param {?WebInspector.HeapSnapshotConstructorsDataGrid.Request=} request
    698       */
    699     _populateChildren: function(request)
    700     {
    701         request = request || new WebInspector.HeapSnapshotConstructorsDataGrid.Request();
    702 
    703         if (this._requestInProgress) {
    704             this._nextRequest = this._requestInProgress.key === request.key ? null : request;
    705             return;
    706         }
    707         if (this._lastKey === request.key)
    708             return;
    709         this._requestInProgress = request;
    710         this.snapshot.aggregates(false, request.key, request.filter, this._aggregatesReceived.bind(this, request.key));
    711     },
    712 
    713     filterSelectIndexChanged: function(profiles, profileIndex)
    714     {
    715         this._profileIndex = profileIndex;
    716 
    717         var request = null;
    718         if (profileIndex !== -1) {
    719             var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
    720             var maxNodeId = profiles[profileIndex].maxJSObjectId;
    721             request = new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId)
    722         }
    723 
    724         this._populateChildren(request);
    725     },
    726 
    727     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
    728 }
    729 
    730 
    731 /**
    732  * @constructor
    733  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
    734  */
    735 WebInspector.HeapSnapshotDiffDataGrid = function()
    736 {
    737     var columns = [
    738         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
    739         {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
    740         {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
    741         {id: "countDelta", title: "# Delta", width: "64px", sortable: true},
    742         {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
    743         {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
    744         {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true}
    745     ];
    746     WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
    747 }
    748 
    749 WebInspector.HeapSnapshotDiffDataGrid.prototype = {
    750     /**
    751      * @override
    752      * @return {number}
    753      */
    754     defaultPopulateCount: function()
    755     {
    756         return 50;
    757     },
    758 
    759     _sortFields: function(sortColumn, sortAscending)
    760     {
    761         return {
    762             object: ["_name", sortAscending, "_count", false],
    763             addedCount: ["_addedCount", sortAscending, "_name", true],
    764             removedCount: ["_removedCount", sortAscending, "_name", true],
    765             countDelta: ["_countDelta", sortAscending, "_name", true],
    766             addedSize: ["_addedSize", sortAscending, "_name", true],
    767             removedSize: ["_removedSize", sortAscending, "_name", true],
    768             sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
    769         }[sortColumn];
    770     },
    771 
    772     setDataSource: function(snapshot)
    773     {
    774         this.snapshot = snapshot;
    775     },
    776 
    777     /**
    778      * @param {!WebInspector.HeapSnapshotProxy} baseSnapshot
    779      */
    780     setBaseDataSource: function(baseSnapshot)
    781     {
    782         this.baseSnapshot = baseSnapshot;
    783         this.dispose();
    784         this.removeTopLevelNodes();
    785         this.resetSortingCache();
    786         if (this.baseSnapshot === this.snapshot) {
    787             this.dispatchEventToListeners("sorting complete");
    788             return;
    789         }
    790         this._populateChildren();
    791     },
    792 
    793     _populateChildren: function()
    794     {
    795         /**
    796          * @this {WebInspector.HeapSnapshotDiffDataGrid}
    797          */
    798         function aggregatesForDiffReceived(aggregatesForDiff)
    799         {
    800             this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
    801 
    802             /**
    803              * @this {WebInspector.HeapSnapshotDiffDataGrid}
    804              */
    805             function didCalculateSnapshotDiff(diffByClassName)
    806             {
    807                 for (var className in diffByClassName) {
    808                     var diff = diffByClassName[className];
    809                     this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff));
    810                 }
    811                 this.sortingChanged();
    812             }
    813         }
    814         // Two snapshots live in different workers isolated from each other. That is why
    815         // we first need to collect information about the nodes in the first snapshot and
    816         // then pass it to the second snapshot to calclulate the diff.
    817         this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
    818     },
    819 
    820     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
    821 }
    822 
    823 
    824 /**
    825  * @constructor
    826  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    827  */
    828 WebInspector.HeapSnapshotDominatorsDataGrid = function()
    829 {
    830     var columns = [
    831         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    832         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    833         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
    834     ];
    835     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    836     this._objectIdToSelect = null;
    837 }
    838 
    839 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
    840     /**
    841      * @override
    842      * @return {number}
    843      */
    844     defaultPopulateCount: function()
    845     {
    846         return 25;
    847     },
    848 
    849     setDataSource: function(snapshot)
    850     {
    851         this.snapshot = snapshot;
    852 
    853         var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex };
    854         this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode));
    855         this.rootNode().sort();
    856 
    857         if (this._objectIdToSelect) {
    858             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect, function(found) {});
    859             this._objectIdToSelect = null;
    860         }
    861     },
    862 
    863     sortingChanged: function()
    864     {
    865         this.rootNode().sort();
    866     },
    867 
    868     /**
    869      * @override
    870      * @param {!HeapProfilerAgent.HeapSnapshotObjectId} id
    871      * @param {function(boolean)} callback
    872      */
    873     highlightObjectByHeapSnapshotId: function(id, callback)
    874     {
    875         if (!this.snapshot) {
    876             this._objectIdToSelect = id;
    877             callback(false);
    878             return;
    879         }
    880 
    881         /**
    882          * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
    883          */
    884         function didGetDominators(dominatorIds)
    885         {
    886             if (!dominatorIds) {
    887                 WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node"));
    888                 callback(false);
    889                 return;
    890             }
    891             var dominatorNode = this.rootNode();
    892             expandNextDominator.call(this, dominatorIds, dominatorNode);
    893         }
    894 
    895         /**
    896          * @this {WebInspector.HeapSnapshotDominatorsDataGrid}
    897          */
    898         function expandNextDominator(dominatorIds, dominatorNode)
    899         {
    900             if (!dominatorNode) {
    901                 console.error("Cannot find dominator node");
    902                 callback(false);
    903                 return;
    904             }
    905             if (!dominatorIds.length) {
    906                 this.highlightNode(dominatorNode);
    907                 dominatorNode.element.scrollIntoViewIfNeeded(true);
    908                 callback(true);
    909                 return;
    910             }
    911             var snapshotObjectId = dominatorIds.pop();
    912             dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds));
    913         }
    914 
    915         this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this));
    916     },
    917 
    918     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    919 }
    920 
    921 
    922 /**
    923  * @constructor
    924  * @extends {WebInspector.DataGrid}
    925  */
    926 WebInspector.AllocationDataGrid = function()
    927 {
    928     var columns = [
    929         {id: "count", title: WebInspector.UIString("Count"), width: "72px", sortable: true},
    930         {id: "size", title: WebInspector.UIString("Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
    931         {id: "name", title: WebInspector.UIString("Function"), disclosure: true, sortable: true},
    932     ];
    933     WebInspector.DataGrid.call(this, columns);
    934     this._linkifier = new WebInspector.Linkifier();
    935 }
    936 
    937 WebInspector.AllocationDataGrid.prototype = {
    938     setDataSource: function(snapshot)
    939     {
    940         this._snapshot = snapshot;
    941         this._snapshot.allocationTracesTops(didReceiveAllocationTracesTops.bind(this));
    942 
    943         /**
    944          * @param {!Array.<!WebInspector.DataGrid>} tops
    945          * @this {WebInspector.AllocationDataGrid}
    946          */
    947         function didReceiveAllocationTracesTops(tops)
    948         {
    949             var root = this.rootNode();
    950             for (var i = 0; i < tops.length; i++)
    951                 root.appendChild(new WebInspector.AllocationGridNode(this, tops[i]));
    952         }
    953     },
    954 
    955     __proto__: WebInspector.DataGrid.prototype
    956 }
    957 
    958 
    959 /**
    960  * @constructor
    961  * @extends {WebInspector.DataGridNode}
    962  * @param {!WebInspector.DataGrid} dataGrid
    963  */
    964 WebInspector.AllocationGridNode = function(dataGrid, data)
    965 {
    966     WebInspector.DataGridNode.call(this, data, data.hasChildren);
    967     this._dataGrid = dataGrid;
    968     this._populated = false;
    969 }
    970 
    971 WebInspector.AllocationGridNode.prototype = {
    972     populate: function()
    973     {
    974         if (this._populated)
    975             return;
    976         this._populated = true;
    977         this._dataGrid._snapshot.allocationNodeCallers(this.data.id, didReceiveCallers.bind(this));
    978 
    979         /**
    980          * @this {WebInspector.AllocationGridNode}
    981          */
    982         function didReceiveCallers(callers)
    983         {
    984             var callersChain = callers.nodesWithSingleCaller;
    985             var parentNode = this;
    986             for (var i = 0; i < callersChain.length; i++) {
    987                 var child = new WebInspector.AllocationGridNode(this._dataGrid, callersChain[i]);
    988                 parentNode.appendChild(child);
    989                 parentNode = child;
    990                 parentNode._populated = true;
    991                 if (this.expanded)
    992                     parentNode.expand();
    993             }
    994 
    995             var callersBranch = callers.branchingCallers;
    996             for (var i = 0; i < callersBranch.length; i++)
    997                 parentNode.appendChild(new WebInspector.AllocationGridNode(this._dataGrid, callersBranch[i]));
    998         }
    999     },
   1000 
   1001     /**
   1002      * @override
   1003      */
   1004     expand: function()
   1005     {
   1006         WebInspector.DataGridNode.prototype.expand.call(this);
   1007         if (this.children.length === 1)
   1008             this.children[0].expand();
   1009     },
   1010 
   1011     /**
   1012      * @override
   1013      * @param {string} columnIdentifier
   1014      * @return {!Element}
   1015      */
   1016     createCell: function(columnIdentifier)
   1017     {
   1018         var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
   1019 
   1020         if (columnIdentifier !== "name")
   1021             return cell;
   1022 
   1023         var functionInfo = this.data;
   1024         if (functionInfo.scriptName) {
   1025             var urlElement = this._dataGrid._linkifier.linkifyLocation(functionInfo.scriptName, functionInfo.line - 1, functionInfo.column - 1, "profile-node-file");
   1026             urlElement.style.maxWidth = "75%";
   1027             cell.insertBefore(urlElement, cell.firstChild);
   1028         }
   1029 
   1030         return cell;
   1031     },
   1032 
   1033     __proto__: WebInspector.DataGridNode.prototype
   1034 }
   1035 
   1036