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         if (node instanceof WebInspector.HeapSnapshotInstanceNode || node instanceof WebInspector.HeapSnapshotObjectNode) {
    111             function revealInDominatorsView()
    112             {
    113                 profilesPanel.showObject(node.snapshotNodeId, "Dominators");
    114             }
    115             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Dominators view" : "Reveal in Dominators View"), revealInDominatorsView.bind(this));
    116         } else if (node instanceof WebInspector.HeapSnapshotDominatorObjectNode) {
    117             function revealInSummaryView()
    118             {
    119                 profilesPanel.showObject(node.snapshotNodeId, "Summary");
    120             }
    121             contextMenu.appendItem(WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "Reveal in Summary view" : "Reveal in Summary View"), revealInSummaryView.bind(this));
    122         }
    123     },
    124 
    125     resetSortingCache: function()
    126     {
    127         delete this._lastSortColumnIdentifier;
    128         delete this._lastSortAscending;
    129     },
    130 
    131     topLevelNodes: function()
    132     {
    133         return this.rootNode().children;
    134     },
    135 
    136     /**
    137      * @param {HeapProfilerAgent.HeapSnapshotObjectId} heapSnapshotObjectId
    138      */
    139     highlightObjectByHeapSnapshotId: function(heapSnapshotObjectId)
    140     {
    141     },
    142 
    143     /**
    144      * @param {WebInspector.HeapSnapshotGridNode} node
    145      */
    146     highlightNode: function(node)
    147     {
    148         var prevNode = this._highlightedNode;
    149         this._clearCurrentHighlight();
    150         this._highlightedNode = node;
    151         this._highlightedNode.element.addStyleClass("highlighted-row");
    152         // If highlighted node hasn't changed reinsert it to make the highlight animation restart.
    153         if (node === prevNode) {
    154             var element = node.element;
    155             var parent = element.parentElement;
    156             var nextSibling = element.nextSibling;
    157             parent.removeChild(element);
    158             parent.insertBefore(element, nextSibling);
    159         }
    160     },
    161 
    162     nodeWasDetached: function(node)
    163     {
    164         if (this._highlightedNode === node)
    165             this._clearCurrentHighlight();
    166     },
    167 
    168     _clearCurrentHighlight: function()
    169     {
    170         if (!this._highlightedNode)
    171             return
    172         this._highlightedNode.element.removeStyleClass("highlighted-row");
    173         this._highlightedNode = null;
    174     },
    175 
    176     changeNameFilter: function(filter)
    177     {
    178         filter = filter.toLowerCase();
    179         var children = this.topLevelNodes();
    180         for (var i = 0, l = children.length; i < l; ++i) {
    181             var node = children[i];
    182             if (node.depth === 0)
    183                 node.revealed = node._name.toLowerCase().indexOf(filter) !== -1;
    184         }
    185         this.updateVisibleNodes();
    186     },
    187 
    188     sortingChanged: function()
    189     {
    190         var sortAscending = this.isSortOrderAscending();
    191         var sortColumnIdentifier = this.sortColumnIdentifier();
    192         if (this._lastSortColumnIdentifier === sortColumnIdentifier && this._lastSortAscending === sortAscending)
    193             return;
    194         this._lastSortColumnIdentifier = sortColumnIdentifier;
    195         this._lastSortAscending = sortAscending;
    196         var sortFields = this._sortFields(sortColumnIdentifier, sortAscending);
    197 
    198         function SortByTwoFields(nodeA, nodeB)
    199         {
    200             var field1 = nodeA[sortFields[0]];
    201             var field2 = nodeB[sortFields[0]];
    202             var result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
    203             if (!sortFields[1])
    204                 result = -result;
    205             if (result !== 0)
    206                 return result;
    207             field1 = nodeA[sortFields[2]];
    208             field2 = nodeB[sortFields[2]];
    209             result = field1 < field2 ? -1 : (field1 > field2 ? 1 : 0);
    210             if (!sortFields[3])
    211                 result = -result;
    212             return result;
    213         }
    214         this._performSorting(SortByTwoFields);
    215     },
    216 
    217     _performSorting: function(sortFunction)
    218     {
    219         this.recursiveSortingEnter();
    220         var children = this._topLevelNodes;
    221         this.rootNode().removeChildren();
    222         children.sort(sortFunction);
    223         for (var i = 0, l = children.length; i < l; ++i) {
    224             var child = children[i];
    225             this.appendChildAfterSorting(child);
    226             if (child.expanded)
    227                 child.sort();
    228         }
    229         this.updateVisibleNodes();
    230         this.recursiveSortingLeave();
    231     },
    232 
    233     appendChildAfterSorting: function(child)
    234     {
    235         var revealed = child.revealed;
    236         this.rootNode().appendChild(child);
    237         child.revealed = revealed;
    238     },
    239 
    240     updateVisibleNodes: function()
    241     {
    242     },
    243 
    244     recursiveSortingEnter: function()
    245     {
    246         ++this._recursiveSortingDepth;
    247     },
    248 
    249     recursiveSortingLeave: function()
    250     {
    251         if (!this._recursiveSortingDepth)
    252             return;
    253         if (!--this._recursiveSortingDepth)
    254             this.dispatchEventToListeners("sorting complete");
    255     },
    256 
    257     __proto__: WebInspector.DataGrid.prototype
    258 }
    259 
    260 
    261 
    262 /**
    263  * @constructor
    264  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    265  */
    266 WebInspector.HeapSnapshotViewportDataGrid = function(columns)
    267 {
    268     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    269     this.scrollContainer.addEventListener("scroll", this._onScroll.bind(this), true);
    270     this._topLevelNodes = [];
    271     this._topPadding = new WebInspector.HeapSnapshotPaddingNode();
    272     this._bottomPadding = new WebInspector.HeapSnapshotPaddingNode();
    273     /**
    274      * @type {WebInspector.HeapSnapshotGridNode}
    275      */
    276     this._nodeToHighlightAfterScroll = null;
    277 }
    278 
    279 WebInspector.HeapSnapshotViewportDataGrid.prototype = {
    280     topLevelNodes: function()
    281     {
    282         return this._topLevelNodes;
    283     },
    284 
    285     appendChildAfterSorting: function(child)
    286     {
    287         // Do nothing here, it will be added in updateVisibleNodes.
    288     },
    289 
    290     updateVisibleNodes: function()
    291     {
    292         var scrollTop = this.scrollContainer.scrollTop;
    293 
    294         var viewPortHeight = this.scrollContainer.offsetHeight;
    295 
    296         this._removePaddingRows();
    297 
    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         var selectedNode = this.selectedNode;
    313 
    314         this.rootNode().removeChildren();
    315         // The height of the view port + invisible top part.
    316         var heightToFill = viewPortHeight + (scrollTop - topPadding);
    317         var filledHeight = 0;
    318         while (i < children.length && filledHeight < heightToFill) {
    319             if (children[i].revealed) {
    320                 this.rootNode().appendChild(children[i]);
    321                 filledHeight += children[i].nodeHeight();
    322             }
    323             ++i;
    324         }
    325 
    326         var bottomPadding = 0;
    327         while (i < children.length) {
    328             bottomPadding += children[i].nodeHeight();
    329             ++i;
    330         }
    331 
    332         this._addPaddingRows(topPadding, bottomPadding);
    333 
    334         if (selectedNode) {
    335             if (selectedNode.parent) {
    336                 selectedNode.select(true);
    337             } else {
    338                 // Keep selection even if the node is not in the current viewport.
    339                 this.selectedNode = selectedNode;
    340             }
    341         }
    342     },
    343 
    344     appendTopLevelNode: function(node)
    345     {
    346         this._topLevelNodes.push(node);
    347     },
    348 
    349     removeTopLevelNodes: function()
    350     {
    351         this.rootNode().removeChildren();
    352         this._topLevelNodes = [];
    353     },
    354 
    355     /**
    356      * @override
    357      * @param {WebInspector.HeapSnapshotGridNode} node
    358      */
    359     highlightNode: function(node)
    360     {
    361         if (this._isScrolledIntoView(node.element))
    362             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, node);
    363         else {
    364             node.element.scrollIntoViewIfNeeded(true);
    365             this._nodeToHighlightAfterScroll = node;
    366         }
    367     },
    368 
    369     _isScrolledIntoView: function(element)
    370     {
    371         var viewportTop = this.scrollContainer.scrollTop;
    372         var viewportBottom = viewportTop + this.scrollContainer.clientHeight;
    373         var elemTop = element.offsetTop
    374         var elemBottom = elemTop + element.offsetHeight;
    375         return elemBottom <= viewportBottom && elemTop >= viewportTop;
    376     },
    377 
    378     _addPaddingRows: function(top, bottom)
    379     {
    380         if (this._topPadding.element.parentNode !== this.dataTableBody)
    381             this.dataTableBody.insertBefore(this._topPadding.element, this.dataTableBody.firstChild);
    382         if (this._bottomPadding.element.parentNode !== this.dataTableBody)
    383             this.dataTableBody.insertBefore(this._bottomPadding.element, this.dataTableBody.lastChild);
    384         this._topPadding.setHeight(top);
    385         this._bottomPadding.setHeight(bottom);
    386     },
    387 
    388     _removePaddingRows: function()
    389     {
    390         this._bottomPadding.removeFromTable();
    391         this._topPadding.removeFromTable();
    392     },
    393 
    394     onResize: function()
    395     {
    396         WebInspector.HeapSnapshotSortableDataGrid.prototype.onResize.call(this);
    397         this.updateVisibleNodes();
    398     },
    399 
    400     _onScroll: function(event)
    401     {
    402         this.updateVisibleNodes();
    403 
    404         if (this._nodeToHighlightAfterScroll) {
    405             WebInspector.HeapSnapshotSortableDataGrid.prototype.highlightNode.call(this, this._nodeToHighlightAfterScroll);
    406             this._nodeToHighlightAfterScroll = null;
    407         }
    408     },
    409 
    410     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    411 }
    412 
    413 /**
    414  * @constructor
    415  */
    416 WebInspector.HeapSnapshotPaddingNode = function()
    417 {
    418     this.element = document.createElement("tr");
    419     this.element.addStyleClass("revealed");
    420 }
    421 
    422 WebInspector.HeapSnapshotPaddingNode.prototype = {
    423    setHeight: function(height)
    424    {
    425        this.element.style.height = height + "px";
    426    },
    427    removeFromTable: function()
    428    {
    429         var parent = this.element.parentNode;
    430         if (parent)
    431             parent.removeChild(this.element);
    432    }
    433 }
    434 
    435 
    436 /**
    437  * @constructor
    438  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    439  * @param {Array.<!WebInspector.DataGrid.ColumnDescriptor>=} columns
    440  */
    441 WebInspector.HeapSnapshotContainmentDataGrid = function(columns)
    442 {
    443     columns = columns || [
    444         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    445         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true},
    446         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    447         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true, sort: WebInspector.DataGrid.Order.Descending}
    448     ];
    449     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    450 }
    451 
    452 WebInspector.HeapSnapshotContainmentDataGrid.prototype = {
    453     setDataSource: function(snapshot, nodeIndex)
    454     {
    455         this.snapshot = snapshot;
    456         var node = new WebInspector.HeapSnapshotNode(snapshot, nodeIndex || snapshot.rootNodeIndex);
    457         var fakeEdge = { node: node };
    458         this.setRootNode(new WebInspector.HeapSnapshotObjectNode(this, false, fakeEdge, null));
    459         this.rootNode().sort();
    460     },
    461 
    462     sortingChanged: function()
    463     {
    464         this.rootNode().sort();
    465     },
    466 
    467     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    468 }
    469 
    470 
    471 /**
    472  * @constructor
    473  * @extends {WebInspector.HeapSnapshotContainmentDataGrid}
    474  */
    475 WebInspector.HeapSnapshotRetainmentDataGrid = function()
    476 {
    477     this.showRetainingEdges = true;
    478     var columns = [
    479         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    480         {id: "distance", title: WebInspector.UIString("Distance"), width: "80px", sortable: true, sort: WebInspector.DataGrid.Order.Ascending},
    481         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    482         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sortable: true}
    483     ];
    484     WebInspector.HeapSnapshotContainmentDataGrid.call(this, columns);
    485 }
    486 
    487 WebInspector.HeapSnapshotRetainmentDataGrid.Events = {
    488     ExpandRetainersComplete: "ExpandRetainersComplete"
    489 }
    490 
    491 WebInspector.HeapSnapshotRetainmentDataGrid.prototype = {
    492     _sortFields: function(sortColumn, sortAscending)
    493     {
    494         return {
    495             object: ["_name", sortAscending, "_count", false],
    496             count: ["_count", sortAscending, "_name", true],
    497             shallowSize: ["_shallowSize", sortAscending, "_name", true],
    498             retainedSize: ["_retainedSize", sortAscending, "_name", true],
    499             distance: ["_distance", sortAscending, "_name", true]
    500         }[sortColumn];
    501     },
    502 
    503     reset: function()
    504     {
    505         this.rootNode().removeChildren();
    506         this.resetSortingCache();
    507     },
    508 
    509     /**
    510      * @param {!WebInspector.HeapSnapshotProxy} snapshot
    511      * @param {number} nodeIndex
    512      */
    513     setDataSource: function(snapshot, nodeIndex)
    514     {
    515         WebInspector.HeapSnapshotContainmentDataGrid.prototype.setDataSource.call(this, snapshot, nodeIndex);
    516 
    517         var dataGrid = this;
    518         var maxExpandLevels = 20;
    519         /**
    520          * @this {!WebInspector.HeapSnapshotObjectNode}
    521          */
    522         function populateComplete()
    523         {
    524             this.removeEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this);
    525             this.expand();
    526             if (--maxExpandLevels > 0 && this.children.length > 0) {
    527                 var retainer = this.children[0];
    528                 if (retainer._distance > 1) {
    529                     retainer.addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, retainer);
    530                     retainer.populate();
    531                     return;
    532                 }
    533             }
    534             dataGrid.dispatchEventToListeners(WebInspector.HeapSnapshotRetainmentDataGrid.Events.ExpandRetainersComplete);
    535         }
    536         this.rootNode().addEventListener(WebInspector.HeapSnapshotGridNode.Events.PopulateComplete, populateComplete, this.rootNode());
    537     },
    538 
    539     __proto__: WebInspector.HeapSnapshotContainmentDataGrid.prototype
    540 }
    541 
    542 /**
    543  * @constructor
    544  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
    545  */
    546 WebInspector.HeapSnapshotConstructorsDataGrid = function()
    547 {
    548     var columns = [
    549         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
    550         {id: "distance", title: WebInspector.UIString("Distance"), width: "90px", sortable: true},
    551         {id: "count", title: WebInspector.UIString("Objects Count"), width: "90px", sortable: true},
    552         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    553         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
    554     ];
    555     WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
    556     this._profileIndex = -1;
    557     this._topLevelNodes = [];
    558 
    559     this._objectIdToSelect = null;
    560 }
    561 
    562 /**
    563  * @constructor
    564  * @param {number=} minNodeId
    565  * @param {number=} maxNodeId
    566  */
    567 WebInspector.HeapSnapshotConstructorsDataGrid.Request = function(minNodeId, maxNodeId)
    568 {
    569     if (typeof minNodeId === "number") {
    570         this.key = minNodeId + ".." + maxNodeId;
    571         this.filter = "function(node) { var id = node.id(); return id > " + minNodeId + " && id <= " + maxNodeId + "; }";
    572     } else {
    573         this.key = "allObjects";
    574         this.filter = null;
    575     }
    576 }
    577 
    578 WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
    579     _sortFields: function(sortColumn, sortAscending)
    580     {
    581         return {
    582             object: ["_name", sortAscending, "_count", false],
    583             distance: ["_distance", sortAscending, "_retainedSize", true],
    584             count: ["_count", sortAscending, "_name", true],
    585             shallowSize: ["_shallowSize", sortAscending, "_name", true],
    586             retainedSize: ["_retainedSize", sortAscending, "_name", true]
    587         }[sortColumn];
    588     },
    589 
    590     /**
    591      * @override
    592      * @param {HeapProfilerAgent.HeapSnapshotObjectId} id
    593      */
    594     highlightObjectByHeapSnapshotId: function(id)
    595     {
    596         if (!this.snapshot) {
    597             this._objectIdToSelect = id;
    598             return;
    599         }
    600 
    601         function didGetClassName(className)
    602         {
    603             var constructorNodes = this.topLevelNodes();
    604             for (var i = 0; i < constructorNodes.length; i++) {
    605                 var parent = constructorNodes[i];
    606                 if (parent._name === className) {
    607                     parent.revealNodeBySnapshotObjectId(parseInt(id, 10));
    608                     return;
    609                 }
    610             }
    611         }
    612         this.snapshot.nodeClassName(parseInt(id, 10), didGetClassName.bind(this));
    613     },
    614 
    615     setDataSource: function(snapshot)
    616     {
    617         this.snapshot = snapshot;
    618         if (this._profileIndex === -1)
    619             this._populateChildren();
    620 
    621         if (this._objectIdToSelect) {
    622             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect);
    623             this._objectIdToSelect = null;
    624         }
    625     },
    626 
    627     /**
    628       * @param {number} minNodeId
    629       * @param {number} maxNodeId
    630       */
    631     setSelectionRange: function(minNodeId, maxNodeId)
    632     {
    633         this._populateChildren(new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId));
    634     },
    635 
    636     _aggregatesReceived: function(key, aggregates)
    637     {
    638         this._requestInProgress = null;
    639         if (this._nextRequest) {
    640             this.snapshot.aggregates(false, this._nextRequest.key, this._nextRequest.filter, this._aggregatesReceived.bind(this, this._nextRequest.key));
    641             this._requestInProgress = this._nextRequest;
    642             this._nextRequest = null;
    643         }
    644         this.dispose();
    645         this.removeTopLevelNodes();
    646         this.resetSortingCache();
    647         for (var constructor in aggregates)
    648             this.appendTopLevelNode(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key));
    649         this.sortingChanged();
    650         this._lastKey = key;
    651     },
    652 
    653     /**
    654       * @param {WebInspector.HeapSnapshotConstructorsDataGrid.Request=} request
    655       */
    656     _populateChildren: function(request)
    657     {
    658         request = request || new WebInspector.HeapSnapshotConstructorsDataGrid.Request();
    659 
    660         if (this._requestInProgress) {
    661             this._nextRequest = this._requestInProgress.key === request.key ? null : request;
    662             return;
    663         }
    664         if (this._lastKey === request.key)
    665             return;
    666         this._requestInProgress = request;
    667         this.snapshot.aggregates(false, request.key, request.filter, this._aggregatesReceived.bind(this, request.key));
    668     },
    669 
    670     filterSelectIndexChanged: function(profiles, profileIndex)
    671     {
    672         this._profileIndex = profileIndex;
    673 
    674         var request = null;
    675         if (profileIndex !== -1) {
    676             var minNodeId = profileIndex > 0 ? profiles[profileIndex - 1].maxJSObjectId : 0;
    677             var maxNodeId = profiles[profileIndex].maxJSObjectId;
    678             request = new WebInspector.HeapSnapshotConstructorsDataGrid.Request(minNodeId, maxNodeId)
    679         }
    680 
    681         this._populateChildren(request);
    682     },
    683 
    684     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
    685 }
    686 
    687 
    688 /**
    689  * @constructor
    690  * @extends {WebInspector.HeapSnapshotViewportDataGrid}
    691  */
    692 WebInspector.HeapSnapshotDiffDataGrid = function()
    693 {
    694     var columns = [
    695         {id: "object", title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true},
    696         {id: "addedCount", title: WebInspector.UIString("# New"), width: "72px", sortable: true},
    697         {id: "removedCount", title: WebInspector.UIString("# Deleted"), width: "72px", sortable: true},
    698         {id: "countDelta", title: "# Delta", width: "64px", sortable: true},
    699         {id: "addedSize", title: WebInspector.UIString("Alloc. Size"), width: "72px", sortable: true, sort: WebInspector.DataGrid.Order.Descending},
    700         {id: "removedSize", title: WebInspector.UIString("Freed Size"), width: "72px", sortable: true},
    701         {id: "sizeDelta", title: "Size Delta", width: "72px", sortable: true}
    702     ];
    703     WebInspector.HeapSnapshotViewportDataGrid.call(this, columns);
    704 }
    705 
    706 WebInspector.HeapSnapshotDiffDataGrid.prototype = {
    707     /**
    708      * @override
    709      * @return {number}
    710      */
    711     defaultPopulateCount: function()
    712     {
    713         return 50;
    714     },
    715 
    716     _sortFields: function(sortColumn, sortAscending)
    717     {
    718         return {
    719             object: ["_name", sortAscending, "_count", false],
    720             addedCount: ["_addedCount", sortAscending, "_name", true],
    721             removedCount: ["_removedCount", sortAscending, "_name", true],
    722             countDelta: ["_countDelta", sortAscending, "_name", true],
    723             addedSize: ["_addedSize", sortAscending, "_name", true],
    724             removedSize: ["_removedSize", sortAscending, "_name", true],
    725             sizeDelta: ["_sizeDelta", sortAscending, "_name", true]
    726         }[sortColumn];
    727     },
    728 
    729     setDataSource: function(snapshot)
    730     {
    731         this.snapshot = snapshot;
    732     },
    733 
    734     /**
    735      * @param {WebInspector.HeapSnapshotProxy} baseSnapshot
    736      */
    737     setBaseDataSource: function(baseSnapshot)
    738     {
    739         this.baseSnapshot = baseSnapshot;
    740         this.dispose();
    741         this.removeTopLevelNodes();
    742         this.resetSortingCache();
    743         if (this.baseSnapshot === this.snapshot) {
    744             this.dispatchEventToListeners("sorting complete");
    745             return;
    746         }
    747         this._populateChildren();
    748     },
    749 
    750     _populateChildren: function()
    751     {
    752         function aggregatesForDiffReceived(aggregatesForDiff)
    753         {
    754             this.snapshot.calculateSnapshotDiff(this.baseSnapshot.uid, aggregatesForDiff, didCalculateSnapshotDiff.bind(this));
    755             function didCalculateSnapshotDiff(diffByClassName)
    756             {
    757                 for (var className in diffByClassName) {
    758                     var diff = diffByClassName[className];
    759                     this.appendTopLevelNode(new WebInspector.HeapSnapshotDiffNode(this, className, diff));
    760                 }
    761                 this.sortingChanged();
    762             }
    763         }
    764         // Two snapshots live in different workers isolated from each other. That is why
    765         // we first need to collect information about the nodes in the first snapshot and
    766         // then pass it to the second snapshot to calclulate the diff.
    767         this.baseSnapshot.aggregatesForDiff(aggregatesForDiffReceived.bind(this));
    768     },
    769 
    770     __proto__: WebInspector.HeapSnapshotViewportDataGrid.prototype
    771 }
    772 
    773 
    774 /**
    775  * @constructor
    776  * @extends {WebInspector.HeapSnapshotSortableDataGrid}
    777  */
    778 WebInspector.HeapSnapshotDominatorsDataGrid = function()
    779 {
    780     var columns = [
    781         {id: "object", title: WebInspector.UIString("Object"), disclosure: true, sortable: true},
    782         {id: "shallowSize", title: WebInspector.UIString("Shallow Size"), width: "120px", sortable: true},
    783         {id: "retainedSize", title: WebInspector.UIString("Retained Size"), width: "120px", sort: WebInspector.DataGrid.Order.Descending, sortable: true}
    784     ];
    785     WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
    786     this._objectIdToSelect = null;
    787 }
    788 
    789 WebInspector.HeapSnapshotDominatorsDataGrid.prototype = {
    790     /**
    791      * @override
    792      * @return {number}
    793      */
    794     defaultPopulateCount: function()
    795     {
    796         return 25;
    797     },
    798 
    799     setDataSource: function(snapshot)
    800     {
    801         this.snapshot = snapshot;
    802 
    803         var fakeNode = { nodeIndex: this.snapshot.rootNodeIndex };
    804         this.setRootNode(new WebInspector.HeapSnapshotDominatorObjectNode(this, fakeNode));
    805         this.rootNode().sort();
    806 
    807         if (this._objectIdToSelect) {
    808             this.highlightObjectByHeapSnapshotId(this._objectIdToSelect);
    809             this._objectIdToSelect = null;
    810         }
    811     },
    812 
    813     sortingChanged: function()
    814     {
    815         this.rootNode().sort();
    816     },
    817 
    818     /**
    819      * @override
    820      * @param {HeapProfilerAgent.HeapSnapshotObjectId} id
    821      */
    822     highlightObjectByHeapSnapshotId: function(id)
    823     {
    824         if (!this.snapshot) {
    825             this._objectIdToSelect = id;
    826             return;
    827         }
    828 
    829         function didGetDominators(dominatorIds)
    830         {
    831             if (!dominatorIds) {
    832                 WebInspector.log(WebInspector.UIString("Cannot find corresponding heap snapshot node"));
    833                 return;
    834             }
    835             var dominatorNode = this.rootNode();
    836             expandNextDominator.call(this, dominatorIds, dominatorNode);
    837         }
    838 
    839         function expandNextDominator(dominatorIds, dominatorNode)
    840         {
    841             if (!dominatorNode) {
    842                 console.error("Cannot find dominator node");
    843                 return;
    844             }
    845             if (!dominatorIds.length) {
    846                 this.highlightNode(dominatorNode);
    847                 dominatorNode.element.scrollIntoViewIfNeeded(true);
    848                 return;
    849             }
    850             var snapshotObjectId = dominatorIds.pop();
    851             dominatorNode.retrieveChildBySnapshotObjectId(snapshotObjectId, expandNextDominator.bind(this, dominatorIds));
    852         }
    853 
    854         this.snapshot.dominatorIdsForNode(parseInt(id, 10), didGetDominators.bind(this));
    855     },
    856 
    857     __proto__: WebInspector.HeapSnapshotSortableDataGrid.prototype
    858 }
    859 
    860